From 9f87d254a16f9529a299f63b7fd342d0ddde00d3 Mon Sep 17 00:00:00 2001 From: liuyan3 Date: Sat, 26 Nov 2022 16:48:09 +0800 Subject: [PATCH] =?UTF-8?q?cla,BFPL=E5=A3=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "BFPL\345\243\271/.circleci/config.yml" | 169 + "BFPL\345\243\271/.clang-format" | 94 + "BFPL\345\243\271/.clang-tidy" | 240 ++ "BFPL\345\243\271/.gitignore" | 73 + "BFPL\345\243\271/.gitmodules" | 3 + "BFPL\345\243\271/CMakeLists.txt" | 105 + "BFPL\345\243\271/ChangeLog.md" | 279 ++ "BFPL\345\243\271/LICENSE" | 201 ++ "BFPL\345\243\271/README.md" | 74 + .../bcos-boostssl/CMakeLists.txt" | 21 + .../bcos-boostssl/CMakeLists.txt" | 7 + .../bcos-boostssl/context/Common.h" | 54 + .../bcos-boostssl/context/ContextBuilder.cpp" | 298 ++ .../bcos-boostssl/context/ContextBuilder.h" | 65 + .../bcos-boostssl/context/ContextConfig.cpp" | 128 + .../bcos-boostssl/context/ContextConfig.h" | 90 + .../bcos-boostssl/context/NodeInfoTools.cpp" | 179 + .../bcos-boostssl/context/NodeInfoTools.h" | 52 + .../bcos-boostssl/httpserver/Common.h" | 54 + .../bcos-boostssl/httpserver/HttpQueue.h" | 79 + .../bcos-boostssl/httpserver/HttpServer.cpp" | 242 ++ .../bcos-boostssl/httpserver/HttpServer.h" | 139 + .../bcos-boostssl/httpserver/HttpSession.h" | 301 ++ .../bcos-boostssl/httpserver/HttpStream.h" | 270 ++ .../bcos-boostssl/interfaces/MessageFace.h" | 71 + .../bcos-boostssl/interfaces/NodeInfoDef.h" | 88 + .../bcos-boostssl/websocket/Common.h" | 67 + .../bcos-boostssl/websocket/WsConfig.h" | 155 + .../bcos-boostssl/websocket/WsConnector.cpp" | 162 + .../bcos-boostssl/websocket/WsConnector.h" | 108 + .../bcos-boostssl/websocket/WsError.h" | 53 + .../websocket/WsInitializer.cpp" | 189 ++ .../bcos-boostssl/websocket/WsInitializer.h" | 64 + .../bcos-boostssl/websocket/WsMessage.cpp" | 108 + .../bcos-boostssl/websocket/WsMessage.h" | 150 + .../bcos-boostssl/websocket/WsService.cpp" | 731 +++++ .../bcos-boostssl/websocket/WsService.h" | 252 ++ .../bcos-boostssl/websocket/WsSession.cpp" | 476 +++ .../bcos-boostssl/websocket/WsSession.h" | 249 ++ .../bcos-boostssl/websocket/WsStream.h" | 344 ++ .../bcos-boostssl/websocket/WsTools.cpp" | 87 + .../bcos-boostssl/websocket/WsTools.h" | 63 + .../bcos-boostssl/test/CMakeLists.txt" | 26 + .../bcos-boostssl/test/exec/CMakeLists.txt" | 25 + .../test/exec/boostssl_delay_perf.cpp" | 248 ++ .../test/exec/boostssl_throughput_perf.cpp" | 294 ++ .../test/exec/echo_client_sample.cpp" | 150 + .../test/exec/echo_server_sample.cpp" | 132 + .../test/exec/http_server_sample.cpp" | 106 + .../test/exec/msg_codec_perf.cpp" | 96 + .../unittests/websocket/WsConfigTest.cpp" | 91 + .../unittests/websocket/WsConnectorTest.cpp" | 54 + .../unittests/websocket/WsMessageTest.cpp" | 127 + "BFPL\345\243\271/bcos-codec/CMakeLists.txt" | 54 + .../bcos-codec/abi/ContractABICodec.cpp" | 194 ++ .../bcos-codec/abi/ContractABICodec.h" | 744 +++++ .../bcos-codec/abi/ContractABIType.cpp" | 280 ++ .../bcos-codec/abi/ContractABIType.h" | 96 + .../bcos-codec/bcos-codec/scale/Common.h" | 74 + .../bcos-codec/bcos-codec/scale/Exceptions.h" | 31 + .../scale/FixedWidthIntegerCodec.h" | 132 + .../bcos-codec/bcos-codec/scale/Scale.h" | 78 + .../bcos-codec/scale/ScaleDecoderStream.cpp" | 162 + .../bcos-codec/scale/ScaleDecoderStream.h" | 431 +++ .../bcos-codec/scale/ScaleEncoderStream.cpp" | 156 + .../bcos-codec/scale/ScaleEncoderStream.h" | 358 ++ .../bcos-codec/wrapper/CodecWrapper.h" | 125 + .../bcos-codec/test/CMakeLists.txt" | 28 + .../test/unittests/ContractABICodecTest.cpp" | 1111 +++++++ .../test/unittests/ScaleCodecTest.cpp" | 1149 +++++++ .../bcos-codec/test/unittests/main/main.cpp" | 24 + "BFPL\345\243\271/bcos-crypto/CMakeLists.txt" | 24 + .../bcos-crypto/ChecksumAddress.h" | 115 + .../bcos-crypto/bcos-crypto/TrivialObject.h" | 99 + .../bcos-crypto/digestsign/DigestSign.h" | 19 + .../digestsign/OpenSSLDigestSign.h" | 101 + .../bcos-crypto/encrypt/AESCrypto.cpp" | 76 + .../bcos-crypto/encrypt/AESCrypto.h" | 67 + .../bcos-crypto/encrypt/Exceptions.h" | 31 + .../bcos-crypto/encrypt/SM4Crypto.cpp" | 73 + .../bcos-crypto/encrypt/SM4Crypto.h" | 66 + .../bcos-crypto/bcos-crypto/hash/Keccak256.h" | 55 + .../bcos-crypto/bcos-crypto/hash/SM3.h" | 52 + .../bcos-crypto/bcos-crypto/hash/Sha256.h" | 51 + .../bcos-crypto/bcos-crypto/hash/Sha3.h" | 52 + .../bcos-crypto/hasher/AnyHasher.h" | 12 + .../bcos-crypto/bcos-crypto/hasher/Hasher.h" | 26 + .../bcos-crypto/hasher/IPPCryptoHasher.h" | 98 + .../bcos-crypto/hasher/OpenSSLHasher.h" | 150 + .../interfaces/crypto/CommonType.h" | 32 + .../interfaces/crypto/CryptoSuite.h" | 71 + .../bcos-crypto/interfaces/crypto/Hash.h" | 78 + .../interfaces/crypto/KeyFactory.h" | 38 + .../interfaces/crypto/KeyInterface.h" | 66 + .../interfaces/crypto/KeyPairFactory.h" | 37 + .../interfaces/crypto/KeyPairInterface.h" | 50 + .../interfaces/crypto/Signature.h" | 60 + .../interfaces/crypto/SymmetricEncryption.h" | 56 + .../bcos-crypto/bcos-crypto/merkle/Merkle.h" | 231 ++ .../bcos-crypto/signature/Exceptions.h" | 36 + .../signature/codec/SignatureData.h" | 70 + .../signature/codec/SignatureDataWithPub.h" | 70 + .../signature/codec/SignatureDataWithV.h" | 67 + .../signature/ed25519/Ed25519Crypto.cpp" | 139 + .../signature/ed25519/Ed25519Crypto.h" | 73 + .../signature/ed25519/Ed25519KeyPair.cpp" | 41 + .../signature/ed25519/Ed25519KeyPair.h" | 47 + .../signature/fastsm2/FastSM2Crypto.h" | 48 + .../signature/fastsm2/FastSM2KeyPair.h" | 41 + .../fastsm2/FastSM2KeyPairFactory.h" | 53 + .../signature/fastsm2/fast_sm2.cpp" | 307 ++ .../bcos-crypto/signature/fastsm2/fast_sm2.h" | 43 + .../signature/key/KeyFactoryImpl.h" | 47 + .../bcos-crypto/signature/key/KeyImpl.h" | 79 + .../bcos-crypto/signature/key/KeyPair.h" | 67 + .../signature/secp256k1/Secp256k1Crypto.cpp" | 136 + .../signature/secp256k1/Secp256k1Crypto.h" | 70 + .../signature/secp256k1/Secp256k1KeyPair.cpp" | 38 + .../signature/secp256k1/Secp256k1KeyPair.h" | 49 + .../bcos-crypto/signature/sm2.cpp" | 68 + .../bcos-crypto/bcos-crypto/signature/sm2.h" | 39 + .../bcos-crypto/signature/sm2/SM2Crypto.cpp" | 127 + .../bcos-crypto/signature/sm2/SM2Crypto.h" | 69 + .../bcos-crypto/signature/sm2/SM2KeyPair.cpp" | 51 + .../bcos-crypto/signature/sm2/SM2KeyPair.h" | 49 + .../signature/sm2/SM2KeyPairFactory.h" | 62 + .../bcos-crypto/bcos-crypto/zkp/Common.h" | 41 + .../bcos-crypto/bcos-crypto/zkp/Exceptions.h" | 31 + .../zkp/discretezkp/DiscreteLogarithmZkp.cpp" | 161 + .../zkp/discretezkp/DiscreteLogarithmZkp.h" | 69 + .../bcos-crypto/demo/CMakeLists.txt" | 15 + .../bcos-crypto/demo/hasher_test.cpp" | 107 + .../bcos-crypto/demo/perf_demo.cpp" | 369 +++ .../bcos-crypto/test/CMakeLists.txt" | 29 + .../test/unittests/EncryptionTest.cpp" | 106 + .../bcos-crypto/test/unittests/HashTest.cpp" | 126 + .../test/unittests/HasherTest.cpp" | 129 + .../test/unittests/SignatureTest.cpp" | 392 +++ .../bcos-crypto/test/unittests/ZkpTest.cpp" | 221 ++ .../bcos-crypto/test/unittests/main.cpp" | 23 + .../test/unittests/testMerkle.cpp" | 143 + .../bcos-executor/CMakeLists.txt" | 37 + .../bcos-executor/src/CallParameters.h" | 125 + .../bcos-executor/src/Common.cpp" | 91 + "BFPL\345\243\271/bcos-executor/src/Common.h" | 391 +++ .../bcos-executor/src/dag/Abi.cpp" | 202 ++ .../bcos-executor/src/dag/Abi.h" | 83 + .../bcos-executor/src/dag/ClockCache.cpp" | 247 ++ .../bcos-executor/src/dag/ClockCache.h" | 307 ++ .../bcos-executor/src/dag/CriticalFields.h" | 132 + .../bcos-executor/src/dag/DAG.cpp" | 148 + .../bcos-executor/src/dag/DAG.h" | 88 + .../bcos-executor/src/dag/ScaleUtils.cpp" | 268 ++ .../bcos-executor/src/dag/ScaleUtils.h" | 38 + .../bcos-executor/src/dag/TxDAG2.cpp" | 94 + .../bcos-executor/src/dag/TxDAG2.h" | 57 + .../bcos-executor/src/dag/TxDAGInterface.h" | 69 + .../src/executive/BlockContext.cpp" | 137 + .../src/executive/BlockContext.h" | 153 + .../CoroutineTransactionExecutive.cpp" | 138 + .../CoroutineTransactionExecutive.h" | 123 + .../src/executive/ExecutiveFactory.cpp" | 76 + .../src/executive/ExecutiveFactory.h" | 71 + .../src/executive/ExecutiveFlowInterface.h" | 102 + .../src/executive/ExecutiveSerialFlow.cpp" | 125 + .../src/executive/ExecutiveSerialFlow.h" | 95 + .../src/executive/ExecutiveStackFlow.cpp" | 223 ++ .../src/executive/ExecutiveStackFlow.h" | 117 + .../src/executive/ExecutiveState.cpp" | 102 + .../src/executive/ExecutiveState.h" | 71 + .../src/executive/LedgerCache.h" | 109 + .../src/executive/SyncStorageWrapper.h" | 120 + .../src/executive/TransactionExecutive.cpp" | 1462 +++++++++ .../src/executive/TransactionExecutive.h" | 227 ++ .../src/executor/ExecuteOutputs.h" | 74 + .../src/executor/SwitchExecutorManager.h" | 667 ++++ .../src/executor/TransactionExecutor.cpp" | 2726 ++++++++++++++++ .../src/executor/TransactionExecutor.h" | 329 ++ .../executor/TransactionExecutorFactory.h" | 123 + .../src/precompiled/BFSPrecompiled.cpp" | 1048 ++++++ .../src/precompiled/BFSPrecompiled.h" | 65 + .../src/precompiled/ConsensusPrecompiled.cpp" | 399 +++ .../src/precompiled/ConsensusPrecompiled.h" | 57 + .../src/precompiled/CryptoPrecompiled.cpp" | 196 ++ .../src/precompiled/CryptoPrecompiled.h" | 57 + .../src/precompiled/KVTablePrecompiled.cpp" | 145 + .../src/precompiled/KVTablePrecompiled.h" | 58 + .../precompiled/SystemConfigPrecompiled.cpp" | 291 ++ .../precompiled/SystemConfigPrecompiled.h" | 61 + .../precompiled/TableManagerPrecompiled.cpp" | 329 ++ .../precompiled/TableManagerPrecompiled.h" | 55 + .../src/precompiled/TablePrecompiled.cpp" | 585 ++++ .../src/precompiled/TablePrecompiled.h" | 73 + .../src/precompiled/common/Common.h" | 167 + .../src/precompiled/common/PrecompiledAbi.h" | 33 + .../precompiled/common/PrecompiledGas.cpp" | 80 + .../src/precompiled/common/PrecompiledGas.h" | 163 + .../precompiled/common/PrecompiledResult.h" | 88 + .../src/precompiled/common/Utilities.cpp" | 396 +++ .../src/precompiled/common/Utilities.h" | 143 + .../extension/AccountManagerPrecompiled.cpp" | 248 ++ .../extension/AccountManagerPrecompiled.h" | 55 + .../extension/AccountPrecompiled.cpp" | 181 ++ .../extension/AccountPrecompiled.h" | 52 + .../extension/AuthManagerPrecompiled.cpp" | 782 +++++ .../extension/AuthManagerPrecompiled.h" | 85 + .../extension/ContractAuthMgrPrecompiled.cpp" | 732 +++++ .../extension/ContractAuthMgrPrecompiled.h" | 91 + .../extension/CpuHeavyPrecompiled.cpp" | 104 + .../extension/CpuHeavyPrecompiled.h" | 78 + .../extension/DagTransferPrecompiled.cpp" | 519 +++ .../extension/DagTransferPrecompiled.h" | 76 + .../extension/GroupSigPrecompiled.cpp" | 99 + .../extension/GroupSigPrecompiled.h" | 41 + .../extension/HelloWorldPrecompiled.cpp" | 128 + .../extension/HelloWorldPrecompiled.h" | 42 + .../PermissionPrecompiledInterface.h" | 71 + .../extension/RingSigPrecompiled.cpp" | 99 + .../extension/RingSigPrecompiled.h" | 41 + .../extension/SmallBankPrecompiled.cpp" | 300 ++ .../extension/SmallBankPrecompiled.h" | 96 + .../precompiled/extension/UserPrecompiled.h" | 31 + .../precompiled/extension/ZkpPrecompiled.cpp" | 303 ++ .../precompiled/extension/ZkpPrecompiled.h" | 82 + .../src/precompiled/solidity/Account.sol" | 23 + .../precompiled/solidity/BfsPrecompiled.sol" | 28 + .../solidity/ConsensusPrecompiled.sol" | 9 + .../solidity/ContractAuthPrecompiled.sol" | 20 + .../src/precompiled/solidity/Crypto.sol" | 11 + .../solidity/GroupSigPrecompiled.sol" | 6 + .../solidity/RingSigPrecompiled.sol" | 6 + .../solidity/SystemConfigPrecompiled.sol" | 8 + .../src/precompiled/solidity/Table.sol" | 89 + .../precompiled/solidity/ZkpPrecompiled.sol" | 15 + .../src/vm/DelegateHostContext.cpp" | 35 + .../src/vm/DelegateHostContext.h" | 46 + .../src/vm/EVMHostInterface.cpp" | 461 +++ .../bcos-executor/src/vm/EVMHostInterface.h" | 40 + .../bcos-executor/src/vm/HostContext.cpp" | 718 +++++ .../bcos-executor/src/vm/HostContext.h" | 207 ++ .../bcos-executor/src/vm/Precompiled.cpp" | 494 +++ .../bcos-executor/src/vm/Precompiled.h" | 191 ++ .../bcos-executor/src/vm/VMFactory.cpp" | 114 + .../bcos-executor/src/vm/VMFactory.h" | 52 + .../bcos-executor/src/vm/VMInstance.cpp" | 86 + .../bcos-executor/src/vm/VMInstance.h" | 83 + .../src/vm/gas_meter/GasInjector.cpp" | 564 ++++ .../src/vm/gas_meter/GasInjector.h" | 86 + .../src/vm/gas_meter/Metric.cpp" | 248 ++ .../bcos-executor/src/vm/gas_meter/Metric.h" | 50 + .../bcos-executor/test/CMakeLists.txt" | 3 + .../test/flow-graph/CMakeLists.txt" | 4 + .../bcos-executor/test/flow-graph/main.cpp" | 81 + .../bcos-executor/test/liquid/hello_world.h" | 894 +++++ .../bcos-executor/test/liquid/hello_world.rs" | 29 + .../test/liquid/hello_world.wasm" | Bin 0 -> 10661 bytes .../test/liquid/hello_world_caller.h" | 820 +++++ .../test/liquid/hello_world_caller.rs" | 38 + .../test/liquid/hello_world_caller.wasm" | Bin 0 -> 9769 bytes .../bcos-executor/test/liquid/transfer.h" | 1071 ++++++ .../bcos-executor/test/liquid/transfer.rs" | 39 + .../bcos-executor/test/liquid/transfer.wasm" | Bin 0 -> 17073 bytes .../test/old/EVMPrecompiledTest.cpp" | 1743 ++++++++++ .../bcos-executor/test/old/MemoryStorage.h" | 183 ++ .../bcos-executor/test/old/StateTest.cpp" | 225 ++ .../test/old/libexecutor/DAGTest.cpp" | 180 ++ .../test/old/libexecutor/ExecutorTest.cpp" | 324 ++ .../test/old/mock/MemoryStorage.h" | 307 ++ .../test/old/mock/MockDispatcher.h" | 94 + .../bcos-executor/test/old/mock/MockLedger.h" | 73 + .../test/solidity/ParallelOk.sol" | 31 + .../test/solidity/TestEvmPrecompiled.sol" | 282 ++ .../precompiled/ConsensusPrecompiled.sol" | 9 + .../test/solidity/precompiled/Crypto.sol" | 11 + .../precompiled/SystemConfigPrecompiled.sol" | 8 + .../test/solidity/test_config.sol" | 56 + .../test/solidity/test_crypto.sol" | 31 + .../test/solidity/test_external_call.sol" | 33 + .../test/trie-test/CMakeLists.txt" | 4 + .../bcos-executor/test/trie-test/main.cpp" | 80 + .../test/unittest/CMakeLists.txt" | 25 + .../test/unittest/container/TestHashMap.cpp" | 93 + .../unittest/libexecutor/TestAbiReader.cpp" | 246 ++ .../libexecutor/TestBlockContext.cpp" | 75 + .../unittest/libexecutor/TestClockCache.cpp" | 155 + .../unittest/libexecutor/TestDagExecutor.cpp" | 1131 +++++++ .../unittest/libexecutor/TestEVMExecutor.cpp" | 1771 ++++++++++ .../libexecutor/TestExecutiveStackFlow.cpp" | 195 ++ .../libexecutor/TestExecutiveState.cpp" | 153 + .../unittest/libexecutor/TestLedgerCache.cpp" | 85 + .../unittest/libexecutor/TestScaleUtils.cpp" | 129 + .../test/unittest/libexecutor/TestTxDAG.cpp" | 237 ++ .../libexecutor/TestWasmExecutor.cpp" | 1281 ++++++++ .../test/unittest/libexecutor/TxDAG.cpp" | 115 + .../test/unittest/libexecutor/TxDAG.h" | 85 + .../AccountPrecompiledTest.cpp" | 875 +++++ .../libprecompiled/AuthPrecompiledTest.cpp" | 1893 +++++++++++ .../libprecompiled/ConfigPrecompiledTest.cpp" | 552 ++++ .../libprecompiled/CryptoPrecompiledTest.cpp" | 402 +++ .../libprecompiled/EVMStateContextTest.cpp" | 467 +++ .../FileSystemPrecompiledTest.cpp" | 1746 ++++++++++ .../GroupSigPrecompiledTest.cpp" | 255 ++ .../KVTablePrecompiledTest.cpp" | 496 +++ .../libprecompiled/PreCompiledFixture.h" | 701 ++++ .../libprecompiled/PrecompiledCallTest.cpp" | 101 + .../libprecompiled/PrecompiledGas.cpp" | 109 + .../RingSigPrecompiledTest.cpp" | 208 ++ .../libprecompiled/TablePrecompiledTest.cpp" | 1474 +++++++++ .../libprecompiled/VRFPrecompiledTest.cpp" | 171 + .../test/unittest/libvm/.gitignore" | 1 + .../libvm/TestTransactionExecutive.cpp" | 21 + .../test/unittest/libvm/WasmPath.h.in" | 7 + .../bcos-executor/test/unittest/main.cpp" | 25 + .../test/unittest/mock/MockBlock.h" | 55 + .../test/unittest/mock/MockBlockHeader.h" | 59 + .../unittest/mock/MockExecutiveFactory.h" | 46 + .../test/unittest/mock/MockExecutiveFlow.h" | 35 + .../test/unittest/mock/MockKeyPageStorage.h" | 132 + .../test/unittest/mock/MockLedger.h" | 143 + .../unittest/mock/MockTransactionExecutive.h" | 52 + .../unittest/mock/MockTransactionalStorage.h" | 106 + .../test/unittest/mock/MockTxPool.h" | 70 + .../test/wasm/infinit_loop.wasm" | Bin 0 -> 267 bytes .../wasm/metric_infinit_loop_global_gas.wasm" | Bin 0 -> 194 bytes .../wasm/metric_infinit_loop_useGas.wasm" | Bin 0 -> 145 bytes .../bcos-executor/tools/CMakeLists.txt" | 5 + .../bcos-executor/tools/inject_meter.cpp" | 90 + .../bcos-framework/CMakeLists.txt" | 49 + .../bcos-framework/bcos-framework/Common.h" | 24 + .../consensus/ConsensusInterface.h" | 86 + .../bcos-framework/consensus/ConsensusNode.h" | 47 + .../consensus/ConsensusNodeInterface.h" | 66 + .../consensus/ConsensusTypeDef.h" | 28 + .../dispatcher/SchedulerInterface.h" | 81 + .../dispatcher/SchedulerTypeDef.h" | 50 + .../election/FailOverTypeDef.h" | 28 + .../election/LeaderElectionInterface.h" | 65 + .../election/LeaderEntryPointInterface.h" | 58 + .../bcos-framework/executor/ExecuteError.h" | 25 + .../executor/ExecutionMessage.h" | 210 ++ .../bcos-framework/executor/ExecutorStatus.h" | 52 + .../executor/NativeExecutionMessage.h" | 177 + .../ParallelTransactionExecutorInterface.h" | 141 + .../executor/PrecompiledTypeDef.h" | 120 + .../executor/SerialTransactionExecutor.h" | 60 + .../front/FrontServiceInterface.h" | 137 + .../gateway/GatewayInterface.h" | 142 + .../bcos-framework/gateway/GatewayTypeDef.h" | 160 + .../bcos-framework/gateway/GroupNodeInfo.h" | 56 + .../bcos-framework/ledger/GenesisConfig.h" | 103 + .../bcos-framework/ledger/LedgerConfig.h" | 118 + .../bcos-framework/ledger/LedgerInterface.h" | 164 + .../bcos-framework/ledger/LedgerTypeDef.h" | 73 + .../multigroup/ChainNodeInfo.h" | 160 + .../multigroup/ChainNodeInfoFactory.h" | 40 + .../bcos-framework/multigroup/GroupInfo.h" | 151 + .../multigroup/GroupInfoCodec.h" | 42 + .../multigroup/GroupInfoFactory.h" | 41 + .../bcos-framework/multigroup/GroupTypeDef.h" | 35 + .../bcos-framework/protocol/AMOPRequest.h" | 141 + .../bcos-framework/protocol/Block.h" | 133 + .../bcos-framework/protocol/BlockFactory.h" | 55 + .../bcos-framework/protocol/BlockHeader.h" | 150 + .../protocol/BlockHeaderFactory.h" | 59 + .../bcos-framework/protocol/CommonError.h" | 46 + .../bcos-framework/protocol/Exceptions.h" | 51 + .../bcos-framework/protocol/GlobalConfig.h" | 92 + .../bcos-framework/protocol/LogEntry.h" | 69 + .../protocol/MemberInterface.h" | 68 + .../bcos-framework/protocol/Protocol.h" | 241 ++ .../bcos-framework/protocol/ProtocolInfo.h" | 65 + .../protocol/ProtocolInfoCodec.h" | 41 + .../protocol/ProtocolTypeDef.h" | 108 + .../bcos-framework/protocol/ServiceDesc.h" | 138 + .../bcos-framework/protocol/Transaction.h" | 190 ++ .../protocol/TransactionFactory.h" | 46 + .../protocol/TransactionMetaData.h" | 55 + .../protocol/TransactionReceipt.h" | 67 + .../protocol/TransactionReceiptFactory.h" | 50 + .../protocol/TransactionSubmitResult.h" | 61 + .../TransactionSubmitResultFactory.h" | 40 + .../bcos-framework/rpc/HandshakeRequest.h" | 98 + .../bcos-framework/rpc/RPCInterface.h" | 81 + .../bcos-framework/sealer/SealerInterface.h" | 52 + .../security/DataEncryptInterface.h" | 52 + .../bcos-framework/storage/Common.h" | 225 ++ .../bcos-framework/storage/Entry.h" | 353 ++ .../bcos-framework/storage/KVStorageHelper.h" | 90 + .../storage/StorageInterface.h" | 129 + .../bcos-framework/storage/Table.h" | 82 + .../bcos-framework/sync/BlockSyncInterface.h" | 58 + .../bcos-framework/sync/SyncConfig.h" | 170 + .../testutils/TestPromptFixture.h" | 93 + .../testutils/faker/FakeFrontService.h" | 234 ++ .../testutils/faker/FakeKVStorage.h" | 111 + .../testutils/faker/FakeLedger.h" | 369 +++ .../testutils/faker/FakeScheduler.h" | 93 + .../testutils/faker/FakeSealer.h" | 76 + .../testutils/faker/FakeTxPool.h" | 114 + .../bcos-framework/txpool/TxPoolInterface.h" | 125 + .../bcos-framework/txpool/TxPoolTypeDef.h" | 33 + .../bcos-framework/test/CMakeLists.txt" | 29 + .../interfaces/ConsensusNodeTest.cpp" | 146 + .../unittests/interfaces/ExecutorTest.cpp" | 60 + .../test/unittests/main/main.cpp" | 24 + "BFPL\345\243\271/bcos-front/CMakeLists.txt" | 40 + .../bcos-front/bcos-front/Common.h" | 22 + .../bcos-front/bcos-front/FrontImpl.h" | 191 ++ .../bcos-front/bcos-front/FrontMessage.cpp" | 89 + .../bcos-front/bcos-front/FrontMessage.h" | 107 + .../bcos-front/bcos-front/FrontService.cpp" | 683 ++++ .../bcos-front/bcos-front/FrontService.h" | 306 ++ .../bcos-front/FrontServiceFactory.cpp" | 61 + .../bcos-front/FrontServiceFactory.h" | 60 + .../bcos-front/test/CMakeLists.txt" | 27 + .../test/unittests/FakeGateway.cpp" | 81 + .../bcos-front/test/unittests/FakeGateway.h" | 128 + .../test/unittests/FrontMessageTest.cpp" | 230 ++ .../test/unittests/FrontServiceTest.cpp" | 323 ++ .../bcos-gateway/CMakeLists.txt" | 54 + .../bcos-gateway/bcos-gateway/Common.h" | 46 + .../bcos-gateway/bcos-gateway/Gateway.cpp" | 548 ++++ .../bcos-gateway/bcos-gateway/Gateway.h" | 213 ++ .../bcos-gateway/GatewayConfig.cpp" | 667 ++++ .../bcos-gateway/GatewayConfig.h" | 189 ++ .../bcos-gateway/GatewayFactory.cpp" | 779 +++++ .../bcos-gateway/GatewayFactory.h" | 131 + .../bcos-gateway/gateway/FrontServiceInfo.h" | 77 + .../gateway/GatewayMessageExtAttributes.h" | 34 + .../gateway/GatewayNodeManager.cpp" | 329 ++ .../gateway/GatewayNodeManager.h" | 117 + .../bcos-gateway/gateway/GatewayStatus.cpp" | 144 + .../bcos-gateway/gateway/GatewayStatus.h" | 72 + .../gateway/LocalRouterTable.cpp" | 295 ++ .../bcos-gateway/gateway/LocalRouterTable.h" | 76 + .../gateway/PeersRouterTable.cpp" | 284 ++ .../bcos-gateway/gateway/PeersRouterTable.h" | 98 + .../gateway/ProGatewayNodeManager.cpp" | 46 + .../gateway/ProGatewayNodeManager.h" | 62 + .../bcos-gateway/libamop/AMOPImpl.cpp" | 574 ++++ .../bcos-gateway/libamop/AMOPImpl.h" | 158 + .../bcos-gateway/libamop/AMOPMessage.cpp" | 73 + .../bcos-gateway/libamop/AMOPMessage.h" | 85 + .../bcos-gateway/libamop/AirTopicManager.h" | 48 + .../bcos-gateway/libamop/Common.h" | 73 + .../bcos-gateway/libamop/TopicManager.cpp" | 488 +++ .../bcos-gateway/libamop/TopicManager.h" | 175 + .../libnetwork/ASIOInterface.cpp" | 36 + .../bcos-gateway/libnetwork/ASIOInterface.h" | 187 ++ .../bcos-gateway/libnetwork/Common.h" | 140 + .../bcos-gateway/libnetwork/Host.cpp" | 515 +++ .../bcos-gateway/libnetwork/Host.h" | 180 ++ .../bcos-gateway/libnetwork/Message.h" | 85 + .../bcos-gateway/libnetwork/Session.cpp" | 626 ++++ .../bcos-gateway/libnetwork/Session.h" | 203 ++ .../bcos-gateway/libnetwork/SessionFace.h" | 63 + .../bcos-gateway/libnetwork/Socket.h" | 86 + .../bcos-gateway/libnetwork/SocketFace.h" | 38 + .../bcos-gateway/libp2p/Common.h" | 28 + .../bcos-gateway/libp2p/P2PInterface.h" | 104 + .../bcos-gateway/libp2p/P2PMessage.cpp" | 352 ++ .../bcos-gateway/libp2p/P2PMessage.h" | 256 ++ .../bcos-gateway/libp2p/P2PMessageV2.cpp" | 100 + .../bcos-gateway/libp2p/P2PMessageV2.h" | 59 + .../bcos-gateway/libp2p/P2PSession.cpp" | 89 + .../bcos-gateway/libp2p/P2PSession.h" | 78 + .../bcos-gateway/libp2p/Service.cpp" | 685 ++++ .../bcos-gateway/libp2p/Service.h" | 266 ++ .../bcos-gateway/libp2p/ServiceV2.cpp" | 377 +++ .../bcos-gateway/libp2p/ServiceV2.h" | 112 + .../libp2p/router/RouterTableImpl.cpp" | 249 ++ .../libp2p/router/RouterTableImpl.h" | 128 + .../libp2p/router/RouterTableInterface.h" | 81 + .../libratelimit/DistributedRateLimiter.cpp" | 263 ++ .../libratelimit/DistributedRateLimiter.h" | 246 ++ .../libratelimit/GatewayRateLimiter.cpp" | 161 + .../libratelimit/GatewayRateLimiter.h" | 114 + .../libratelimit/ModuleWhiteList.h" | 111 + .../libratelimit/RateLimiterFactory.h" | 79 + .../libratelimit/RateLimiterInterface.h" | 76 + .../libratelimit/RateLimiterManager.cpp" | 162 + .../libratelimit/RateLimiterManager.h" | 101 + .../libratelimit/RateLimiterStat.cpp" | 301 ++ .../libratelimit/RateLimiterStat.h" | 134 + .../libratelimit/TokenBucketRateLimiter.cpp" | 168 + .../libratelimit/TokenBucketRateLimiter.h" | 116 + .../protocol/GatewayNodeStatus.cpp" | 61 + .../protocol/GatewayNodeStatus.h" | 82 + .../bcos-gateway/test/CMakeLists.txt" | 40 + .../test/common/FrontServiceBuilder.h" | 61 + .../test/integtests/GatewayTest.cpp" | 155 + .../bcos-gateway/test/main/CMakeLists.txt" | 7 + .../bcos-gateway/test/main/main.cpp" | 134 + .../test/unittests/GatewayConfigTest.cpp" | 321 ++ .../test/unittests/GatewayFactoryTest.cpp" | 96 + .../test/unittests/GatewayMessageTest.cpp" | 488 +++ .../unittests/GatewayNodeManagerTest.cpp" | 449 +++ .../test/unittests/ModuleWhiteListTest.cpp" | 52 + .../unittests/RateLimiterManagerTest.cpp" | 330 ++ .../test/unittests/amop/AMOPMessageTest.cpp" | 104 + .../test/unittests/amop/TopicManagerTest.cpp" | 139 + .../test/unittests/data/ca/ca.crt" | 31 + .../test/unittests/data/ca/node.crt" | 30 + .../test/unittests/data/ca/node.key" | 52 + .../unittests/data/config/config_ipv4.ini" | 57 + .../unittests/data/config/config_ipv6.ini" | 49 + .../data/config/json/nodes_ipv4.json" | 1 + .../data/config/json/nodes_ipv6.json" | 1 + .../test/unittests/data/sm_ca/sm_ca.crt" | 12 + .../test/unittests/data/sm_ca/sm_ennode.crt" | 11 + .../test/unittests/data/sm_ca/sm_ennode.key" | 5 + .../test/unittests/data/sm_ca/sm_node.crt" | 35 + .../test/unittests/data/sm_ca/sm_node.key" | 5 + .../test/unittests/data/sm_ca/sm_node.nodeid" | 1 + .../bcos-leader-election/CMakeLists.txt" | 31 + .../src/CampaignConfig.cpp" | 145 + .../src/CampaignConfig.h" | 118 + .../bcos-leader-election/src/Common.h" | 21 + .../src/ElectionConfig.cpp" | 92 + .../src/ElectionConfig.h" | 110 + .../src/LeaderElection.cpp" | 249 ++ .../src/LeaderElection.h" | 95 + .../src/LeaderElectionFactory.h" | 63 + .../src/LeaderEntryPoint.h" | 91 + .../src/WatcherConfig.cpp" | 150 + .../bcos-leader-election/src/WatcherConfig.h" | 107 + "BFPL\345\243\271/bcos-ledger/CMakeLists.txt" | 41 + .../bcos-ledger/src/libledger/Ledger.cpp" | 1769 ++++++++++ .../bcos-ledger/src/libledger/Ledger.h" | 152 + .../src/libledger/utilities/Common.h" | 61 + .../utilities/MerkleProofUtility.cpp" | 93 + .../libledger/utilities/MerkleProofUtility.h" | 77 + .../bcos-ledger/test/CMakeLists.txt" | 32 + .../bcos-ledger/test/main/main.cpp" | 31 + .../bcos-ledger/test/mock/MockKeyFactor.h" | 102 + .../bcos-ledger/test/mock/MockStorage.h" | 460 +++ .../bcos-ledger/test/mock/MockTable.h" | 101 + .../test/unittests/ledger/LedgerTest.cpp" | 1164 +++++++ .../test/unittests/ledger/common/FakeBlock.h" | 151 + .../ledger/common/FakeBlockHeader.h" | 132 + .../unittests/ledger/common/FakeReceipt.h" | 90 + .../ledger/common/FakeTransaction.h" | 104 + "BFPL\345\243\271/bcos-pbft/CMakeLists.txt" | 62 + .../bcos-pbft/bcos-pbft/core/Common.h" | 33 + .../bcos-pbft/core/ConsensusConfig.cpp" | 158 + .../bcos-pbft/core/ConsensusConfig.h" | 169 + .../bcos-pbft/core/ConsensusEngine.h" | 87 + .../bcos-pbft/bcos-pbft/core/Proposal.h" | 156 + .../bcos-pbft/core/StateMachine.cpp" | 188 ++ .../bcos-pbft/bcos-pbft/core/StateMachine.h" | 80 + .../bcos-pbft/core/proto/Consensus.proto" | 16 + .../framework/ConsensusConfigInterface.h" | 56 + .../framework/ConsensusEngineInterface.h" | 40 + .../bcos-pbft/framework/ProposalInterface.h" | 72 + .../framework/StateMachineInterface.h" | 41 + .../bcos-pbft/bcos-pbft/pbft/PBFTFactory.cpp" | 82 + .../bcos-pbft/bcos-pbft/pbft/PBFTFactory.h" | 58 + .../bcos-pbft/bcos-pbft/pbft/PBFTImpl.cpp" | 190 ++ .../bcos-pbft/bcos-pbft/pbft/PBFTImpl.h" | 156 + .../bcos-pbft/pbft/cache/PBFTCache.cpp" | 391 +++ .../bcos-pbft/pbft/cache/PBFTCache.h" | 222 ++ .../bcos-pbft/pbft/cache/PBFTCacheFactory.h" | 41 + .../pbft/cache/PBFTCacheProcessor.cpp" | 1191 +++++++ .../pbft/cache/PBFTCacheProcessor.h" | 282 ++ .../bcos-pbft/pbft/config/PBFTConfig.cpp" | 477 +++ .../bcos-pbft/pbft/config/PBFTConfig.h" | 441 +++ .../bcos-pbft/pbft/engine/BlockValidator.cpp" | 182 ++ .../bcos-pbft/pbft/engine/BlockValidator.h" | 58 + .../bcos-pbft/pbft/engine/PBFTEngine.cpp" | 1655 ++++++++++ .../bcos-pbft/pbft/engine/PBFTEngine.h" | 245 ++ .../bcos-pbft/pbft/engine/PBFTLogSync.cpp" | 189 ++ .../bcos-pbft/pbft/engine/PBFTLogSync.h" | 76 + .../bcos-pbft/pbft/engine/PBFTTimer.h" | 80 + .../bcos-pbft/pbft/engine/Validator.cpp" | 132 + .../bcos-pbft/pbft/engine/Validator.h" | 245 ++ .../pbft/interfaces/NewViewMsgInterface.h" | 42 + .../interfaces/PBFTBaseMessageInterface.h" | 80 + .../pbft/interfaces/PBFTCodecInterface.h" | 43 + .../pbft/interfaces/PBFTMessageFactory.h" | 144 + .../pbft/interfaces/PBFTMessageInterface.h" | 46 + .../pbft/interfaces/PBFTProposalInterface.h" | 53 + .../pbft/interfaces/PBFTRequestInterface.h" | 38 + .../bcos-pbft/pbft/interfaces/PBFTStorage.h" | 55 + .../pbft/interfaces/ViewChangeMsgInterface.h" | 44 + .../pbft/protocol/PB/PBFTBaseMessage.h" | 153 + .../bcos-pbft/pbft/protocol/PB/PBFTCodec.cpp" | 106 + .../bcos-pbft/pbft/protocol/PB/PBFTCodec.h" | 60 + .../pbft/protocol/PB/PBFTMessage.cpp" | 140 + .../bcos-pbft/pbft/protocol/PB/PBFTMessage.h" | 124 + .../protocol/PB/PBFTMessageFactoryImpl.h" | 89 + .../pbft/protocol/PB/PBFTNewViewMsg.cpp" | 81 + .../pbft/protocol/PB/PBFTNewViewMsg.h" | 88 + .../pbft/protocol/PB/PBFTProposal.h" | 115 + .../bcos-pbft/pbft/protocol/PB/PBFTRequest.h" | 76 + .../pbft/protocol/PB/PBFTViewChangeMsg.cpp" | 89 + .../pbft/protocol/PB/PBFTViewChangeMsg.h" | 91 + .../bcos-pbft/pbft/protocol/proto/PBFT.proto" | 69 + .../bcos-pbft/pbft/storage/LedgerStorage.cpp" | 457 +++ .../bcos-pbft/pbft/storage/LedgerStorage.h" | 122 + .../bcos-pbft/pbft/utilities/Common.h" | 48 + .../bcos-pbft/test/CMakeLists.txt" | 30 + .../test/unittests/core/TimerTest.cpp" | 117 + .../bcos-pbft/test/unittests/main/main.cpp" | 24 + .../test/unittests/pbft/PBFTConfigTest.cpp" | 173 + .../test/unittests/pbft/PBFTEngineTest.cpp" | 279 ++ .../test/unittests/pbft/PBFTFixture.h" | 418 +++ .../unittests/pbft/PBFTViewChangeTest.cpp" | 159 + .../unittests/protocol/FakePBFTMessage.h" | 589 ++++ .../unittests/protocol/PBFTMessageTest.cpp" | 111 + .../bcos-protocol/CMakeLists.txt" | 41 + .../bcos-protocol/CMakeLists.txt" | 15 + .../bcos-protocol/bcos-protocol/Common.h" | 89 + .../bcos-protocol/ParallelMerkleProof.cpp" | 119 + .../bcos-protocol/ParallelMerkleProof.h" | 38 + .../bcos-protocol/TransactionStatus.h" | 162 + .../TransactionSubmitResultFactoryImpl.h" | 41 + .../TransactionSubmitResultImpl.h" | 77 + .../bcos-protocol/amop/TopicItem.h" | 89 + .../bcos-protocol/test/CMakeLists.txt" | 29 + .../test/unittests/main/main.cpp" | 24 + "BFPL\345\243\271/bcos-rpc/CMakeLists.txt" | 46 + "BFPL\345\243\271/bcos-rpc/bcos-rpc/Common.h" | 39 + "BFPL\345\243\271/bcos-rpc/bcos-rpc/Rpc.cpp" | 251 ++ "BFPL\345\243\271/bcos-rpc/bcos-rpc/Rpc.h" | 110 + .../bcos-rpc/bcos-rpc/RpcFactory.cpp" | 461 +++ .../bcos-rpc/bcos-rpc/RpcFactory.h" | 101 + .../bcos-rpc/bcos-rpc/amop/AMOPClient.cpp" | 570 ++++ .../bcos-rpc/bcos-rpc/amop/AMOPClient.h" | 193 ++ .../bcos-rpc/bcos-rpc/amop/AirAMOPClient.h" | 78 + .../bcos-rpc/bcos-rpc/event/Common.h" | 39 + .../bcos-rpc/bcos-rpc/event/EventSub.cpp" | 551 ++++ .../bcos-rpc/bcos-rpc/event/EventSub.h" | 184 ++ .../bcos-rpc/event/EventSubMatcher.cpp" | 103 + .../bcos-rpc/event/EventSubMatcher.h" | 53 + .../bcos-rpc/bcos-rpc/event/EventSubParams.h" | 68 + .../bcos-rpc/event/EventSubRequest.cpp" | 296 ++ .../bcos-rpc/event/EventSubRequest.h" | 71 + .../bcos-rpc/event/EventSubResponse.cpp" | 107 + .../bcos-rpc/event/EventSubResponse.h" | 55 + .../bcos-rpc/bcos-rpc/event/EventSubTask.h" | 120 + .../bcos-rpc/groupmgr/AirGroupManager.h" | 104 + .../bcos-rpc/bcos-rpc/groupmgr/Common.h" | 66 + .../bcos-rpc/groupmgr/GroupManager.cpp" | 363 +++ .../bcos-rpc/groupmgr/GroupManager.h" | 212 ++ .../bcos-rpc/groupmgr/NodeService.cpp" | 101 + .../bcos-rpc/bcos-rpc/groupmgr/NodeService.h" | 115 + .../bcos-rpc/groupmgr/TarsGroupManager.cpp" | 80 + .../bcos-rpc/groupmgr/TarsGroupManager.h" | 63 + .../bcos-rpc/bcos-rpc/jsonrpc/Common.h" | 165 + .../jsonrpc/DupTestTxJsonRpcImpl_2_0.h" | 109 + .../jsonrpc/DuplicateTransactionFactory.cpp" | 38 + .../jsonrpc/DuplicateTransactionFactory.h" | 35 + .../bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp" | 1351 ++++++++ .../bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h" | 197 ++ .../bcos-rpc/jsonrpc/JsonRpcInterface.cpp" | 265 ++ .../bcos-rpc/jsonrpc/JsonRpcInterface.h" | 277 ++ .../bcos-rpc/test/CMakeLists.txt" | 30 + .../bcos-rpc/test/unittests/main/main.cpp" | 3 + .../bcos-scheduler/CMakeLists.txt" | 19 + .../bcos-scheduler/src/BlockExecutive.cpp" | 1669 ++++++++++ .../bcos-scheduler/src/BlockExecutive.h" | 200 ++ .../src/BlockExecutiveFactory.cpp" | 72 + .../src/BlockExecutiveFactory.h" | 56 + .../bcos-scheduler/src/Common.h" | 16 + .../bcos-scheduler/src/DmcExecutor.cpp" | 471 +++ .../bcos-scheduler/src/DmcExecutor.h" | 145 + .../bcos-scheduler/src/DmcStepRecorder.cpp" | 96 + .../bcos-scheduler/src/DmcStepRecorder.h" | 83 + .../bcos-scheduler/src/Executive.h" | 46 + .../bcos-scheduler/src/ExecutivePool.cpp" | 190 ++ .../bcos-scheduler/src/ExecutivePool.h" | 101 + .../bcos-scheduler/src/ExecutorManager.cpp" | 136 + .../bcos-scheduler/src/ExecutorManager.h" | 153 + .../bcos-scheduler/src/GraphKeyLocks.cpp" | 277 ++ .../bcos-scheduler/src/GraphKeyLocks.h" | 127 + .../bcos-scheduler/src/SchedulerFactory.h" | 80 + .../bcos-scheduler/src/SchedulerImpl.cpp" | 1036 ++++++ .../bcos-scheduler/src/SchedulerImpl.h" | 262 ++ .../bcos-scheduler/src/SchedulerManager.cpp" | 451 +++ .../bcos-scheduler/src/SchedulerManager.h" | 157 + .../src/SerialBlockExecutive.cpp" | 272 ++ .../src/SerialBlockExecutive.h" | 77 + .../src/TarsRemoteExecutorManager.cpp" | 307 ++ .../src/TarsRemoteExecutorManager.h" | 98 + .../bcos-scheduler/test/CMakeLists.txt" | 15 + .../bcos-scheduler/test/main.cpp" | 3 + .../test/mock/MockBlockExecutive.h" | 167 + .../test/mock/MockBlockExecutiveFactory.h" | 78 + .../test/mock/MockDeadLockExecutor.h" | 117 + .../test/mock/MockDmcExecutor.h" | 239 ++ .../bcos-scheduler/test/mock/MockExecutor.h" | 185 ++ .../bcos-scheduler/test/mock/MockExecutor3.h" | 119 + .../test/mock/MockExecutorForCall.h" | 87 + .../test/mock/MockExecutorForCreate.h" | 90 + .../test/mock/MockExecutorForMessageDAG.h" | 163 + .../bcos-scheduler/test/mock/MockLedger.h" | 121 + .../bcos-scheduler/test/mock/MockLedger2.h" | 91 + .../bcos-scheduler/test/mock/MockLedger3.h" | 160 + .../test/mock/MockMultiParallelExecutor.h" | 102 + .../bcos-scheduler/test/mock/MockRPC.h" | 33 + .../test/mock/MockTransactionalStorage.h" | 90 + .../bcos-scheduler/test/mock/MockTxPool1.h" | 110 + .../test/testBlockExecutive.cpp" | 517 +++ .../test/testChecksumAddress.cpp" | 73 + .../bcos-scheduler/test/testCommitBlock.cpp" | 167 + .../bcos-scheduler/test/testDmcExecutor.cpp" | 369 +++ .../test/testDmcStepRecorder.cpp" | 91 + .../test/testExecutivePool.cpp" | 249 ++ .../test/testExecutorManager.cpp" | 178 + .../bcos-scheduler/test/testKeyLocks.cpp" | 114 + .../bcos-scheduler/test/testScheduler.cpp" | 592 ++++ .../test/testSchedulerImpl.cpp" | 606 ++++ "BFPL\345\243\271/bcos-sdk/CMakeLists.txt" | 33 + .../bcos-sdk/bcos-cpp-sdk/Sdk.h" | 110 + .../bcos-sdk/bcos-cpp-sdk/SdkFactory.cpp" | 225 ++ .../bcos-sdk/bcos-cpp-sdk/SdkFactory.h" | 62 + .../bcos-sdk/bcos-cpp-sdk/amop/AMOP.cpp" | 287 ++ .../bcos-sdk/bcos-cpp-sdk/amop/AMOP.h" | 141 + .../bcos-cpp-sdk/amop/AMOPInterface.h" | 78 + .../bcos-cpp-sdk/amop/AMOPRequest.cpp" | 100 + .../bcos-sdk/bcos-cpp-sdk/amop/AMOPRequest.h" | 72 + .../bcos-sdk/bcos-cpp-sdk/amop/Common.h" | 48 + .../bcos-cpp-sdk/amop/TopicManager.cpp" | 76 + .../bcos-cpp-sdk/amop/TopicManager.h" | 54 + .../bcos-sdk/bcos-cpp-sdk/config/Config.cpp" | 237 ++ .../bcos-sdk/bcos-cpp-sdk/config/Config.h" | 55 + .../bcos-sdk/bcos-cpp-sdk/event/Common.h" | 55 + .../bcos-sdk/bcos-cpp-sdk/event/EventSub.cpp" | 515 +++ .../bcos-sdk/bcos-cpp-sdk/event/EventSub.h" | 137 + .../bcos-cpp-sdk/event/EventSubInterface.h" | 55 + .../bcos-cpp-sdk/event/EventSubParams.cpp" | 191 ++ .../bcos-cpp-sdk/event/EventSubParams.h" | 105 + .../bcos-cpp-sdk/event/EventSubRequest.cpp" | 287 ++ .../bcos-cpp-sdk/event/EventSubRequest.h" | 77 + .../bcos-cpp-sdk/event/EventSubResponse.cpp" | 108 + .../bcos-cpp-sdk/event/EventSubResponse.h" | 58 + .../bcos-cpp-sdk/event/EventSubStatus.h" | 35 + .../bcos-cpp-sdk/event/EventSubTask.h" | 95 + .../multigroup/JsonChainNodeInfoCodec.h" | 196 ++ .../multigroup/JsonGroupInfoCodec.h" | 147 + .../bcos-sdk/bcos-cpp-sdk/rpc/Common.h" | 87 + .../bcos-cpp-sdk/rpc/JsonRpcImpl.cpp" | 509 +++ .../bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcImpl.h" | 155 + .../bcos-cpp-sdk/rpc/JsonRpcInterface.h" | 124 + .../bcos-cpp-sdk/rpc/JsonRpcRequest.cpp" | 120 + .../bcos-cpp-sdk/rpc/JsonRpcRequest.h" | 122 + .../utilities/abi/ContractABICodec.cpp" | 200 ++ .../utilities/abi/ContractABICodec.h" | 651 ++++ .../utilities/abi/ContractABIType.cpp" | 280 ++ .../utilities/abi/ContractABIType.h" | 96 + .../utilities/abi/ContractEventTopic.h" | 121 + .../bcos-cpp-sdk/ws/BlockNumberInfo.cpp" | 104 + .../bcos-cpp-sdk/ws/BlockNumberInfo.h" | 62 + .../bcos-sdk/bcos-cpp-sdk/ws/Common.h" | 25 + .../bcos-cpp-sdk/ws/HandshakeResponse.cpp" | 128 + .../bcos-cpp-sdk/ws/HandshakeResponse.h" | 78 + .../bcos-sdk/bcos-cpp-sdk/ws/Service.cpp" | 783 +++++ .../bcos-sdk/bcos-cpp-sdk/ws/Service.h" | 170 + .../bcos-sdk/sample/CMakeLists.txt" | 4 + .../bcos-sdk/sample/amop/CMakeLists.txt" | 22 + .../bcos-sdk/sample/amop/broadcast.cpp" | 83 + .../bcos-sdk/sample/amop/publish.cpp" | 109 + .../bcos-sdk/sample/amop/subscribe.cpp" | 103 + .../bcos-sdk/sample/config/config_sample.ini" | 26 + .../sample/config/sm_config_sample.ini" | 28 + .../bcos-sdk/sample/eventsub/CMakeLists.txt" | 10 + .../bcos-sdk/sample/eventsub/eventsub.cpp" | 127 + .../bcos-sdk/sample/rpc/CMakeLists.txt" | 10 + .../bcos-sdk/sample/rpc/blocknotifier.cpp" | 90 + .../bcos-sdk/sample/tx/CMakeLists.txt" | 13 + .../bcos-sdk/sample/tx/deploy_hello.cpp" | 268 ++ .../bcos-sdk/sample/tx/random_perf.cpp" | 76 + .../bcos-sdk/sample/tx/tx_sign_perf.cpp" | 199 ++ .../bcos-sdk/tests/CMakeLists.txt" | 29 + .../unittests/abi/ContractEventTopicTest.cpp" | 248 ++ .../unittests/amop/TopicManagerTest.cpp" | 155 + .../unittests/event/EventSubParamsTest.cpp" | 77 + .../unittests/event/EventSubRequestTest.cpp" | 140 + .../unittests/event/EventSubResponseTest.cpp" | 74 + .../event/EventSubTaskStateTest.cpp" | 40 + .../unittests/event/EventSubTaskTest.cpp" | 60 + .../tests/unittests/event/EventSubTest.cpp" | 214 ++ .../tests/unittests/fake/WsServiceFake.h" | 65 + .../tests/unittests/fake/WsSessionFake.h" | 68 + .../bcos-sdk/tests/unittests/main.cpp" | 4 + .../tests/unittests/tx/TransactionTest.cpp" | 66 + .../unittests/ws/BlockNumberInfoTest.cpp" | 56 + .../unittests/ws/HandshakeResponseTest.cpp" | 91 + "BFPL\345\243\271/bcos-sealer/CMakeLists.txt" | 29 + .../bcos-sealer/bcos-sealer/Common.h" | 22 + .../bcos-sealer/bcos-sealer/Sealer.cpp" | 176 + .../bcos-sealer/bcos-sealer/Sealer.h" | 69 + .../bcos-sealer/bcos-sealer/SealerConfig.h" | 63 + .../bcos-sealer/SealerFactory.cpp" | 44 + .../bcos-sealer/bcos-sealer/SealerFactory.h" | 45 + .../bcos-sealer/SealingManager.cpp" | 303 ++ .../bcos-sealer/bcos-sealer/SealingManager.h" | 153 + .../bcos-security/CMakeLists.txt" | 37 + .../bcos-security/bcos-security/Common.h" | 26 + .../bcos-security/DataEncryption.cpp" | 169 + .../bcos-security/DataEncryption.h" | 66 + .../bcos-security/KeyCenter.cpp" | 248 ++ .../bcos-security/bcos-security/KeyCenter.h" | 107 + .../bcos-security/test/CMakeLists.txt" | 28 + .../libsecurity/DataEncryptionTest.cpp" | 34 + .../test/unittests/main/main.cpp" | 24 + .../bcos-storage/CMakeLists.txt" | 38 + .../bcos-storage/bcos-storage/Common.cpp" | 36 + .../bcos-storage/bcos-storage/Common.h" | 48 + .../bcos-storage/RocksDBStorage.cpp" | 574 ++++ .../bcos-storage/RocksDBStorage.h" | 84 + .../bcos-storage/TiKVStorage.cpp" | 591 ++++ .../bcos-storage/bcos-storage/TiKVStorage.h" | 101 + .../bcos-storage/test/CMakeLists.txt" | 4 + .../test/unittest/CMakeLists.txt" | 44 + .../test/unittest/TestRocksDBStorage.cpp" | 694 ++++ .../test/unittest/TestTiKVStorage.cpp" | 1384 ++++++++ .../bcos-storage/test/unittest/main.cpp" | 24 + .../bcos-storage/tools/CMakeLists.txt" | 12 + .../bcos-storage/tools/reader.cpp" | 220 ++ .../bcos-storage/tools/storageTool.cpp" | 528 +++ "BFPL\345\243\271/bcos-sync/CMakeLists.txt" | 72 + .../bcos-sync/bcos-sync/BlockSync.cpp" | 833 +++++ .../bcos-sync/bcos-sync/BlockSync.h" | 143 + .../bcos-sync/bcos-sync/BlockSyncConfig.cpp" | 174 + .../bcos-sync/bcos-sync/BlockSyncConfig.h" | 191 ++ .../bcos-sync/bcos-sync/BlockSyncFactory.cpp" | 52 + .../bcos-sync/bcos-sync/BlockSyncFactory.h" | 58 + .../interfaces/BlockRequestInterface.h" | 39 + .../interfaces/BlockSyncMsgFactory.h" | 64 + .../interfaces/BlockSyncMsgInterface.h" | 47 + .../interfaces/BlockSyncStatusInterface.h" | 46 + .../interfaces/BlocksMsgInterface.h" | 43 + .../bcos-sync/protocol/PB/BlockRequestImpl.h" | 54 + .../protocol/PB/BlockSyncMsgFactoryImpl.h" | 85 + .../bcos-sync/protocol/PB/BlockSyncMsgImpl.h" | 64 + .../protocol/PB/BlockSyncStatusImpl.cpp" | 64 + .../protocol/PB/BlockSyncStatusImpl.h" | 66 + .../bcos-sync/protocol/PB/BlocksMsgImpl.h" | 75 + .../bcos-sync/protocol/proto/BlockSync.proto" | 21 + .../bcos-sync/state/DownloadRequestQueue.cpp" | 88 + .../bcos-sync/state/DownloadRequestQueue.h" | 77 + .../bcos-sync/state/DownloadingQueue.cpp" | 762 +++++ .../bcos-sync/state/DownloadingQueue.h" | 133 + .../bcos-sync/state/SyncPeerStatus.cpp" | 220 ++ .../bcos-sync/state/SyncPeerStatus.h" | 106 + .../bcos-sync/bcos-sync/utilities/Common.h" | 47 + .../bcos-sync/test/CMakeLists.txt" | 30 + .../test/unittests/faker/FakeConsensus.h" | 94 + .../bcos-sync/test/unittests/main/main.cpp" | 24 + .../unittests/protocol/BlockSyncMsgTest.cpp" | 160 + .../test/unittests/sync/BlockSyncTest.cpp" | 214 ++ .../test/unittests/sync/SyncConfigTest.cpp" | 83 + .../test/unittests/sync/SyncFixture.h" | 176 + "BFPL\345\243\271/bcos-table/CMakeLists.txt" | 45 + .../bcos-table/src/CacheStorageFactory.cpp" | 13 + .../bcos-table/src/CacheStorageFactory.h" | 44 + .../bcos-table/src/KeyPageStorage.cpp" | 1051 ++++++ .../bcos-table/src/KeyPageStorage.h" | 1258 ++++++++ .../bcos-table/src/StateStorage.h" | 685 ++++ .../bcos-table/src/StateStorageInterface.h" | 127 + .../bcos-table/src/StorageInterface.cpp" | 161 + .../bcos-table/src/StorageWrapper.h" | 167 + "BFPL\345\243\271/bcos-table/src/Table.cpp" | 136 + .../bcos-table/test/CMakeLists.txt" | 31 + .../test/unittests/libtable/Entry.cpp" | 223 ++ .../test/unittests/libtable/Hash.h" | 49 + .../test/unittests/libtable/Table.cpp" | 244 ++ .../test/unittests/libtable/TablePerf.cpp" | 133 + .../libtable/TestKeyPageStorage.cpp" | 2871 +++++++++++++++++ .../unittests/libtable/TestStateStorage.cpp" | 1230 +++++++ .../bcos-table/test/unittests/main/main.cpp" | 24 + .../mock/MockTransactionalStorage.h" | 83 + .../bcos-tars-protocol/CMakeLists.txt" | 54 + .../bcos-tars-protocol/Common.h" | 398 +++ .../bcos-tars-protocol/ErrorConverter.h" | 95 + .../client/ExecutorServiceClient.cpp" | 588 ++++ .../client/ExecutorServiceClient.h" | 107 + .../client/FrontServiceClient.h" | 257 ++ .../client/GatewayServiceClient.cpp" | 23 + .../client/GatewayServiceClient.h" | 443 +++ .../client/LedgerServiceClient.cpp" | 337 ++ .../client/LedgerServiceClient.h" | 115 + .../client/PBFTServiceClient.cpp" | 207 ++ .../client/PBFTServiceClient.h" | 219 ++ .../client/RpcServiceClient.cpp" | 23 + .../client/RpcServiceClient.h" | 251 ++ .../client/SchedulerServiceClient.cpp" | 246 ++ .../client/SchedulerServiceClient.h" | 84 + .../client/TxPoolServiceClient.h" | 500 +++ .../bcos-tars-protocol/doc/README_CN.md" | 29 + .../bcos-tars-protocol/impl/TarsHashable.h" | 140 + .../bcos-tars-protocol/impl/TarsOutput.h" | 13 + .../impl/TarsSerializable.h" | 37 + .../impl/TarsServantProxyCallback.h" | 216 ++ .../bcos-tars-protocol/impl/TarsStruct.h" | 19 + .../protocol/BlockFactoryImpl.h" | 105 + .../protocol/BlockHeaderFactoryImpl.h" | 64 + .../protocol/BlockHeaderImpl.cpp" | 172 + .../protocol/BlockHeaderImpl.h" | 204 ++ .../protocol/BlockImpl.cpp" | 165 + .../bcos-tars-protocol/protocol/BlockImpl.h" | 205 ++ .../protocol/ExecutionMessageImpl.cpp" | 86 + .../protocol/ExecutionMessageImpl.h" | 236 ++ .../protocol/ExecutionResultImpl.h" | 62 + .../protocol/GroupInfoCodecImpl.h" | 64 + .../protocol/GroupNodeInfoImpl.h" | 127 + .../bcos-tars-protocol/protocol/MemberImpl.h" | 86 + .../protocol/ProtocolInfoCodecImpl.cpp" | 51 + .../protocol/ProtocolInfoCodecImpl.h" | 43 + .../protocol/RouterTable.tars" | 13 + .../protocol/TransactionFactoryImpl.h" | 100 + .../protocol/TransactionImpl.cpp" | 79 + .../protocol/TransactionImpl.h" | 100 + .../protocol/TransactionMetaDataImpl.h" | 86 + .../protocol/TransactionReceiptFactoryImpl.h" | 96 + .../protocol/TransactionReceiptImpl.cpp" | 70 + .../protocol/TransactionReceiptImpl.h" | 112 + .../TransactionSubmitResultFactoryImpl.h" | 50 + .../protocol/TransactionSubmitResultImpl.h" | 117 + .../bcos-tars-protocol/tars/Block.tars" | 50 + .../tars/CommonProtocol.tars" | 6 + .../tars/ExecutionMessage.tars" | 33 + .../tars/ExecutionResult.tars" | 9 + .../tars/ExecutorService.tars" | 32 + .../tars/ExecutorStatus.tars" | 7 + .../tars/FrontService.tars" | 22 + .../bcos-tars-protocol/tars/GatewayInfo.tars" | 32 + .../tars/GatewayService.tars" | 32 + .../bcos-tars-protocol/tars/GroupInfo.tars" | 25 + .../tars/LedgerConfig.tars" | 19 + .../tars/LedgerService.tars" | 23 + .../bcos-tars-protocol/tars/LightNode.tars" | 68 + .../bcos-tars-protocol/tars/Member.tars" | 6 + .../bcos-tars-protocol/tars/PBFTService.tars" | 19 + .../tars/ProtocolInfo.tars" | 9 + .../bcos-tars-protocol/tars/RpcService.tars" | 12 + .../tars/SchedulerService.tars" | 21 + .../tars/StorageService.tars" | 52 + .../bcos-tars-protocol/tars/Transaction.tars" | 22 + .../tars/TransactionMetaData.tars" | 9 + .../tars/TransactionReceipt.tars" | 23 + .../tars/TransactionSubmitResult.tars" | 15 + .../bcos-tars-protocol/tars/TwoPCParams.tars" | 8 + .../tars/TxPoolService.tars" | 25 + .../bcos-tars-protocol/testutil/FakeBlock.h" | 228 ++ .../testutil/FakeBlockHeader.h" | 230 ++ .../testutil/FakeTransaction.h" | 122 + .../testutil/FakeTransactionReceipt.h" | 135 + .../bcos-tars-protocol/test/CMakeLists.txt" | 31 + .../bcos-tars-protocol/test/ProtocolTest.cpp" | 695 ++++ .../test/ProxyCallbackTest.cpp" | 149 + .../test/ServiceClientTest.cpp" | 137 + .../bcos-tars-protocol/test/main.cpp" | 3 + "BFPL\345\243\271/bcos-tool/CMakeLists.txt" | 50 + .../bcos-tool/bcos-tool/BfsFileFactory.cpp" | 166 + .../bcos-tool/bcos-tool/BfsFileFactory.h" | 107 + .../bcos-tool/bcos-tool/ConsensusNode.h" | 70 + .../bcos-tool/bcos-tool/Exceptions.h" | 58 + .../bcos-tool/LedgerConfigFetcher.cpp" | 204 ++ .../bcos-tool/LedgerConfigFetcher.h" | 72 + .../bcos-tool/bcos-tool/NodeConfig.cpp" | 874 +++++ .../bcos-tool/bcos-tool/NodeConfig.h" | 364 +++ .../bcos-tool/NodeTimeMaintenance.cpp" | 105 + .../bcos-tool/NodeTimeMaintenance.h" | 44 + .../bcos-tool/bcos-tool/VersionConverter.h" | 77 + .../bcos-tool/test/CMakeLists.txt" | 28 + .../libtool/NodeTimeMaintenanceTest.cpp" | 89 + .../unittests/libtool/VersionConvert.cpp" | 24 + .../bcos-tool/test/unittests/main/main.cpp" | 24 + "BFPL\345\243\271/bcos-txpool/CMakeLists.txt" | 45 + .../bcos-txpool/bcos-txpool/CMakeLists.txt" | 32 + .../bcos-txpool/bcos-txpool/Sync.h" | 21 + .../bcos-txpool/bcos-txpool/TxPool.cpp" | 498 +++ .../bcos-txpool/bcos-txpool/TxPool.h" | 172 + .../bcos-txpool/bcos-txpool/TxPoolConfig.h" | 86 + .../bcos-txpool/TxPoolFactory.cpp" | 78 + .../bcos-txpool/bcos-txpool/TxPoolFactory.h" | 57 + .../bcos-txpool/sync/TransactionSync.cpp" | 782 +++++ .../bcos-txpool/sync/TransactionSync.h" | 154 + .../bcos-txpool/sync/TransactionSyncConfig.h" | 85 + .../interfaces/TransactionSyncInterface.h" | 54 + .../sync/interfaces/TxsSyncMsgFactory.h" | 56 + .../sync/interfaces/TxsSyncMsgInterface.h" | 58 + .../sync/protocol/PB/TxsSyncMsg.cpp" | 100 + .../sync/protocol/PB/TxsSyncMsg.h" | 62 + .../sync/protocol/PB/TxsSyncMsgFactoryImpl.h" | 43 + .../sync/protocol/proto/TxsSync.proto" | 9 + .../bcos-txpool/sync/utilities/Common.h" | 39 + .../interfaces/NonceCheckerInterface.h" | 54 + .../interfaces/TxPoolStorageInterface.h" | 125 + .../txpool/interfaces/TxValidatorInterface.h" | 49 + .../txpool/storage/MemoryStorage.cpp" | 1147 +++++++ .../txpool/storage/MemoryStorage.h" | 172 + .../txpool/validator/LedgerNonceChecker.cpp" | 99 + .../txpool/validator/LedgerNonceChecker.h" | 65 + .../txpool/validator/TxPoolNonceChecker.cpp" | 92 + .../txpool/validator/TxPoolNonceChecker.h" | 49 + .../txpool/validator/TxValidator.cpp" | 84 + .../txpool/validator/TxValidator.h" | 63 + .../bcos-txpool/test/CMakeLists.txt" | 33 + .../bcos-txpool/test/demo/txpool_demo.cpp" | 99 + .../bcos-txpool/test/unittests/main/main.cpp" | 24 + .../test/unittests/sync/FakeTxsSyncMsg.h" | 65 + .../test/unittests/sync/TxsSyncMsgTest.cpp" | 49 + .../test/unittests/txpool/TxPoolFixture.h" | 269 ++ .../test/unittests/txpool/TxPoolTest.cpp" | 646 ++++ .../test/unittests/txpool/TxsSyncTest.cpp" | 295 ++ .../bcos-utilities/CMakeLists.txt" | 22 + .../bcos-utilities/bcos-utilities/Base64.cpp" | 71 + .../bcos-utilities/bcos-utilities/Base64.h" | 31 + .../bcos-utilities/BoostLog.cpp" | 47 + .../bcos-utilities/bcos-utilities/BoostLog.h" | 86 + .../bcos-utilities/BoostLogInitializer.cpp" | 218 ++ .../bcos-utilities/BoostLogInitializer.h" | 150 + .../CallbackCollectionHandler.h" | 102 + .../bcos-utilities/bcos-utilities/Common.cpp" | 175 + .../bcos-utilities/bcos-utilities/Common.h" | 307 ++ .../bcos-utilities/ConcurrentQueue.h" | 81 + .../bcos-utilities/DataConvertUtility.cpp" | 95 + .../bcos-utilities/DataConvertUtility.h" | 384 +++ .../bcos-utilities/bcos-utilities/Error.h" | 136 + .../bcos-utilities/Exceptions.h" | 66 + .../bcos-utilities/FileUtility.cpp" | 62 + .../bcos-utilities/FileUtility.h" | 44 + .../bcos-utilities/FixedBytes.cpp" | 27 + .../bcos-utilities/FixedBytes.h" | 727 +++++ .../bcos-utilities/IOServicePool.h" | 112 + .../bcos-utilities/JsonDataConvertUtility.h" | 121 + .../bcos-utilities/bcos-utilities/Log.h" | 3 + .../bcos-utilities/bcos-utilities/Ranges.h" | 9 + .../bcos-utilities/RefDataContainer.h" | 213 ++ .../bcos-utilities/ThreadPool.h" | 77 + .../bcos-utilities/bcos-utilities/Timer.cpp" | 117 + .../bcos-utilities/bcos-utilities/Timer.h" | 113 + .../bcos-utilities/bcos-utilities/Worker.cpp" | 147 + .../bcos-utilities/bcos-utilities/Worker.h" | 101 + .../bcos-utilities/ZstdCompress.cpp" | 94 + .../bcos-utilities/ZstdCompress.h" | 37 + .../bcos-utilities/test/CMakeLists.txt" | 28 + .../unittests/libutilities/Base64Test.cpp" | 98 + .../unittests/libutilities/CommonTest.cpp" | 217 ++ .../libutilities/DataConvertUtilityTest.cpp" | 131 + .../JsonDataConvertUtilityTest.cpp" | 110 + .../libutilities/RefDataContainerTest.cpp" | 178 + .../libutilities/TestFixedBytes.cpp" | 56 + .../unittests/libutilities/WorkerTest.cpp" | 71 + .../libutilities/ZstdCompressTest.cpp" | 53 + .../test/unittests/main/main.cpp" | 23 + .../testutils/TestPromptFixture.h" | 93 + "BFPL\345\243\271/benchmark/CMakeLists.txt" | 4 + "BFPL\345\243\271/benchmark/merkleBench.cpp" | 128 + .../cmake/CompilerSettings.cmake" | 185 ++ "BFPL\345\243\271/cmake/Coverage.cmake" | 31 + .../cmake/GenerateSources.cmake" | 21 + .../cmake/IncludeDirectories.cmake" | 15 + "BFPL\345\243\271/cmake/Options.cmake" | 144 + .../cmake/ProjectBCOSWASM.cmake" | 69 + .../cmake/ProjectGroupSig.cmake" | 56 + .../cmake/ProjectTCMalloc.cmake" | 43 + .../cmake/ProjectTiKVClient.cmake" | 37 + "BFPL\345\243\271/cmake/ProjectWABT.cmake" | 40 + .../cmake/SearchTestCases.cmake" | 65 + "BFPL\345\243\271/cmake/TargetSettings.cmake" | 72 + "BFPL\345\243\271/cmake/Version.cmake" | 2 + .../cmake/fiscobcos-config.cmake.in" | 5 + .../cmake/shell/check-commit.sh" | 110 + "BFPL\345\243\271/cmake/test/CMakeLists.txt" | 36 + "BFPL\345\243\271/concepts/CMakeLists.txt" | 12 + .../concepts/bcos-concepts/Basic.h" | 62 + .../concepts/bcos-concepts/ByteBuffer.h" | 45 + .../concepts/bcos-concepts/Exception.h" | 15 + .../concepts/bcos-concepts/Hash.h" | 26 + .../concepts/bcos-concepts/Serialize.h" | 30 + .../concepts/bcos-concepts/front/Front.h" | 45 + .../concepts/bcos-concepts/ledger/Ledger.h" | 122 + .../concepts/bcos-concepts/protocol/Block.h" | 78 + .../bcos-concepts/protocol/Receipt.h" | 38 + .../bcos-concepts/protocol/Transaction.h" | 39 + .../bcos-concepts/scheduler/Scheduler.h" | 27 + .../concepts/bcos-concepts/storage/Storage.h" | 42 + .../transaction_pool/TransactionPool.h" | 28 + .../concepts/tests/testTask.cpp" | 0 "BFPL\345\243\271/docs/FISCO_BCOS_Logo.svg" | 15 + "BFPL\345\243\271/docs/README_EN.md" | 57 + .../fisco-bcos-air/AirNodeInitializer.cpp" | 132 + .../fisco-bcos-air/AirNodeInitializer.h" | 57 + .../fisco-bcos-air/CMakeLists.txt" | 5 + "BFPL\345\243\271/fisco-bcos-air/Common.h" | 59 + "BFPL\345\243\271/fisco-bcos-air/main.cpp" | 86 + .../fisco-bcos-demo/CMakeLists.txt" | 14 + .../distributed_ratelimiter_checker.cpp" | 219 ++ .../fisco-bcos-demo/echo_client_sample.cpp" | 111 + .../fisco-bcos-demo/echo_server_sample.cpp" | 78 + .../fisco-bcos-tars-service/CMakeLists.txt" | 40 + .../Common/TarsUtils.h" | 182 ++ .../ExecutorServiceServer.cpp" | 259 ++ .../ExecutorService/ExecutorServiceServer.h" | 81 + .../ExecutorService/main/CMakeLists.txt" | 7 + .../main/ExecutorServiceApp.cpp" | 210 ++ .../main/ExecutorServiceApp.h" | 69 + .../ExecutorService/main/main.cpp" | 54 + .../FrontService/FrontServiceServer.cpp" | 150 + .../FrontService/FrontServiceServer.h" | 59 + .../GatewayService/GatewayInitializer.cpp" | 124 + .../GatewayService/GatewayInitializer.h" | 71 + .../GatewayService/GatewayServiceServer.cpp" | 65 + .../GatewayService/GatewayServiceServer.h" | 150 + .../GatewayService/main/Application.cpp" | 125 + .../GatewayService/main/CMakeLists.txt" | 18 + .../LedgerService/CMakeLists.txt" | 7 + .../LedgerService/LedgerServiceServer.cpp" | 230 ++ .../LedgerService/LedgerServiceServer.h" | 67 + .../NodeService/NodeServiceApp.cpp" | 194 ++ .../NodeService/NodeServiceApp.h" | 68 + .../NodeService/max/CMakeLists.txt" | 16 + .../NodeService/max/main.cpp" | 55 + .../NodeService/pro/CMakeLists.txt" | 16 + .../NodeService/pro/main.cpp" | 59 + .../PBFTService/PBFTServiceClient.h" | 198 ++ .../PBFTService/PBFTServiceServer.cpp" | 147 + .../PBFTService/PBFTServiceServer.h" | 108 + .../RpcService/RpcInitializer.cpp" | 164 + .../RpcService/RpcInitializer.h" | 73 + .../RpcService/RpcServiceServer.cpp" | 67 + .../RpcService/RpcServiceServer.h" | 37 + .../RpcService/main/Application.cpp" | 127 + .../RpcService/main/CMakeLists.txt" | 14 + .../SchedulerServiceServer.cpp" | 132 + .../SchedulerServiceServer.h" | 76 + .../SchedulerService/main/CMakeLists.txt" | 9 + .../main/SchedulerServiceApp.cpp" | 181 ++ .../main/SchedulerServiceApp.h" | 68 + .../SchedulerService/main/main.cpp" | 54 + .../TxPoolService/TxPoolServiceServer.cpp" | 263 ++ .../TxPoolService/TxPoolServiceServer.h" | 77 + .../libinitializer/AuthInitializer.h" | 76 + .../libinitializer/BfsInitializer.h" | 50 + .../libinitializer/CMakeLists.txt" | 36 + .../libinitializer/CommandHelper.cpp" | 152 + .../libinitializer/CommandHelper.h" | 39 + "BFPL\345\243\271/libinitializer/Common.h" | 91 + .../libinitializer/ExecutorInitializer.h" | 24 + .../FrontServiceInitializer.cpp" | 190 ++ .../libinitializer/FrontServiceInitializer.h" | 85 + .../libinitializer/Initializer.cpp" | 511 +++ .../libinitializer/Initializer.h" | 105 + .../libinitializer/LedgerInitializer.h" | 44 + .../libinitializer/LightNodeInitializer.cpp" | 1 + .../libinitializer/LightNodeInitializer.h" | 306 ++ .../libinitializer/PBFTInitializer.cpp" | 541 ++++ .../libinitializer/PBFTInitializer.h" | 130 + .../libinitializer/ProPBFTInitializer.cpp" | 167 + .../libinitializer/ProPBFTInitializer.h" | 68 + .../libinitializer/ProtocolInitializer.cpp" | 120 + .../libinitializer/ProtocolInitializer.h" | 68 + .../libinitializer/SchedulerInitializer.h" | 72 + .../libinitializer/StorageInitializer.h" | 92 + .../libinitializer/TxPoolInitializer.cpp" | 97 + .../libinitializer/TxPoolInitializer.h" | 68 + "BFPL\345\243\271/libtask/CMakeLists.txt" | 16 + .../libtask/bcos-task/Coroutine.h" | 9 + "BFPL\345\243\271/libtask/bcos-task/Task.h" | 165 + "BFPL\345\243\271/libtask/bcos-task/Wait.h" | 89 + .../libtask/tests/CMakeLists.txt" | 9 + "BFPL\345\243\271/libtask/tests/TaskTest.cpp" | 142 + "BFPL\345\243\271/libtask/tests/main.cpp" | 4 + "BFPL\345\243\271/lightnode/CMakeLists.txt" | 13 + .../lightnode/bcos-lightnode/Log.h" | 5 + .../bcos-lightnode/ledger/LedgerImpl.h" | 586 ++++ .../lightnode/bcos-lightnode/rpc/Converter.h" | 161 + .../bcos-lightnode/rpc/LightNodeRPC.h" | 545 ++++ .../scheduler/SchedulerWrapperImpl.h" | 84 + .../bcos-lightnode/storage/StorageImpl.h" | 97 + .../transaction_pool/TransactionPoolImpl.h" | 61 + .../fisco-bcos-lightnode/CMakeLists.txt" | 6 + .../fisco-bcos-lightnode/RPCInitializer.h" | 186 ++ .../client/LedgerClientImpl.h" | 133 + .../client/P2PClientImpl.h" | 195 ++ .../client/SchedulerClientImpl.h" | 41 + .../client/TransactionPoolClientImpl.h" | 46 + .../lightnode/fisco-bcos-lightnode/main.cpp" | 229 ++ .../lightnode/tests/CMakeLists.txt" | 10 + .../lightnode/tests/LedgerTest.cpp" | 382 +++ .../lightnode/tests/P2PClientTest.cpp" | 15 + .../lightnode/tests/RPCTest.cpp" | 0 .../lightnode/tests/SchedulerTest.cpp" | 0 .../lightnode/tests/StorageTest.cpp" | 28 + .../lightnode/tests/TransactionPoolTest.cpp" | 118 + "BFPL\345\243\271/lightnode/tests/main.cpp" | 4 + "BFPL\345\243\271/tests/CMakeLists.txt" | 15 + "BFPL\345\243\271/tests/perf/CMakeLists.txt" | 3 + "BFPL\345\243\271/tests/perf/benchmark.cpp" | 348 ++ "BFPL\345\243\271/tests/unittest/main.cpp" | 4 + "BFPL\345\243\271/tools/.ci/Dockerfile" | 45 + "BFPL\345\243\271/tools/.ci/Dockerfile_env" | 13 + "BFPL\345\243\271/tools/.ci/check-commit.sh" | 123 + "BFPL\345\243\271/tools/.ci/ci_check_air.sh" | 173 + "BFPL\345\243\271/tools/.ci/ci_check_pro.sh" | 193 ++ .../tools/.ci/clear_build_cache.sh" | 88 + "BFPL\345\243\271/tools/.ci/requirements.txt" | 2 + .../linux/framework/docker-compose.yml" | 45 + .../bridge/linux/node/docker-compose.yml" | 23 + .../bridge/linux/node/gen_compose_files.sh" | 38 + .../bridge/mac/framework/docker-compose.yml" | 57 + .../bridge/mac/node/docker-compose.yml" | 29 + .../host/linux/framework/docker-compose.yml" | 29 + .../docker/host/linux/monitor/compose.yaml" | 32 + .../host/linux/monitor/grafana/grafana.ini" | 13 + .../linux/monitor/prometheus/prometheus.yml" | 19 + .../host/linux/monitor/start_monitor.sh" | 17 + .../host/linux/monitor/stop_monitor.sh" | 17 + .../host/linux/node/docker-compose.yml" | 12 + .../max/conf/config-deploy-example.toml" | 76 + .../max/conf/config-node-expand-example.toml" | 41 + .../conf/config-service-expand-example.toml" | 36 + .../pro/conf/config-build-example.toml" | 143 + .../pro/conf/config-deploy-example.toml" | 109 + .../pro/conf/config-node-expand-example.toml" | 43 + .../conf/config-service-expand-example.toml" | 35 + .../tools/BcosBuilder/requirements.txt" | 5 + .../src/command/monitor_command_impl.py" | 35 + .../src/command/node_command_impl.py" | 75 + .../src/command/service_command_impl.py" | 129 + .../BcosBuilder/src/common/parser_handler.py" | 454 +++ .../BcosBuilder/src/common/utilities.py" | 360 +++ .../BcosBuilder/src/config/chain_config.py" | 398 +++ .../src/config/max_node_config_generator.py" | 124 + .../src/config/monitor_config_generator.py" | 230 ++ .../src/config/node_config_generator.py" | 543 ++++ .../src/config/service_config_generator.py" | 397 +++ .../src/config/tars_config_generator.py" | 40 + .../src/controller/binary_controller.py" | 95 + .../src/controller/monitor_controller.py" | 37 + .../src/controller/node_controller.py" | 222 ++ .../src/controller/service_controller.py" | 215 ++ .../src/networkmgr/network_manager.py" | 64 + .../BcosBuilder/src/scripts/generate_cert.sh" | 688 ++++ .../BcosBuilder/src/scripts/mtail/node.mtail" | 51 + .../src/scripts/mtail/start_mtail_monitor.sh" | 44 + .../src/scripts/mtail/stop_mtail_monitor.sh" | 36 + .../src/service/key_center_service.py" | 69 + .../BcosBuilder/src/service/tars_service.py" | 551 ++++ .../tools/BcosBuilder/src/tpl/config.genesis" | 34 + .../BcosBuilder/src/tpl/config.ini.executor" | 27 + .../BcosBuilder/src/tpl/config.ini.gateway" | 77 + .../BcosBuilder/src/tpl/config.ini.node" | 57 + .../tools/BcosBuilder/src/tpl/config.ini.rpc" | 46 + .../BcosBuilder/src/tpl/tars_gateway.conf" | 40 + .../tools/BcosBuilder/src/tpl/tars_node.conf" | 80 + .../tools/BcosBuilder/src/tpl/tars_rpc.conf" | 40 + .../tools/BcosBuilder/src/tpl/tars_start.sh" | 33 + .../BcosBuilder/src/tpl/tars_start_all.sh" | 14 + .../tools/BcosBuilder/src/tpl/tars_stop.sh" | 30 + .../BcosBuilder/src/tpl/tars_stop_all.sh" | 14 + .../tools/template/Dashboard.json" | 1084 +++++++ "BFPL\345\243\271/vcpkg-configuration.json" | 21 + "BFPL\345\243\271/vcpkg.json" | 112 + 1258 files changed, 200080 insertions(+) create mode 100644 "BFPL\345\243\271/.circleci/config.yml" create mode 100644 "BFPL\345\243\271/.clang-format" create mode 100644 "BFPL\345\243\271/.clang-tidy" create mode 100644 "BFPL\345\243\271/.gitignore" create mode 100644 "BFPL\345\243\271/.gitmodules" create mode 100644 "BFPL\345\243\271/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/ChangeLog.md" create mode 100644 "BFPL\345\243\271/LICENSE" create mode 100644 "BFPL\345\243\271/README.md" create mode 100644 "BFPL\345\243\271/bcos-boostssl/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/Common.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/ContextBuilder.cpp" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/ContextBuilder.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/ContextConfig.cpp" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/ContextConfig.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/NodeInfoTools.cpp" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/NodeInfoTools.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/Common.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpQueue.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpServer.cpp" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpServer.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpSession.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpStream.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/interfaces/MessageFace.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/interfaces/NodeInfoDef.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/Common.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsConfig.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsConnector.cpp" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsConnector.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsError.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsInitializer.cpp" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsInitializer.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsMessage.cpp" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsMessage.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsService.cpp" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsService.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsSession.cpp" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsSession.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsStream.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsTools.cpp" create mode 100644 "BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsTools.h" create mode 100644 "BFPL\345\243\271/bcos-boostssl/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-boostssl/test/exec/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-boostssl/test/exec/boostssl_delay_perf.cpp" create mode 100644 "BFPL\345\243\271/bcos-boostssl/test/exec/boostssl_throughput_perf.cpp" create mode 100644 "BFPL\345\243\271/bcos-boostssl/test/exec/echo_client_sample.cpp" create mode 100644 "BFPL\345\243\271/bcos-boostssl/test/exec/echo_server_sample.cpp" create mode 100644 "BFPL\345\243\271/bcos-boostssl/test/exec/http_server_sample.cpp" create mode 100644 "BFPL\345\243\271/bcos-boostssl/test/exec/msg_codec_perf.cpp" create mode 100644 "BFPL\345\243\271/bcos-boostssl/test/unittests/websocket/WsConfigTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-boostssl/test/unittests/websocket/WsConnectorTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-boostssl/test/unittests/websocket/WsMessageTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-codec/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-codec/bcos-codec/abi/ContractABICodec.cpp" create mode 100644 "BFPL\345\243\271/bcos-codec/bcos-codec/abi/ContractABICodec.h" create mode 100644 "BFPL\345\243\271/bcos-codec/bcos-codec/abi/ContractABIType.cpp" create mode 100644 "BFPL\345\243\271/bcos-codec/bcos-codec/abi/ContractABIType.h" create mode 100644 "BFPL\345\243\271/bcos-codec/bcos-codec/scale/Common.h" create mode 100644 "BFPL\345\243\271/bcos-codec/bcos-codec/scale/Exceptions.h" create mode 100644 "BFPL\345\243\271/bcos-codec/bcos-codec/scale/FixedWidthIntegerCodec.h" create mode 100644 "BFPL\345\243\271/bcos-codec/bcos-codec/scale/Scale.h" create mode 100644 "BFPL\345\243\271/bcos-codec/bcos-codec/scale/ScaleDecoderStream.cpp" create mode 100644 "BFPL\345\243\271/bcos-codec/bcos-codec/scale/ScaleDecoderStream.h" create mode 100644 "BFPL\345\243\271/bcos-codec/bcos-codec/scale/ScaleEncoderStream.cpp" create mode 100644 "BFPL\345\243\271/bcos-codec/bcos-codec/scale/ScaleEncoderStream.h" create mode 100644 "BFPL\345\243\271/bcos-codec/bcos-codec/wrapper/CodecWrapper.h" create mode 100644 "BFPL\345\243\271/bcos-codec/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-codec/test/unittests/ContractABICodecTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-codec/test/unittests/ScaleCodecTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-codec/test/unittests/main/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/ChecksumAddress.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/TrivialObject.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/digestsign/DigestSign.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/digestsign/OpenSSLDigestSign.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/AESCrypto.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/AESCrypto.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/Exceptions.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/SM4Crypto.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/SM4Crypto.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/hash/Keccak256.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/hash/SM3.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/hash/Sha256.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/hash/Sha3.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/hasher/AnyHasher.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/hasher/Hasher.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/hasher/IPPCryptoHasher.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/hasher/OpenSSLHasher.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/CommonType.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/CryptoSuite.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/Hash.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/KeyFactory.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/KeyInterface.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/KeyPairFactory.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/KeyPairInterface.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/Signature.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/SymmetricEncryption.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/merkle/Merkle.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/Exceptions.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/codec/SignatureData.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/codec/SignatureDataWithPub.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/codec/SignatureDataWithV.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/ed25519/Ed25519Crypto.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/ed25519/Ed25519Crypto.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/ed25519/Ed25519KeyPair.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/ed25519/Ed25519KeyPair.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/FastSM2Crypto.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/FastSM2KeyPair.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/FastSM2KeyPairFactory.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/fast_sm2.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/fast_sm2.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/key/KeyFactoryImpl.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/key/KeyImpl.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/key/KeyPair.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1Crypto.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1Crypto.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1KeyPair.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1KeyPair.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2Crypto.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2Crypto.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2KeyPair.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2KeyPair.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2KeyPairFactory.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/zkp/Common.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/zkp/Exceptions.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/zkp/discretezkp/DiscreteLogarithmZkp.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/bcos-crypto/zkp/discretezkp/DiscreteLogarithmZkp.h" create mode 100644 "BFPL\345\243\271/bcos-crypto/demo/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-crypto/demo/hasher_test.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/demo/perf_demo.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-crypto/test/unittests/EncryptionTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/test/unittests/HashTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/test/unittests/HasherTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/test/unittests/SignatureTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/test/unittests/ZkpTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/test/unittests/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-crypto/test/unittests/testMerkle.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-executor/src/CallParameters.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/Common.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/Common.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/dag/Abi.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/dag/Abi.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/dag/ClockCache.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/dag/ClockCache.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/dag/CriticalFields.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/dag/DAG.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/dag/DAG.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/dag/ScaleUtils.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/dag/ScaleUtils.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/dag/TxDAG2.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/dag/TxDAG2.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/dag/TxDAGInterface.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executive/BlockContext.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executive/BlockContext.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executive/CoroutineTransactionExecutive.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executive/CoroutineTransactionExecutive.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executive/ExecutiveFactory.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executive/ExecutiveFactory.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executive/ExecutiveFlowInterface.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executive/ExecutiveSerialFlow.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executive/ExecutiveSerialFlow.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executive/ExecutiveStackFlow.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executive/ExecutiveStackFlow.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executive/ExecutiveState.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executive/ExecutiveState.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executive/LedgerCache.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executive/SyncStorageWrapper.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executive/TransactionExecutive.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executive/TransactionExecutive.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executor/ExecuteOutputs.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executor/SwitchExecutorManager.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executor/TransactionExecutor.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executor/TransactionExecutor.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/executor/TransactionExecutorFactory.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/BFSPrecompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/BFSPrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/ConsensusPrecompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/ConsensusPrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/CryptoPrecompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/CryptoPrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/KVTablePrecompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/KVTablePrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/SystemConfigPrecompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/SystemConfigPrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/TableManagerPrecompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/TableManagerPrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/TablePrecompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/TablePrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/common/Common.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/common/PrecompiledAbi.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/common/PrecompiledGas.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/common/PrecompiledGas.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/common/PrecompiledResult.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/common/Utilities.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/common/Utilities.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/AccountManagerPrecompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/AccountManagerPrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/AccountPrecompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/AccountPrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/AuthManagerPrecompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/AuthManagerPrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/ContractAuthMgrPrecompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/ContractAuthMgrPrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/CpuHeavyPrecompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/CpuHeavyPrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/DagTransferPrecompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/DagTransferPrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/GroupSigPrecompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/GroupSigPrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/HelloWorldPrecompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/HelloWorldPrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/PermissionPrecompiledInterface.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/RingSigPrecompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/RingSigPrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/SmallBankPrecompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/SmallBankPrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/UserPrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/ZkpPrecompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/extension/ZkpPrecompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/solidity/Account.sol" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/solidity/BfsPrecompiled.sol" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/solidity/ConsensusPrecompiled.sol" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/solidity/ContractAuthPrecompiled.sol" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/solidity/Crypto.sol" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/solidity/GroupSigPrecompiled.sol" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/solidity/RingSigPrecompiled.sol" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/solidity/SystemConfigPrecompiled.sol" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/solidity/Table.sol" create mode 100644 "BFPL\345\243\271/bcos-executor/src/precompiled/solidity/ZkpPrecompiled.sol" create mode 100644 "BFPL\345\243\271/bcos-executor/src/vm/DelegateHostContext.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/vm/DelegateHostContext.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/vm/EVMHostInterface.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/vm/EVMHostInterface.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/vm/HostContext.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/vm/HostContext.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/vm/Precompiled.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/vm/Precompiled.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/vm/VMFactory.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/vm/VMFactory.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/vm/VMInstance.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/vm/VMInstance.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/vm/gas_meter/GasInjector.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/vm/gas_meter/GasInjector.h" create mode 100644 "BFPL\345\243\271/bcos-executor/src/vm/gas_meter/Metric.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/src/vm/gas_meter/Metric.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-executor/test/flow-graph/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-executor/test/flow-graph/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/liquid/hello_world.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/liquid/hello_world.rs" create mode 100644 "BFPL\345\243\271/bcos-executor/test/liquid/hello_world.wasm" create mode 100644 "BFPL\345\243\271/bcos-executor/test/liquid/hello_world_caller.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/liquid/hello_world_caller.rs" create mode 100644 "BFPL\345\243\271/bcos-executor/test/liquid/hello_world_caller.wasm" create mode 100644 "BFPL\345\243\271/bcos-executor/test/liquid/transfer.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/liquid/transfer.rs" create mode 100644 "BFPL\345\243\271/bcos-executor/test/liquid/transfer.wasm" create mode 100644 "BFPL\345\243\271/bcos-executor/test/old/EVMPrecompiledTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/old/MemoryStorage.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/old/StateTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/old/libexecutor/DAGTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/old/libexecutor/ExecutorTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/old/mock/MemoryStorage.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/old/mock/MockDispatcher.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/old/mock/MockLedger.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/solidity/ParallelOk.sol" create mode 100644 "BFPL\345\243\271/bcos-executor/test/solidity/TestEvmPrecompiled.sol" create mode 100644 "BFPL\345\243\271/bcos-executor/test/solidity/precompiled/ConsensusPrecompiled.sol" create mode 100644 "BFPL\345\243\271/bcos-executor/test/solidity/precompiled/Crypto.sol" create mode 100644 "BFPL\345\243\271/bcos-executor/test/solidity/precompiled/SystemConfigPrecompiled.sol" create mode 100644 "BFPL\345\243\271/bcos-executor/test/solidity/test_config.sol" create mode 100644 "BFPL\345\243\271/bcos-executor/test/solidity/test_crypto.sol" create mode 100644 "BFPL\345\243\271/bcos-executor/test/solidity/test_external_call.sol" create mode 100644 "BFPL\345\243\271/bcos-executor/test/trie-test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-executor/test/trie-test/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/container/TestHashMap.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestAbiReader.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestBlockContext.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestClockCache.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestDagExecutor.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestEVMExecutor.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestExecutiveStackFlow.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestExecutiveState.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestLedgerCache.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestScaleUtils.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestTxDAG.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestWasmExecutor.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TxDAG.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TxDAG.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/AccountPrecompiledTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/AuthPrecompiledTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/ConfigPrecompiledTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/CryptoPrecompiledTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/EVMStateContextTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/FileSystemPrecompiledTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/GroupSigPrecompiledTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/KVTablePrecompiledTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/PreCompiledFixture.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/PrecompiledCallTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/PrecompiledGas.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/RingSigPrecompiledTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/TablePrecompiledTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/VRFPrecompiledTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libvm/.gitignore" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libvm/TestTransactionExecutive.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/libvm/WasmPath.h.in" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/mock/MockBlock.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/mock/MockBlockHeader.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/mock/MockExecutiveFactory.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/mock/MockExecutiveFlow.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/mock/MockKeyPageStorage.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/mock/MockLedger.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/mock/MockTransactionExecutive.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/mock/MockTransactionalStorage.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/unittest/mock/MockTxPool.h" create mode 100644 "BFPL\345\243\271/bcos-executor/test/wasm/infinit_loop.wasm" create mode 100644 "BFPL\345\243\271/bcos-executor/test/wasm/metric_infinit_loop_global_gas.wasm" create mode 100644 "BFPL\345\243\271/bcos-executor/test/wasm/metric_infinit_loop_useGas.wasm" create mode 100644 "BFPL\345\243\271/bcos-executor/tools/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-executor/tools/inject_meter.cpp" create mode 100644 "BFPL\345\243\271/bcos-framework/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/Common.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/consensus/ConsensusInterface.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/consensus/ConsensusNode.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/consensus/ConsensusNodeInterface.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/consensus/ConsensusTypeDef.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/dispatcher/SchedulerInterface.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/dispatcher/SchedulerTypeDef.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/election/FailOverTypeDef.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/election/LeaderElectionInterface.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/election/LeaderEntryPointInterface.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/executor/ExecuteError.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/executor/ExecutionMessage.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/executor/ExecutorStatus.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/executor/NativeExecutionMessage.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/executor/ParallelTransactionExecutorInterface.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/executor/PrecompiledTypeDef.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/executor/SerialTransactionExecutor.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/front/FrontServiceInterface.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/gateway/GatewayInterface.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/gateway/GatewayTypeDef.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/gateway/GroupNodeInfo.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/ledger/GenesisConfig.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/ledger/LedgerConfig.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/ledger/LedgerInterface.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/ledger/LedgerTypeDef.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/ChainNodeInfo.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/ChainNodeInfoFactory.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/GroupInfo.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/GroupInfoCodec.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/GroupInfoFactory.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/GroupTypeDef.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/AMOPRequest.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/Block.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/BlockFactory.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/BlockHeader.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/BlockHeaderFactory.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/CommonError.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/Exceptions.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/GlobalConfig.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/LogEntry.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/MemberInterface.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/Protocol.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/ProtocolInfo.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/ProtocolInfoCodec.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/ProtocolTypeDef.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/ServiceDesc.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/Transaction.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionFactory.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionMetaData.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionReceipt.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionReceiptFactory.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionSubmitResult.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionSubmitResultFactory.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/rpc/HandshakeRequest.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/rpc/RPCInterface.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/sealer/SealerInterface.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/security/DataEncryptInterface.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/storage/Common.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/storage/Entry.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/storage/KVStorageHelper.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/storage/StorageInterface.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/storage/Table.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/sync/BlockSyncInterface.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/sync/SyncConfig.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/testutils/TestPromptFixture.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeFrontService.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeKVStorage.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeLedger.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeScheduler.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeSealer.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeTxPool.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/txpool/TxPoolInterface.h" create mode 100644 "BFPL\345\243\271/bcos-framework/bcos-framework/txpool/TxPoolTypeDef.h" create mode 100644 "BFPL\345\243\271/bcos-framework/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-framework/test/unittests/interfaces/ConsensusNodeTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-framework/test/unittests/interfaces/ExecutorTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-framework/test/unittests/main/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-front/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-front/bcos-front/Common.h" create mode 100644 "BFPL\345\243\271/bcos-front/bcos-front/FrontImpl.h" create mode 100644 "BFPL\345\243\271/bcos-front/bcos-front/FrontMessage.cpp" create mode 100644 "BFPL\345\243\271/bcos-front/bcos-front/FrontMessage.h" create mode 100644 "BFPL\345\243\271/bcos-front/bcos-front/FrontService.cpp" create mode 100644 "BFPL\345\243\271/bcos-front/bcos-front/FrontService.h" create mode 100644 "BFPL\345\243\271/bcos-front/bcos-front/FrontServiceFactory.cpp" create mode 100644 "BFPL\345\243\271/bcos-front/bcos-front/FrontServiceFactory.h" create mode 100644 "BFPL\345\243\271/bcos-front/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-front/test/unittests/FakeGateway.cpp" create mode 100644 "BFPL\345\243\271/bcos-front/test/unittests/FakeGateway.h" create mode 100644 "BFPL\345\243\271/bcos-front/test/unittests/FrontMessageTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-front/test/unittests/FrontServiceTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/Common.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/Gateway.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/Gateway.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/GatewayConfig.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/GatewayConfig.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/GatewayFactory.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/GatewayFactory.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/FrontServiceInfo.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayMessageExtAttributes.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayNodeManager.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayNodeManager.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayStatus.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayStatus.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/LocalRouterTable.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/LocalRouterTable.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/PeersRouterTable.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/PeersRouterTable.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/ProGatewayNodeManager.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/ProGatewayNodeManager.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AMOPImpl.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AMOPImpl.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AMOPMessage.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AMOPMessage.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AirTopicManager.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/Common.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/TopicManager.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/TopicManager.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/ASIOInterface.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/ASIOInterface.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Common.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Host.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Host.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Message.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Session.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Session.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/SessionFace.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Socket.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/SocketFace.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/Common.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PInterface.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PMessage.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PMessage.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PMessageV2.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PMessageV2.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PSession.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PSession.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/Service.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/Service.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/ServiceV2.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/ServiceV2.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/router/RouterTableImpl.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/router/RouterTableImpl.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/router/RouterTableInterface.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/DistributedRateLimiter.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/DistributedRateLimiter.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/GatewayRateLimiter.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/GatewayRateLimiter.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/ModuleWhiteList.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterFactory.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterInterface.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterManager.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterManager.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterStat.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterStat.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/TokenBucketRateLimiter.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/TokenBucketRateLimiter.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/protocol/GatewayNodeStatus.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/bcos-gateway/protocol/GatewayNodeStatus.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/common/FrontServiceBuilder.h" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/integtests/GatewayTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/main/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/main/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/GatewayConfigTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/GatewayFactoryTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/GatewayMessageTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/GatewayNodeManagerTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/ModuleWhiteListTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/RateLimiterManagerTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/amop/AMOPMessageTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/amop/TopicManagerTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/data/ca/ca.crt" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/data/ca/node.crt" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/data/ca/node.key" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/data/config/config_ipv4.ini" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/data/config/config_ipv6.ini" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/data/config/json/nodes_ipv4.json" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/data/config/json/nodes_ipv6.json" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_ca.crt" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_ennode.crt" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_ennode.key" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_node.crt" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_node.key" create mode 100644 "BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_node.nodeid" create mode 100644 "BFPL\345\243\271/bcos-leader-election/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-leader-election/src/CampaignConfig.cpp" create mode 100644 "BFPL\345\243\271/bcos-leader-election/src/CampaignConfig.h" create mode 100644 "BFPL\345\243\271/bcos-leader-election/src/Common.h" create mode 100644 "BFPL\345\243\271/bcos-leader-election/src/ElectionConfig.cpp" create mode 100644 "BFPL\345\243\271/bcos-leader-election/src/ElectionConfig.h" create mode 100644 "BFPL\345\243\271/bcos-leader-election/src/LeaderElection.cpp" create mode 100644 "BFPL\345\243\271/bcos-leader-election/src/LeaderElection.h" create mode 100644 "BFPL\345\243\271/bcos-leader-election/src/LeaderElectionFactory.h" create mode 100644 "BFPL\345\243\271/bcos-leader-election/src/LeaderEntryPoint.h" create mode 100644 "BFPL\345\243\271/bcos-leader-election/src/WatcherConfig.cpp" create mode 100644 "BFPL\345\243\271/bcos-leader-election/src/WatcherConfig.h" create mode 100644 "BFPL\345\243\271/bcos-ledger/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-ledger/src/libledger/Ledger.cpp" create mode 100644 "BFPL\345\243\271/bcos-ledger/src/libledger/Ledger.h" create mode 100644 "BFPL\345\243\271/bcos-ledger/src/libledger/utilities/Common.h" create mode 100644 "BFPL\345\243\271/bcos-ledger/src/libledger/utilities/MerkleProofUtility.cpp" create mode 100644 "BFPL\345\243\271/bcos-ledger/src/libledger/utilities/MerkleProofUtility.h" create mode 100644 "BFPL\345\243\271/bcos-ledger/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-ledger/test/main/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-ledger/test/mock/MockKeyFactor.h" create mode 100644 "BFPL\345\243\271/bcos-ledger/test/mock/MockStorage.h" create mode 100644 "BFPL\345\243\271/bcos-ledger/test/mock/MockTable.h" create mode 100644 "BFPL\345\243\271/bcos-ledger/test/unittests/ledger/LedgerTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-ledger/test/unittests/ledger/common/FakeBlock.h" create mode 100644 "BFPL\345\243\271/bcos-ledger/test/unittests/ledger/common/FakeBlockHeader.h" create mode 100644 "BFPL\345\243\271/bcos-ledger/test/unittests/ledger/common/FakeReceipt.h" create mode 100644 "BFPL\345\243\271/bcos-ledger/test/unittests/ledger/common/FakeTransaction.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/core/Common.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/core/ConsensusConfig.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/core/ConsensusConfig.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/core/ConsensusEngine.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/core/Proposal.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/core/StateMachine.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/core/StateMachine.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/core/proto/Consensus.proto" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/framework/ConsensusConfigInterface.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/framework/ConsensusEngineInterface.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/framework/ProposalInterface.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/framework/StateMachineInterface.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/PBFTFactory.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/PBFTFactory.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/PBFTImpl.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/PBFTImpl.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCache.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCache.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCacheFactory.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCacheProcessor.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCacheProcessor.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/config/PBFTConfig.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/config/PBFTConfig.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/BlockValidator.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/BlockValidator.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTEngine.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTEngine.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTLogSync.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTLogSync.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTTimer.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/Validator.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/Validator.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/NewViewMsgInterface.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTBaseMessageInterface.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTCodecInterface.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTMessageFactory.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTMessageInterface.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTProposalInterface.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTRequestInterface.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTStorage.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/ViewChangeMsgInterface.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTBaseMessage.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTCodec.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTCodec.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTMessage.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTMessage.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTMessageFactoryImpl.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTNewViewMsg.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTNewViewMsg.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTProposal.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTRequest.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTViewChangeMsg.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTViewChangeMsg.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/proto/PBFT.proto" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/storage/LedgerStorage.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/storage/LedgerStorage.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/utilities/Common.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-pbft/test/unittests/core/TimerTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/test/unittests/main/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/test/unittests/pbft/PBFTConfigTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/test/unittests/pbft/PBFTEngineTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/test/unittests/pbft/PBFTFixture.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/test/unittests/pbft/PBFTViewChangeTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-pbft/test/unittests/protocol/FakePBFTMessage.h" create mode 100644 "BFPL\345\243\271/bcos-pbft/test/unittests/protocol/PBFTMessageTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-protocol/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-protocol/bcos-protocol/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-protocol/bcos-protocol/Common.h" create mode 100644 "BFPL\345\243\271/bcos-protocol/bcos-protocol/ParallelMerkleProof.cpp" create mode 100644 "BFPL\345\243\271/bcos-protocol/bcos-protocol/ParallelMerkleProof.h" create mode 100644 "BFPL\345\243\271/bcos-protocol/bcos-protocol/TransactionStatus.h" create mode 100644 "BFPL\345\243\271/bcos-protocol/bcos-protocol/TransactionSubmitResultFactoryImpl.h" create mode 100644 "BFPL\345\243\271/bcos-protocol/bcos-protocol/TransactionSubmitResultImpl.h" create mode 100644 "BFPL\345\243\271/bcos-protocol/bcos-protocol/amop/TopicItem.h" create mode 100644 "BFPL\345\243\271/bcos-protocol/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-protocol/test/unittests/main/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-rpc/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/Common.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/Rpc.cpp" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/Rpc.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/RpcFactory.cpp" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/RpcFactory.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/amop/AMOPClient.cpp" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/amop/AMOPClient.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/amop/AirAMOPClient.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/event/Common.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSub.cpp" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSub.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubMatcher.cpp" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubMatcher.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubParams.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubRequest.cpp" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubRequest.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubResponse.cpp" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubResponse.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubTask.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/AirGroupManager.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/Common.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/GroupManager.cpp" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/GroupManager.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/NodeService.cpp" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/NodeService.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/TarsGroupManager.cpp" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/TarsGroupManager.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/Common.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/DupTestTxJsonRpcImpl_2_0.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/DuplicateTransactionFactory.cpp" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/DuplicateTransactionFactory.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp" create mode 100644 "BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h" create mode 100644 "BFPL\345\243\271/bcos-rpc/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-rpc/test/unittests/main/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/BlockExecutive.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/BlockExecutive.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/BlockExecutiveFactory.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/BlockExecutiveFactory.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/Common.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/DmcExecutor.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/DmcExecutor.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/DmcStepRecorder.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/DmcStepRecorder.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/Executive.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/ExecutivePool.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/ExecutivePool.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/ExecutorManager.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/ExecutorManager.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/GraphKeyLocks.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/GraphKeyLocks.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/SchedulerFactory.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/SchedulerImpl.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/SchedulerImpl.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/SchedulerManager.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/SchedulerManager.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/SerialBlockExecutive.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/SerialBlockExecutive.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/TarsRemoteExecutorManager.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/src/TarsRemoteExecutorManager.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/mock/MockBlockExecutive.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/mock/MockBlockExecutiveFactory.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/mock/MockDeadLockExecutor.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/mock/MockDmcExecutor.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutor.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutor3.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutorForCall.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutorForCreate.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutorForMessageDAG.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/mock/MockLedger.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/mock/MockLedger2.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/mock/MockLedger3.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/mock/MockMultiParallelExecutor.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/mock/MockRPC.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/mock/MockTransactionalStorage.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/mock/MockTxPool1.h" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/testBlockExecutive.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/testChecksumAddress.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/testCommitBlock.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/testDmcExecutor.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/testDmcStepRecorder.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/testExecutivePool.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/testExecutorManager.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/testKeyLocks.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/testScheduler.cpp" create mode 100644 "BFPL\345\243\271/bcos-scheduler/test/testSchedulerImpl.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/Sdk.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/SdkFactory.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/SdkFactory.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOP.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOP.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOPInterface.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOPRequest.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOPRequest.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/Common.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/TopicManager.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/TopicManager.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/config/Config.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/config/Config.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/Common.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSub.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSub.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubInterface.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubParams.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubParams.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubRequest.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubRequest.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubResponse.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubResponse.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubStatus.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubTask.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/multigroup/JsonChainNodeInfoCodec.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/multigroup/JsonGroupInfoCodec.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/Common.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcImpl.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcImpl.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcInterface.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcRequest.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcRequest.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractABICodec.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractABICodec.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractABIType.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractABIType.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractEventTopic.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/BlockNumberInfo.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/BlockNumberInfo.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/Common.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/HandshakeResponse.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/HandshakeResponse.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/Service.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/Service.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/sample/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-sdk/sample/amop/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-sdk/sample/amop/broadcast.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/sample/amop/publish.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/sample/amop/subscribe.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/sample/config/config_sample.ini" create mode 100644 "BFPL\345\243\271/bcos-sdk/sample/config/sm_config_sample.ini" create mode 100644 "BFPL\345\243\271/bcos-sdk/sample/eventsub/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-sdk/sample/eventsub/eventsub.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/sample/rpc/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-sdk/sample/rpc/blocknotifier.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/sample/tx/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-sdk/sample/tx/deploy_hello.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/sample/tx/random_perf.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/sample/tx/tx_sign_perf.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/tests/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-sdk/tests/unittests/abi/ContractEventTopicTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/tests/unittests/amop/TopicManagerTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubParamsTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubRequestTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubResponseTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubTaskStateTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubTaskTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/tests/unittests/fake/WsServiceFake.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/tests/unittests/fake/WsSessionFake.h" create mode 100644 "BFPL\345\243\271/bcos-sdk/tests/unittests/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/tests/unittests/tx/TransactionTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/tests/unittests/ws/BlockNumberInfoTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-sdk/tests/unittests/ws/HandshakeResponseTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-sealer/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-sealer/bcos-sealer/Common.h" create mode 100644 "BFPL\345\243\271/bcos-sealer/bcos-sealer/Sealer.cpp" create mode 100644 "BFPL\345\243\271/bcos-sealer/bcos-sealer/Sealer.h" create mode 100644 "BFPL\345\243\271/bcos-sealer/bcos-sealer/SealerConfig.h" create mode 100644 "BFPL\345\243\271/bcos-sealer/bcos-sealer/SealerFactory.cpp" create mode 100644 "BFPL\345\243\271/bcos-sealer/bcos-sealer/SealerFactory.h" create mode 100644 "BFPL\345\243\271/bcos-sealer/bcos-sealer/SealingManager.cpp" create mode 100644 "BFPL\345\243\271/bcos-sealer/bcos-sealer/SealingManager.h" create mode 100644 "BFPL\345\243\271/bcos-security/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-security/bcos-security/Common.h" create mode 100644 "BFPL\345\243\271/bcos-security/bcos-security/DataEncryption.cpp" create mode 100644 "BFPL\345\243\271/bcos-security/bcos-security/DataEncryption.h" create mode 100644 "BFPL\345\243\271/bcos-security/bcos-security/KeyCenter.cpp" create mode 100644 "BFPL\345\243\271/bcos-security/bcos-security/KeyCenter.h" create mode 100644 "BFPL\345\243\271/bcos-security/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-security/test/unittests/libsecurity/DataEncryptionTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-security/test/unittests/main/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-storage/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-storage/bcos-storage/Common.cpp" create mode 100644 "BFPL\345\243\271/bcos-storage/bcos-storage/Common.h" create mode 100644 "BFPL\345\243\271/bcos-storage/bcos-storage/RocksDBStorage.cpp" create mode 100644 "BFPL\345\243\271/bcos-storage/bcos-storage/RocksDBStorage.h" create mode 100644 "BFPL\345\243\271/bcos-storage/bcos-storage/TiKVStorage.cpp" create mode 100644 "BFPL\345\243\271/bcos-storage/bcos-storage/TiKVStorage.h" create mode 100644 "BFPL\345\243\271/bcos-storage/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-storage/test/unittest/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-storage/test/unittest/TestRocksDBStorage.cpp" create mode 100644 "BFPL\345\243\271/bcos-storage/test/unittest/TestTiKVStorage.cpp" create mode 100644 "BFPL\345\243\271/bcos-storage/test/unittest/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-storage/tools/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-storage/tools/reader.cpp" create mode 100644 "BFPL\345\243\271/bcos-storage/tools/storageTool.cpp" create mode 100644 "BFPL\345\243\271/bcos-sync/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/BlockSync.cpp" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/BlockSync.h" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/BlockSyncConfig.cpp" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/BlockSyncConfig.h" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/BlockSyncFactory.cpp" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/BlockSyncFactory.h" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlockRequestInterface.h" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlockSyncMsgFactory.h" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlockSyncMsgInterface.h" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlockSyncStatusInterface.h" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlocksMsgInterface.h" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockRequestImpl.h" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockSyncMsgFactoryImpl.h" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockSyncMsgImpl.h" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockSyncStatusImpl.cpp" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockSyncStatusImpl.h" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlocksMsgImpl.h" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/protocol/proto/BlockSync.proto" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/state/DownloadRequestQueue.cpp" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/state/DownloadRequestQueue.h" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/state/DownloadingQueue.cpp" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/state/DownloadingQueue.h" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/state/SyncPeerStatus.cpp" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/state/SyncPeerStatus.h" create mode 100644 "BFPL\345\243\271/bcos-sync/bcos-sync/utilities/Common.h" create mode 100644 "BFPL\345\243\271/bcos-sync/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-sync/test/unittests/faker/FakeConsensus.h" create mode 100644 "BFPL\345\243\271/bcos-sync/test/unittests/main/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-sync/test/unittests/protocol/BlockSyncMsgTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-sync/test/unittests/sync/BlockSyncTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-sync/test/unittests/sync/SyncConfigTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-sync/test/unittests/sync/SyncFixture.h" create mode 100644 "BFPL\345\243\271/bcos-table/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-table/src/CacheStorageFactory.cpp" create mode 100644 "BFPL\345\243\271/bcos-table/src/CacheStorageFactory.h" create mode 100644 "BFPL\345\243\271/bcos-table/src/KeyPageStorage.cpp" create mode 100644 "BFPL\345\243\271/bcos-table/src/KeyPageStorage.h" create mode 100644 "BFPL\345\243\271/bcos-table/src/StateStorage.h" create mode 100644 "BFPL\345\243\271/bcos-table/src/StateStorageInterface.h" create mode 100644 "BFPL\345\243\271/bcos-table/src/StorageInterface.cpp" create mode 100644 "BFPL\345\243\271/bcos-table/src/StorageWrapper.h" create mode 100644 "BFPL\345\243\271/bcos-table/src/Table.cpp" create mode 100644 "BFPL\345\243\271/bcos-table/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-table/test/unittests/libtable/Entry.cpp" create mode 100644 "BFPL\345\243\271/bcos-table/test/unittests/libtable/Hash.h" create mode 100644 "BFPL\345\243\271/bcos-table/test/unittests/libtable/Table.cpp" create mode 100644 "BFPL\345\243\271/bcos-table/test/unittests/libtable/TablePerf.cpp" create mode 100644 "BFPL\345\243\271/bcos-table/test/unittests/libtable/TestKeyPageStorage.cpp" create mode 100644 "BFPL\345\243\271/bcos-table/test/unittests/libtable/TestStateStorage.cpp" create mode 100644 "BFPL\345\243\271/bcos-table/test/unittests/main/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-table/test/unittests/mock/MockTransactionalStorage.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/Common.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/ErrorConverter.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/ExecutorServiceClient.cpp" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/ExecutorServiceClient.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/FrontServiceClient.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/GatewayServiceClient.cpp" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/GatewayServiceClient.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/LedgerServiceClient.cpp" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/LedgerServiceClient.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/PBFTServiceClient.cpp" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/PBFTServiceClient.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/RpcServiceClient.cpp" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/RpcServiceClient.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/SchedulerServiceClient.cpp" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/SchedulerServiceClient.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/TxPoolServiceClient.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/doc/README_CN.md" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsHashable.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsOutput.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsSerializable.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsServantProxyCallback.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsStruct.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockFactoryImpl.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockHeaderFactoryImpl.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockHeaderImpl.cpp" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockHeaderImpl.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockImpl.cpp" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockImpl.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ExecutionMessageImpl.cpp" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ExecutionMessageImpl.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ExecutionResultImpl.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/GroupInfoCodecImpl.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/GroupNodeInfoImpl.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/MemberImpl.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ProtocolInfoCodecImpl.cpp" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ProtocolInfoCodecImpl.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/RouterTable.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionFactoryImpl.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.cpp" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionMetaDataImpl.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptFactoryImpl.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.cpp" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionSubmitResultFactoryImpl.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionSubmitResultImpl.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/Block.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/CommonProtocol.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ExecutionMessage.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ExecutionResult.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ExecutorService.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ExecutorStatus.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/FrontService.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/GatewayInfo.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/GatewayService.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/GroupInfo.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/LedgerConfig.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/LedgerService.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/LightNode.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/Member.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/PBFTService.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ProtocolInfo.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/RpcService.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/SchedulerService.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/StorageService.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/Transaction.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TransactionMetaData.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TransactionReceipt.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TransactionSubmitResult.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TwoPCParams.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TxPoolService.tars" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/testutil/FakeBlock.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/testutil/FakeBlockHeader.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/testutil/FakeTransaction.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/testutil/FakeTransactionReceipt.h" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/test/ProtocolTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/test/ProxyCallbackTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/test/ServiceClientTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-tars-protocol/test/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-tool/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-tool/bcos-tool/BfsFileFactory.cpp" create mode 100644 "BFPL\345\243\271/bcos-tool/bcos-tool/BfsFileFactory.h" create mode 100644 "BFPL\345\243\271/bcos-tool/bcos-tool/ConsensusNode.h" create mode 100644 "BFPL\345\243\271/bcos-tool/bcos-tool/Exceptions.h" create mode 100644 "BFPL\345\243\271/bcos-tool/bcos-tool/LedgerConfigFetcher.cpp" create mode 100644 "BFPL\345\243\271/bcos-tool/bcos-tool/LedgerConfigFetcher.h" create mode 100644 "BFPL\345\243\271/bcos-tool/bcos-tool/NodeConfig.cpp" create mode 100644 "BFPL\345\243\271/bcos-tool/bcos-tool/NodeConfig.h" create mode 100644 "BFPL\345\243\271/bcos-tool/bcos-tool/NodeTimeMaintenance.cpp" create mode 100644 "BFPL\345\243\271/bcos-tool/bcos-tool/NodeTimeMaintenance.h" create mode 100644 "BFPL\345\243\271/bcos-tool/bcos-tool/VersionConverter.h" create mode 100644 "BFPL\345\243\271/bcos-tool/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-tool/test/unittests/libtool/NodeTimeMaintenanceTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-tool/test/unittests/libtool/VersionConvert.cpp" create mode 100644 "BFPL\345\243\271/bcos-tool/test/unittests/main/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-txpool/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/Sync.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPool.cpp" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPool.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPoolConfig.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPoolFactory.cpp" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPoolFactory.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/TransactionSync.cpp" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/TransactionSync.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/TransactionSyncConfig.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/interfaces/TransactionSyncInterface.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/interfaces/TxsSyncMsgFactory.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/interfaces/TxsSyncMsgInterface.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/protocol/PB/TxsSyncMsg.cpp" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/protocol/PB/TxsSyncMsg.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/protocol/PB/TxsSyncMsgFactoryImpl.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/protocol/proto/TxsSync.proto" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/utilities/Common.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/interfaces/NonceCheckerInterface.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/interfaces/TxPoolStorageInterface.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/interfaces/TxValidatorInterface.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/LedgerNonceChecker.cpp" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/LedgerNonceChecker.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/TxPoolNonceChecker.cpp" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/TxPoolNonceChecker.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/TxValidator.cpp" create mode 100644 "BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/TxValidator.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-txpool/test/demo/txpool_demo.cpp" create mode 100644 "BFPL\345\243\271/bcos-txpool/test/unittests/main/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-txpool/test/unittests/sync/FakeTxsSyncMsg.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/test/unittests/sync/TxsSyncMsgTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-txpool/test/unittests/txpool/TxPoolFixture.h" create mode 100644 "BFPL\345\243\271/bcos-txpool/test/unittests/txpool/TxPoolTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-txpool/test/unittests/txpool/TxsSyncTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/Base64.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/Base64.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/BoostLog.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/BoostLog.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/BoostLogInitializer.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/BoostLogInitializer.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/CallbackCollectionHandler.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/Common.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/Common.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/ConcurrentQueue.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/DataConvertUtility.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/DataConvertUtility.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/Error.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/Exceptions.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/FileUtility.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/FileUtility.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/FixedBytes.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/FixedBytes.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/IOServicePool.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/JsonDataConvertUtility.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/Log.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/Ranges.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/RefDataContainer.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/ThreadPool.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/Timer.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/Timer.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/Worker.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/Worker.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/ZstdCompress.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/bcos-utilities/ZstdCompress.h" create mode 100644 "BFPL\345\243\271/bcos-utilities/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/Base64Test.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/CommonTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/DataConvertUtilityTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/JsonDataConvertUtilityTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/RefDataContainerTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/TestFixedBytes.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/WorkerTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/ZstdCompressTest.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/test/unittests/main/main.cpp" create mode 100644 "BFPL\345\243\271/bcos-utilities/testutils/TestPromptFixture.h" create mode 100644 "BFPL\345\243\271/benchmark/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/benchmark/merkleBench.cpp" create mode 100644 "BFPL\345\243\271/cmake/CompilerSettings.cmake" create mode 100644 "BFPL\345\243\271/cmake/Coverage.cmake" create mode 100644 "BFPL\345\243\271/cmake/GenerateSources.cmake" create mode 100644 "BFPL\345\243\271/cmake/IncludeDirectories.cmake" create mode 100644 "BFPL\345\243\271/cmake/Options.cmake" create mode 100644 "BFPL\345\243\271/cmake/ProjectBCOSWASM.cmake" create mode 100644 "BFPL\345\243\271/cmake/ProjectGroupSig.cmake" create mode 100644 "BFPL\345\243\271/cmake/ProjectTCMalloc.cmake" create mode 100644 "BFPL\345\243\271/cmake/ProjectTiKVClient.cmake" create mode 100644 "BFPL\345\243\271/cmake/ProjectWABT.cmake" create mode 100644 "BFPL\345\243\271/cmake/SearchTestCases.cmake" create mode 100644 "BFPL\345\243\271/cmake/TargetSettings.cmake" create mode 100644 "BFPL\345\243\271/cmake/Version.cmake" create mode 100644 "BFPL\345\243\271/cmake/fiscobcos-config.cmake.in" create mode 100644 "BFPL\345\243\271/cmake/shell/check-commit.sh" create mode 100644 "BFPL\345\243\271/cmake/test/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/concepts/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/concepts/bcos-concepts/Basic.h" create mode 100644 "BFPL\345\243\271/concepts/bcos-concepts/ByteBuffer.h" create mode 100644 "BFPL\345\243\271/concepts/bcos-concepts/Exception.h" create mode 100644 "BFPL\345\243\271/concepts/bcos-concepts/Hash.h" create mode 100644 "BFPL\345\243\271/concepts/bcos-concepts/Serialize.h" create mode 100644 "BFPL\345\243\271/concepts/bcos-concepts/front/Front.h" create mode 100644 "BFPL\345\243\271/concepts/bcos-concepts/ledger/Ledger.h" create mode 100644 "BFPL\345\243\271/concepts/bcos-concepts/protocol/Block.h" create mode 100644 "BFPL\345\243\271/concepts/bcos-concepts/protocol/Receipt.h" create mode 100644 "BFPL\345\243\271/concepts/bcos-concepts/protocol/Transaction.h" create mode 100644 "BFPL\345\243\271/concepts/bcos-concepts/scheduler/Scheduler.h" create mode 100644 "BFPL\345\243\271/concepts/bcos-concepts/storage/Storage.h" create mode 100644 "BFPL\345\243\271/concepts/bcos-concepts/transaction_pool/TransactionPool.h" create mode 100644 "BFPL\345\243\271/concepts/tests/testTask.cpp" create mode 100644 "BFPL\345\243\271/docs/FISCO_BCOS_Logo.svg" create mode 100644 "BFPL\345\243\271/docs/README_EN.md" create mode 100644 "BFPL\345\243\271/fisco-bcos-air/AirNodeInitializer.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-air/AirNodeInitializer.h" create mode 100644 "BFPL\345\243\271/fisco-bcos-air/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/fisco-bcos-air/Common.h" create mode 100644 "BFPL\345\243\271/fisco-bcos-air/main.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-demo/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/fisco-bcos-demo/distributed_ratelimiter_checker.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-demo/echo_client_sample.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-demo/echo_server_sample.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/Common/TarsUtils.h" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/ExecutorServiceServer.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/ExecutorServiceServer.h" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/main/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/main/ExecutorServiceApp.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/main/ExecutorServiceApp.h" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/main/main.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/FrontService/FrontServiceServer.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/FrontService/FrontServiceServer.h" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/GatewayInitializer.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/GatewayInitializer.h" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/GatewayServiceServer.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/GatewayServiceServer.h" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/main/Application.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/main/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/LedgerService/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/LedgerService/LedgerServiceServer.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/LedgerService/LedgerServiceServer.h" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/NodeService/NodeServiceApp.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/NodeService/NodeServiceApp.h" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/NodeService/max/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/NodeService/max/main.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/NodeService/pro/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/NodeService/pro/main.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/PBFTService/PBFTServiceClient.h" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/PBFTService/PBFTServiceServer.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/PBFTService/PBFTServiceServer.h" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/RpcService/RpcInitializer.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/RpcService/RpcInitializer.h" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/RpcService/RpcServiceServer.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/RpcService/RpcServiceServer.h" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/RpcService/main/Application.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/RpcService/main/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/SchedulerServiceServer.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/SchedulerServiceServer.h" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/main/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/main/SchedulerServiceApp.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/main/SchedulerServiceApp.h" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/main/main.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/TxPoolService/TxPoolServiceServer.cpp" create mode 100644 "BFPL\345\243\271/fisco-bcos-tars-service/TxPoolService/TxPoolServiceServer.h" create mode 100644 "BFPL\345\243\271/libinitializer/AuthInitializer.h" create mode 100644 "BFPL\345\243\271/libinitializer/BfsInitializer.h" create mode 100644 "BFPL\345\243\271/libinitializer/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/libinitializer/CommandHelper.cpp" create mode 100644 "BFPL\345\243\271/libinitializer/CommandHelper.h" create mode 100644 "BFPL\345\243\271/libinitializer/Common.h" create mode 100644 "BFPL\345\243\271/libinitializer/ExecutorInitializer.h" create mode 100644 "BFPL\345\243\271/libinitializer/FrontServiceInitializer.cpp" create mode 100644 "BFPL\345\243\271/libinitializer/FrontServiceInitializer.h" create mode 100644 "BFPL\345\243\271/libinitializer/Initializer.cpp" create mode 100644 "BFPL\345\243\271/libinitializer/Initializer.h" create mode 100644 "BFPL\345\243\271/libinitializer/LedgerInitializer.h" create mode 100644 "BFPL\345\243\271/libinitializer/LightNodeInitializer.cpp" create mode 100644 "BFPL\345\243\271/libinitializer/LightNodeInitializer.h" create mode 100644 "BFPL\345\243\271/libinitializer/PBFTInitializer.cpp" create mode 100644 "BFPL\345\243\271/libinitializer/PBFTInitializer.h" create mode 100644 "BFPL\345\243\271/libinitializer/ProPBFTInitializer.cpp" create mode 100644 "BFPL\345\243\271/libinitializer/ProPBFTInitializer.h" create mode 100644 "BFPL\345\243\271/libinitializer/ProtocolInitializer.cpp" create mode 100644 "BFPL\345\243\271/libinitializer/ProtocolInitializer.h" create mode 100644 "BFPL\345\243\271/libinitializer/SchedulerInitializer.h" create mode 100644 "BFPL\345\243\271/libinitializer/StorageInitializer.h" create mode 100644 "BFPL\345\243\271/libinitializer/TxPoolInitializer.cpp" create mode 100644 "BFPL\345\243\271/libinitializer/TxPoolInitializer.h" create mode 100644 "BFPL\345\243\271/libtask/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/libtask/bcos-task/Coroutine.h" create mode 100644 "BFPL\345\243\271/libtask/bcos-task/Task.h" create mode 100644 "BFPL\345\243\271/libtask/bcos-task/Wait.h" create mode 100644 "BFPL\345\243\271/libtask/tests/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/libtask/tests/TaskTest.cpp" create mode 100644 "BFPL\345\243\271/libtask/tests/main.cpp" create mode 100644 "BFPL\345\243\271/lightnode/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/lightnode/bcos-lightnode/Log.h" create mode 100644 "BFPL\345\243\271/lightnode/bcos-lightnode/ledger/LedgerImpl.h" create mode 100644 "BFPL\345\243\271/lightnode/bcos-lightnode/rpc/Converter.h" create mode 100644 "BFPL\345\243\271/lightnode/bcos-lightnode/rpc/LightNodeRPC.h" create mode 100644 "BFPL\345\243\271/lightnode/bcos-lightnode/scheduler/SchedulerWrapperImpl.h" create mode 100644 "BFPL\345\243\271/lightnode/bcos-lightnode/storage/StorageImpl.h" create mode 100644 "BFPL\345\243\271/lightnode/bcos-lightnode/transaction_pool/TransactionPoolImpl.h" create mode 100644 "BFPL\345\243\271/lightnode/fisco-bcos-lightnode/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/lightnode/fisco-bcos-lightnode/RPCInitializer.h" create mode 100644 "BFPL\345\243\271/lightnode/fisco-bcos-lightnode/client/LedgerClientImpl.h" create mode 100644 "BFPL\345\243\271/lightnode/fisco-bcos-lightnode/client/P2PClientImpl.h" create mode 100644 "BFPL\345\243\271/lightnode/fisco-bcos-lightnode/client/SchedulerClientImpl.h" create mode 100644 "BFPL\345\243\271/lightnode/fisco-bcos-lightnode/client/TransactionPoolClientImpl.h" create mode 100644 "BFPL\345\243\271/lightnode/fisco-bcos-lightnode/main.cpp" create mode 100644 "BFPL\345\243\271/lightnode/tests/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/lightnode/tests/LedgerTest.cpp" create mode 100644 "BFPL\345\243\271/lightnode/tests/P2PClientTest.cpp" create mode 100644 "BFPL\345\243\271/lightnode/tests/RPCTest.cpp" create mode 100644 "BFPL\345\243\271/lightnode/tests/SchedulerTest.cpp" create mode 100644 "BFPL\345\243\271/lightnode/tests/StorageTest.cpp" create mode 100644 "BFPL\345\243\271/lightnode/tests/TransactionPoolTest.cpp" create mode 100644 "BFPL\345\243\271/lightnode/tests/main.cpp" create mode 100644 "BFPL\345\243\271/tests/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/tests/perf/CMakeLists.txt" create mode 100644 "BFPL\345\243\271/tests/perf/benchmark.cpp" create mode 100644 "BFPL\345\243\271/tests/unittest/main.cpp" create mode 100644 "BFPL\345\243\271/tools/.ci/Dockerfile" create mode 100644 "BFPL\345\243\271/tools/.ci/Dockerfile_env" create mode 100644 "BFPL\345\243\271/tools/.ci/check-commit.sh" create mode 100644 "BFPL\345\243\271/tools/.ci/ci_check_air.sh" create mode 100644 "BFPL\345\243\271/tools/.ci/ci_check_pro.sh" create mode 100644 "BFPL\345\243\271/tools/.ci/clear_build_cache.sh" create mode 100644 "BFPL\345\243\271/tools/.ci/requirements.txt" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/docker/bridge/linux/framework/docker-compose.yml" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/docker/bridge/linux/node/docker-compose.yml" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/docker/bridge/linux/node/gen_compose_files.sh" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/docker/bridge/mac/framework/docker-compose.yml" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/docker/bridge/mac/node/docker-compose.yml" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/framework/docker-compose.yml" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/compose.yaml" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/grafana/grafana.ini" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/prometheus/prometheus.yml" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/start_monitor.sh" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/stop_monitor.sh" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/node/docker-compose.yml" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/max/conf/config-deploy-example.toml" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/max/conf/config-node-expand-example.toml" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/max/conf/config-service-expand-example.toml" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/pro/conf/config-build-example.toml" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/pro/conf/config-deploy-example.toml" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/pro/conf/config-node-expand-example.toml" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/pro/conf/config-service-expand-example.toml" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/requirements.txt" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/command/monitor_command_impl.py" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/command/node_command_impl.py" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/command/service_command_impl.py" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/common/parser_handler.py" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/common/utilities.py" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/config/chain_config.py" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/config/max_node_config_generator.py" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/config/monitor_config_generator.py" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/config/node_config_generator.py" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/config/service_config_generator.py" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/config/tars_config_generator.py" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/controller/binary_controller.py" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/controller/monitor_controller.py" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/controller/node_controller.py" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/controller/service_controller.py" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/networkmgr/network_manager.py" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/scripts/generate_cert.sh" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/scripts/mtail/node.mtail" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/scripts/mtail/start_mtail_monitor.sh" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/scripts/mtail/stop_mtail_monitor.sh" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/service/key_center_service.py" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/service/tars_service.py" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.genesis" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.ini.executor" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.ini.gateway" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.ini.node" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.ini.rpc" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_gateway.conf" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_node.conf" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_rpc.conf" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_start.sh" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_start_all.sh" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_stop.sh" create mode 100644 "BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_stop_all.sh" create mode 100644 "BFPL\345\243\271/tools/template/Dashboard.json" create mode 100644 "BFPL\345\243\271/vcpkg-configuration.json" create mode 100644 "BFPL\345\243\271/vcpkg.json" diff --git "a/BFPL\345\243\271/.circleci/config.yml" "b/BFPL\345\243\271/.circleci/config.yml" new file mode 100644 index 00000000..81b03579 --- /dev/null +++ "b/BFPL\345\243\271/.circleci/config.yml" @@ -0,0 +1,169 @@ +version: 2.1 + +commands: + install_depencies: + description: Setup Ubuntu dependencies + parameters: + packages: + type: string + default: "" + steps: + - run: + name: Setup dependencies + command: | + apt-get update + ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime + apt-get install -y cmake g++ git curl build-essential autoconf texinfo cmake flex bison libzstd-dev zlib1g-dev libpython3-dev python-dev << parameters.packages >> + echo 'export CCACHE_DIR=/ccache' >> $BASH_ENV + curl https://sh.rustup.rs -sSf | bash -s -- -y + source $HOME/.cargo/env + compile: + description: Compile + parameters: + options: + type: string + default: "" + compiler: + type: string + default: "" + steps: + - run: + name: Compile + no_output_timeout: 40m + command: | + source $HOME/.cargo/env + mkdir -p build && cd build + << parameters.compiler >> cmake -DURL_BASE=github.com -DCMAKE_BUILD_TYPE=debug -DHUNTER_JOBS_NUMBER=1 << parameters.options >> .. + make -j2 +executors: + ubuntu-bionic: + docker: + - image: ubuntu:18.04 + ubuntu-focal: + docker: + - image: ubuntu:20.04 + +jobs: + build_test: + working_directory: /FISCO-BCOS + executor: ubuntu-focal + steps: + - install_depencies: + packages: "git curl build-essential cmake ccache lcov libzstd-dev libgmp-dev" + - checkout + - restore_cache: + keys: + - deps-cache-v1-{{ .Environment.CIRCLE_JOB }}-{{ .Environment.CIRCLE_BRANCH }}-{{ checksum ".circleci/config.yml" }} + - deps-cache-v1-{{ .Environment.CIRCLE_JOB }}-{{ .Environment.CIRCLE_BRANCH }}- + - deps-cache-v1-{{ .Environment.CIRCLE_JOB }}- + - compile: + options: "-DTESTS=ON -DCOVERAGE=ON -DCMAKE_BUILD_TYPE=Debug" + - save_cache: + key: deps-cache-v1-{{ .Environment.CIRCLE_JOB }}-{{ .Environment.CIRCLE_BRANCH }}-{{ checksum ".circleci/config.yml" }} + paths: + - deps + - /ccache + - /root/.hunter + - run: + name: Unit test + command: | + cd build + CTEST_OUTPUT_ON_FAILURE=TRUE make test + rm -rf /FISCO-BCOS/deps + - persist_to_workspace: + root: / + paths: + - FISCO-BCOS/build/* + - FISCO-BCOS/test/* + - FISCO-BCOS/tools/* + + generate_coverage: + working_directory: /FISCO-BCOS + executor: ubuntu-focal + steps: + - install_depencies: + packages: "lcov" + - attach_workspace: + at: / + - run: + name: Upload Coverage + command: | + bash <(curl -s https://codecov.io/bash) -C $CIRCLE_SHA1 -f "!*/deps/*" > /dev/null + + + build_test_guomi: + working_directory: /FISCO-BCOS-GM + docker: + - image: centos:7 + steps: + - run: + name: Setup dependencies + command: | + yum install -y epel-release centos-release-scl + yum install -y git make gcc gcc-c++ gmp-static glibc-static glibc-devel cmake3 ccache devtoolset-7 libzstd-devel zlib-devel flex bison python-devel python3-devel && source /opt/rh/devtoolset-7/enable + yum install -y https://repo.ius.io/ius-release-el7.rpm https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm + echo 'export CCACHE_DIR=/ccache' >> $BASH_ENV + curl https://sh.rustup.rs -sSf | bash -s -- -y + source $HOME/.cargo/env + - checkout + - restore_cache: + keys: + - deps-v1-{{ .Environment.CIRCLE_JOB }}-{{ .Environment.CIRCLE_BRANCH }}-{{ checksum ".circleci/config.yml" }} + - deps-v1-{{ .Environment.CIRCLE_JOB }}-{{ .Environment.CIRCLE_BRANCH }}- + - deps-v1-{{ .Environment.CIRCLE_JOB }}- + - run: + name: Compile + no_output_timeout: 40m + command: | + source /opt/rh/devtoolset-7/enable + source $HOME/.cargo/env + yum list devtoolset-7\* + mkdir -p build && cd build + cmake3 -DURL_BASE=github.com -DCMAKE_BUILD_TYPE=debug -DTESTS=ON -DHUNTER_JOBS_NUMBER=1 .. + make -j2 + - save_cache: + key: deps-v1-{{ .Environment.CIRCLE_JOB }}-{{ .Environment.CIRCLE_BRANCH }}-{{ checksum ".circleci/config.yml" }} + paths: + - deps + - /ccache + - /root/.hunter + - run: + name: Unit test + command: | + cd build + make test + - run: + name: Run GM nodes + command: | + cd build + ../tools/BcosAirBuilder/build_chain.sh -l "127.0.0.1:4" -e fisco-bcos-air/fisco-bcos -s && cd nodes/127.0.0.1 && bash start_all.sh && sleep 10 && [[ $(ps -ef| grep fisco-bcos |grep -v grep | wc -l) == 4 ]] + + +workflows: + version: 2 + build_and_test: + jobs: + - build_test: + filters: + branches: + only: + - /^pull.*/ + tags: + ignore: /.*/ + - generate_coverage: + requires: + - build_test + filters: + branches: + only: + - /^pull.*/ + tags: + ignore: /.*/ + - build_test_guomi: + filters: + branches: + only: + - /^pull.*/ + - /^release.*/ + tags: + ignore: /.*/ \ No newline at end of file diff --git "a/BFPL\345\243\271/.clang-format" "b/BFPL\345\243\271/.clang-format" new file mode 100644 index 00000000..de0f373e --- /dev/null +++ "b/BFPL\345\243\271/.clang-format" @@ -0,0 +1,94 @@ +--- +Language: Cpp +# BasedOnStyle: Chromium +AccessModifierOffset: -4 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: true +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterCaseLabel: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: false +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: BeforeColon +ColumnLimit: 100 +CommentPragmas: "^ IWYU pragma:" +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +ForEachMacros: [foreach, Q_FOREACH, BOOST_FOREACH] +IncludeCategories: + - Regex: '^".*' + Priority: 1 + - Regex: "^' + Priority: 2 + - Regex: "^<.*" + Priority: 99 + - Regex: ".*" + Priority: 4 +IndentCaseLabels: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: false +PenaltyBreakAssignment: 1 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 50 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +TabWidth: 4 +UseTab: Never \ No newline at end of file diff --git "a/BFPL\345\243\271/.clang-tidy" "b/BFPL\345\243\271/.clang-tidy" new file mode 100644 index 00000000..3a04bb6d --- /dev/null +++ "b/BFPL\345\243\271/.clang-tidy" @@ -0,0 +1,240 @@ +--- +Checks: 'readability-*,clang-diagnostic-*,clang-analyzer-*,bugprone-*,cppcoreguidelines-*,modernize-*,performance-*,-modernize-use-trailing-return-type,-modernize-use-nodiscard' +WarningsAsErrors: '' +HeaderFilterRegex: '' +AnalyzeTemporaryDtors: false +FormatStyle: none +User: more +CheckOptions: + - key: modernize-replace-auto-ptr.IncludeStyle + value: llvm + - key: cppcoreguidelines-no-malloc.Reallocations + value: '::realloc' + - key: cppcoreguidelines-owning-memory.LegacyResourceConsumers + value: '::free;::realloc;::freopen;::fclose' + - key: bugprone-reserved-identifier.Invert + value: 'false' + - key: bugprone-narrowing-conversions.PedanticMode + value: 'false' + - key: bugprone-unused-return-value.CheckedFunctions + value: '::std::async;::std::launder;::std::remove;::std::remove_if;::std::unique;::std::unique_ptr::release;::std::basic_string::empty;::std::vector::empty;::std::back_inserter;::std::distance;::std::find;::std::find_if;::std::inserter;::std::lower_bound;::std::make_pair;::std::map::count;::std::map::find;::std::map::lower_bound;::std::multimap::equal_range;::std::multimap::upper_bound;::std::set::count;::std::set::find;::std::setfill;::std::setprecision;::std::setw;::std::upper_bound;::std::vector::at;::bsearch;::ferror;::feof;::isalnum;::isalpha;::isblank;::iscntrl;::isdigit;::isgraph;::islower;::isprint;::ispunct;::isspace;::isupper;::iswalnum;::iswprint;::iswspace;::isxdigit;::memchr;::memcmp;::strcmp;::strcoll;::strncmp;::strpbrk;::strrchr;::strspn;::strstr;::wcscmp;::access;::bind;::connect;::difftime;::dlsym;::fnmatch;::getaddrinfo;::getopt;::htonl;::htons;::iconv_open;::inet_addr;::isascii;::isatty;::mmap;::newlocale;::openat;::pathconf;::pthread_equal;::pthread_getspecific;::pthread_mutex_trylock;::readdir;::readlink;::recvmsg;::regexec;::scandir;::semget;::setjmp;::shm_open;::shmget;::sigismember;::strcasecmp;::strsignal;::ttyname' + - key: cert-dcl16-c.NewSuffixes + value: 'L;LL;LU;LLU' + - key: cppcoreguidelines-macro-usage.CheckCapsOnly + value: 'false' + - key: bugprone-narrowing-conversions.WarnOnFloatingPointNarrowingConversion + value: 'true' + - key: bugprone-assert-side-effect.IgnoredFunctions + value: __builtin_expect + - key: cppcoreguidelines-narrowing-conversions.IgnoreConversionFromTypes + value: '' + - key: cert-str34-c.DiagnoseSignedUnsignedCharComparisons + value: 'false' + - key: bugprone-narrowing-conversions.WarnWithinTemplateInstantiation + value: 'false' + - key: cert-err33-c.CheckedFunctions + value: '::aligned_alloc;::asctime_s;::at_quick_exit;::atexit;::bsearch;::bsearch_s;::btowc;::c16rtomb;::c32rtomb;::calloc;::clock;::cnd_broadcast;::cnd_init;::cnd_signal;::cnd_timedwait;::cnd_wait;::ctime_s;::fclose;::fflush;::fgetc;::fgetpos;::fgets;::fgetwc;::fopen;::fopen_s;::fprintf;::fprintf_s;::fputc;::fputs;::fputwc;::fputws;::fread;::freopen;::freopen_s;::fscanf;::fscanf_s;::fseek;::fsetpos;::ftell;::fwprintf;::fwprintf_s;::fwrite;::fwscanf;::fwscanf_s;::getc;::getchar;::getenv;::getenv_s;::gets_s;::getwc;::getwchar;::gmtime;::gmtime_s;::localtime;::localtime_s;::malloc;::mbrtoc16;::mbrtoc32;::mbsrtowcs;::mbsrtowcs_s;::mbstowcs;::mbstowcs_s;::memchr;::mktime;::mtx_init;::mtx_lock;::mtx_timedlock;::mtx_trylock;::mtx_unlock;::printf_s;::putc;::putwc;::raise;::realloc;::remove;::rename;::scanf;::scanf_s;::setlocale;::setvbuf;::signal;::snprintf;::snprintf_s;::sprintf;::sprintf_s;::sscanf;::sscanf_s;::strchr;::strerror_s;::strftime;::strpbrk;::strrchr;::strstr;::strtod;::strtof;::strtoimax;::strtok;::strtok_s;::strtol;::strtold;::strtoll;::strtoul;::strtoull;::strtoumax;::strxfrm;::swprintf;::swprintf_s;::swscanf;::swscanf_s;::thrd_create;::thrd_detach;::thrd_join;::thrd_sleep;::time;::timespec_get;::tmpfile;::tmpfile_s;::tmpnam;::tmpnam_s;::tss_create;::tss_get;::tss_set;::ungetc;::ungetwc;::vfprintf;::vfprintf_s;::vfscanf;::vfscanf_s;::vfwprintf;::vfwprintf_s;::vfwscanf;::vfwscanf_s;::vprintf_s;::vscanf;::vscanf_s;::vsnprintf;::vsnprintf_s;::vsprintf;::vsprintf_s;::vsscanf;::vsscanf_s;::vswprintf;::vswprintf_s;::vswscanf;::vswscanf_s;::vwprintf_s;::vwscanf;::vwscanf_s;::wcrtomb;::wcschr;::wcsftime;::wcspbrk;::wcsrchr;::wcsrtombs;::wcsrtombs_s;::wcsstr;::wcstod;::wcstof;::wcstoimax;::wcstok;::wcstok_s;::wcstol;::wcstold;::wcstoll;::wcstombs;::wcstombs_s;::wcstoul;::wcstoull;::wcstoumax;::wcsxfrm;::wctob;::wctrans;::wctype;::wmemchr;::wprintf_s;::wscanf;::wscanf_s;' + - key: bugprone-suspicious-string-compare.WarnOnLogicalNotComparison + value: 'false' + - key: cppcoreguidelines-explicit-virtual-functions.AllowOverrideAndFinal + value: 'false' + - key: bugprone-easily-swappable-parameters.QualifiersMix + value: 'false' + - key: bugprone-suspicious-string-compare.WarnOnImplicitComparison + value: 'true' + - key: bugprone-argument-comment.CommentNullPtrs + value: '0' + - key: bugprone-narrowing-conversions.WarnOnIntegerToFloatingPointNarrowingConversion + value: 'true' + - key: cppcoreguidelines-owning-memory.LegacyResourceProducers + value: '::malloc;::aligned_alloc;::realloc;::calloc;::fopen;::freopen;::tmpfile' + - key: cppcoreguidelines-narrowing-conversions.WarnOnFloatingPointNarrowingConversion + value: 'true' + - key: bugprone-easily-swappable-parameters.SuppressParametersUsedTogether + value: 'true' + - key: cppcoreguidelines-init-variables.IncludeStyle + value: llvm + - key: bugprone-argument-comment.StrictMode + value: '0' + - key: bugprone-easily-swappable-parameters.NamePrefixSuffixSilenceDissimilarityTreshold + value: '1' + - key: bugprone-unhandled-self-assignment.WarnOnlyIfThisHasSuspiciousField + value: 'true' + - key: google-readability-namespace-comments.ShortNamespaceLines + value: '10' + - key: bugprone-suspicious-string-compare.StringCompareLikeFunctions + value: '' + - key: cppcoreguidelines-narrowing-conversions.WarnOnIntegerNarrowingConversion + value: 'true' + - key: bugprone-easily-swappable-parameters.IgnoredParameterNames + value: '"";iterator;Iterator;begin;Begin;end;End;first;First;last;Last;lhs;LHS;rhs;RHS' + - key: cppcoreguidelines-prefer-member-initializer.UseAssignment + value: 'false' + - key: cppcoreguidelines-explicit-virtual-functions.FinalSpelling + value: final + - key: bugprone-narrowing-conversions.WarnOnIntegerNarrowingConversion + value: 'true' + - key: modernize-loop-convert.NamingStyle + value: CamelCase + - key: bugprone-suspicious-include.ImplementationFileExtensions + value: 'c;cc;cpp;cxx' + - key: cppcoreguidelines-pro-type-member-init.UseAssignment + value: 'false' + - key: bugprone-suspicious-missing-comma.SizeThreshold + value: '5' + - key: bugprone-suspicious-include.HeaderFileExtensions + value: ';h;hh;hpp;hxx' + - key: bugprone-argument-comment.CommentCharacterLiterals + value: '0' + - key: bugprone-argument-comment.CommentIntegerLiterals + value: '0' + - key: bugprone-stringview-nullptr.IncludeStyle + value: llvm + - key: modernize-pass-by-value.IncludeStyle + value: llvm + - key: bugprone-sizeof-expression.WarnOnSizeOfThis + value: 'true' + - key: bugprone-string-constructor.WarnOnLargeLength + value: 'true' + - key: bugprone-too-small-loop-variable.MagnitudeBitsUpperLimit + value: '16' + - key: cppcoreguidelines-explicit-virtual-functions.OverrideSpelling + value: override + - key: bugprone-argument-comment.CommentFloatLiterals + value: '0' + - key: modernize-use-nullptr.NullMacros + value: 'NULL' + - key: bugprone-dynamic-static-initializers.HeaderFileExtensions + value: ';h;hh;hpp;hxx' + - key: bugprone-suspicious-enum-usage.StrictMode + value: 'false' + - key: cppcoreguidelines-macro-usage.AllowedRegexp + value: '^DEBUG_*' + - key: bugprone-suspicious-missing-comma.MaxConcatenatedTokens + value: '5' + - key: cppcoreguidelines-narrowing-conversions.PedanticMode + value: 'false' + - key: bugprone-implicit-widening-of-multiplication-result.UseCXXHeadersInCppSources + value: 'true' + - key: bugprone-not-null-terminated-result.WantToUseSafeFunctions + value: 'true' + - key: bugprone-string-constructor.LargeLengthThreshold + value: '8388608' + - key: cppcoreguidelines-avoid-magic-numbers.IgnoreAllFloatingPointValues + value: 'false' + - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions + value: 'false' + - key: bugprone-implicit-widening-of-multiplication-result.UseCXXStaticCastsInCppSources + value: 'true' + - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField + value: 'false' + - key: bugprone-exception-escape.FunctionsThatShouldNotThrow + value: '' + - key: bugprone-argument-comment.CommentStringLiterals + value: '0' + - key: modernize-loop-convert.MaxCopySize + value: '16' + - key: bugprone-easily-swappable-parameters.MinimumLength + value: '2' + - key: cppcoreguidelines-pro-bounds-constant-array-index.GslHeader + value: '' + - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors + value: 'false' + - key: bugprone-signed-char-misuse.CharTypdefsToIgnore + value: '' + - key: bugprone-sizeof-expression.WarnOnSizeOfConstant + value: 'true' + - key: bugprone-argument-comment.CommentBoolLiterals + value: '0' + - key: bugprone-argument-comment.CommentUserDefinedLiterals + value: '0' + - key: bugprone-easily-swappable-parameters.IgnoredParameterTypeSuffixes + value: 'bool;Bool;_Bool;it;It;iterator;Iterator;inputit;InputIt;forwardit;ForwardIt;bidirit;BidirIt;constiterator;const_iterator;Const_Iterator;Constiterator;ConstIterator;RandomIt;randomit;random_iterator;ReverseIt;reverse_iterator;reverse_const_iterator;ConstReverseIterator;Const_Reverse_Iterator;const_reverse_iterator;Constreverseiterator;constreverseiterator' + - key: google-readability-braces-around-statements.ShortStatementLines + value: '1' + - key: bugprone-reserved-identifier.AllowedIdentifiers + value: '' + - key: cppcoreguidelines-pro-type-member-init.IgnoreArrays + value: 'false' + - key: cppcoreguidelines-avoid-magic-numbers.IgnoredFloatingPointValues + value: '1.0;100.0;' + - key: cppcoreguidelines-macro-usage.IgnoreCommandLineMacros + value: 'true' + - key: bugprone-signal-handler.AsyncSafeFunctionSet + value: POSIX + - key: cppcoreguidelines-pro-bounds-constant-array-index.IncludeStyle + value: llvm + - key: bugprone-easily-swappable-parameters.ModelImplicitConversions + value: 'true' + - key: cppcoreguidelines-narrowing-conversions.WarnWithinTemplateInstantiation + value: 'false' + - key: cppcoreguidelines-narrowing-conversions.WarnOnEquivalentBitWidth + value: 'true' + - key: bugprone-misplaced-widening-cast.CheckImplicitCasts + value: 'false' + - key: cppcoreguidelines-avoid-magic-numbers.IgnoredIntegerValues + value: '1;2;3;4;' + - key: modernize-loop-convert.MinConfidence + value: reasonable + - key: cppcoreguidelines-avoid-magic-numbers.IgnorePowersOf2IntegerValues + value: 'false' + - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnorePublicMemberVariables + value: 'false' + - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted + value: 'false' + - key: google-readability-namespace-comments.SpacesBeforeComments + value: '2' + - key: cppcoreguidelines-avoid-magic-numbers.IgnoreBitFieldsWidths + value: 'true' + - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: 'true' + - key: bugprone-argument-comment.IgnoreSingleArgument + value: '0' + - key: bugprone-suspicious-missing-comma.RatioThreshold + value: '0.200000' + - key: cppcoreguidelines-no-malloc.Allocations + value: '::malloc;::calloc' + - key: bugprone-narrowing-conversions.WarnOnEquivalentBitWidth + value: 'true' + - key: bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression + value: 'false' + - key: bugprone-assert-side-effect.CheckFunctionCalls + value: 'false' + - key: bugprone-narrowing-conversions.IgnoreConversionFromTypes + value: '' + - key: cppcoreguidelines-narrowing-conversions.WarnOnIntegerToFloatingPointNarrowingConversion + value: 'true' + - key: bugprone-string-constructor.StringNames + value: '::std::basic_string;::std::basic_string_view' + - key: bugprone-assert-side-effect.AssertMacros + value: assert,NSAssert,NSCAssert + - key: bugprone-exception-escape.IgnoredExceptions + value: '' + - key: bugprone-signed-char-misuse.DiagnoseSignedUnsignedCharComparisons + value: 'true' + - key: llvm-qualified-auto.AddConstToQualified + value: 'false' + - key: cppcoreguidelines-init-variables.MathHeader + value: '' + - key: google-readability-function-size.StatementThreshold + value: '800' + - key: llvm-else-after-return.WarnOnConditionVariables + value: 'false' + - key: bugprone-sizeof-expression.WarnOnSizeOfCompareToConstant + value: 'true' + - key: bugprone-reserved-identifier.AggressiveDependentMemberLookup + value: 'false' + - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor + value: 'false' + - key: cppcoreguidelines-no-malloc.Deallocations + value: '::free' + - key: bugprone-dangling-handle.HandleClasses + value: 'std::basic_string_view;std::experimental::basic_string_view' + - key: bugprone-implicit-widening-of-multiplication-result.IncludeStyle + value: llvm + - key: llvm-else-after-return.WarnOnUnfixable + value: 'false' + - key: readability-identifier-length.IgnoredVariableNames + value: 'it|ar|i|j|k|e|id|ss|os|tx' +... + diff --git "a/BFPL\345\243\271/.gitignore" "b/BFPL\345\243\271/.gitignore" new file mode 100644 index 00000000..a51a763b --- /dev/null +++ "b/BFPL\345\243\271/.gitignore" @@ -0,0 +1,73 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# java +*.class + + +# ignore some directory +build** +deps +deps/** + +# vs +*.sln +*.vcxproj* +.vs/ +.vscode/ +.idea/ + +# macOS +.DS_Store + +# log +*.log + +# python +*.pyc + +# vcpkg +vcpkg + +# clangd file +compile_commands.json +.cache + +# generated test +tools/BcosAirBuilder/nodes** +tools/BcosProBuilder/generated** +tools/BcosAirBuilder/* +*.log + +test/* +/CMakeSettings.json diff --git "a/BFPL\345\243\271/.gitmodules" "b/BFPL\345\243\271/.gitmodules" new file mode 100644 index 00000000..fe07c999 --- /dev/null +++ "b/BFPL\345\243\271/.gitmodules" @@ -0,0 +1,3 @@ +[submodule "vcpkg"] + path = vcpkg + url = https://github.com/microsoft/vcpkg diff --git "a/BFPL\345\243\271/CMakeLists.txt" "b/BFPL\345\243\271/CMakeLists.txt" new file mode 100644 index 00000000..8356f835 --- /dev/null +++ "b/BFPL\345\243\271/CMakeLists.txt" @@ -0,0 +1,105 @@ + cmake_minimum_required(VERSION 3.14) + +if(NOT DEFINED URL_BASE) + set(URL_BASE "github.com") +endif() + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) + +include(cmake/Version.cmake) +include(Options) +configure_project() + +# vcpkg init +if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) + find_package(Git REQUIRED) + execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive -- vcpkg WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake" + CACHE STRING "Vcpkg toolchain file") +endif() + +project(FISCO-BCOS VERSION ${VERSION}) + +include(CompilerSettings) +include(BuildInfoGenerator) + +include(IncludeDirectories) +include(TargetSettings) + +add_subdirectory(bcos-boostssl) +add_subdirectory(bcos-framework) +add_subdirectory(bcos-crypto) +add_subdirectory(bcos-utilities) + +if(WITH_LIGHTNODE) + add_subdirectory(lightnode) +endif() + +if(FULLNODE) + include(ProjectWABT) + include(ProjectBCOSWASM) + include(ProjectGroupSig) + + add_subdirectory(bcos-codec) + add_subdirectory(bcos-sealer) + add_subdirectory(bcos-security) + add_subdirectory(bcos-table) + add_subdirectory(bcos-tool) + add_subdirectory(bcos-scheduler) + add_subdirectory(bcos-executor) + add_subdirectory(bcos-storage) + add_subdirectory(bcos-ledger) + add_subdirectory(bcos-protocol) + add_subdirectory(bcos-tars-protocol) + add_subdirectory(bcos-rpc) + add_subdirectory(bcos-gateway) + add_subdirectory(bcos-pbft) + add_subdirectory(bcos-txpool) + add_subdirectory(bcos-sync) + add_subdirectory(bcos-front) + add_subdirectory(bcos-leader-election) + add_subdirectory(libinitializer) + add_subdirectory(fisco-bcos-air) + add_subdirectory(concepts) + add_subdirectory(libtask) + + if(WITH_TARS_SERVICES) + add_subdirectory(fisco-bcos-tars-service) + endif() + + if(TESTS) + enable_testing() + add_subdirectory(tests) + add_subdirectory(benchmark) + endif() +endif() + +if(WITH_CPPSDK) + add_subdirectory(bcos-sdk) +endif() + +# for code coverage +if(COVERAGE) + include(Coverage) + config_coverage("coverage" "'/usr*' 'boost/*'") +endif() + +print_config("FISCO BCOS") + +include(CMakePackageConfigHelpers) +configure_package_config_file( + "${CMAKE_SOURCE_DIR}/cmake/fiscobcos-config.cmake.in" + "${CMAKE_BINARY_DIR}/fiscobcos-config.cmake" + INSTALL_DESTINATION "share/fiscobcos" +) + +install( + FILES "${CMAKE_BINARY_DIR}/fiscobcos-config.cmake" + DESTINATION "share/fiscobcos" +) + +install( + EXPORT fiscobcosTargets + DESTINATION share/fiscobcos + NAMESPACE fiscobcos:: +) \ No newline at end of file diff --git "a/BFPL\345\243\271/ChangeLog.md" "b/BFPL\345\243\271/ChangeLog.md" new file mode 100644 index 00000000..a94af904 --- /dev/null +++ "b/BFPL\345\243\271/ChangeLog.md" @@ -0,0 +1,279 @@ +### 3.0.1 + +(2022-09-23) + +**修复** + +* 修复RPC 模块的内存增长问题 +* 修复优雅退出问题 +* 修复max版本稳定性文档 + +#### 兼容性 + +3.0.1 版本与3.0.0 版本数据完全兼容,Solidity合约源码兼容,但与2.0及3.0 rc版本不兼容。如果要从2.0版本升级到3.0版本,需要做数据迁移。 + +| | 推荐版本 | 最低版本 | 说明 | +| ---------- | --------- | ------------------------ | ---------------------------------- | +| Console | 3.0.1 | 3.0.0 | | +| Java SDK | 3.0.1 | 3.0.0 | | +| CPP SDK | 3.0.0 | 3.0.0 | | +| Solidity | 0.8.11 | 最低 0.4.25,最高 0.8.11 | 需根据合约版本下载编译器(控制台) | +| WBC-Liquid | 1.0.0-rc3 | 1.0.0-rc3 | | + +### 3.0.0 + +(2022-08-24) + +#### 新架构 + +**Air / Pro / Max** :满足不同的部署场景 + +- **Air**:传统的区块链架构,所有功能在一个区块链节点中(all-in-one),满足开发者和简单场景的部署需求 +- **Pro**:网关 + RPC + 区块链节点,满足机构内外部环境隔离部署需求 +- **Max**:网关 + RPC + 区块链节点(主备) + 多个交易执行器,满足追求高可用和极致的性能的需求 + +#### 新机制 + +**流程:流水线共识** + +以流水线的方式生成区块,提升性能 + +- 将区块生成过程拆分为四个阶段:打包,共识,执行,落盘 +- 连续的区块在执行时以流水线的方式走过四个阶段(103在打包,102在共识,101在执行,100在落盘) +- 连续出块时,性能趋近于流水线中执行时间最长阶段的性能 + +**执行:确定性多合约并行** + +实现合约间交易的并行执行与调度的机制 + +- 高效:不同合约的交易可并行执行,提高交易处理效率 +- 易用:对开发者透明,自动进行交易并行执行与冲突处理 +- 通用:支持EVM、WASM、Precompiled 或其它合约 + +**存储:KeyPage** + +参考内存页的缓存机制实现高效的区块链存储 + +* 将key-value组织成页的方式存储 +* 提升访存局部性,降低存储空间占用 + +**继承与升级** + +* DAG并行执行:不再依赖基于并行编程框架,可根据solidity代码自动生成冲突参数,实现合约内交易的并行执行 +* PBFT共识算法:立即一致的共识算法,实现交易秒级确认 +* 更多请参考在线文档 + +#### 新功能 + +**区块链文件系统** + +用命令行管理区块链资源,如合约,表等 + +- 命令:pwd, cd, ls, tree, mkdir, ln +- 功能:将合约地址与路径绑定,即可用路径调用合约 + +**权限治理** + +开启后,对区块链的设置需进行多方投票允许 + +* 角色:治理者、管理员、用户 +* 被控制的操作:部署合约、合约接口调用、系统参数设置等 + +**WBC-Liquid:WeBankBlockchain-Liquid(简称WBC-Liquid)** + +不仅支持Soldity写合约,也支持用Rust写合约 + +- Liquid是基于Rust语言的智能合约编程语言 +- 集成WASM运行环境,支持WBC-Liquid智能合约。 +- WBC-Liquid智能合约支持智能分析冲突字段,自动开启DAG。 + +**继承与升级** + +* Solidity:目前已支持至0.8.11版本 +* CRUD:采用表结构存储数据,对业务开发更友好,3.0中封装了更易用的接口 +* AMOP:链上信使协议,借助区块链的P2P网络实现信息传输,实现接入区块链的应用间的数据通信 +* 落盘加密:区块链节点的私钥和数据加密存储于物理硬盘中,物理硬件丢失也无法解密 +* 密码算法:内置群环签名等密码算法,可实现各种安全多方计算场景 +* 更多请参考在线文档 + +#### 兼容性 + +3.0版本与以往各版本数据和协议不兼容,Solidity合约源码兼容。如果要从2.0版本升级到3.0版本,需要做数据迁移。 + +| | 推荐版本 | 最低版本 | 说明 | +| ---------- | --------- | ------------------------ | ---------------------------------- | +| 控制台 | 3.0.0 | 3.0.0 | | +| Java SDK | 3.0.0 | 3.0.0 | | +| CPP SDK | 3.0.0 | 3.0.0 | | +| Console | 3.0.0 | 3.0.0 | | +| Solidity | 0.8.11 | 最低 0.4.25,最高 0.8.11 | 需根据合约版本下载编译器(控制台) | +| WBC-Liquid | 1.0.0-rc3 | 1.0.0-rc3 | | + + + +### 3.0.0-rc4 + +(2022-06-30) + +**新增** + +- 实现`Max`版本FISCO-BCOS, 存储采用分布式存储TiKV,执行模块独立成服务,存储和执行均可横向扩展,且支持自动化主备恢复,可支撑海量交易上链场景 +- 从数据到协议层全面设计并实现兼容性框架,可保证协议和数据的安全升级 +- 支持CRUD合约接口,简化区块链应用开发门槛 +- 支持群环签名合约接口,丰富链上隐私计算能力 +- 支持合约生命周期管理功能,包括合约冻结、解冻 +- 支持数据落盘加密 +- 基于`mtail` + `prometheus` + `grafana` + `ansiable`实现区块链系统监控 + + +**更改** + +- 引入KeyPage,优化读存储性能 +- 基于Rip协议原理,实现网络转发功能,提升网络鲁棒性 +- 支持linux aarch64平台 +- 更新权限治理合约,将节点角色管理、系统配置修改、合约生命周期管理等功能纳入到治理框架 +- 重构权限治理合约,计算逻辑可升级 +- 优化DMC执行框架的性能 +- 优化RPC和P2P的网络性能 +- 优化`Pro`版FISCO-BCOS建链脚本,支持以机构维度配置RPC、Gateway、BcosNodeService等服务 + +**修复** + +- 修复[#issue 2448](https://github.com/FISCO-BCOS/FISCO-BCOS/issues/2448) + + +**兼容性** + +3.0.0-rc4版本与3.0.0-rc3版本数据和协议不兼容,Solidity/WBC-Liquid合约源码兼容。如果要从3.0.0-rc3版本升级到3.0.0-rc4版本,需要做数据迁移。 + +| | 推荐版本 | 最低版本 | 说明 | +| ---------- | ----------------------- | --------- | ---------------------- | +| 控制台 | 3.0.0-rc4 | 3.0.0-rc4 | | +| Java SDK | 3.0.0-rc4 | 3.0.0-rc4 | | +| CPP SDK | 3.0.0-rc4 | 3.0.0-rc4 | | +| WeBASE | 暂时不支持(预计lab-rc4版本支持) | 暂时不支持(预计lab-rc4版本支持) | | +| Solidity | 最高支持 solidity 0.8.11.0 | 0.6.10 | | +| Liquid | 1.0.0-rc3 | 1.0.0-rc2 | | + + +### 3.0.0-rc3 +(2022-03-31) + +**新增** + +- 支持Solidity合约并行冲突字段分析 +- 将密码学、交易编解码等相关逻辑整合到bcos-cpp-sdk中,并封装成通用的C接口 +- WASM虚拟机支持合约调用合约 +- 新增bcos-wasm替代Hera +- `BFS`支持软链接功能 +- 支持通过`setSystemConfig`系统合约的`tx_gas_limit`关键字动态修改交易执行的gas限制 +- 部署合约存储合约ABI + + +**更改** + +- 升级EVM虚拟机到最新,支持Solidity 0.8 +- 机构层面优化网络广播,减少机构间外网带宽占用 +- 支持国密加速库,国密签名和验签性能提升5-10倍 +- EVM节点支持`BFS`,使用`BFS`替代`CNS` +- DAG框架统一支持Solidity和WBC-Liquid + +**修复** + +- 修复[#issue 2312](https://github.com/FISCO-BCOS/FISCO-BCOS/issues/2312) +- 修复[#issue 2307](https://github.com/FISCO-BCOS/FISCO-BCOS/issues/2307) +- 修复[#issue 2254](https://github.com/FISCO-BCOS/FISCO-BCOS/issues/2254) +- 修复[#issue 2211](https://github.com/FISCO-BCOS/FISCO-BCOS/issues/2211) +- 修复[#issue 2195](https://github.com/FISCO-BCOS/FISCO-BCOS/issues/2195) + +**兼容性** + +3.0.0-rc3版本与3.0.0-rc2版本数据和协议不兼容,Solidity/WBC-Liquid合约源码兼容。如果要从3.0.0-rc2版本升级到3.0.0-rc3版本,需要做数据迁移。 + +| | 推荐版本 | 最低版本 | 说明 | +| ---------- | ----------------------- | --------- | ---------------------- | +| 控制台 | 3.0.0-rc3 | 3.0.0-rc3 | | +| Java SDK | 3.0.0-rc3 | 3.0.0-rc3 | | +| CPP SDK | 3.0.0-rc3 | 3.0.0-rc3 | | +| WeBASE | 暂时不支持(预计lab-rc3版本支持) | 暂时不支持(预计lab-rc2版本支持) | | +| Solidity | 最高支持 solidity 0.8.11.0 | 0.6.10 | | +| Liquid | 1.0.0-rc3 | 1.0.0-rc2 | | + + +### 3.0.0-rc2 +(2022-02-23) + +**更改** + +- 优化代码仓库管理复杂度,将多个子仓库集中到FISCO BCOS统一管理 +- 交易由`Base64`编码修改为十六进制编码 +- 升级`bcos-boostssl`和`bcos-utilities`依赖到最新版本 +- 修改`bytesN`类型数据的Scale编解码 +- 优化交易处理流程,避免交易多次重复验签导致的性能损耗 +- 优化事件推送模块的块高获取方法 + + +**修复** + +- 修复scheduler调度交易过程中导致的内存泄露 +- 修复DMC+DAG调度过程中执行不一致的问题 +- 修复[Issue 2132](https://github.com/FISCO-BCOS/FISCO-BCOS/issues/2132) +- 修复[Issue 2124](https://github.com/FISCO-BCOS/FISCO-BCOS/issues/2124) +- 修复部分场景新节点入网没有触发快速视图切换,导致节点数满足`(2*f+1)`却共识异常的问题 +- 修复部分变量访问线程不安全导致节点崩溃的问题 +- 修复AMOP订阅多个topics失败的问题 + +**兼容性** + +3.0.0-rc2版本与3.0.0-rc1版本数据和协议不兼容,Solidity/WBC-Liquid合约源码兼容。如果要从3.0.0-rc1版本升级到3.0.0-rc2版本,需要做数据迁移。 + +| | 推荐版本 | 最低版本 | 说明 | +| ---------- | ----------------------- | --------- | ---------------------- | +| 控制台 | 3.0.0-rc2 | 3.0.0-rc2 | | +| Java SDK | 3.0.0-rc2 | 3.0.0-rc2 | | +| CPP SDK | 3.0.0-rc2 | 3.0.0-rc2 | | +| WeBASE | 暂时不支持(预计lab-rc2版本支持) | 暂时不支持(预计lab-rc2版本支持) | | +| Solidity | 最高支持 solidity 0.6.10 | 0.6.10 | | +| Liquid | 1.0.0-rc2 | 1.0.0-rc2 | | + +### 3.0.0-rc1 +(2021-12-09) + +**新增** + +## 微服务架构 +- 提供通用的区块链接入规范。 +- 提供管理平台,用户可以一键部署、扩容、获得接口粒度的监控信息。 + +## 确定性多合约并行 +- 易用:区块链底层自动并行,无需使用者预先提供冲突字段。 +- 高效:区块内的交易不重复执行,没有预执行或预分析的流程。 +- 通用:无论 EVM、WASM、Precompiled 或其它合约,都能使用此方案。 + +## 区块链文件系统 +- 引入文件系统概念来组织链上资源,用户可以像浏览文件一样浏览链上资源。 +- 基于区块链文件系统实现管理功能,如分区、权限等,更直观。 + +## 流水线PBFT共识 +- 交易排序与交易执行相互独立,实现流水线架构,提升资源利用率。 +- 支持批量共识,对区间内区块并行共识处理,提升性能。 +- 支持单个共识Leader连续出块,提升性能。 + +## WBC-Liquid +- 集成WASM运行环境,支持Liquid智能合约。 +- Liquid智能合约支持智能分析冲突字段,自动开启DAG。 + +**修复** + +**兼容性** + +3.0版本与2.0版本数据和协议不兼容,Solidity合约源码兼容。如果要从2.0版本升级到3.0版本,需要做数据迁移。 + +| | 推荐版本 | 最低版本 | 说明 | +| ---------- | ----------------------- | --------- | ---------------------- | +| 控制台 | 3.0.0-rc1 | 3.0.0-rc1 | | +| Java SDK | 3.0.0-rc1 | 3.0.0-rc1 | | +| CPP SDK | 3.0.0-rc1 | 3.0.0-rc1 | | +| WeBASE | lab-rc1 | lab-rc1 | | +| Solidity | 最高支持 solidity 0.6.10 | 0.6.10 | | +| Liquid | 1.0.0-rc2 | 1.0.0-rc2 | | diff --git "a/BFPL\345\243\271/LICENSE" "b/BFPL\345\243\271/LICENSE" new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ "b/BFPL\345\243\271/LICENSE" @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git "a/BFPL\345\243\271/README.md" "b/BFPL\345\243\271/README.md" new file mode 100644 index 00000000..14801b90 --- /dev/null +++ "b/BFPL\345\243\271/README.md" @@ -0,0 +1,74 @@ +中文 / [English](docs/README_EN.md) + +![](./docs/FISCO_BCOS_Logo.svg) + +[![codecov](https://codecov.io/gh/FISCO-BCOS/FISCO-BCOS/branch/master/graph/badge.svg)](https://codecov.io/gh/FISCO-BCOS/FISCO-BCOS) +[![CodeFactor](https://www.codefactor.io/repository/github/fisco-bcos/FISCO-BCOS/badge)](https://www.codefactor.io/repository/github/fisco-bcos/FISCO-BCOS) +[![GitHub All Releases](https://img.shields.io/github/downloads/FISCO-BCOS/FISCO-BCOS/total.svg)](https://github.com/FISCO-BCOS/FISCO-BCOS) + +FISCO BCOS是由微众牵头的金链盟主导研发、对外开源、安全可控的企业级金融区块链底层技术平台。 +单链配置下,性能TPS可达万级。提供群组架构、并行计算、分布式存储、可插拔的共识机制、隐私保护算法、支持全链路国密算法等诸多特性。 +经过多个机构、多个应用,长时间在生产环境中的实践检验,具备金融级的高性能、高可用性及高安全性。 + +**架构** + +Air 、Pro、Max:可部署为三种架构形态 + +- **轻便Air版**:拥有与 v2.0版本相同的形态,所有功能在一个区块链节点中(all-in-one)。该架构简单,可快速部署在任意环境中。你可以用它进行区块链入门、开发、测试、POC验证等工作。 +- **专业Pro版**:该架构通过将区块链节点的接入层模块独立为进程,在实现接入层与核心模块分区部署的同时,让区块链核心功模块以多群组方式扩展。该架构实现了分区隔离,可应对将来可能的业务拓展,适合有持续业务扩展的生产环境。 +- **大容量Max版**:该架构在Pro版的基础上提供链的核心模块主备切换的能力,并可通过多机部署交易执行器和接入分布式存储TiKV,实现计算与存储的平行拓展。该架构中的一个节点由一系列微服务组成,但它依赖较高的运维能力,适合需要海量计算和存储的场景。 + +**功能** + +- 区块链文件系统:所见即所得的合约数据管理 +- SDK基础库:更方便的全平台国密接入 +- 交易并行冲突分析工具:自动生成交易冲突变量 +- WBC-Liquid:用Rust写合约 +- 权限治理框架:多方投票治理区块链 + +**特性** + +- Pipeline:区块流水线,连续且紧凑地生成区块 +- DMC:实现交易处理性能的多机拓展 +- PBFT共识算法:立即一致的共识算法,实现交易秒级确认 +- +TiKV:分布式事务性提交,支撑海量存储 + +## 技术文档 + +- **[FISCO BCOS 3.x技术文档](https://fisco-bcos-doc.readthedocs.io/zh_CN/latest/)** + +- **[FISCO BCOS 2.x 技术文档(stable)](https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/)** + + +## 落地应用案例 + +FISCO BCOS已落地应用达数百个,场景覆盖政务、金融、公益、医疗、教育、交通、版权、商品溯源、供应链、招聘、农业、社交、游戏等多个领域,如: + +- 金融业:机构间对账、供应链金融、旅游金融等。 +- 司法服务:仲裁链、电子借据等。 +- 文化版权:版权存证与交易等。 +- 社会管理:不动产登记等。 + +此处提供一些具有代表性的[落地应用案例](https://mp.weixin.qq.com/s/RJwRMChWt6mhJrysyBLAmA)。 + +## 贡献代码 + +- 我们欢迎并非常感谢您的贡献,请参阅[代码贡献流程](https://mp.weixin.qq.com/s/_w_auH8X4SQQWO3lhfNrbQ)。 + +- 如项目对您有帮助,欢迎star支持! + +## 加入我们的社区 + +**FISCO BCOS开源社区**是国内庞大且活跃的开源社区,开源以来,围绕FISCO BCOS所构建的开源社区已汇集超3000家企业及机构、7万余名个人成员共建共治共享,成功支持政务、金融、农业、公益、文娱、供应链、物联网等重点应用领域的数百个区块链应用场景落地,收集到的标杆应用超过200个,构建出庞大且活跃的开源联盟链生态圈。 + +如您对FISCO BCOS开源技术及应用感兴趣,欢迎加入社区获得更多支持与帮助。 +- [MVP](https://www.fisco.com.cn/news_6/494.html) +- [贡献者](https://www.fisco.com.cn/news_6/510.html) +- [合作伙伴](https://www.fisco.com.cn/news_6/492.html) + + +![](https://raw.githubusercontent.com/FISCO-BCOS/LargeFiles/master/images/QR_image.png) + +## License + +FISCO BCOS的开源协议为Apache License 2.0, 详情参见[LICENSE](LICENSE)。 diff --git "a/BFPL\345\243\271/bcos-boostssl/CMakeLists.txt" "b/BFPL\345\243\271/bcos-boostssl/CMakeLists.txt" new file mode 100644 index 00000000..e1365732 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/CMakeLists.txt" @@ -0,0 +1,21 @@ +include(Version) +project(bcos-boostssl VERSION ${VERSION}) + +file(GLOB_RECURSE SRCS bcos-boostssl/*.cpp) + +find_package(OpenSSL REQUIRED) + +add_library(bcos-boostssl ${SRCS}) +target_include_directories(bcos-boostssl PUBLIC + $ + $) +target_link_libraries(bcos-boostssl PUBLIC bcos-framework bcos-utilities OpenSSL::SSL) + +# if(TESTS) +# enable_testing() +# add_subdirectory(test) +# endif() + +include(GNUInstallDirs) +install(TARGETS bcos-boostssl EXPORT fiscobcosTargets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") +install(DIRECTORY "bcos-boostssl" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" FILES_MATCHING PATTERN "*.h") \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/CMakeLists.txt" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/CMakeLists.txt" new file mode 100644 index 00000000..fa8fabae --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/CMakeLists.txt" @@ -0,0 +1,7 @@ + +file(GLOB_RECURSE SRCS "*.cpp") +file(GLOB_RECURSE HEADERS "*.h") + +add_library(${BOOSTSSL_TARGET} ${SRCS} ${HEADERS}) +target_link_libraries(${BOOSTSSL_TARGET} PUBLIC tassl::ssl tassl::crypto ${UTILITIES_TARGET} bcos-framework Boost::system Boost::filesystem +Boost::log Boost::chrono Boost::iostreams Boost::thread ) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/Common.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/Common.h" new file mode 100644 index 00000000..91de6c3f --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/Common.h" @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2021 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: octopus + * @date 2021-06-14 + */ + +#pragma once +#include +#include +#include + + +#define CONTEXT_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE(m_moduleName) << "[BOOSTSSL][CTX]" +#define NODEINFO_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE(m_moduleName) << "[BOOSTSSL][NODEINFO]" + +namespace bcos +{ // namespace bcos +namespace boostssl +{ +inline X509* toX509(const char* _pemBuffer) +{ + BIO* bio_mem = BIO_new(BIO_s_mem()); + BIO_puts(bio_mem, _pemBuffer); + X509* x509 = PEM_read_bio_X509(bio_mem, NULL, NULL, NULL); + BIO_free(bio_mem); + // X509_free(x509); + return x509; +} + +inline EVP_PKEY* toEvpPkey(const char* _pemBuffer) +{ + BIO* bio_mem = BIO_new_mem_buf(_pemBuffer, -1); + EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bio_mem, NULL, NULL, NULL); + BIO_free(bio_mem); + // EVP_PKEY_free(pkey); + return pkey; +} + +} // namespace boostssl +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/ContextBuilder.cpp" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/ContextBuilder.cpp" new file mode 100644 index 00000000..0d4acd52 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/ContextBuilder.cpp" @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2021 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 ContextBuilder.cpp + * @author: octopus + * @date 2021-06-14 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::boostssl; +using namespace bcos::boostssl::context; + +// static const std::string DEFAULT_CONFIG = "./boostssl.ini"; + +std::shared_ptr ContextBuilder::readFileContent(boost::filesystem::path const& _file) +{ + std::shared_ptr content = std::make_shared(); + boost::filesystem::ifstream fileStream(_file, std::ifstream::binary); + if (!fileStream) + { + return content; + } + fileStream.seekg(0, fileStream.end); + auto length = fileStream.tellg(); + if (length == 0) + { + return content; + } + fileStream.seekg(0, fileStream.beg); + content->resize(length); + fileStream.read((char*)content->data(), length); + return content; +} + +std::shared_ptr ContextBuilder::buildSslContext( + bool _server, const std::string& _configPath) +{ + auto config = std::make_shared(); + config->initConfig(_configPath); + return buildSslContext(_server, *config); +} + +std::shared_ptr ContextBuilder::buildSslContext( + bool _server, const ContextConfig& _contextConfig) +{ + if (_contextConfig.isCertPath()) + { + if (_contextConfig.sslType() != "sm_ssl") + { + return buildSslContext(_contextConfig.certConfig()); + } + return buildSslContext(_server, _contextConfig.smCertConfig()); + } + else + { + if (_contextConfig.sslType() != "sm_ssl") + { + return buildSslContextByCertContent(_contextConfig.certConfig()); + } + return buildSslContextByCertContent(_server, _contextConfig.smCertConfig()); + } +} + +std::shared_ptr ContextBuilder::buildSslContext( + const ContextConfig::CertConfig& _certConfig) +{ + std::shared_ptr sslContext = + std::make_shared(boost::asio::ssl::context::tlsv12); + + auto keyContent = + readFileContent(boost::filesystem::path(_certConfig.nodeKey)); // node.key content + boost::asio::const_buffer keyBuffer(keyContent->data(), keyContent->size()); + sslContext->use_private_key(keyBuffer, boost::asio::ssl::context::file_format::pem); + + // node.crt + sslContext->use_certificate_chain_file(_certConfig.nodeCert); + + auto caCertContent = readFileContent(boost::filesystem::path(_certConfig.caCert)); // ca.crt + sslContext->add_certificate_authority( + boost::asio::const_buffer(caCertContent->data(), caCertContent->size())); + + std::string caPath; + if (!caPath.empty()) + { + sslContext->add_verify_path(caPath); + } + + sslContext->set_verify_mode(boost::asio::ssl::context_base::verify_peer | + boost::asio::ssl::verify_fail_if_no_peer_cert); + + return sslContext; +} + +std::shared_ptr ContextBuilder::buildSslContext( + bool _server, const ContextConfig::SMCertConfig& _smCertConfig) +{ + SSL_CTX* ctx = NULL; + if (_server) + { + const SSL_METHOD* meth = SSLv23_server_method(); + ctx = SSL_CTX_new(meth); + } + else + { + const SSL_METHOD* meth = CNTLS_client_method(); + ctx = SSL_CTX_new(meth); + } + + std::shared_ptr sslContext = + std::make_shared(ctx); + + sslContext->set_verify_mode(boost::asio::ssl::context_base::verify_none); + + /* Load the server certificate into the SSL_CTX structure */ + if (SSL_CTX_use_certificate_file( + sslContext->native_handle(), _smCertConfig.nodeCert.c_str(), SSL_FILETYPE_PEM) <= 0) + { + ERR_print_errors_fp(stderr); + BOOST_THROW_EXCEPTION(std::runtime_error("SSL_CTX_use_certificate_file error")); + } + + /* Load the private-key corresponding to the server certificate */ + if (SSL_CTX_use_PrivateKey_file( + sslContext->native_handle(), _smCertConfig.nodeKey.c_str(), SSL_FILETYPE_PEM) <= 0) + { + ERR_print_errors_fp(stderr); + BOOST_THROW_EXCEPTION(std::runtime_error("SSL_CTX_use_PrivateKey_file error")); + } + + /* Check if the server certificate and private-key matches */ + if (!SSL_CTX_check_private_key(sslContext->native_handle())) + { + fprintf(stderr, "Private key does not match the certificate public key\n"); + BOOST_THROW_EXCEPTION(std::runtime_error("SSL_CTX_check_private_key error")); + } + + /* Load the server encrypt certificate into the SSL_CTX structure */ + if (SSL_CTX_use_enc_certificate_file( + sslContext->native_handle(), _smCertConfig.enNodeCert.c_str(), SSL_FILETYPE_PEM) <= 0) + { + ERR_print_errors_fp(stderr); + BOOST_THROW_EXCEPTION(std::runtime_error("SSL_CTX_use_enc_certificate_file error")); + } + + /* Load the private-key corresponding to the server encrypt certificate */ + if (SSL_CTX_use_enc_PrivateKey_file( + sslContext->native_handle(), _smCertConfig.enNodeKey.c_str(), SSL_FILETYPE_PEM) <= 0) + { + ERR_print_errors_fp(stderr); + BOOST_THROW_EXCEPTION(std::runtime_error("SSL_CTX_use_enc_PrivateKey_file error")); + } + + /* Check if the server encrypt certificate and private-key matches */ + if (!SSL_CTX_check_enc_private_key(sslContext->native_handle())) + { + fprintf(stderr, "Private key does not match the certificate public key\n"); + ERR_print_errors_fp(stderr); + BOOST_THROW_EXCEPTION(std::runtime_error("SSL_CTX_check_enc_private_key error")); + } + + /* Load the RSA CA certificate into the SSL_CTX structure + if (!SSL_CTX_load_verify_locations( + sslContext->native_handle(), _smCertConfig.caCert.c_str(), NULL)) + { + ERR_print_errors_fp(stderr); + BOOST_THROW_EXCEPTION(std::runtime_error("SSL_CTX_load_verify_locations error")); + } + */ + auto caCertContent = readFileContent(boost::filesystem::path(_smCertConfig.caCert)); // ca.crt + sslContext->add_certificate_authority( + boost::asio::const_buffer(caCertContent->data(), caCertContent->size())); + + sslContext->set_verify_mode(boost::asio::ssl::context_base::verify_peer | + boost::asio::ssl::verify_fail_if_no_peer_cert); + return sslContext; +} + +std::shared_ptr ContextBuilder::buildSslContextByCertContent( + const ContextConfig::CertConfig& _certConfig) +{ + std::shared_ptr sslContext = + std::make_shared(boost::asio::ssl::context::tlsv12); + + sslContext->use_private_key( + boost::asio::const_buffer(_certConfig.nodeKey.data(), _certConfig.nodeKey.size()), + boost::asio::ssl::context::file_format::pem); + sslContext->use_certificate_chain( + boost::asio::const_buffer(_certConfig.nodeCert.data(), _certConfig.nodeCert.size())); + + sslContext->add_certificate_authority( + boost::asio::const_buffer(_certConfig.caCert.data(), _certConfig.caCert.size())); + + std::string caPath; + if (!caPath.empty()) + { + sslContext->add_verify_path(caPath); + } + + sslContext->set_verify_mode(boost::asio::ssl::context_base::verify_peer | + boost::asio::ssl::verify_fail_if_no_peer_cert); + + return sslContext; +} + +std::shared_ptr ContextBuilder::buildSslContextByCertContent( + bool _server, const ContextConfig::SMCertConfig& _smCertConfig) +{ + SSL_CTX* ctx = NULL; + if (_server) + { + const SSL_METHOD* meth = SSLv23_server_method(); + ctx = SSL_CTX_new(meth); + } + else + { + const SSL_METHOD* meth = CNTLS_client_method(); + ctx = SSL_CTX_new(meth); + } + + std::shared_ptr sslContext = + std::make_shared(ctx); + + sslContext->set_verify_mode(boost::asio::ssl::context_base::verify_none); + + sslContext->use_certificate_chain(boost::asio::const_buffer( + _smCertConfig.nodeCert.data(), _smCertConfig.nodeCert.size())); // node.crt + + sslContext->use_private_key( + boost::asio::const_buffer(_smCertConfig.nodeKey.data(), _smCertConfig.nodeKey.size()), + boost::asio::ssl::context::file_format::pem); // node.key + + /* Check if the server certificate and private-key matches */ + if (!SSL_CTX_check_private_key(sslContext->native_handle())) + { + BOOST_THROW_EXCEPTION(std::runtime_error("SSL_CTX_check_private_key error")); + } + + /* Load the server encrypt certificate into the SSL_CTX structure */ + if (!SSL_CTX_use_enc_certificate( + sslContext->native_handle(), toX509(_smCertConfig.enNodeCert.c_str()))) + { + ERR_print_errors_fp(stderr); + BOOST_THROW_EXCEPTION(std::runtime_error("SSL_CTX_use_enc_certificate_file error")); + } + + /* Load the private-key corresponding to the server encrypt certificate */ + if (!SSL_CTX_use_enc_PrivateKey( + sslContext->native_handle(), toEvpPkey(_smCertConfig.enNodeKey.c_str()))) + { + ERR_print_errors_fp(stderr); + BOOST_THROW_EXCEPTION(std::runtime_error("SSL_CTX_use_enc_PrivateKey_file error")); + } + + /* Check if the server encrypt certificate and private-key matches */ + if (!SSL_CTX_check_enc_private_key(sslContext->native_handle())) + { + ERR_print_errors_fp(stderr); + BOOST_THROW_EXCEPTION(std::runtime_error("SSL_CTX_check_enc_private_key error")); + } + + sslContext->add_certificate_authority(boost::asio::const_buffer( + _smCertConfig.caCert.data(), _smCertConfig.caCert.size())); // ca.crt + + /* Load the RSA CA certificate into the SSL_CTX structure + if (!SSL_CTX_load_verify_locations( + sslContext->native_handle(), _smCertConfig.caCert.c_str(), NULL)) + { + ERR_print_errors_fp(stderr); + BOOST_THROW_EXCEPTION(std::runtime_error("SSL_CTX_load_verify_locations error")); + } + */ + + sslContext->set_verify_mode(boost::asio::ssl::context_base::verify_peer | + boost::asio::ssl::verify_fail_if_no_peer_cert); + return sslContext; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/ContextBuilder.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/ContextBuilder.h" new file mode 100644 index 00000000..41f09fab --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/ContextBuilder.h" @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2021 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 ContextBuilder.h + * @author: octopus + * @date 2021-06-14 + */ +#pragma once +#include +#include +#include + +namespace bcos +{ +namespace boostssl +{ +namespace context +{ +class ContextBuilder +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + +public: + std::shared_ptr readFileContent(boost::filesystem::path const& _file); + + std::string moduleName() { return m_moduleName; } + void setModuleName(std::string _moduleName) { m_moduleName = _moduleName; } + +public: + std::shared_ptr buildSslContext( + bool _server, const std::string& _configPath); + std::shared_ptr buildSslContext( + bool _server, const ContextConfig& _contextConfig); + +private: + std::shared_ptr buildSslContext( + const ContextConfig::CertConfig& _certConfig); + std::shared_ptr buildSslContext( + bool _server, const ContextConfig::SMCertConfig& _smCertConfig); + std::shared_ptr buildSslContextByCertContent( + const ContextConfig::CertConfig& _certConfig); + std::shared_ptr buildSslContextByCertContent( + bool _server, const ContextConfig::SMCertConfig& _smCertConfig); + +private: + std::string m_moduleName = "DEFAULT"; +}; + +} // namespace context +} // namespace boostssl +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/ContextConfig.cpp" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/ContextConfig.cpp" new file mode 100644 index 00000000..462ed7b0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/ContextConfig.cpp" @@ -0,0 +1,128 @@ +/** @file ContextConfig.cpp + * @author octopus + * @date 2021-06-14 + */ + +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::boostssl; +using namespace bcos::boostssl::context; +/** + * @brief: loads configuration items from the config.ini + * @param _configPath: config.ini path + * @return void + */ +void ContextConfig::initConfig(std::string const& _configPath) +{ + try + { + boost::property_tree::ptree pt; + boost::property_tree::ini_parser::read_ini(_configPath, pt); + std::string sslType = pt.get("common.ssl_type", "ssl"); + if ("sm_ssl" != sslType) + { // SSL + initCertConfig(pt); + } + else + { // SM SSL + initSMCertConfig(pt); + } + + m_sslType = sslType; + } + catch (const std::exception& e) + { + boost::filesystem::path currentPath(boost::filesystem::current_path()); + + CONTEXT_LOG(WARNING) << LOG_DESC("initConfig failed") << LOG_KV("configPath", _configPath) + << LOG_KV("currentPath", currentPath.string()) + << LOG_KV("error", boost::diagnostic_information(e)); + + + BOOST_THROW_EXCEPTION(std::runtime_error("initConfig: currentPath:" + currentPath.string() + + " ,error:" + boost::diagnostic_information(e))); + } + + CONTEXT_LOG(INFO) << LOG_DESC("initConfig") << LOG_KV("sslType", m_sslType) + << LOG_KV("configPath", _configPath); +} + +/// loads ca configuration items from the configuration file +void ContextConfig::initCertConfig(const boost::property_tree::ptree& _pt) +{ + std::string caPath = _pt.get("cert.ca_path", "./"); + std::string caCertFile = caPath + "/" + _pt.get("cert.ca_cert", "ca.crt"); + std::string nodeCertFile = caPath + "/" + _pt.get("cert.node_cert", "node.crt"); + std::string nodeKeyFile = caPath + "/" + _pt.get("cert.node_key", "node.key"); + + CONTEXT_LOG(INFO) << LOG_DESC("initCertConfig") << LOG_KV("ca_path", caPath) + << LOG_KV("ca_cert", caCertFile) << LOG_KV("node_cert", nodeCertFile) + << LOG_KV("node_key", nodeKeyFile); + + checkFileExist(caCertFile); + checkFileExist(nodeCertFile); + checkFileExist(nodeKeyFile); + + CertConfig certConfig; + certConfig.caCert = caCertFile; + certConfig.nodeCert = nodeCertFile; + certConfig.nodeKey = nodeKeyFile; + + m_certConfig = certConfig; + + CONTEXT_LOG(INFO) << LOG_DESC("initCertConfig") << LOG_KV("ca", certConfig.caCert) + << LOG_KV("node_cert", certConfig.nodeCert) + << LOG_KV("node_key", certConfig.nodeKey); +} + +// loads sm ca configuration items from the configuration file +void ContextConfig::initSMCertConfig(const boost::property_tree::ptree& _pt) +{ + std::string caPath = _pt.get("cert.ca_path", "./"); + std::string smCaCertFile = caPath + "/" + _pt.get("cert.sm_ca_cert", "sm_ca.crt"); + std::string smNodeCertFile = + caPath + "/" + _pt.get("cert.sm_node_cert", "sm_node.crt"); + std::string smNodeKeyFile = + caPath + "/" + _pt.get("cert.sm_node_key", "sm_node.key"); + std::string smEnNodeCertFile = + caPath + "/" + _pt.get("cert.sm_ennode_cert", "sm_ennode.crt"); + std::string smEnNodeKeyFile = + caPath + "/" + _pt.get("cert.sm_ennode_key", "sm_ennode.key"); + + checkFileExist(smCaCertFile); + checkFileExist(smNodeCertFile); + checkFileExist(smNodeKeyFile); + checkFileExist(smEnNodeCertFile); + checkFileExist(smEnNodeKeyFile); + + SMCertConfig smCertConfig; + smCertConfig.caCert = smCaCertFile; + smCertConfig.nodeCert = smNodeCertFile; + smCertConfig.nodeKey = smNodeKeyFile; + smCertConfig.enNodeCert = smEnNodeCertFile; + smCertConfig.enNodeKey = smEnNodeKeyFile; + + m_smCertConfig = smCertConfig; + + CONTEXT_LOG(INFO) << LOG_DESC("initSMCertConfig") << LOG_KV("ca_path", caPath) + << LOG_KV("sm_ca_cert", smCertConfig.caCert) + << LOG_KV("sm_node_cert", smCertConfig.nodeCert) + << LOG_KV("sm_node_key", smCertConfig.nodeKey) + << LOG_KV("sm_ennode_cert", smCertConfig.enNodeCert) + << LOG_KV("sm_ennode_key", smCertConfig.enNodeKey); +} + +void ContextConfig::checkFileExist(const std::string& _path) +{ + auto isExist = boost::filesystem::exists(boost::filesystem::path(_path)); + if (!isExist) + { + BOOST_THROW_EXCEPTION(std::runtime_error("file not exist: " + _path)); + } +} diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/ContextConfig.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/ContextConfig.h" new file mode 100644 index 00000000..2632e8e6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/ContextConfig.h" @@ -0,0 +1,90 @@ + +/** @file ContextConfig.h + * @author octopus + * @date 2021-06-14 + */ + +#pragma once +#include +#include +#include + +namespace bcos +{ +namespace boostssl +{ +namespace context +{ +class ContextConfig +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + ContextConfig() = default; + ~ContextConfig() = default; + +public: + // cert for ssl connection + struct CertConfig + { + std::string caCert; + std::string nodeKey; + std::string nodeCert; + }; + + // cert for sm ssl connection + struct SMCertConfig + { + std::string caCert; + std::string nodeCert; + std::string nodeKey; + std::string enNodeCert; + std::string enNodeKey; + }; + +public: + /** + * @brief: loads configuration items from the boostssl.ini + * @param _configPath: + * @return void + */ + void initConfig(std::string const& _configPath); + // loads ca configuration items from the configuration file + void initCertConfig(const boost::property_tree::ptree& _pt); + // loads sm ca configuration items from the configuration file + void initSMCertConfig(const boost::property_tree::ptree& _pt); + // check if file exist, exception will be throw if the file not exist + void checkFileExist(const std::string& _path); + +public: + bool isCertPath() const { return m_isCertPath; } + void setIsCertPath(bool _isCertPath) { m_isCertPath = _isCertPath; } + + std::string sslType() const { return m_sslType; } + void setSslType(const std::string _sslType) { m_sslType = _sslType; } + + const CertConfig& certConfig() const { return m_certConfig; } + void setCertConfig(const CertConfig& _certConfig) { m_certConfig = _certConfig; } + + const SMCertConfig& smCertConfig() const { return m_smCertConfig; } + void setSmCertConfig(const SMCertConfig& _smCertConfig) { m_smCertConfig = _smCertConfig; } + + std::string moduleName() { return m_moduleName; } + void setModuleName(std::string _moduleName) { m_moduleName = _moduleName; } + + +private: + // is the cert path or cert file content + bool m_isCertPath = true; + // ssl type, support ssl && sm_ssl + std::string m_sslType; + // cert config for ssl + CertConfig m_certConfig; + SMCertConfig m_smCertConfig; + std::string m_moduleName = "DEFAULT"; +}; + +} // namespace context +} // namespace boostssl +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/NodeInfoTools.cpp" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/NodeInfoTools.cpp" new file mode 100644 index 00000000..de270cd2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/NodeInfoTools.cpp" @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2021 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 NodeInfoTools.cpp + * @author: lucasli + * @date 2022-03-07 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos::boostssl::context; + +/* + * @brief : functions called after openssl handshake, + * maily to get node id and verify whether the certificate has been + * expired + * @param nodeIDOut : also return value, pointer points to the node id string + * @return std::function: + * return true: verify success + * return false: verify failed + * modifications 2019.03.20: append subject name and issuer name after nodeIDOut + * for demand of fisco-bcos-browser + */ +std::function NodeInfoTools::newVerifyCallback( + std::shared_ptr nodeIDOut) +{ + return [nodeIDOut](bool preverified, boost::asio::ssl::verify_context& ctx) { + try + { + /// return early when the certificate is invalid + if (!preverified) + { + return false; + } + /// get the object points to certificate + X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); + if (!cert) + { + NODEINFO_LOG(INFO) << LOG_DESC("Get cert failed"); + return preverified; + } + + auto sslContextPubHexHandler = initSSLContextPubHexHandler(); + if (!sslContextPubHexHandler(cert, *nodeIDOut.get())) + { + return preverified; + } + + int crit = 0; + BASIC_CONSTRAINTS* basic = + (BASIC_CONSTRAINTS*)X509_get_ext_d2i(cert, NID_basic_constraints, &crit, NULL); + if (!basic) + { + NODEINFO_LOG(INFO) << LOG_DESC("Get ca basic failed"); + return preverified; + } + + if (basic->ca) + { + // ca or agency certificate + NODEINFO_LOG(TRACE) << LOG_DESC("Ignore CA certificate"); + BASIC_CONSTRAINTS_free(basic); + return preverified; + } + + BASIC_CONSTRAINTS_free(basic); + return preverified; + } + catch (std::exception& e) + { + NODEINFO_LOG(WARNING) << LOG_DESC("Cert verify failed") + << boost::diagnostic_information(e); + return preverified; + } + }; +} + +std::function +NodeInfoTools::initCert2PubHexHandler() +{ + return [](const std::string& _cert, std::string& _pubHex) -> bool { + std::string errorMessage; + do + { + auto certContent = readContentsToString(boost::filesystem::path(_cert)); + if (!certContent || certContent->empty()) + { + errorMessage = "unable to load cert content, cert: " + _cert; + break; + } + + NODEINFO_LOG(INFO) << LOG_DESC("initCert2PubHexHandler") << LOG_KV("cert", _cert) + << LOG_KV("certContent: ", certContent); + + std::shared_ptr bioMem(BIO_new(BIO_s_mem()), [](BIO* p) { + if (p != NULL) + { + BIO_free(p); + } + }); + + if (!bioMem) + { + errorMessage = "BIO_new error"; + break; + } + + BIO_write(bioMem.get(), certContent->data(), certContent->size()); + std::shared_ptr x509Ptr( + PEM_read_bio_X509(bioMem.get(), NULL, NULL, NULL), [](X509* p) { + if (p != NULL) + { + X509_free(p); + } + }); + + if (!x509Ptr) + { + errorMessage = "PEM_read_bio_X509 error"; + break; + } + + ASN1_BIT_STRING* pubKey = X509_get0_pubkey_bitstr(x509Ptr.get()); + if (pubKey == NULL) + { + errorMessage = "X509_get0_pubkey_bitstr error"; + break; + } + + auto hex = bcos::toHexString(pubKey->data, pubKey->data + pubKey->length, ""); + _pubHex = *hex.get(); + + NODEINFO_LOG(INFO) << LOG_DESC("initCert2PubHexHandler ") << LOG_KV("cert", _cert) + << LOG_KV("pubHex: ", _pubHex); + return true; + } while (0); + + NODEINFO_LOG(WARNING) << LOG_DESC("initCert2PubHexHandler") << LOG_KV("cert", _cert) + << LOG_KV("errorMessage", errorMessage); + return false; + }; +} + +std::function NodeInfoTools::initSSLContextPubHexHandler() +{ + return [](X509* x509, std::string& _pubHex) -> bool { + ASN1_BIT_STRING* pubKey = + X509_get0_pubkey_bitstr(x509); // csc->current_cert is an X509 struct + if (pubKey == NULL) + { + return false; + } + + auto hex = bcos::toHexString(pubKey->data, pubKey->data + pubKey->length, ""); + _pubHex = *hex.get(); + + NODEINFO_LOG(INFO) << LOG_DESC("[NEW]SSLContext pubHex: " + _pubHex); + return true; + }; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/NodeInfoTools.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/NodeInfoTools.h" new file mode 100644 index 00000000..c045d50d --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/context/NodeInfoTools.h" @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2021 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 NodeInfoTools.h + * @author: lucasli + * @date 2022-03-07 + */ +#pragma once +#include +#include +#include +#include + +namespace bcos +{ +namespace boostssl +{ +namespace context +{ +static std::string m_moduleName = "DEFAULT"; + +class NodeInfoTools +{ +public: + // register the function fetch pub hex from the cert + static std::function + initCert2PubHexHandler(); + // register the function fetch public key from the ssl context + static std::function initSSLContextPubHexHandler(); + + static std::function newVerifyCallback( + std::shared_ptr nodeIDOut); + + static std::string moduleName() { return m_moduleName; } + static void setModuleName(std::string _moduleName) { m_moduleName = _moduleName; } +}; + +} // namespace context +} // namespace boostssl +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/Common.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/Common.h" new file mode 100644 index 00000000..4b714f49 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/Common.h" @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2021 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: octopus + * @date 2021-07-08 + */ +#pragma once + +#include +#include +#include +#include +#include + + +#define HTTP_LISTEN(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE(m_moduleName) << "[HTTP][LISTEN]" +#define HTTP_SESSION(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE(m_moduleName) << "[HTTP][SESSION]" +#define HTTP_SERVER(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE(m_moduleName) << "[HTTP][SERVER]" +#define HTTP_STREAM(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE(m_moduleName) << "[HTTP][STREAM]" + + +namespace bcos +{ +namespace boostssl +{ +namespace http +{ +class HttpStream; +using HttpRequest = boost::beast::http::request; +using HttpResponse = boost::beast::http::response>; +using HttpRequestPtr = std::shared_ptr; +using HttpResponsePtr = std::shared_ptr; +using HttpReqHandler = + std::function)>; +using WsUpgradeHandler = + std::function, HttpRequest&&, std::shared_ptr)>; + +static const int PARSER_BODY_LIMITATION = 100 * 1024 * 1024; +} // namespace http +} // namespace boostssl +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpQueue.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpQueue.h" new file mode 100644 index 00000000..663bf319 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpQueue.h" @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2021 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 + * m_limitations under the License. + * + * @file HttpQueue.h + * @author: octopus + * @date 2021-07-08 + */ +#pragma once +#include + +namespace bcos +{ +namespace boostssl +{ +namespace http +{ +// The queue for http request pipeline +class Queue +{ +private: + // the maximum number size of queue + std::size_t m_limit; + // messages to be send + std::vector m_allResp; + // send handler + std::function m_sender; + +public: + explicit Queue(std::size_t _limit = 16) : m_limit(_limit) { m_allResp.reserve(m_limit); } + + void setSender(std::function _sender) { m_sender = _sender; } + std::function sender() const { return m_sender; } + + std::size_t limit() const { return m_limit; } + void setLimit(std::size_t _limit) { m_limit = _limit; } + + // if the queue reached the m_limit + bool isFull() const { return m_allResp.size() >= m_limit; } + + // called when a message finishes sending + // returns `true` if the caller should initiate a read + bool onWrite() + { + BOOST_ASSERT(!m_allResp.empty()); + auto const was_full = isFull(); + m_allResp.erase(m_allResp.begin()); + if (!m_allResp.empty()) + { + m_sender(m_allResp.front()); + } + return was_full; + } + + // enqueue and waiting called by the HTTP handler to send a response. + void enqueue(HttpResponsePtr _msg) + { + m_allResp.push_back(_msg); + // there was no previous work, start this one + if (m_allResp.size() == 1) + { + m_sender(m_allResp.front()); + } + } +}; +} // namespace http +} // namespace boostssl +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpServer.cpp" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpServer.cpp" new file mode 100644 index 00000000..9794ba18 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpServer.cpp" @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2021 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 + * m_limitations under the License. + * + * @file HttpHttpServer.h + * @author: octopus + * @date 2021-07-08 + */ + +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::boostssl; +using namespace bcos::boostssl::http; +using namespace bcos::boostssl::context; + +// start http server +void HttpServer::start() +{ + if (m_acceptor && m_acceptor->is_open()) + { + HTTP_SERVER(INFO) << LOG_BADGE("startListen") << LOG_DESC("http server is running"); + return; + } + + HTTP_SERVER(INFO) << LOG_BADGE("startListen") << LOG_KV("listenIP", m_listenIP) + << LOG_KV("listenPort", m_listenPort); + + auto address = boost::asio::ip::make_address(m_listenIP); + auto endpoint = boost::asio::ip::tcp::endpoint{address, m_listenPort}; + + boost::beast::error_code ec; + m_acceptor->open(endpoint.protocol(), ec); + if (ec) + { + HTTP_SERVER(WARNING) << LOG_BADGE("open") << LOG_KV("error", ec) + << LOG_KV("message", ec.message()); + BOOST_THROW_EXCEPTION(std::runtime_error("acceptor open failed")); + } + + // allow address reuse + m_acceptor->set_option(boost::asio::socket_base::reuse_address(true), ec); + if (ec) + { + HTTP_SERVER(WARNING) << LOG_BADGE("set_option") << LOG_KV("error", ec) + << LOG_KV("message", ec.message()); + + BOOST_THROW_EXCEPTION(std::runtime_error("acceptor set_option failed")); + } + + m_acceptor->bind(endpoint, ec); + if (ec) + { + HTTP_SERVER(WARNING) << LOG_BADGE("bind") << LOG_KV("error", ec) + << LOG_KV("message", ec.message()); + BOOST_THROW_EXCEPTION(std::runtime_error("acceptor bind failed")); + } + + m_acceptor->listen(boost::asio::socket_base::max_listen_connections, ec); + if (ec) + { + HTTP_SERVER(WARNING) << LOG_BADGE("listen") << LOG_KV("error", ec) + << LOG_KV("message", ec.message()); + BOOST_THROW_EXCEPTION(std::runtime_error("acceptor listen failed")); + } + + // start accept + doAccept(); + + HTTP_SERVER(INFO) << LOG_BADGE("startListen") << LOG_KV("ip", endpoint.address().to_string()) + << LOG_KV("port", endpoint.port()); +} + +void HttpServer::stop() +{ + if (m_acceptor && m_acceptor->is_open()) + { + m_acceptor->close(); + } + + + HTTP_SERVER(INFO) << LOG_BADGE("stop") << LOG_DESC("http server"); +} + +void HttpServer::doAccept() +{ + // The new connection gets its own strand + m_acceptor->async_accept(*(m_ioservicePool->getIOService()), + boost::beast::bind_front_handler(&HttpServer::onAccept, shared_from_this())); +} + +void HttpServer::onAccept(boost::beast::error_code ec, boost::asio::ip::tcp::socket socket) +{ + if (ec) + { + HTTP_SERVER(WARNING) << LOG_BADGE("accept") << LOG_KV("error", ec) + << LOG_KV("message", ec.message()); + return doAccept(); + } + + boost::system::error_code sec; + auto localEndpoint = socket.local_endpoint(sec); + if(sec) { + HTTP_SERVER(WARNING) << LOG_BADGE("accept") << LOG_KV("local_endpoint error", sec) + << LOG_KV("message", sec.message()); + ws::WsTools::close(socket); + return doAccept(); + } + auto remoteEndpoint = socket.remote_endpoint(sec); + if(sec) { + HTTP_SERVER(WARNING) << LOG_BADGE("accept") << LOG_KV("remote_endpoint error", sec) + << LOG_KV("message", sec.message()); + ws::WsTools::close(socket); + return doAccept(); + } + socket.set_option(boost::asio::ip::tcp::no_delay(true)); + + HTTP_SERVER(INFO) << LOG_BADGE("accept") << LOG_KV("local_endpoint", localEndpoint) + << LOG_KV("remote_endpoint", remoteEndpoint); + + bool useSsl = !disableSsl(); + if (!useSsl) + { // non ssl , start http session + auto httpStream = m_httpStreamFactory->buildHttpStream( + std::make_shared(std::move(socket)), m_moduleName); + buildHttpSession(httpStream, nullptr)->run(); + + return doAccept(); + } + + // ssl should be used, start ssl handshake + auto self = std::weak_ptr(shared_from_this()); + auto ss = std::make_shared>( + boost::beast::tcp_stream(std::move(socket)), *m_ctx); + + std::shared_ptr nodeId = std::make_shared(); + ss->set_verify_callback(NodeInfoTools::newVerifyCallback(nodeId)); + + ss->async_handshake(boost::asio::ssl::stream_base::server, + [this, ss, localEndpoint, remoteEndpoint, nodeId, self](boost::beast::error_code _ec) { + if (_ec) + { + HTTP_SERVER(INFO) << LOG_BADGE("async_handshake") + << LOG_DESC("ssl handshake failed") + << LOG_KV("local", localEndpoint) + << LOG_KV("remote", remoteEndpoint) + << LOG_KV("error", _ec.message()); + ws::WsTools::close(ss->next_layer().socket()); + return; + } + + auto server = self.lock(); + if (server) + { + auto httpStream = server->httpStreamFactory()->buildHttpStream(ss, m_moduleName); + server->buildHttpSession(httpStream, nodeId)->run(); + } + }); + + return doAccept(); +} + + +HttpSession::Ptr HttpServer::buildHttpSession( + HttpStream::Ptr _httpStream, std::shared_ptr _nodeId) +{ + auto session = std::make_shared(_httpStream->moduleName()); + + auto queue = std::make_shared(); + auto self = std::weak_ptr(session); + queue->setSender([self](HttpResponsePtr _httpResp) { + auto session = self.lock(); + if (!session) + { + return; + } + + // HTTP_SESSION(TRACE) << LOG_BADGE("Queue::Write") << LOG_KV("resp", + // _httpResp->body()) + // << LOG_KV("keep_alive", _httpResp->keep_alive()); + + session->httpStream()->asyncWrite(*_httpResp, + [self, _httpResp](boost::beast::error_code ec, std::size_t bytes_transferred) { + auto session = self.lock(); + if (!session) + { + return; + } + session->onWrite(_httpResp->need_eof(), ec, bytes_transferred); + }); + }); + + session->setQueue(queue); + session->setHttpStream(_httpStream); + session->setRequestHandler(m_httpReqHandler); + session->setWsUpgradeHandler(m_wsUpgradeHandler); + session->setThreadPool(threadPool()); + session->setNodeId(_nodeId); + + return session; +} + +/** + * @brief: create http server + * @param _listenIP: listen ip + * @param _listenPort: listen port + * @param _threadCount: thread count + * @param _ioc: io_context + * @param _ctx: ssl context + * @return HttpServer::Ptr: + */ +HttpServer::Ptr HttpServerFactory::buildHttpServer(const std::string& _listenIP, + uint16_t _listenPort, std::shared_ptr _ioc, + std::shared_ptr _ctx, std::string _moduleName) +{ + std::string m_moduleName = _moduleName; + // create httpserver and launch a listening port + auto server = std::make_shared(_listenIP, _listenPort, _moduleName); + auto acceptor = std::make_shared((*_ioc)); + auto httpStreamFactory = std::make_shared(); + server->setCtx(_ctx); + server->setAcceptor(acceptor); + server->setHttpStreamFactory(httpStreamFactory); + + HTTP_SERVER(INFO) << LOG_BADGE("buildHttpServer") << LOG_KV("listenIP", _listenIP) + << LOG_KV("listenPort", _listenPort); + return server; +} diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpServer.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpServer.h" new file mode 100644 index 00000000..09eb89ee --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpServer.h" @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2021 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 + * m_limitations under the License. + * + * @file HttpHttpServer.h + * @author: octopus + * @date 2021-07-08 + */ +#pragma once + +#include +#include +#include +#include +namespace bcos +{ +namespace boostssl +{ +namespace http +{ +// The http server impl +class HttpServer : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + +public: + HttpServer(const std::string& _listenIP, uint16_t _listenPort, std::string _moduleName) + : m_listenIP(_listenIP), m_listenPort(_listenPort), m_moduleName(_moduleName) + {} + + ~HttpServer() { stop(); } + +public: + // start http server + void start(); + void stop(); + + // accept connection + void doAccept(); + // handle connection + void onAccept(boost::beast::error_code ec, boost::asio::ip::tcp::socket socket); + +public: + HttpSession::Ptr buildHttpSession( + HttpStream::Ptr _stream, std::shared_ptr _nodeId); + +public: + HttpReqHandler httpReqHandler() const { return m_httpReqHandler; } + void setHttpReqHandler(HttpReqHandler _httpReqHandler) { m_httpReqHandler = _httpReqHandler; } + + std::shared_ptr acceptor() const { return m_acceptor; } + void setAcceptor(std::shared_ptr _acceptor) + { + m_acceptor = _acceptor; + } + + std::shared_ptr ctx() const { return m_ctx; } + void setCtx(std::shared_ptr _ctx) { m_ctx = _ctx; } + + std::shared_ptr threadPool() const { return m_threadPool; } + void setThreadPool(std::shared_ptr _threadPool) + { + m_threadPool = _threadPool; + } + + WsUpgradeHandler wsUpgradeHandler() const { return m_wsUpgradeHandler; } + void setWsUpgradeHandler(WsUpgradeHandler _wsUpgradeHandler) + { + m_wsUpgradeHandler = _wsUpgradeHandler; + } + + HttpStreamFactory::Ptr httpStreamFactory() const { return m_httpStreamFactory; } + void setHttpStreamFactory(HttpStreamFactory::Ptr _httpStreamFactory) + { + m_httpStreamFactory = _httpStreamFactory; + } + + bool disableSsl() const { return m_disableSsl; } + void setDisableSsl(bool _disableSsl) { m_disableSsl = _disableSsl; } + + std::string moduleName() { return m_moduleName; } + + void setIOServicePool(bcos::IOServicePool::Ptr _ioservicePool) + { + m_ioservicePool = _ioservicePool; + } + +private: + std::string m_listenIP; + uint16_t m_listenPort; + bool m_disableSsl; + std::string m_moduleName; + + HttpReqHandler m_httpReqHandler; + WsUpgradeHandler m_wsUpgradeHandler; + + std::shared_ptr m_acceptor; + std::shared_ptr m_ctx; + + std::shared_ptr m_threadPool; + std::shared_ptr m_httpStreamFactory; + bcos::IOServicePool::Ptr m_ioservicePool; +}; + +// The http server factory +class HttpServerFactory : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + +public: + /** + * @brief: create http server + * @param _listenIP: listen ip + * @param _listenPort: listen port + * @param _ioc: io_context + * @param _ctx: ssl context + * @return HttpServer::Ptr: + */ + HttpServer::Ptr buildHttpServer(const std::string& _listenIP, uint16_t _listenPort, + std::shared_ptr _ioc, + std::shared_ptr _ctx, std::string _moduleName); +}; + +} // namespace http +} // namespace boostssl +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpSession.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpSession.h" new file mode 100644 index 00000000..9e3b107c --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpSession.h" @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2021 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 + * m_limitations under the License. + * + * @file HttpSession.h + * @author: octopus + * @date 2021-07-08 + */ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace boostssl +{ +namespace http +{ +// The http session for connection +class HttpSession : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + +public: + HttpSession(std::string _moduleName) : m_moduleName(_moduleName) + { + HTTP_SESSION(DEBUG) << LOG_KV("[NEWOBJ][HTTPSESSION]", this); + } + + virtual ~HttpSession() + { + doClose(); + HTTP_SESSION(DEBUG) << LOG_KV("[DELOBJ][HTTPSESSION]", this); + } + + // start the HttpSession + void run() + { + boost::asio::dispatch(m_httpStream->stream().get_executor(), + boost::beast::bind_front_handler(&HttpSession::doRead, shared_from_this())); + } + + void doRead() + { + m_parser.emplace(); + // set limit to http request size, 100m + m_parser->body_limit(PARSER_BODY_LIMITATION); + + auto session = shared_from_this(); + m_httpStream->asyncRead(m_buffer, m_parser, + [session](boost::system::error_code _ec, std::size_t bytes_transferred) { + session->onRead(_ec, bytes_transferred); + }); + } + + void onRead(boost::beast::error_code ec, std::size_t bytes_transferred) + { + try + { + // the peer client closed the connection + if (ec == boost::beast::http::error::end_of_stream) + { + HTTP_SESSION(TRACE) << LOG_BADGE("onRead") << LOG_DESC("end of stream"); + // return doClose(); + return; + } + + if (ec) + { + HTTP_SESSION(WARNING) << LOG_BADGE("onRead") << LOG_DESC("close the connection") + << LOG_KV("error", ec); + // return doClose(); + return; + } + + auto self = std::weak_ptr(shared_from_this()); + if (boost::beast::websocket::is_upgrade(m_parser->get())) + { + HTTP_SESSION(INFO) << LOG_BADGE("onRead") << LOG_DESC("websocket upgrade"); + if (m_wsUpgradeHandler) + { + auto httpSession = self.lock(); + if (!httpSession) + { + return; + } + m_wsUpgradeHandler(m_httpStream, m_parser->release(), httpSession->nodeId()); + } + else + { + HTTP_SESSION(WARNING) << LOG_BADGE("onRead") + << LOG_DESC( + "the session will be closed for " + "unsupported websocket upgrade"); + // doClose(); + return; + } + return; + } + + HTTP_SESSION(INFO) << LOG_BADGE("onRead") << LOG_DESC("receive http request"); + + handleRequest(m_parser->release()); + } + catch (std::exception const& e) + { + HTTP_SESSION(WARNING) << LOG_DESC("onRead exception") + << LOG_KV("bytesSize", bytes_transferred) + << LOG_KV("error", boost::diagnostic_information(e)); + } + + if (!m_queue->isFull()) + { + doRead(); + } + } + + void onWrite(bool close, boost::beast::error_code ec, std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if (ec) + { + HTTP_SESSION(WARNING) << LOG_BADGE("onWrite") << LOG_DESC("close the connection") + << LOG_KV("error", ec); + // return doClose(); + return; + } + + if (close) + { + // we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + // return doClose(); + return; + } + + if (m_queue->onWrite()) + { + // read the next request + doRead(); + } + } + + void doClose() + { + if (m_httpStream) + { + m_httpStream->close(); + } + } + + /** + * @brief: handle http request and send the response + * @param req: http request object + * @return void: + */ + void handleRequest(HttpRequest&& _httpRequest) + { + HTTP_SESSION(DEBUG) << LOG_BADGE("handleRequest") << LOG_DESC("request") + << LOG_KV("method", _httpRequest.method_string()) + << LOG_KV("target", _httpRequest.target()) + << LOG_KV("body", _httpRequest.body()) + << LOG_KV("keep_alive", _httpRequest.keep_alive()) + << LOG_KV("need_eof", _httpRequest.need_eof()); + + auto startT = utcTime(); + unsigned version = _httpRequest.version(); + auto self = std::weak_ptr(shared_from_this()); + if (m_httpReqHandler) + { + std::string request = _httpRequest.body(); + m_httpReqHandler(request, [self, version, startT](bcos::bytes _content) { + auto session = self.lock(); + if (!session) + { + return; + } + auto resp = session->buildHttpResp( + boost::beast::http::status::ok, version, std::move(_content)); + // put the response into the queue and waiting to be send + session->queue()->enqueue(resp); + BCOS_LOG(TRACE) << LOG_BADGE(session->m_moduleName) << LOG_BADGE("handleRequest") + << LOG_DESC("response") + << LOG_KV("body", std::string_view((const char*)resp->body().data(), + resp->body().size())) + << LOG_KV("keep_alive", resp->keep_alive()) + << LOG_KV("timecost", (utcTime() - startT)); + }); + } + else + { + // unsupported http service + auto resp = + buildHttpResp(boost::beast::http::status::http_version_not_supported, version, {}); + auto session = self.lock(); + if (!session) + { + return; + } + // put the response into the queue and waiting to be send + session->queue()->enqueue(resp); + + HTTP_SESSION(WARNING) << LOG_BADGE("handleRequest") + << LOG_DESC("unsupported http service") + << LOG_KV( + "body", std::string_view((const char*)resp->body().data(), + resp->body().size())); + } + } + + /** + * @brief: build http response object + * @param status: http response status + * @param content: http response content + * @return HttpResponsePtr: + */ + HttpResponsePtr buildHttpResp( + boost::beast::http::status status, unsigned version, bcos::bytes content) + { + auto msg = std::make_shared(status, version); + msg->set(boost::beast::http::field::server, BOOST_BEAST_VERSION_STRING); + msg->set(boost::beast::http::field::content_type, "application/json"); + msg->keep_alive(true); // default , keep alive + msg->body() = std::move(content); + msg->prepare_payload(); + return msg; + } + + HttpReqHandler httpReqHandler() const { return m_httpReqHandler; } + void setRequestHandler(HttpReqHandler _httpReqHandler) { m_httpReqHandler = _httpReqHandler; } + + WsUpgradeHandler wsUpgradeHandler() const { return m_wsUpgradeHandler; } + void setWsUpgradeHandler(WsUpgradeHandler _wsUpgradeHandler) + { + m_wsUpgradeHandler = _wsUpgradeHandler; + } + std::shared_ptr queue() { return m_queue; } + void setQueue(std::shared_ptr _queue) { m_queue = _queue; } + + HttpStream::Ptr httpStream() { return m_httpStream; } + void setHttpStream(HttpStream::Ptr _httpStream) { m_httpStream = _httpStream; } + + void setThreadPool(std::shared_ptr _threadPool) + { + m_threadPool = _threadPool; + } + + std::shared_ptr nodeId() { return m_nodeId; } + void setNodeId(std::shared_ptr _nodeId) { m_nodeId = _nodeId; } + + std::string moduleName() { return m_moduleName; } + void setModuleName(std::string _moduleName) { m_moduleName = _moduleName; } + + +private: + HttpStream::Ptr m_httpStream; + + boost::beast::flat_buffer m_buffer; + + std::shared_ptr m_queue; + + std::shared_ptr m_threadPool; + + HttpReqHandler m_httpReqHandler; + WsUpgradeHandler m_wsUpgradeHandler; + // the parser is stored in an optional container so we can + // construct it from scratch it at the beginning of each new message. + boost::optional> m_parser; + + std::shared_ptr m_nodeId; + + std::string m_moduleName = "DEFAULT"; +}; + +} // namespace http +} // namespace boostssl +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpStream.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpStream.h" new file mode 100644 index 00000000..5f400109 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/httpserver/HttpStream.h" @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2021 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 + * m_limitations under the License. + * + * @file HttpStream.h + * @author: octopus + * @date 2021-10-31 + */ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace boostssl +{ +namespace http +{ +using HttpStreamRWHandler = std::function; + +// The http stream +class HttpStream +{ +public: + using Ptr = std::shared_ptr; + + virtual ~HttpStream() {} + +public: + virtual boost::beast::tcp_stream& stream() = 0; + + virtual ws::WsStreamDelegate::Ptr wsStream() = 0; + + virtual bool open() = 0; + virtual void close() = 0; + + virtual void asyncRead(boost::beast::flat_buffer& _buffer, + boost::optional>& + _parser, + HttpStreamRWHandler _handler) = 0; + + virtual void asyncWrite(const HttpResponse& _httpResp, HttpStreamRWHandler _handler) = 0; + + virtual std::string localEndpoint() + { + try + { + auto& s = stream(); + auto localEndPoint = s.socket().local_endpoint(); + auto endPoint = + localEndPoint.address().to_string() + ":" + std::to_string(localEndPoint.port()); + return endPoint; + } + catch (...) + { + } + + return std::string(""); + } + + virtual std::string remoteEndpoint() + { + try + { + auto& s = stream(); + auto remoteEndpoint = s.socket().remote_endpoint(); + auto endPoint = + remoteEndpoint.address().to_string() + ":" + std::to_string(remoteEndpoint.port()); + return endPoint; + } + catch (...) + { + } + + return std::string(""); + } + + virtual std::string moduleName() { return m_moduleName; } + virtual void setModuleName(std::string _moduleName) { m_moduleName = _moduleName; } + + +protected: + std::atomic m_closed{false}; + std::string m_moduleName = "DEFAULT"; +}; + +// The http stream +class HttpStreamImpl : public HttpStream, public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + +public: + HttpStreamImpl(std::shared_ptr _stream, std::string _moduleName) + : m_stream(_stream) + { + setModuleName(_moduleName); + HTTP_STREAM(DEBUG) << LOG_KV("[NEWOBJ][HttpStreamImpl]", this); + } + virtual ~HttpStreamImpl() + { + HTTP_STREAM(DEBUG) << LOG_KV("[DELOBJ][HttpStreamImpl]", this); + close(); + } + +public: + virtual boost::beast::tcp_stream& stream() override { return *m_stream; } + + virtual ws::WsStreamDelegate::Ptr wsStream() override + { + m_closed.store(true); + auto builder = std::make_shared(); + return builder->build(m_stream, m_moduleName); + } + + virtual bool open() override + { + if (!m_closed.load() && m_stream) + { + return m_stream->socket().is_open(); + } + return false; + } + virtual void close() override + { + if (m_closed.load()) + { + return; + } + + bool trueValue = true; + bool falseValue = false; + if (m_closed.compare_exchange_strong(falseValue, trueValue)) + { + HTTP_STREAM(INFO) << LOG_DESC("close the stream") << LOG_KV("this", this); + ws::WsTools::close(m_stream->socket()); + } + } + + virtual void asyncRead(boost::beast::flat_buffer& _buffer, + boost::optional>& + _parser, + HttpStreamRWHandler _handler) override + { + boost::beast::http::async_read(*m_stream, _buffer, *_parser, _handler); + } + + virtual void asyncWrite(const HttpResponse& _httpResp, HttpStreamRWHandler _handler) override + { + boost::beast::http::async_write(*m_stream, _httpResp, _handler); + } + + +private: + std::shared_ptr m_stream; +}; + +// The http stream +class HttpStreamSslImpl : public HttpStream, public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + +public: + HttpStreamSslImpl(std::shared_ptr> _stream, + std::string _moduleName) + : m_stream(_stream) + { + setModuleName(_moduleName); + HTTP_STREAM(DEBUG) << LOG_KV("[NEWOBJ][HttpStreamSslImpl]", this); + } + + virtual ~HttpStreamSslImpl() + { + HTTP_STREAM(DEBUG) << LOG_KV("[DELOBJ][HttpStreamSslImpl]", this); + close(); + } + +public: + virtual boost::beast::tcp_stream& stream() override { return m_stream->next_layer(); } + + virtual ws::WsStreamDelegate::Ptr wsStream() override + { + m_closed.store(true); + auto builder = std::make_shared(); + return builder->build(m_stream, m_moduleName); + } + + virtual bool open() override + { + if (!m_closed.load() && m_stream) + { + return m_stream->next_layer().socket().is_open(); + } + + return false; + } + + virtual void close() override + { + if (m_closed.load()) + { + return; + } + + bool trueValue = true; + bool falseValue = false; + if (m_closed.compare_exchange_strong(falseValue, trueValue)) + { + HTTP_STREAM(INFO) << LOG_DESC("close the ssl stream") << LOG_KV("this", this); + ws::WsTools::close(m_stream->next_layer().socket()); + } + } + + virtual void asyncRead(boost::beast::flat_buffer& _buffer, + boost::optional>& + _parser, + HttpStreamRWHandler _handler) override + { + boost::beast::http::async_read(*m_stream, _buffer, *_parser, _handler); + } + + virtual void asyncWrite(const HttpResponse& _httpResp, HttpStreamRWHandler _handler) override + { + boost::beast::http::async_write(*m_stream, _httpResp, _handler); + } + +private: + std::shared_ptr> m_stream; +}; + +class HttpStreamFactory +{ +public: + using Ptr = std::shared_ptr; + +public: + HttpStream::Ptr buildHttpStream( + std::shared_ptr _stream, std::string _moduleName) + { + return std::make_shared(_stream, _moduleName); + } + + HttpStream::Ptr buildHttpStream( + std::shared_ptr> _stream, + std::string _moduleName) + { + return std::make_shared(_stream, _moduleName); + } +}; +} // namespace http +} // namespace boostssl +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/interfaces/MessageFace.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/interfaces/MessageFace.h" new file mode 100644 index 00000000..045503b2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/interfaces/MessageFace.h" @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 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 MessageFace.h + * @author: lucasli + * @date 2022-02-16 + */ +#pragma once + +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace boostssl +{ +class MessageFace +{ +public: + using Ptr = std::shared_ptr; + +public: + virtual ~MessageFace() {} + + virtual uint16_t version() const = 0; + virtual void setVersion(uint16_t) = 0; + virtual uint16_t packetType() const = 0; + virtual void setPacketType(uint16_t) = 0; + virtual std::string const& seq() const = 0; + virtual void setSeq(std::string) = 0; + virtual uint16_t ext() const = 0; + virtual void setExt(uint16_t) = 0; + virtual std::shared_ptr payload() const = 0; + virtual void setPayload(std::shared_ptr) = 0; + + virtual bool encode(bcos::bytes& _buffer) = 0; + virtual int64_t decode(bytesConstRef _buffer) = 0; + + virtual bool isRespPacket() const = 0; + virtual void setRespPacket() = 0; + virtual uint32_t length() const = 0; +}; + +class MessageFaceFactory +{ +public: + using Ptr = std::shared_ptr; + +public: + virtual ~MessageFaceFactory() {} + virtual MessageFace::Ptr buildMessage() = 0; + virtual std::string newSeq() = 0; +}; + +} // namespace boostssl +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/interfaces/NodeInfoDef.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/interfaces/NodeInfoDef.h" new file mode 100644 index 00000000..cf9f007c --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/interfaces/NodeInfoDef.h" @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2021 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 NodeInfoDef.h + * @author: lucasli + * @date 2022-04-02 + */ +#pragma once + +#include +#include +#include +#include + +namespace bcos +{ +namespace boostssl +{ + +/** + * @brief client end endpoint. Node will connect to NodeIPEndpoint. + */ +struct NodeIPEndpoint +{ + using Ptr = std::shared_ptr; + NodeIPEndpoint() = default; + NodeIPEndpoint(std::string const& _host, uint16_t _port) : m_host(_host), m_port(_port) {} + NodeIPEndpoint(const NodeIPEndpoint& _nodeIPEndpoint) = default; + NodeIPEndpoint(boost::asio::ip::address _addr, uint16_t _port) + : m_host(_addr.to_string()), m_port(_port), m_ipv6(_addr.is_v6()) + {} + + virtual ~NodeIPEndpoint() = default; + NodeIPEndpoint(const boost::asio::ip::tcp::endpoint& _endpoint) + { + m_host = _endpoint.address().to_string(); + m_port = _endpoint.port(); + m_ipv6 = _endpoint.address().is_v6(); + } + bool operator<(const NodeIPEndpoint& rhs) const + { + if (m_host + std::to_string(m_port) < rhs.m_host + std::to_string(rhs.m_port)) + { + return true; + } + return false; + } + bool operator==(const NodeIPEndpoint& rhs) const + { + return (m_host + std::to_string(m_port) == rhs.m_host + std::to_string(rhs.m_port)); + } + operator boost::asio::ip::tcp::endpoint() const + { + return boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(m_host), m_port); + } + + // Get the port associated with the endpoint. + uint16_t port() const { return m_port; }; + + // Get the IP address associated with the endpoint. + std::string address() const { return m_host; }; + bool isIPv6() const { return m_ipv6; } + + std::string m_host; + uint16_t m_port; + bool m_ipv6 = false; +}; + +inline std::ostream& operator<<(std::ostream& _out, NodeIPEndpoint const& _endpoint) +{ + _out << _endpoint.address() << ":" << _endpoint.port(); + return _out; +} + +} // namespace boostssl +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/Common.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/Common.h" new file mode 100644 index 00000000..42a2414c --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/Common.h" @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2021 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: octopus + * @date 2021-07-08 + */ +#pragma once + +#include +#include +#include +#include +#include + +#define BOOST_SSL_LOG(LEVEL) BCOS_LOG(LEVEL) << "[BOOSTSSL]" +#define WEBSOCKET_TOOL(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE(m_moduleName) << "[WS][TOOL]" +#define WEBSOCKET_CONNECTOR(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE(m_moduleName) << "[WS][CONNECTOR]" +#define WEBSOCKET_VERSION(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE(m_moduleName) << "[WS][VERSION]" +#define WEBSOCKET_SESSION(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE(m_moduleName) << "[WS][SESSION]" +#define WEBSOCKET_MESSAGE(LEVEL) BCOS_LOG(LEVEL) << "[WS][MESSAGE]" +#define WEBSOCKET_SERVICE(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE(m_moduleName) << "[WS][SERVICE]" +#define WEBSOCKET_STREAM(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE(m_moduleName) << "[WS][STREAM]" +#define WEBSOCKET_SSL_STREAM(LEVEL) \ + BCOS_LOG(LEVEL) << LOG_BADGE(m_moduleName) << "[WS][SSL][STREAM]" +#define WEBSOCKET_INITIALIZER(LEVEL) \ + BCOS_LOG(LEVEL) << LOG_BADGE(m_moduleName) << "[WS][INITIALIZER]" + +namespace bcos +{ +namespace boostssl +{ +namespace ws +{ +class WsSession; + +using RespCallBack = std::function, std::shared_ptr)>; + +using WsConnectHandler = std::function)>; +using WsDisconnectHandler = std::function)>; +using WsRecvMessageHandler = + std::function, std::shared_ptr)>; +using VerifyCallback = boost::function; + +struct Options +{ + Options(uint32_t _timeout) : timeout(_timeout) {} + Options() : timeout(0) {} + uint32_t timeout = 0; ///< The timeout value of async function, in milliseconds. +}; + +} // namespace ws +} // namespace boostssl +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsConfig.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsConfig.h" new file mode 100644 index 00000000..004bf48d --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsConfig.h" @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2021 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 WsConfig.h + * @author: octopus + * @date 2021-08-23 + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#define MIN_HEART_BEAT_PERIOD_MS (10000) +#define MIN_RECONNECT_PERIOD_MS (10000) +#define DEFAULT_MESSAGE_TIMEOUT_MS (-1) +#define DEFAULT_MAX_MESSAGE_SIZE (32 * 1024 * 1024) +#define MIN_THREAD_POOL_SIZE (1) + +namespace bcos +{ +namespace boostssl +{ +namespace ws +{ +using EndPoints = std::set; +using EndPointsPtr = std::shared_ptr>; +using EndPointsConstPtr = std::shared_ptr>; + +enum WsModel : uint16_t +{ + None = 0, + Client = 0x01, + Server = 0x10, + Mixed = Client | Server +}; + +class WsConfig +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + +private: + // ws work model, as server or as client or server & client + WsModel m_model = WsModel::None; + + // the listen ip when ws work as server + std::string m_listenIP; + // the listen port when ws work as server + uint16_t m_listenPort; + + // whether smSSL or not, default not + bool m_smSSL = false; + + // list of connected server nodes when ws work as client + EndPointsPtr m_connectPeers; + + // thread pool size + uint32_t m_threadPoolSize{4}; + + // time out for send message + int32_t m_sendMsgTimeout{DEFAULT_MESSAGE_TIMEOUT_MS}; + + // time interval for reconnection + uint32_t m_reconnectPeriod{MIN_RECONNECT_PERIOD_MS}; + + // time interval for heartbeat + uint32_t m_heartbeatPeriod{MIN_HEART_BEAT_PERIOD_MS}; + + bool m_disableSsl{false}; + + // cert config for boostssl + std::shared_ptr m_contextConfig; + + // the max message to be send or read + uint32_t m_maxMsgSize{DEFAULT_MAX_MESSAGE_SIZE}; + + std::string m_moduleName = "DEFAULT"; + +public: + void setModel(WsModel _model) { m_model = _model; } + WsModel model() const { return m_model; } + + bool asClient() const { return m_model & WsModel::Client; } + bool asServer() const { return m_model & WsModel::Server; } + + void setListenIP(const std::string _listenIP) { m_listenIP = _listenIP; } + std::string listenIP() const { return m_listenIP; } + + void setListenPort(uint16_t _listenPort) { m_listenPort = _listenPort; } + uint16_t listenPort() const { return m_listenPort; } + + void setSmSSL(bool _isSmSSL) { m_smSSL = _isSmSSL; } + bool smSSL() { return m_smSSL; } + + void setMaxMsgSize(uint32_t _maxMsgSize) { m_maxMsgSize = _maxMsgSize; } + uint32_t maxMsgSize() const { return m_maxMsgSize; } + + uint32_t reconnectPeriod() const + { + return m_reconnectPeriod > MIN_RECONNECT_PERIOD_MS ? m_reconnectPeriod : + MIN_RECONNECT_PERIOD_MS; + } + void setReconnectPeriod(uint32_t _reconnectPeriod) { m_reconnectPeriod = _reconnectPeriod; } + + uint32_t heartbeatPeriod() const + { + return m_heartbeatPeriod > MIN_HEART_BEAT_PERIOD_MS ? m_heartbeatPeriod : + MIN_HEART_BEAT_PERIOD_MS; + } + void setHeartbeatPeriod(uint32_t _heartbeatPeriod) { m_heartbeatPeriod = _heartbeatPeriod; } + + int32_t sendMsgTimeout() const { return m_sendMsgTimeout; } + void setSendMsgTimeout(int32_t _sendMsgTimeout) { m_sendMsgTimeout = _sendMsgTimeout; } + + uint32_t threadPoolSize() const + { + return m_threadPoolSize ? m_threadPoolSize : MIN_THREAD_POOL_SIZE; + } + void setThreadPoolSize(uint32_t _threadPoolSize) { m_threadPoolSize = _threadPoolSize; } + + EndPointsPtr connectPeers() const { return m_connectPeers; } + void setConnectPeers(EndPointsPtr _connectPeers) { m_connectPeers = _connectPeers; } + bool disableSsl() const { return m_disableSsl; } + void setDisableSsl(bool _disableSsl) { m_disableSsl = _disableSsl; } + + std::shared_ptr contextConfig() const { return m_contextConfig; } + void setContextConfig(std::shared_ptr _contextConfig) + { + m_contextConfig = _contextConfig; + } + + std::string moduleName() { return m_moduleName; } + void setModuleName(std::string _moduleName) { m_moduleName = _moduleName; } +}; +} // namespace ws +} // namespace boostssl +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsConnector.cpp" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsConnector.cpp" new file mode 100644 index 00000000..0857bd8c --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsConnector.cpp" @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2021 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 + * m_limitations under the License. + * + * @file WsConnector.cpp + * @author: octopus + * @date 2021-08-23 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; +using namespace bcos::boostssl::context; + +// TODO: how to set timeout for connect to wsServer ??? +void WsConnector::connectToWsServer(const std::string& _host, uint16_t _port, bool _disableSsl, + std::function, std::shared_ptr)> + _callback) +{ + auto ioc = m_ioservicePool->getIOService(); + auto ctx = m_ctx; + + std::string endpoint = _host + ":" + std::to_string(_port); + // check if last connect opr done + if (!insertPendingConns(endpoint)) + { + WEBSOCKET_CONNECTOR(WARNING) + << LOG_BADGE("connectToWsServer") << LOG_DESC("insertPendingConns") + << LOG_KV("endpoint", endpoint); + _callback(boost::beast::error_code(boost::asio::error::would_block), "", nullptr, nullptr); + return; + } + + auto resolver = m_resolver; + auto builder = m_builder; + auto connector = shared_from_this(); + + // resolve host + resolver->async_resolve(_host.c_str(), std::to_string(_port).c_str(), + [this, _host, _port, _disableSsl, endpoint, ioc, ctx, connector, builder, _callback]( + boost::beast::error_code _ec, boost::asio::ip::tcp::resolver::results_type _results) { + if (_ec) + { + WEBSOCKET_CONNECTOR(WARNING) + << LOG_BADGE("connectToWsServer") << LOG_DESC("async_resolve failed") + << LOG_KV("error", _ec) << LOG_KV("errorMessage", _ec.message()) + << LOG_KV("endpoint", endpoint); + _callback(_ec, "", nullptr, nullptr); + connector->erasePendingConns(endpoint); + return; + } + + WEBSOCKET_CONNECTOR(TRACE) + << LOG_BADGE("connectToWsServer") << LOG_DESC("async_resolve success") + << LOG_KV("endPoint", endpoint); + + // create raw tcp stream + auto rawStream = std::make_shared(*ioc); + // rawStream->expires_after(std::chrono::seconds(30)); + + // async connect + rawStream->async_connect(_results, + [this, _host, _port, _disableSsl, endpoint, ctx, connector, builder, rawStream, + _callback](boost::beast::error_code _ec, + boost::asio::ip::tcp::resolver::results_type::endpoint_type _ep) mutable { + if (_ec) + { + WEBSOCKET_CONNECTOR(WARNING) + << LOG_BADGE("connectToWsServer") << LOG_DESC("async_connect failed") + << LOG_KV("error", _ec.message()) << LOG_KV("endpoint", endpoint); + _callback(_ec, "", nullptr, nullptr); + connector->erasePendingConns(endpoint); + return; + } + + WEBSOCKET_CONNECTOR(INFO) + << LOG_BADGE("connectToWsServer") << LOG_DESC("async_connect success") + << LOG_KV("endpoint", endpoint); + + auto wsStreamDelegate = + builder->build(_disableSsl, ctx, rawStream, m_moduleName); + + std::shared_ptr nodeId = std::make_shared(); + wsStreamDelegate->setVerifyCallback( + _disableSsl, NodeInfoTools::newVerifyCallback(nodeId)); + + // start ssl handshake + wsStreamDelegate->asyncHandshake([this, wsStreamDelegate, connector, _host, + _port, endpoint, _ep, _callback, + nodeId](boost::beast::error_code _ec) { + if (_ec) + { + WEBSOCKET_CONNECTOR(WARNING) + << LOG_BADGE("connectToWsServer") + << LOG_DESC("ssl async_handshake failed") << LOG_KV("host", _host) + << LOG_KV("port", _port) << LOG_KV("error", _ec.message()); + _callback(_ec, " ssl handshake failed", nullptr, nullptr); + connector->erasePendingConns(endpoint); + return; + } + + WEBSOCKET_CONNECTOR(INFO) << LOG_BADGE("connectToWsServer") + << LOG_DESC("ssl async_handshake success") + << LOG_KV("host", _host) << LOG_KV("port", _port); + + // turn off the timeout on the tcp_stream, because + // the websocket stream has its own timeout system. + wsStreamDelegate->tcpStream().expires_never(); + + std::string tmpHost = _host + ':' + std::to_string(_ep.port()); + + // websocket async handshake + wsStreamDelegate->asyncWsHandshake(tmpHost, "/", + [this, connector, _host, _port, endpoint, _callback, wsStreamDelegate, + nodeId](boost::beast::error_code _ec) mutable { + if (_ec) + { + WEBSOCKET_CONNECTOR(WARNING) + << LOG_BADGE("connectToWsServer") + << LOG_DESC("websocket async_handshake failed") + << LOG_KV("error", _ec.message()) << LOG_KV("host", _host) + << LOG_KV("port", _port); + _callback(_ec, "", nullptr, nullptr); + connector->erasePendingConns(endpoint); + return; + } + + WEBSOCKET_CONNECTOR(INFO) + << LOG_BADGE("connectToWsServer") + << LOG_DESC("websocket handshake successfully") + << LOG_KV("host", _host) << LOG_KV("port", _port); + _callback(_ec, "", wsStreamDelegate, nodeId); + connector->erasePendingConns(endpoint); + }); + }); + }); + }); +} diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsConnector.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsConnector.h" new file mode 100644 index 00000000..befa2659 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsConnector.h" @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2021 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 + * m_limitations under the License. + * + * @file WsConnector.h + * @author: octopus + * @date 2021-08-23 + */ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace boostssl +{ +namespace ws +{ +class WsConnector : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + WsConnector(std::shared_ptr _resolver) : m_resolver(_resolver) + {} + +public: + /** + * @brief: connect to the server + * @param _host: the remote server host, support ipv4, ipv6, domain name + * @param _port: the remote server port + * @param _disableSsl: disable ssl + * @param _callback: + * @return void: + */ + void connectToWsServer(const std::string& _host, uint16_t _port, bool _disableSsl, + std::function, std::shared_ptr)> + _callback); + +public: + bool erasePendingConns(const std::string& _nodeIPEndpoint) + { + std::lock_guard l(x_pendingConns); + return m_pendingConns.erase(_nodeIPEndpoint); + } + + bool insertPendingConns(const std::string& _nodeIPEndpoint) + { + std::lock_guard l(x_pendingConns); + auto p = m_pendingConns.insert(_nodeIPEndpoint); + return p.second; + } + +public: + void setResolver(std::shared_ptr _resolver) + { + m_resolver = _resolver; + } + std::shared_ptr resolver() const { return m_resolver; } + + void setIOServicePool(IOServicePool::Ptr _ioservicePool) { m_ioservicePool = _ioservicePool; } + + void setCtx(std::shared_ptr _ctx) { m_ctx = _ctx; } + std::shared_ptr ctx() const { return m_ctx; } + + void setBuilder(std::shared_ptr _builder) { m_builder = _builder; } + std::shared_ptr builder() const { return m_builder; } + + std::string moduleName() { return m_moduleName; } + void setModuleName(std::string _moduleName) { m_moduleName = _moduleName; } + +private: + std::shared_ptr m_builder; + std::shared_ptr m_resolver; + std::shared_ptr m_ctx; + + mutable std::mutex x_pendingConns; + std::set m_pendingConns; + + std::string m_moduleName = "DEFAULT"; + IOServicePool::Ptr m_ioservicePool; +}; +} // namespace ws +} // namespace boostssl +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsError.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsError.h" new file mode 100644 index 00000000..649183ea --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsError.h" @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2021 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 WsError.h + * @author: octopus + * @date 2021-10-02 + */ +#pragma once + +namespace bcos +{ +namespace boostssl +{ +namespace ws +{ +enum WsError +{ + AcceptError = -4000, + ReadError = -4001, + WriteError = -4002, + PingError = -4003, + PongError = -4004, + PacketError = -4005, + SessionDisconnect = -4006, + UserDisconnect = -4007, + TimeOut = -4008, + NoActiveCons = -4009, + EndPointNotExist = -4010, + MessageOverflow = -4011, + UndefinedException = -4012, + MessageEncodeError = -4013 +}; + +inline bool notRetryAgain(int _wsError) +{ + return (_wsError == boostssl::ws::WsError::MessageOverflow); +} + +} // namespace ws +} // namespace boostssl +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsInitializer.cpp" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsInitializer.cpp" new file mode 100644 index 00000000..09fa7c7a --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsInitializer.cpp" @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2021 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 WsFactory.cpp + * @author: octopus + * @date 2021-09-29 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::boostssl; +using namespace bcos::boostssl::context; +using namespace bcos::boostssl::ws; +using namespace bcos::boostssl::http; + +void WsInitializer::initWsService(WsService::Ptr _wsService) +{ + std::shared_ptr _config = m_config; + std::string m_moduleName = _config->moduleName(); + auto messageFactory = m_messageFactory; + if (!messageFactory) + { + messageFactory = std::make_shared(); + } + + auto sessionFactory = m_sessionFactory; + if (!sessionFactory) + { + sessionFactory = std::make_shared(); + } + + auto threadPoolSize = _config->threadPoolSize() > 0 ? _config->threadPoolSize() : + std::thread::hardware_concurrency(); + if (!threadPoolSize) + { + threadPoolSize = 16; + } + auto wsServiceWeakPtr = std::weak_ptr(_wsService); + auto ioServicePool = std::make_shared(); + _wsService->setIOServicePool(ioServicePool); + + auto resolver = + std::make_shared((*(ioServicePool->getIOService()))); + auto connector = std::make_shared(resolver); + connector->setIOServicePool(ioServicePool); + + auto builder = std::make_shared(); + auto threadPool = std::make_shared("t_ws_pool", threadPoolSize); + + // init module_name for log + WsTools::setModuleName(m_moduleName); + NodeInfoTools::setModuleName(m_moduleName); + connector->setModuleName(m_moduleName); + + std::shared_ptr srvCtx = nullptr; + std::shared_ptr clientCtx = nullptr; + if (!_config->disableSsl()) + { + auto contextBuilder = std::make_shared(); + + // init module_name for log + contextBuilder->setModuleName(m_moduleName); + _config->contextConfig()->setModuleName(m_moduleName); + + srvCtx = contextBuilder->buildSslContext(true, *_config->contextConfig()); + clientCtx = contextBuilder->buildSslContext(false, *_config->contextConfig()); + } + + if (_config->asServer()) + { + WEBSOCKET_INITIALIZER(INFO) + << LOG_BADGE("initWsService") << LOG_DESC("start websocket service as server"); + + if (!WsTools::validIP(_config->listenIP())) + { + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "invalid listen ip, value: " + _config->listenIP())); + } + + if (!WsTools::validPort(_config->listenPort())) + { + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment( + "invalid listen port, value: " + std::to_string(_config->listenPort()))); + } + + auto httpServerFactory = std::make_shared(); + auto httpServer = httpServerFactory->buildHttpServer(_config->listenIP(), + _config->listenPort(), ioServicePool->getIOService(), srvCtx, m_moduleName); + httpServer->setIOServicePool(ioServicePool); + httpServer->setDisableSsl(_config->disableSsl()); + httpServer->setThreadPool(threadPool); + httpServer->setWsUpgradeHandler( + [wsServiceWeakPtr](std::shared_ptr _httpStream, HttpRequest&& _httpRequest, + std::shared_ptr _nodeId) { + auto service = wsServiceWeakPtr.lock(); + if (service) + { + std::string nodeIdString = _nodeId == nullptr ? "" : *_nodeId.get(); + auto session = service->newSession(_httpStream->wsStream(), nodeIdString); + session->startAsServer(_httpRequest); + } + }); + + _wsService->setHttpServer(httpServer); + _wsService->setHostPort(_config->listenIP(), _config->listenPort()); + } + + if (_config->asClient()) + { + auto connectPeers = _config->connectPeers(); + WEBSOCKET_INITIALIZER(INFO) + << LOG_BADGE("initWsService") << LOG_DESC("start websocket service as client") + << LOG_KV("connected size", connectPeers ? connectPeers->size() : 0); + + if (connectPeers) + { + for (auto& peer : *connectPeers) + { + if (!WsTools::validIP(peer.address())) + { + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "invalid connected peer, value: " + peer.address())); + } + + if (!WsTools::validPort(peer.port())) + { + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment( + "invalid connect port, value: " + std::to_string(peer.port()))); + } + } + + // connectPeers info is valid then set connectPeers info into wsService + _wsService->setReconnectedPeers(connectPeers); + } + else + { + WEBSOCKET_INITIALIZER(WARNING) + << LOG_BADGE("initWsService") << LOG_DESC("there has no connected server config"); + } + } + + connector->setCtx(clientCtx); + connector->setBuilder(builder); + + _wsService->setConfig(_config); + _wsService->setConnector(connector); + _wsService->setThreadPool(threadPool); + _wsService->setMessageFactory(messageFactory); + _wsService->setSessionFactory(sessionFactory); + + WEBSOCKET_INITIALIZER(INFO) + << LOG_BADGE("initWsService") << LOG_DESC("initializer for websocket service") + << LOG_KV("listenIP", _config->listenIP()) << LOG_KV("listenPort", _config->listenPort()) + << LOG_KV("disableSsl", _config->disableSsl()) << LOG_KV("server", _config->asServer()) + << LOG_KV("client", _config->asClient()) + << LOG_KV("threadPoolSize", _config->threadPoolSize()) + << LOG_KV("maxMsgSize", _config->maxMsgSize()) + << LOG_KV("msgTimeOut", _config->sendMsgTimeout()) + << LOG_KV("connected peers", _config->connectPeers() ? _config->connectPeers()->size() : 0); +} diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsInitializer.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsInitializer.h" new file mode 100644 index 00000000..66020d9f --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsInitializer.h" @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2021 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 WsInitializer.h + * @author: octopus + * @date 2021-09-29 + */ +#pragma once + +#include +#include +#include + +namespace bcos +{ +namespace boostssl +{ +namespace ws +{ +class WsInitializer +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + +public: + std::shared_ptr messageFactory() const { return m_messageFactory; } + void setMessageFactory(std::shared_ptr _messageFactory) + { + m_messageFactory = _messageFactory; + } + + std::shared_ptr config() const { return m_config; } + void setConfig(std::shared_ptr _config) { m_config = _config; } + + std::shared_ptr sessionFactory() { return m_sessionFactory; } + void setSessionFactory(std::shared_ptr _sessionFactory) + { + m_sessionFactory = _sessionFactory; + } + +public: + void initWsService(WsService::Ptr _wsService); + +private: + std::shared_ptr m_messageFactory; + std::shared_ptr m_config; + WsSessionFactory::Ptr m_sessionFactory; +}; +} // namespace ws +} // namespace boostssl +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsMessage.cpp" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsMessage.cpp" new file mode 100644 index 00000000..08ac7b5e --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsMessage.cpp" @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2021 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 WsMessage.h + * @author: octopus + * @date 2021-07-28 + */ +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; + +// version(2) + type(2) + status(2) + seqLength(2) + ext(2) + payload(N) +constexpr size_t WsMessage::MESSAGE_MIN_LENGTH = 10; + +bool WsMessage::encode(bytes& _buffer) +{ + _buffer.clear(); + + uint16_t version = boost::asio::detail::socket_ops::host_to_network_short(m_version); + uint16_t type = boost::asio::detail::socket_ops::host_to_network_short(m_packetType); + int16_t status = boost::asio::detail::socket_ops::host_to_network_short(m_status); + uint16_t seqLength = boost::asio::detail::socket_ops::host_to_network_short(m_seq.size()); + uint16_t ext = boost::asio::detail::socket_ops::host_to_network_short(m_ext); + + _buffer.insert(_buffer.end(), (byte*)&version, (byte*)&version + 2); + _buffer.insert(_buffer.end(), (byte*)&type, (byte*)&type + 2); + _buffer.insert(_buffer.end(), (byte*)&status, (byte*)&status + 2); + _buffer.insert(_buffer.end(), (byte*)&seqLength, (byte*)&seqLength + 2); + _buffer.insert(_buffer.end(), m_seq.begin(), m_seq.end()); + _buffer.insert(_buffer.end(), (byte*)&ext, (byte*)&ext + 2); + _buffer.insert(_buffer.end(), m_payload->begin(), m_payload->end()); + + m_length = _buffer.size(); + return true; +} + +int64_t WsMessage::decode(bytesConstRef _buffer) +{ + auto length = _buffer.size(); + if (length < MESSAGE_MIN_LENGTH) + { + return -1; + } + + m_seq.clear(); + m_payload->clear(); + + auto dataBuffer = _buffer.data(); + auto p = _buffer.data(); + size_t offset = 0; + + // version field + m_version = boost::asio::detail::socket_ops::network_to_host_short(*((uint16_t*)p)); + p += 2; + offset += 2; + + // type field + m_packetType = boost::asio::detail::socket_ops::network_to_host_short(*((uint16_t*)p)); + p += 2; + offset += 2; + + // status field + m_status = boost::asio::detail::socket_ops::network_to_host_short(*((uint16_t*)p)); + p += 2; + offset += 2; + + // seqLength + uint16_t seqLength = boost::asio::detail::socket_ops::network_to_host_short(*((uint16_t*)p)); + p += 2; + offset += 2; + + CHECK_OFFSET(offset + seqLength, length); + // seq field + m_seq.insert(m_seq.begin(), p, p + seqLength); + p += seqLength; + offset += seqLength; + + // ext field + CHECK_OFFSET(offset + 2, length); + m_ext = boost::asio::detail::socket_ops::network_to_host_short(*((uint16_t*)p)); + p += 2; + offset += 2; + + // data field + if (p) + { + m_payload->insert(m_payload->begin(), p, dataBuffer + length); + } + m_length = length; + return length; +} diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsMessage.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsMessage.h" new file mode 100644 index 00000000..319253e6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsMessage.h" @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2021 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 WsMessage.h + * @author: octopus + * @date 2021-07-28 + */ +#pragma once + +#include "bcos-boostssl/websocket/Common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define CHECK_OFFSET(offset, length) \ + do \ + { \ + if ((offset) > (length)) \ + { \ + throw std::out_of_range("Out of range error, offset:" + std::to_string(offset) + \ + " ,length: " + std::to_string(length)); \ + } \ + } while (0); + +namespace bcos +{ +namespace boostssl +{ +namespace ws +{ +// the message format for ws protocol +class WsMessage : public boostssl::MessageFace +{ +public: + // version(2) + type(2) + status(2) + seqLength(2) + ext(2) + payload(N) + const static size_t MESSAGE_MIN_LENGTH; + + using Ptr = std::shared_ptr; + WsMessage() + { + m_payload = std::make_shared(); + if (c_fileLogLevel == LogLevel::TRACE) [[unlikely]] + { + WEBSOCKET_MESSAGE(TRACE) << LOG_KV("[NEWOBJ][WsMessage]", this); + } + } + virtual ~WsMessage() + { + if (c_fileLogLevel == LogLevel::TRACE) [[unlikely]] + { + WEBSOCKET_MESSAGE(TRACE) << LOG_KV("[DELOBJ][WsMessage]", this); + } + } + + + virtual uint16_t version() const override { return m_version; } + virtual void setVersion(uint16_t) override {} + virtual uint16_t packetType() const override { return m_packetType; } + virtual void setPacketType(uint16_t _packetType) override { m_packetType = _packetType; } + virtual int16_t status() { return m_status; } + virtual void setStatus(int16_t _status) { m_status = _status; } + virtual std::string const& seq() const override { return m_seq; } + virtual void setSeq(std::string _seq) override { m_seq = _seq; } + virtual std::shared_ptr payload() const override { return m_payload; } + virtual void setPayload(std::shared_ptr _payload) override + { + m_payload = _payload; + } + virtual uint16_t ext() const override { return m_ext; } + virtual void setExt(uint16_t _ext) override { m_ext = _ext; } + + + virtual bool encode(bcos::bytes& _buffer) override; + virtual int64_t decode(bytesConstRef _buffer) override; + + bool isRespPacket() const override + { + return (m_ext & bcos::protocol::MessageExtFieldFlag::Response) != 0; + } + void setRespPacket() override { m_ext |= bcos::protocol::MessageExtFieldFlag::Response; } + + virtual uint32_t length() const override { return m_length; } + +private: + uint16_t m_version = 0; + uint16_t m_packetType = 0; + std::string m_seq; + uint16_t m_ext = 0; + std::shared_ptr m_payload; + + int16_t m_status{0}; + uint32_t m_length; +}; + +class WsMessageFactory : public boostssl::MessageFaceFactory +{ +public: + using Ptr = std::shared_ptr; + WsMessageFactory() = default; + virtual ~WsMessageFactory() {} + +public: + virtual std::string newSeq() override + { + std::string seq = boost::uuids::to_string(boost::uuids::random_generator()()); + seq.erase(std::remove(seq.begin(), seq.end(), '-'), seq.end()); + return seq; + } + + virtual boostssl::MessageFace::Ptr buildMessage() override + { + auto msg = std::make_shared(); + return msg; + } + + virtual std::shared_ptr buildMessage( + uint16_t _type, std::shared_ptr _data) + { + auto msg = std::make_shared(); + + msg->setPacketType(_type); + msg->setPayload(_data); + + return msg; + } +}; + +} // namespace ws +} // namespace boostssl +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsService.cpp" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsService.cpp" new file mode 100644 index 00000000..2a92938f --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsService.cpp" @@ -0,0 +1,731 @@ +/* + * Copyright (C) 2021 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 WsService.cpp + * @author: octopus + * @date 2021-07-28 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace std::chrono_literals; +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; + +WsService::WsService(std::string _moduleName) : m_moduleName(_moduleName) +{ + WEBSOCKET_SERVICE(INFO) << LOG_KV("[NEWOBJ][WsService]", this); +} + +WsService::~WsService() +{ + stop(); + WEBSOCKET_SERVICE(INFO) << LOG_KV("[DELOBJ][WsService]", this); +} + +void WsService::start() +{ + if (m_running) + { + WEBSOCKET_SERVICE(INFO) << LOG_BADGE("start") << LOG_DESC("websocket service is running"); + return; + } + m_running = true; + + // start ioc thread + if (m_ioservicePool) + { + m_ioservicePool->start(); + } + + // start as server + if (m_config->asServer()) + { + m_httpServer->start(); + } + + // start as client + if (m_config->asClient()) + { + if (m_config->connectPeers() && !m_config->connectPeers()->empty()) + { + // Connect to peers and wait for at least one connection to be successfully + // established + syncConnectToEndpoints(m_config->connectPeers()); + } + + reconnect(); + } + + reportConnectedNodes(); + + WEBSOCKET_SERVICE(INFO) << LOG_BADGE("start") + << LOG_DESC("start websocket service successfully") + << LOG_KV("model", m_config->model()) + << LOG_KV("max msg size", m_config->maxMsgSize()); +} + +void WsService::stop() +{ + if (!m_running) + { + WEBSOCKET_SERVICE(INFO) << LOG_BADGE("stop") + << LOG_DESC("websocket service has been stopped"); + return; + } + m_running = false; + + // stop ioc thread + if (m_ioservicePool) + { + m_ioservicePool->stop(); + } + + // cancel reconnect task + if (m_reconnect) + { + m_reconnect->cancel(); + } + + // cancel heartbeat task + if (m_heartbeat) + { + m_heartbeat->cancel(); + } + + + WEBSOCKET_SERVICE(INFO) << LOG_BADGE("stop") << LOG_DESC("stop websocket service successfully"); +} + + +void WsService::reportConnectedNodes() +{ + auto ss = sessions(); + WEBSOCKET_SERVICE(INFO) << LOG_DESC("connected nodes") << LOG_KV("count", ss.size()); + + m_heartbeat = std::make_shared( + *(m_timerIoc), boost::posix_time::milliseconds(m_config->heartbeatPeriod())); + auto self = std::weak_ptr(shared_from_this()); + m_heartbeat->async_wait([self](const boost::system::error_code& _error) { + if (_error == boost::asio::error::operation_aborted) + { + return; + } + try + { + auto service = self.lock(); + if (!service) + { + return; + } + service->reportConnectedNodes(); + } + catch (std::exception const& e) + { + BOOST_SSL_LOG(WARNING) << LOG_DESC("connected nodes exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +std::string WsService::genConnectError( + const std::string& _error, const std::string& endpoint, bool end) +{ + std::string msg = _error; + msg += ":/"; + msg += endpoint; + if (!end) + { + msg += ", "; + } + return msg; +} + +void WsService::syncConnectToEndpoints(EndPointsPtr _peers) +{ + std::string errorMsg; + std::size_t sucCount = 0; + + auto vPromise = asyncConnectToEndpoints(_peers); + + for (std::size_t i = 0; i < vPromise->size(); ++i) + { + auto fut = (*vPromise)[i]->get_future(); + + auto status = fut.wait_for(std::chrono::milliseconds(m_waitConnectFinishTimeout)); + auto [errCode, errMsg, endpoint] = fut.get(); + switch (status) + { + case std::future_status::deferred: + break; + case std::future_status::timeout: + errorMsg += genConnectError("connection timeout", endpoint, i == vPromise->size() - 1); + break; + case std::future_status::ready: + + try + { + if (errCode) + { + errorMsg += genConnectError( + errMsg.empty() ? errCode.message() : errMsg + " " + errCode.message(), + endpoint, i == vPromise->size() - 1); + } + else + { + sucCount++; + } + } + catch (std::exception& _e) + { + WEBSOCKET_SERVICE(WARNING) + << LOG_BADGE("syncConnectToEndpoints") << LOG_DESC("future get throw exception") + << LOG_KV("e", _e.what()); + } + break; + } + } + + if (sucCount == 0) + { + stop(); + BOOST_THROW_EXCEPTION(std::runtime_error("[" + boost::to_lower_copy(errorMsg) + "]")); + return; + } +} + +std::shared_ptr>>>> +WsService::asyncConnectToEndpoints(EndPointsPtr _peers) +{ + auto vPromise = std::make_shared>>>>(); + + for (auto& peer : *_peers) + { + std::string connectedEndPoint = peer.address() + ":" + std::to_string(peer.port()); + + /* + WEBSOCKET_SERVICE(DEBUG) << LOG_BADGE("asyncConnect") + << LOG_DESC("try to connect to endpoint") + << LOG_KV("host", (peer.address())) << LOG_KV("port", peer.port()); + */ + + auto p = std::make_shared< + std::promise>>(); + vPromise->push_back(p); + + std::string host = peer.address(); + uint16_t port = peer.port(); + + auto self = std::weak_ptr(shared_from_this()); + m_connector->connectToWsServer(host, port, m_config->disableSsl(), + [p, self, connectedEndPoint](boost::beast::error_code _ec, + const std::string& _extErrorMsg, + std::shared_ptr _wsStreamDelegate, + std::shared_ptr _nodeId) { + auto service = self.lock(); + if (!service) + { + return; + } + + auto futResult = std::make_tuple(_ec, _extErrorMsg, connectedEndPoint); + p->set_value(futResult); + + if (_ec) + { + return; + } + + auto session = service->newSession(_wsStreamDelegate, *_nodeId.get()); + session->setConnectedEndPoint(connectedEndPoint); + session->startAsClient(); + }); + } + + return vPromise; +} + +void WsService::reconnect() +{ + auto self = std::weak_ptr(shared_from_this()); + m_reconnect = std::make_shared( + *(m_timerIoc), boost::posix_time::milliseconds(m_config->reconnectPeriod())); + + m_reconnect->async_wait([self, this](const boost::system::error_code& _error) { + if (_error == boost::asio::error::operation_aborted) + { + return; + } + try + { + auto service = self.lock(); + if (!service) + { + return; + } + + auto connectPeers = std::make_shared>(); + + // select all disconnected nodes + ReadGuard l(x_peers); + for (auto& peer : *m_reconnectedPeers) + { + std::string connectedEndPoint = peer.address() + ":" + std::to_string(peer.port()); + auto session = getSession(connectedEndPoint); + if (session) + { + continue; + } + connectPeers->insert(peer); + } + + + if (!connectPeers->empty()) + { + for (auto reconnectPeer : *connectPeers) + { + WEBSOCKET_SERVICE(INFO) + << ("reconnect") + << LOG_KV("peer", reconnectPeer.address() + ":" + + std::to_string(reconnectPeer.port())); + } + asyncConnectToEndpoints(connectPeers); + } + + + service->reconnect(); + } + catch (std::exception const& e) + { + BOOST_SSL_LOG(WARNING) << LOG_DESC("reconnect exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +bool WsService::registerMsgHandler(uint16_t _msgType, MsgHandler _msgHandler) +{ + UpgradableGuard l(x_msgTypeHandlers); + if (m_msgType2Method.count(_msgType) || !_msgHandler) + { + return false; + } + UpgradeGuard ul(l); + m_msgType2Method[_msgType] = _msgHandler; + return true; +} + +MsgHandler WsService::getMsgHandler(uint16_t _type) +{ + ReadGuard l(x_msgTypeHandlers); + if (m_msgType2Method.count(_type)) + { + return m_msgType2Method[_type]; + } + return nullptr; +} + +bool WsService::eraseMsgHandler(uint16_t _type) +{ + UpgradableGuard l(x_msgTypeHandlers); + if (!m_msgType2Method.count(_type)) + { + return false; + } + UpgradeGuard ul(l); + m_msgType2Method.erase(_type); + return true; +} + +std::shared_ptr WsService::newSession( + std::shared_ptr _wsStreamDelegate, std::string const& _nodeId) +{ + _wsStreamDelegate->setMaxReadMsgSize(m_config->maxMsgSize()); + + std::string endPoint = _wsStreamDelegate->remoteEndpoint(); + auto session = m_sessionFactory->createSession(m_moduleName); + + session->setWsStreamDelegate(_wsStreamDelegate); + session->setIoc(m_ioservicePool->getIOService()); + session->setThreadPool(threadPool()); + session->setMessageFactory(messageFactory()); + session->setEndPoint(endPoint); + session->setConnectedEndPoint(endPoint); + session->setMaxWriteMsgSize(m_config->maxMsgSize()); + session->setSendMsgTimeout(m_config->sendMsgTimeout()); + session->setNodeId(_nodeId); + + auto self = std::weak_ptr(shared_from_this()); + session->setConnectHandler([self](Error::Ptr _error, std::shared_ptr _session) { + auto wsService = self.lock(); + if (wsService) + { + wsService->onConnect(_error, _session); + } + }); + session->setDisconnectHandler( + [self](Error::Ptr _error, std::shared_ptr _session) { + auto wsService = self.lock(); + if (wsService) + { + wsService->onDisconnect(_error, _session); + } + }); + session->setRecvMessageHandler( + [self](std::shared_ptr message, std::shared_ptr session) { + auto wsService = self.lock(); + if (wsService) + { + wsService->onRecvMessage(std::move(message), std::move(session)); + } + }); + + WEBSOCKET_SERVICE(INFO) << LOG_BADGE("newSession") << LOG_DESC("start the session") + << LOG_KV("endPoint", endPoint); + return session; +} + +void WsService::addSession(std::shared_ptr _session) +{ + auto connectedEndPoint = _session->connectedEndPoint(); + auto endpoint = _session->endPoint(); + bool ok = false; + { + boost::unique_lock lock(x_mutex); + auto it = m_sessions.find(connectedEndPoint); + if (it == m_sessions.end()) + { + m_sessions[connectedEndPoint] = _session; + ok = true; + } + } + + // thread pool + for (auto& conHandler : m_connectHandlers) + { + conHandler(_session); + } + + WEBSOCKET_SERVICE(INFO) << LOG_BADGE("addSession") << LOG_DESC("add session to mapping") + << LOG_KV("connectedEndPoint", connectedEndPoint) + << LOG_KV("endPoint", endpoint) << LOG_KV("result", ok); +} + +void WsService::removeSession(const std::string& _endPoint) +{ + { + boost::unique_lock lock(x_mutex); + m_sessions.erase(_endPoint); + } + + WEBSOCKET_SERVICE(INFO) << LOG_BADGE("removeSession") << LOG_KV("endpoint", _endPoint); +} + +std::shared_ptr WsService::getSession(const std::string& _endPoint) +{ + boost::shared_lock lock(x_mutex); + auto it = m_sessions.find(_endPoint); + if (it != m_sessions.end()) + { + return it->second; + } + return nullptr; +} + +WsSessions WsService::sessions() +{ + WsSessions sessions; + { + boost::shared_lock lock(x_mutex); + for (const auto& session : m_sessions) + { + if (session.second && session.second->isConnected()) + { + sessions.push_back(session.second); + } + } + } + + return sessions; +} + +/** + * @brief: session connect + * @param _error: + * @param _session: session + * @return void: + */ +void WsService::onConnect(Error::Ptr _error, std::shared_ptr _session) +{ + std::ignore = _error; + std::string endpoint = ""; + std::string connectedEndPoint = ""; + if (_session) + { + endpoint = _session->endPoint(); + connectedEndPoint = _session->connectedEndPoint(); + } + + addSession(_session); + + WEBSOCKET_SERVICE(INFO) << LOG_BADGE("onConnect") << LOG_KV("endpoint", endpoint) + << LOG_KV("connectedEndPoint", connectedEndPoint) + << LOG_KV("refCount", _session.use_count()); +} + +/** + * @brief: session disconnect + * @param _error: the reason of disconnection + * @param _session: session + * @return void: + */ +void WsService::onDisconnect(Error::Ptr _error, std::shared_ptr _session) +{ + std::ignore = _error; + std::string endpoint = ""; + std::string connectedEndPoint = ""; + if (_session) + { + endpoint = _session->endPoint(); + connectedEndPoint = _session->connectedEndPoint(); + } + + // clear the session + removeSession(connectedEndPoint); + + for (auto& disHandler : m_disconnectHandlers) + { + disHandler(_session); + } + + WEBSOCKET_SERVICE(INFO) << LOG_BADGE("onDisconnect") << LOG_KV("endpoint", endpoint) + << LOG_KV("connectedEndPoint", connectedEndPoint) + << LOG_KV("refCount", _session ? _session.use_count() : -1); +} + +void WsService::onRecvMessage( + std::shared_ptr message, std::shared_ptr session) +{ + auto& seq = message->seq(); + + WEBSOCKET_SERVICE(TRACE) << LOG_BADGE("onRecvMessage") + << LOG_DESC("receive message from server") + << LOG_KV("type", message->packetType()) << LOG_KV("seq", seq) + << LOG_KV("endpoint", session->endPoint()) + << LOG_KV("data size", message->payload()->size()) + << LOG_KV("use_count", session.use_count()); + + auto typeHandler = getMsgHandler(message->packetType()); + if (typeHandler) + { + typeHandler(std::move(message), std::move(session)); + } + else + { + WEBSOCKET_SERVICE(WARNING) + << LOG_BADGE("onRecvMessage") << LOG_DESC("unrecognized message type") + << LOG_KV("type", message->packetType()) << LOG_KV("endpoint", session->endPoint()) + << LOG_KV("seq", seq) << LOG_KV("data size", message->payload()->size()) + << LOG_KV("use_count", session.use_count()); + } +} + +void WsService::asyncSendMessageByEndPoint(const std::string& _endPoint, + std::shared_ptr _msg, Options _options, RespCallBack _respFunc) +{ + std::shared_ptr session = getSession(_endPoint); + if (!session) + { + if (_respFunc) + { + auto error = std::make_shared( + WsError::EndPointNotExist, "there has no connection of the endpoint exist"); + _respFunc(error, nullptr, nullptr); + } + + return; + } + + session->asyncSendMessage(_msg, _options, _respFunc); +} + +void WsService::asyncSendMessage( + std::shared_ptr _msg, Options _options, RespCallBack _respCallBack) +{ + auto seq = _msg->seq(); + return asyncSendMessage(sessions(), _msg, _options, _respCallBack); +} + +void WsService::asyncSendMessage(const WsSessions& _ss, std::shared_ptr _msg, + Options _options, RespCallBack _respFunc) +{ + class Retry : public std::enable_shared_from_this + { + public: + WsSessions ss; + std::shared_ptr msg; + Options options; + RespCallBack respFunc; + + public: + void trySendMessageWithOutCB() + { + if (ss.empty()) + { + return; + } + + auto seed = std::chrono::system_clock::now().time_since_epoch().count(); + std::default_random_engine e(seed); + std::shuffle(ss.begin(), ss.end(), e); + + auto session = *ss.begin(); + ss.erase(ss.begin()); + + + session->asyncSendMessage(msg, options); + } + + void trySendMessageWithCB() + { + if (ss.empty()) + { + auto error = std::make_shared( + WsError::NoActiveCons, "there has no active connection available"); + respFunc(error, nullptr, nullptr); + return; + } + + auto seed = std::chrono::system_clock::now().time_since_epoch().count(); + std::default_random_engine e(seed); + std::shuffle(ss.begin(), ss.end(), e); + + auto session = *ss.begin(); + ss.erase(ss.begin()); + + auto self = shared_from_this(); + std::string endPoint = session->endPoint(); + auto moduleName = session->moduleName(); + // Note: should not pass session to the lamda operator[], this will lead to memory leak + session->asyncSendMessage(msg, options, + [self, endPoint, moduleName, callback = respFunc](Error::Ptr _error, + std::shared_ptr _msg, + std::shared_ptr _session) { + if (_error && _error->errorCode() != 0) + { + BOOST_SSL_LOG(WARNING) + << LOG_BADGE(moduleName) << LOG_BADGE("asyncSendMessage") + << LOG_DESC("callback error") << LOG_KV("endpoint", endPoint) + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()); + + if (self->respFunc) + { + return self->respFunc(_error, _msg, _session); + } + } + + callback(_error, _msg, _session); + }); + } + }; + + auto retry = std::make_shared(); + retry->ss = _ss; + retry->msg = _msg; + + retry->options = _options; + retry->respFunc = _respFunc; + + if (_respFunc) + { + retry->trySendMessageWithCB(); + } + else + { + retry->trySendMessageWithOutCB(); + } + + // auto size = _ss.size(); + // auto seq = _msg->seq(); + // int32_t timeout = _options.timeout > 0 ? _options.timeout : m_config->sendMsgTimeout(); + + // WEBSOCKET_SERVICE(DEBUG) << LOG_BADGE("asyncSendMessage") << + // LOG_KV("seq", seq) + // << LOG_KV("size", size) << LOG_KV("timeout", timeout); +} + +void WsService::asyncSendMessage(const std::set& _endPoints, + std::shared_ptr _msg, Options _options, RespCallBack _respFunc) +{ + ws::WsSessions ss; + for (const std::string& endPoint : _endPoints) + { + auto s = getSession(endPoint); + if (s) + { + ss.push_back(s); + } + else + { + WEBSOCKET_SERVICE(DEBUG) + << LOG_BADGE("asyncSendMessage") + << LOG_DESC("there has no connection of the endpoint exist, skip") + << LOG_KV("endPoint", endPoint); + } + } + + return asyncSendMessage(ss, _msg, _options, _respFunc); +} + +void WsService::broadcastMessage(std::shared_ptr _msg) +{ + broadcastMessage(sessions(), _msg); +} + +void WsService::broadcastMessage( + const WsSession::Ptrs& _ss, std::shared_ptr _msg) +{ + for (auto& session : _ss) + { + if (session->isConnected()) + { + session->asyncSendMessage(_msg); + } + } + + WEBSOCKET_SERVICE(DEBUG) << LOG_BADGE("broadcastMessage"); +} diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsService.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsService.h" new file mode 100644 index 00000000..f47a29d8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsService.h" @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2021 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 WsService.h + * @author: octopus + * @date 2021-07-28 + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace boostssl +{ +namespace ws +{ +using WsSessions = std::vector>; +using MsgHandler = + std::function, std::shared_ptr)>; +using ConnectHandler = std::function)>; +using DisconnectHandler = std::function)>; +using HandshakeHandler = std::function, std::shared_ptr)>; + +class WsService : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + WsService(std::string _moduleName = "DEFAULT"); + virtual ~WsService(); + +public: + virtual void start(); + virtual void stop(); + virtual void reconnect(); + virtual void reportConnectedNodes(); + + std::shared_ptr>>>> + asyncConnectToEndpoints(EndPointsPtr _peers); + + std::string genConnectError(const std::string& _error, const std::string& endpoint, bool end); + void syncConnectToEndpoints(EndPointsPtr _peers); + +public: + std::shared_ptr newSession( + std::shared_ptr _wsStreamDelegate, std::string const& _nodeId); + std::shared_ptr getSession(const std::string& _endPoint); + void addSession(std::shared_ptr _session); + void removeSession(const std::string& _endPoint); + WsSessions sessions(); + +public: + virtual void onConnect(bcos::Error::Ptr _error, std::shared_ptr _session); + virtual void onDisconnect(bcos::Error::Ptr _error, std::shared_ptr _session); + + virtual void onRecvMessage( + std::shared_ptr _msg, std::shared_ptr _session); + + virtual void asyncSendMessage(std::shared_ptr _msg, + Options _options = Options(), RespCallBack _respFunc = RespCallBack()); + virtual void asyncSendMessage(const WsSessions& _ss, + std::shared_ptr _msg, Options _options = Options(), + RespCallBack _respFunc = RespCallBack()); + virtual void asyncSendMessage(const std::set& _endPoints, + std::shared_ptr _msg, Options _options = Options(), + RespCallBack _respFunc = RespCallBack()); + + virtual void asyncSendMessageByEndPoint(const std::string& _endPoint, + std::shared_ptr _msg, Options _options = Options(), + RespCallBack _respFunc = RespCallBack()); + + virtual void broadcastMessage(std::shared_ptr _msg); + virtual void broadcastMessage( + const WsSession::Ptrs& _ss, std::shared_ptr _msg); + +public: + std::shared_ptr messageFactory() { return m_messageFactory; } + void setMessageFactory(std::shared_ptr _messageFactory) + { + m_messageFactory = _messageFactory; + } + + std::shared_ptr sessionFactory() { return m_sessionFactory; } + void setSessionFactory(std::shared_ptr _sessionFactory) + { + m_sessionFactory = _sessionFactory; + } + int32_t waitConnectFinishTimeout() const { return m_waitConnectFinishTimeout; } + void setWaitConnectFinishTimeout(int32_t _timeout) { m_waitConnectFinishTimeout = _timeout; } + + std::string moduleName() { return m_moduleName; } + void setModuleName(std::string _moduleName) { m_moduleName = _moduleName; } + + std::shared_ptr threadPool() const { return m_threadPool; } + void setThreadPool(std::shared_ptr _threadPool) + { + m_threadPool = _threadPool; + } + + void setIOServicePool(IOServicePool::Ptr _ioservicePool) + { + m_ioservicePool = _ioservicePool; + m_timerIoc = m_ioservicePool->getIOService(); + } + + std::shared_ptr connector() const { return m_connector; } + void setConnector(std::shared_ptr _connector) { m_connector = _connector; } + + void setHostPort(std::string host, uint16_t port) + { + m_listenHost = host; + m_listenPort = port; + } + std::string listenHost() { return m_listenHost; } + uint16_t listenPort() { return m_listenPort; } + + WsConfig::Ptr config() const { return m_config; } + void setConfig(WsConfig::Ptr _config) { m_config = _config; } + + std::shared_ptr httpServer() const { return m_httpServer; } + void setHttpServer(std::shared_ptr _httpServer) + { + m_httpServer = _httpServer; + } + + bool registerMsgHandler(uint16_t _msgType, MsgHandler _msgHandler); + + MsgHandler getMsgHandler(uint16_t _type); + + bool eraseMsgHandler(uint16_t _msgType); + + void registerConnectHandler(ConnectHandler _connectHandler) + { + m_connectHandlers.push_back(_connectHandler); + } + + void registerDisconnectHandler(DisconnectHandler _disconnectHandler) + { + m_disconnectHandlers.push_back(_disconnectHandler); + } + + void registerHandshakeHandler(HandshakeHandler _handshakeHandler) + { + m_handshakeHandlers.push_back(_handshakeHandler); + } + + void setReconnectedPeers(EndPointsPtr _reconnectedPeers) + { + WriteGuard l(x_peers); + m_reconnectedPeers = _reconnectedPeers; + } + EndPointsPtr reconnectedPeers() const + { + ReadGuard l(x_peers); + return m_reconnectedPeers; + } + +private: + bool m_running{false}; + + int32_t m_waitConnectFinishTimeout = 30000; + std::string m_moduleName; + + // MessageFaceFactory + std::shared_ptr m_messageFactory; + // ThreadPool + std::shared_ptr m_threadPool; + // listen host port + std::string m_listenHost = ""; + uint16_t m_listenPort = 0; + // nodeID + std::string m_nodeID; + // Config + std::shared_ptr m_config; + + // list of reconnected server nodes updated by upper module, such as p2pservice + EndPointsPtr m_reconnectedPeers; + mutable bcos::SharedMutex x_peers; + + // ws connector + std::shared_ptr m_connector; + // reconnect timer + std::shared_ptr m_reconnect; + // heartbeat timer + std::shared_ptr m_heartbeat; + // http server + std::shared_ptr m_httpServer; + +private: + // mutex for m_sessions + mutable boost::shared_mutex x_mutex; + // all active sessions + std::unordered_map> m_sessions; + // type => handler + std::unordered_map m_msgType2Method; + mutable SharedMutex x_msgTypeHandlers; + // connected handlers, the handers will be called after ws protocol handshake + // is complete + std::vector m_connectHandlers; + // disconnected handlers, the handers will be called when ws session + // disconnected + std::vector m_disconnectHandlers; + // handshake handlers, the handers will be called when ws session + // disconnected + std::vector m_handshakeHandlers; + // sessionFactory + WsSessionFactory::Ptr m_sessionFactory; + + IOServicePool::Ptr m_ioservicePool; + + std::shared_ptr m_timerIoc; +}; + +} // namespace ws +} // namespace boostssl +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsSession.cpp" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsSession.cpp" new file mode 100644 index 00000000..a2485c74 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsSession.cpp" @@ -0,0 +1,476 @@ +/* + * Copyright (C) 2021 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 + * m_limitations under the License. + * + * @file WsSession.cpp + * @author: octopus + * @date 2021-07-08 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MESSAGE_SEND_DELAY_REPORT_MS (5000) +#define MAX_MESSAGE_SEND_DELAY_MS (5000) + +using namespace bcos; +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; +using namespace bcos::boostssl::http; + +WsSession::WsSession(std::string _moduleName) : m_moduleName(_moduleName) +{ + WEBSOCKET_SESSION(INFO) << LOG_KV("[NEWOBJ][WSSESSION]", this); +} +void WsSession::drop(uint32_t _reason) +{ + if (m_isDrop) + { + WEBSOCKET_SESSION(INFO) << LOG_BADGE("drop") + << LOG_DESC("the session has already been dropped") + << LOG_KV("endpoint", m_endPoint) << LOG_KV("session", this); + return; + } + + m_isDrop = true; + + WEBSOCKET_SESSION(INFO) << LOG_BADGE("drop") << LOG_KV("reason", _reason) + << LOG_KV("endpoint", m_endPoint) << LOG_KV("session", this); + + auto self = std::weak_ptr(shared_from_this()); + // call callbacks + { + auto error = std::make_shared( + WsError::SessionDisconnect, "the session has been disconnected"); + + ReadGuard l(x_callback); + WEBSOCKET_SESSION(INFO) << LOG_BADGE("drop") << LOG_KV("reason", _reason) + << LOG_KV("endpoint", m_endPoint) + << LOG_KV("cb size", m_callbacks.size()) << LOG_KV("session", this); + + for (auto& cbEntry : m_callbacks) + { + auto callback = cbEntry.second; + if (callback->timer) + { + callback->timer->cancel(); + } + + WEBSOCKET_SESSION(TRACE) + << LOG_DESC("the session has been disconnected") << LOG_KV("seq", cbEntry.first); + + m_threadPool->enqueue( + [callback, error]() { callback->respCallBack(error, nullptr, nullptr); }); + } + } + + // clear callbacks + { + WriteGuard lock(x_callback); + m_callbacks.clear(); + } + + if (m_wsStreamDelegate) + { + m_wsStreamDelegate->close(); + } + + m_threadPool->enqueue([self]() { + auto session = self.lock(); + if (session) + { + session->disconnectHandler()(nullptr, session); + } + }); +} + +// start WsSession as client +void WsSession::startAsClient() +{ + if (m_connectHandler) + { + auto session = shared_from_this(); + m_connectHandler(nullptr, session); + } + // read message + asyncRead(); + + WEBSOCKET_SESSION(INFO) << LOG_BADGE("startAsClient") + << LOG_DESC("websocket handshake successfully") + << LOG_KV("endPoint", m_endPoint) << LOG_KV("session", this); +} + +// start WsSession as server +void WsSession::startAsServer(HttpRequest _httpRequest) +{ + WEBSOCKET_SESSION(INFO) << LOG_BADGE("startAsServer") << LOG_DESC("start websocket handshake") + << LOG_KV("endPoint", m_endPoint) << LOG_KV("session", this); + m_wsStreamDelegate->asyncAccept( + _httpRequest, std::bind(&WsSession::onWsAccept, shared_from_this(), std::placeholders::_1)); +} + +void WsSession::onWsAccept(boost::beast::error_code _ec) +{ + if (_ec) + { + WEBSOCKET_SESSION(WARNING) << LOG_BADGE("onWsAccept") << LOG_KV("error", _ec.message()); + return drop(WsError::AcceptError); + } + + if (connectHandler()) + { + connectHandler()(nullptr, shared_from_this()); + } + + asyncRead(); + + WEBSOCKET_SESSION(INFO) << LOG_BADGE("onWsAccept") + << LOG_DESC("websocket handshake successfully") + << LOG_KV("endPoint", endPoint()) << LOG_KV("session", this); +} + +void WsSession::onReadPacket(boost::beast::flat_buffer& _buffer) +{ + try + { + auto data = boost::asio::buffer_cast(boost::beast::buffers_front(_buffer.data())); + auto size = boost::asio::buffer_size(m_buffer.data()); + + auto message = m_messageFactory->buildMessage(); + if (message->decode(bytesConstRef(data, size)) < 0) + { // invalid packet, stop this session ? + WEBSOCKET_SESSION(WARNING) + << LOG_BADGE("onReadPacket") << LOG_DESC("decode packet error") + << LOG_KV("endpoint", endPoint()) << LOG_KV("session", this); + return drop(WsError::PacketError); + } + + m_buffer.consume(_buffer.size()); + onMessage(message); + } + catch (std::exception const& e) + { + WEBSOCKET_SESSION(WARNING) << LOG_DESC("onReadPacket: decode message exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } +} + +void WsSession::onMessage(bcos::boostssl::MessageFace::Ptr _message) +{ + auto self = std::weak_ptr(shared_from_this()); + // task enqueue + m_threadPool->enqueue([_message, self]() { + auto session = self.lock(); + if (!session) + { + return; + } + auto callback = session->getAndRemoveRespCallback(_message->seq(), true, _message); + if (callback) + { + if (callback->timer) + { + callback->timer->cancel(); + } + + callback->respCallBack(nullptr, _message, session); + } + else + { + session->recvMessageHandler()(_message, session); + } + }); +} + +void WsSession::asyncRead() +{ + if (!isConnected()) + { + WEBSOCKET_SESSION(TRACE) << LOG_BADGE("asyncRead") + << LOG_DESC("session has been disconnected") + << LOG_KV("endpoint", endPoint()) << LOG_KV("session", this); + return; + } + try + { + auto self = std::weak_ptr(shared_from_this()); + m_wsStreamDelegate->asyncRead(m_buffer, [self](boost::beast::error_code _ec, std::size_t) { + auto session = self.lock(); + if (!session) + { + return; + } + + if (_ec) + { + BCOS_LOG(WARNING) << "[WS][SESSION]" << LOG_BADGE("asyncRead") + << LOG_KV("error", _ec.message()) + << LOG_KV("endpoint", session->endPoint()) + << LOG_KV("refCount", session.use_count()); + + return session->drop(WsError::ReadError); + } + + session->onReadPacket(session->buffer()); + session->asyncRead(); + }); + } + catch (const std::exception& _e) + { + WEBSOCKET_SESSION(WARNING) + << LOG_BADGE("asyncRead") << LOG_DESC("exception") << LOG_KV("endpoint", endPoint()) + << LOG_KV("session", this) << LOG_KV("what", std::string(_e.what())); + drop(WsError::ReadError); + } +} + +void WsSession::onWritePacket() +{ + if (m_writing) + { + return; + } + WriteGuard l(x_writeQueue); + if (m_writing) + { + return; + } + if (m_writeQueue.empty()) + { + m_writing = false; + return; + } + m_writing = true; + auto msg = m_writeQueue.top(); + m_writeQueue.pop(); + asyncWrite(msg->buffer); +} + +void WsSession::asyncWrite(std::shared_ptr _buffer) +{ + if (!isConnected()) + { + WEBSOCKET_SESSION(TRACE) << LOG_BADGE("asyncWrite") + << LOG_DESC("session has been disconnected") + << LOG_KV("endpoint", endPoint()) << LOG_KV("session", this); + return; + } + + try + { + auto self = std::weak_ptr(shared_from_this()); + // Note: add one simple way to monitor message sending latency + // Note: the lambda[] should not include session directly, this will cause memory leak + m_wsStreamDelegate->asyncWrite( + *_buffer, [self, _buffer](boost::beast::error_code _ec, std::size_t) { + auto session = self.lock(); + if (!session) + { + return; + } + if (_ec) + { + BCOS_LOG(WARNING) << LOG_BADGE(session->moduleName()) << LOG_BADGE("Session") + << LOG_BADGE("asyncWrite") << LOG_KV("message", _ec.message()) + << LOG_KV("endpoint", session->endPoint()); + return session->drop(WsError::WriteError); + } + if (session->m_writing) + { + session->m_writing = false; + } + session->onWritePacket(); + }); + } + catch (const std::exception& _e) + { + WEBSOCKET_SESSION(WARNING) + << LOG_BADGE("asyncWrite") << LOG_DESC("async_write throw exception") + << LOG_KV("session", this) << LOG_KV("endpoint", endPoint()) + << LOG_KV("what", std::string(_e.what())); + drop(WsError::WriteError); + } +} + +void WsSession::send(std::shared_ptr buffer) +{ + auto msg = std::make_shared(); + msg->buffer = std::move(buffer); + { + WriteGuard lock(x_writeQueue); + // data to be sent is always enqueue first + m_writeQueue.push(msg); + } + onWritePacket(); +} + +/** + * @brief: send message with callback + * @param _msg: message to be send + * @param _options: options + * @param _respCallback: callback + * @return void: + */ +void WsSession::asyncSendMessage( + std::shared_ptr _msg, Options _options, RespCallBack _respFunc) +{ + auto seq = _msg->seq(); + + if (!isConnected()) + { + WEBSOCKET_SESSION(WARNING) + << LOG_BADGE("asyncSendMessage") << LOG_DESC("the session has been disconnected") + << LOG_KV("seq", seq) << LOG_KV("endpoint", endPoint()); + + if (_respFunc) + { + auto error = std::make_shared( + WsError::SessionDisconnect, "the session has been disconnected"); + _respFunc(error, nullptr, nullptr); + } + + return; + } + + // check if message size overflow + if ((int64_t)_msg->payload()->size() > (int64_t)maxWriteMsgSize()) + { + if (_respFunc) + { + auto error = std::make_shared(WsError::MessageOverflow, "Message size overflow"); + _respFunc(error, nullptr, nullptr); + } + + WEBSOCKET_SESSION(WARNING) + << LOG_BADGE("asyncSendMessage") << LOG_DESC("send message size overflow") + << LOG_KV("endpoint", endPoint()) << LOG_KV("seq", seq) + << LOG_KV("msgSize", _msg->payload()->size()) + << LOG_KV("maxWriteMsgSize", maxWriteMsgSize()); + return; + } + + auto buffer = std::make_shared(); + auto r = _msg->encode(*buffer); + if (!r) + { + if (_respFunc) + { + auto error = + std::make_shared(WsError::MessageEncodeError, "Message encode failed"); + _respFunc(error, nullptr, nullptr); + } + + WEBSOCKET_SESSION(WARNING) + << LOG_BADGE("asyncSendMessage") << LOG_DESC("message encode failed") + << LOG_KV("endpoint", endPoint()) << LOG_KV("seq", seq) + << LOG_KV("msgSize", _msg->payload()->size()) + << LOG_KV("maxWriteMsgSize", maxWriteMsgSize()); + return; + } + + if (_respFunc) + { // callback + auto callback = std::make_shared(); + callback->respCallBack = _respFunc; + auto timeout = _options.timeout > 0 ? _options.timeout : m_sendMsgTimeout; + if (timeout > 0) + { + // create new timer to handle timeout + auto timer = std::make_shared( + *m_ioc, boost::posix_time::milliseconds(timeout)); + + callback->timer = timer; + auto self = std::weak_ptr(shared_from_this()); + timer->async_wait([self, seq](const boost::system::error_code& e) { + auto session = self.lock(); + if (session) + { + session->onRespTimeout(e, seq); + } + }); + } + + addRespCallback(seq, callback); + } + + { + boost::asio::post(m_wsStreamDelegate->tcpStream().get_executor(), + boost::beast::bind_front_handler(&WsSession::send, shared_from_this(), buffer)); + } +} + +void WsSession::addRespCallback(const std::string& _seq, CallBack::Ptr _callback) +{ + WriteGuard lock(x_callback); + m_callbacks[_seq] = _callback; +} + +WsSession::CallBack::Ptr WsSession::getAndRemoveRespCallback( + const std::string& _seq, bool _remove, std::shared_ptr _message) +{ + // Session need check response packet and message isn't a respond packet, so message don't have + // a callback. Otherwise message has a callback. + if (needCheckRspPacket() && _message && !_message->isRespPacket()) + { + return nullptr; + } + + CallBack::Ptr callback = nullptr; + { + UpgradableGuard l(x_callback); + auto it = m_callbacks.find(_seq); + if (it != m_callbacks.end()) + { + callback = it->second; + if (_remove) + { + UpgradeGuard ul(l); + m_callbacks.erase(it); + } + } + } + + return callback; +} + +void WsSession::onRespTimeout(const boost::system::error_code& _error, const std::string& _seq) +{ + if (_error) + { + return; + } + + auto callback = getAndRemoveRespCallback(_seq); + if (!callback) + { + return; + } + + WEBSOCKET_SESSION(WARNING) << LOG_BADGE("onRespTimeout") << LOG_KV("seq", _seq); + + auto error = + std::make_shared(WsError::TimeOut, "waiting for message response timed out"); + m_threadPool->enqueue([callback, error]() { callback->respCallBack(error, nullptr, nullptr); }); +} diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsSession.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsSession.h" new file mode 100644 index 00000000..e10e23c3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsSession.h" @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2021 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 + * m_limitations under the License. + * + * @file WsSession.h + * @author: octopus + * @date 2021-07-28 + */ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace boostssl +{ +namespace ws +{ +class WsService; +// The websocket session for connection +class WsSession : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + using Ptrs = std::vector>; + +public: + WsSession(std::string _moduleName = "DEFAULT"); + + virtual ~WsSession() { WEBSOCKET_SESSION(INFO) << LOG_KV("[DELOBJ][WSSESSION]", this); } + + void drop(uint32_t _reason); + +public: + // start WsSession as client + void startAsClient(); + // start WsSession as server + void startAsServer(bcos::boostssl::http::HttpRequest _httpRequest); + + virtual void onMessage(bcos::boostssl::MessageFace::Ptr _message); + + + virtual bool isConnected() + { + return !m_isDrop && m_wsStreamDelegate && m_wsStreamDelegate->open(); + } + /** + * @brief: async send message + * @param _msg: message + * @param _options: options + * @param _respCallback: callback + * @return void: + */ + virtual void asyncSendMessage(std::shared_ptr _msg, + Options _options = Options(), RespCallBack _respCallback = RespCallBack()); + + + std::string endPoint() const { return m_endPoint; } + void setEndPoint(const std::string& _endPoint) { m_endPoint = _endPoint; } + + std::string connectedEndPoint() const { return m_connectedEndPoint; } + void setConnectedEndPoint(const std::string& _connectedEndPoint) + { + m_connectedEndPoint = _connectedEndPoint; + } + + void setConnectHandler(WsConnectHandler _connectHandler) { m_connectHandler = _connectHandler; } + WsConnectHandler connectHandler() { return m_connectHandler; } + + void setDisconnectHandler(WsDisconnectHandler _disconnectHandler) + { + m_disconnectHandler = _disconnectHandler; + } + WsDisconnectHandler disconnectHandler() { return m_disconnectHandler; } + + void setRecvMessageHandler(WsRecvMessageHandler _recvMessageHandler) + { + m_recvMessageHandler = _recvMessageHandler; + } + WsRecvMessageHandler recvMessageHandler() { return m_recvMessageHandler; } + + std::shared_ptr messageFactory() { return m_messageFactory; } + void setMessageFactory(std::shared_ptr _messageFactory) + { + m_messageFactory = _messageFactory; + } + + std::shared_ptr ioc() const { return m_ioc; } + void setIoc(std::shared_ptr _ioc) { m_ioc = _ioc; } + + std::shared_ptr threadPool() const { return m_threadPool; } + void setThreadPool(std::shared_ptr _threadPool) + { + m_threadPool = _threadPool; + } + + void setVersion(uint16_t _version) { m_version.store(_version); } + uint16_t version() const { return m_version.load(); } + + WsStreamDelegate::Ptr wsStreamDelegate() { return m_wsStreamDelegate; } + void setWsStreamDelegate(WsStreamDelegate::Ptr _wsStreamDelegate) + { + m_wsStreamDelegate = _wsStreamDelegate; + } + + boost::beast::flat_buffer& buffer() { return m_buffer; } + void setBuffer(boost::beast::flat_buffer _buffer) { m_buffer = std::move(_buffer); } + + int32_t sendMsgTimeout() const { return m_sendMsgTimeout; } + void setSendMsgTimeout(int32_t _sendMsgTimeout) { m_sendMsgTimeout = _sendMsgTimeout; } + + int32_t maxWriteMsgSize() const { return m_maxWriteMsgSize; } + void setMaxWriteMsgSize(int32_t _maxWriteMsgSize) { m_maxWriteMsgSize = _maxWriteMsgSize; } + + std::size_t msgQueueSize() + { + bcos::ReadGuard l(x_writeQueue); + return m_writeQueue.size(); + } + + std::string nodeId() { return m_nodeId; } + void setNodeId(std::string _nodeId) { m_nodeId = _nodeId; } + + std::string moduleName() { return m_moduleName; } + void setModuleName(std::string _moduleName) { m_moduleName = _moduleName; } + + bool needCheckRspPacket() { return m_needCheckRspPacket; } + void setNeedCheckRspPacket(bool _needCheckRespPacket) + { + m_needCheckRspPacket = _needCheckRespPacket; + } + +protected: + struct CallBack + { + using Ptr = std::shared_ptr; + RespCallBack respCallBack; + std::shared_ptr timer; + }; + virtual void addRespCallback(const std::string& _seq, CallBack::Ptr _callback); + CallBack::Ptr getAndRemoveRespCallback(const std::string& _seq, bool _remove = true, + std::shared_ptr _message = nullptr); + virtual void onRespTimeout(const boost::system::error_code& _error, const std::string& _seq); + + virtual void onWsAccept(boost::beast::error_code _ec); + + virtual void asyncRead(); + virtual void asyncWrite(std::shared_ptr _buffer); + + virtual void send(std::shared_ptr _buffer); + + // async read + virtual void onReadPacket(boost::beast::flat_buffer& _buffer); + void onWritePacket(); + +protected: + // flag for message that need to check respond packet like p2pmessage + bool m_needCheckRspPacket = false; + // + std::atomic_bool m_isDrop = false; + // websocket protocol version + std::atomic m_version = 0; + std::string m_moduleName; + + // buffer used to read message + boost::beast::flat_buffer m_buffer; + + std::string m_endPoint; + std::string m_connectedEndPoint; + std::string m_nodeId; + + // + int32_t m_sendMsgTimeout = -1; + // + int32_t m_maxWriteMsgSize = -1; + + // + WsStreamDelegate::Ptr m_wsStreamDelegate; + // callbacks + mutable bcos::SharedMutex x_callback; + std::unordered_map m_callbacks; + + // callback handler + WsConnectHandler m_connectHandler; + WsDisconnectHandler m_disconnectHandler; + WsRecvMessageHandler m_recvMessageHandler; + + // message factory + std::shared_ptr m_messageFactory; + // thread pool + std::shared_ptr m_threadPool; + // ioc + std::shared_ptr m_ioc; + + struct Message + { + std::shared_ptr buffer; + }; + + // send message queue + mutable bcos::SharedMutex x_writeQueue; + std::priority_queue> m_writeQueue; + std::atomic_bool m_writing = {false}; +}; + +class WsSessionFactory +{ +public: + using Ptr = std::shared_ptr; + WsSessionFactory() = default; + virtual ~WsSessionFactory() {} + +public: + virtual WsSession::Ptr createSession(std::string _moduleName) + { + auto session = std::make_shared(_moduleName); + return session; + } +}; + +} // namespace ws +} // namespace boostssl +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsStream.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsStream.h" new file mode 100644 index 00000000..a3d81c66 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsStream.h" @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2021 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 WsStream.h + * @author: octopus + * @date 2021-10-29 + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace boostssl +{ +namespace ws +{ +using WsStreamRWHandler = std::function; +using WsStreamHandshakeHandler = std::function; + +template +class WsStream +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + WsStream( + std::shared_ptr> _stream, std::string _moduleName) + : m_stream(_stream), m_moduleName(_moduleName) + { + initDefaultOpt(); + WEBSOCKET_STREAM(INFO) << LOG_KV("[NEWOBJ][WsStream]", this); + } + + virtual ~WsStream() + { + WEBSOCKET_STREAM(INFO) << LOG_KV("[DELOBJ][WsStream]", this); + close(); + } + + void initDefaultOpt() + { + /* //TODO: close compress temp + // default open compress option + { + boost::beast::websocket::permessage_deflate opt; + opt.client_enable = true; // for clients + opt.server_enable = true; // for servers + + m_stream->set_option(opt); + } + */ + + // default timeout option + { + boost::beast::websocket::stream_base::timeout opt; + // Note: make it config + opt.handshake_timeout = std::chrono::milliseconds(30000); + // idle time + opt.idle_timeout = std::chrono::milliseconds(10000); + // open ping/pong option + opt.keep_alive_pings = true; + + m_stream->set_option(opt); + m_stream->auto_fragment(false); + m_stream->secure_prng(false); + m_stream->write_buffer_bytes(2 * 1024 * 1024); + } + } + +public: + //--------------- set opt params for websocket stream + // begin----------------------------- + void setMaxReadMsgSize(uint32_t _maxValue) { m_stream->read_message_max(_maxValue); } + + template + void setOpt(OPT _opt) + { + m_stream->set_option(_opt); + } + //--------------- set opt params for websocket stream end + //------------------------------- + + std::string moduleName() { return m_moduleName; } + void setModuleName(std::string _moduleName) { m_moduleName = _moduleName; } + +public: + bool open() { return !m_closed.load() && m_stream->is_open(); } + + void close() + { + bool trueValue = true; + bool falseValue = false; + if (m_closed.compare_exchange_strong(falseValue, trueValue)) + { + auto& ss = boost::beast::get_lowest_layer(*m_stream); + ws::WsTools::close(ss.socket()); + WEBSOCKET_STREAM(INFO) + << LOG_DESC("the real action to close the stream") << LOG_KV("this", this); + } + return; + } + + boost::beast::tcp_stream& tcpStream() { return boost::beast::get_lowest_layer(*m_stream); } + + std::shared_ptr> stream() const { return m_stream; } + +public: + void asyncWrite(const bcos::bytes& _buffer, WsStreamRWHandler _handler) + { + m_stream->binary(true); + m_stream->async_write(boost::asio::buffer(_buffer), _handler); + } + + void asyncRead(boost::beast::flat_buffer& _buffer, WsStreamRWHandler _handler) + { + m_stream->async_read(_buffer, _handler); + } + + void asyncHandshake(const std::string& _host, const std::string& _target, + std::function _handler) + { + m_stream->async_handshake(_host, _target, _handler); + } + + void asyncAccept( + bcos::boostssl::http::HttpRequest _httpRequest, WsStreamHandshakeHandler _handler) + { + m_stream->async_accept(_httpRequest, boost::beast::bind_front_handler(_handler)); + } + + virtual std::string localEndpoint() + { + try + { + auto& s = tcpStream(); + auto localEndPoint = s.socket().local_endpoint(); + auto endPoint = + localEndPoint.address().to_string() + ":" + std::to_string(localEndPoint.port()); + return endPoint; + } + catch (const std::exception& e) + { + WEBSOCKET_STREAM(WARNING) << LOG_BADGE("localEndpoint") << LOG_KV("e", e.what()); + } + + return std::string(""); + } + + virtual std::string remoteEndpoint() + { + try + { + auto& s = tcpStream(); + auto remoteEndpoint = s.socket().remote_endpoint(); + auto endPoint = + remoteEndpoint.address().to_string() + ":" + std::to_string(remoteEndpoint.port()); + return endPoint; + } + catch (const std::exception& e) + { + WEBSOCKET_STREAM(WARNING) << LOG_BADGE("remoteEndpoint") << LOG_KV("e", e.what()); + } + + return std::string(""); + } + +private: + std::atomic m_closed{false}; + std::shared_ptr> m_stream; + std::string m_moduleName = "DEFAULT"; +}; + +using RawWsStream = WsStream; +using SslWsStream = WsStream>; + +class WsStreamDelegate +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + +public: + WsStreamDelegate(RawWsStream::Ptr _rawStream) : m_isSsl(false), m_rawStream(_rawStream) {} + WsStreamDelegate(SslWsStream::Ptr _sslStream) : m_isSsl(true), m_sslStream(_sslStream) {} + +public: + void setMaxReadMsgSize(uint32_t _maxValue) + { + m_isSsl ? m_sslStream->setMaxReadMsgSize(_maxValue) : + m_rawStream->setMaxReadMsgSize(_maxValue); + } + bool open() { return m_isSsl ? m_sslStream->open() : m_rawStream->open(); } + void close() { return m_isSsl ? m_sslStream->close() : m_rawStream->close(); } + std::string localEndpoint() + { + return m_isSsl ? m_sslStream->localEndpoint() : m_rawStream->localEndpoint(); + } + std::string remoteEndpoint() + { + return m_isSsl ? m_sslStream->remoteEndpoint() : m_rawStream->remoteEndpoint(); + } + + void asyncWrite(const bcos::bytes& _buffer, WsStreamRWHandler _handler) + { + return m_isSsl ? m_sslStream->asyncWrite(_buffer, _handler) : + m_rawStream->asyncWrite(_buffer, _handler); + } + + void asyncRead(boost::beast::flat_buffer& _buffer, WsStreamRWHandler _handler) + { + return m_isSsl ? m_sslStream->asyncRead(_buffer, _handler) : + m_rawStream->asyncRead(_buffer, _handler); + } + + void asyncWsHandshake(const std::string& _host, const std::string& _target, + std::function _handler) + { + return m_isSsl ? m_sslStream->asyncHandshake(_host, _target, _handler) : + m_rawStream->asyncHandshake(_host, _target, _handler); + } + + void asyncAccept( + bcos::boostssl::http::HttpRequest _httpRequest, WsStreamHandshakeHandler _handler) + { + return m_isSsl ? m_sslStream->asyncAccept(_httpRequest, _handler) : + m_rawStream->asyncAccept(_httpRequest, _handler); + } + + void asyncHandshake(std::function _handler) + { + if (m_isSsl) + { + m_sslStream->stream()->next_layer().async_handshake( + boost::asio::ssl::stream_base::client, _handler); + } + else + { // callback directly + _handler(make_error_code(boost::system::errc::success)); + } + } + + boost::beast::tcp_stream& tcpStream() + { + return m_isSsl ? m_sslStream->tcpStream() : m_rawStream->tcpStream(); + } + + void setVerifyCallback(bool _disableSsl, VerifyCallback callback, bool = true) + { + if (!_disableSsl) + { + m_sslStream->stream()->next_layer().set_verify_callback(callback); + } + } + +private: + bool m_isSsl{false}; + + RawWsStream::Ptr m_rawStream; + SslWsStream::Ptr m_sslStream; +}; + +class WsStreamDelegateBuilder +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + +public: + WsStreamDelegate::Ptr build( + std::shared_ptr _tcpStream, std::string _moduleName) + { + _tcpStream->socket().set_option(boost::asio::ip::tcp::no_delay(true)); + auto wsStream = std::make_shared>( + std::move(*_tcpStream)); + auto rawWsStream = std::make_shared>( + wsStream, _moduleName); + return std::make_shared(rawWsStream); + } + + WsStreamDelegate::Ptr build( + std::shared_ptr> _sslStream, + std::string _moduleName) + { + _sslStream->next_layer().socket().set_option(boost::asio::ip::tcp::no_delay(true)); + auto wsStream = std::make_shared< + boost::beast::websocket::stream>>( + std::move(*_sslStream)); + auto sslWsStream = std::make_shared< + bcos::boostssl::ws::WsStream>>( + wsStream, _moduleName); + return std::make_shared(sslWsStream); + } + + WsStreamDelegate::Ptr build(bool _disableSsl, std::shared_ptr _ctx, + std::shared_ptr _tcpStream, std::string _moduleName) + { + if (_disableSsl) + { + return build(_tcpStream, _moduleName); + } + + auto sslStream = std::make_shared>( + std::move(*_tcpStream), *_ctx); + return build(sslStream, _moduleName); + } +}; + +} // namespace ws +} // namespace boostssl +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsTools.cpp" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsTools.cpp" new file mode 100644 index 00000000..f63fa88a --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsTools.cpp" @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2021 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 + * m_limitations under the License. + * + * @file WsTools.cpp + * @author: octopus + * @date 2021-10-19 + */ +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; + +bool WsTools::stringToEndPoint(const std::string& _peer, NodeIPEndpoint& _endpoint) +{ + // ipv4: 127.0.0.1:12345 => NodeIPEndpoint + // ipv6: [0:1]:12345 => NodeIPEndpoint + + std::string ip; + uint16_t port = 0; + bool valid = false; + + std::vector s; + boost::split(s, _peer, boost::is_any_of("]"), boost::token_compress_on); + if (s.size() == 2) + { // ipv6 + ip = s[0].data() + 1; + port = boost::lexical_cast(s[1].data() + 1); + valid = true; + } + else if (s.size() == 1) + { // ipv4 + std::vector v; + boost::split(v, _peer, boost::is_any_of(":"), boost::token_compress_on); + ip = v[0]; + port = boost::lexical_cast(v[1]); + valid = true; + } + + valid = validIP(ip) && validPort(port); + + if (!valid) + { + WEBSOCKET_TOOL(WARNING) << LOG_DESC("peer is not valid ip:port format") + << LOG_KV("peer", _peer); + } + + if (valid) + { + _endpoint = NodeIPEndpoint(ip, port); + } + + return valid; +} + +void WsTools::close(boost::asio::ip::tcp::socket& _socket) +{ + try + { + boost::beast::error_code ec; + _socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + if (_socket.is_open()) + { + _socket.close(); + } + } + catch (std::exception const& e) + { + WEBSOCKET_TOOL(WARNING) << LOG_DESC("WsTools close exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsTools.h" "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsTools.h" new file mode 100644 index 00000000..a8139967 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/bcos-boostssl/websocket/WsTools.h" @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2021 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 + * m_limitations under the License. + * + * @file WsTools.h + * @author: octopus + * @date 2021-10-10 + */ +#pragma once +#include "bcos-boostssl/websocket/WsConfig.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace boostssl +{ +namespace ws +{ +static std::string m_moduleName = "DEFAULT"; +class WsTools +{ +public: + static bool validIP(const std::string& _ip) + { + boost::system::error_code ec; + boost::asio::ip::address::from_string(_ip, ec); + if (ec) + { + return false; + } + return true; + } + + static bool validPort(uint16_t _port) { return _port > 1024; } + + static bool stringToEndPoint(const std::string& peer, NodeIPEndpoint& _endpoint); + + static void close(boost::asio::ip::tcp::socket& skt); + + static std::string moduleName() { return m_moduleName; } + static void setModuleName(std::string _moduleName) { m_moduleName = _moduleName; } +}; +} // namespace ws +} // namespace boostssl +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-boostssl/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-boostssl/test/CMakeLists.txt" new file mode 100644 index 00000000..1eaebd03 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/test/CMakeLists.txt" @@ -0,0 +1,26 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of bcos-rpc +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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(GLOB_RECURSE SOURCES "unittests/*.cpp" "unittests/*.h" "unittests/*.sol") + +# cmake settings +set(TEST_BINARY_NAME test-boostssl) + +add_executable(${TEST_BINARY_NAME} ${SOURCES}) +target_include_directories(${TEST_BINARY_NAME} PRIVATE .) +find_package(Boost REQUIRED unit_test_framework) +target_link_libraries(${TEST_BINARY_NAME} boostssl-websocket boostssl-httpserver bcos-utilities bcos-framework Boost::unit_test_framework) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-boostssl/test/exec/CMakeLists.txt" "b/BFPL\345\243\271/bcos-boostssl/test/exec/CMakeLists.txt" new file mode 100644 index 00000000..ee27eae1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/test/exec/CMakeLists.txt" @@ -0,0 +1,25 @@ +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") + +file(GLOB SRC_LIST "*.cpp") +file(GLOB HEADERS "*.h") + +# OpenSSL +find_package(TASSL REQUIRED MODULE) + +add_executable(http-server-sample http_server_sample.cpp) +target_link_libraries(http-server-sample PUBLIC ${BOOSTSSL_TARGET} bcos-utilities::bcos-utilities OpenSSL::SSL OpenSSL::Crypto) + +add_executable(echo-server-sample echo_server_sample.cpp) +target_link_libraries(echo-server-sample PUBLIC ${BOOSTSSL_TARGET} bcos-utilities::bcos-utilities OpenSSL::SSL OpenSSL::Crypto) + +add_executable(echo-client-sample echo_client_sample.cpp) +target_link_libraries(echo-client-sample PUBLIC ${BOOSTSSL_TARGET} bcos-utilities::bcos-utilities OpenSSL::SSL OpenSSL::Crypto) + +add_executable(msg-codec-perf msg_codec_perf.cpp) +target_link_libraries(msg-codec-perf PUBLIC ${BOOSTSSL_TARGET} bcos-utilities::bcos-utilities OpenSSL::SSL OpenSSL::Crypto) + +add_executable(boostssl-delay-perf boostssl_delay_perf.cpp) +target_link_libraries(boostssl-delay-perf PUBLIC ${BOOSTSSL_TARGET} bcos-utilities::bcos-utilities OpenSSL::SSL OpenSSL::Crypto) + +add_executable(boostssl-throughput-perf boostssl_throughput_perf.cpp) +target_link_libraries(boostssl-throughput-perf PUBLIC ${BOOSTSSL_TARGET} bcos-utilities::bcos-utilities OpenSSL::SSL OpenSSL::Crypto) diff --git "a/BFPL\345\243\271/bcos-boostssl/test/exec/boostssl_delay_perf.cpp" "b/BFPL\345\243\271/bcos-boostssl/test/exec/boostssl_delay_perf.cpp" new file mode 100644 index 00000000..13dc8a4d --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/test/exec/boostssl_delay_perf.cpp" @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2021 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 boostssl_delay_perf.cpp + * @author: octopus + * @date 2021-10-31 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; +using namespace bcos::boostssl::http; +using namespace bcos::boostssl::context; +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +void usage() +{ + std::cerr << "Usage: \n" + << " \t boostssl-delay-perf server \n " + << " \t boostssl-delay-perf client " + " \n" + << "Example:\n" + << " \t ./boostssl-delay-perf server 127.0.0.1 20200 true \n" + << " \t ./boostssl-delay-perf client 127.0.0.1 20200 true 100000 1024 \n"; + std::exit(0); +} + +void initLog(const std::string& _configPath = "./clog.ini") +{ + boost::property_tree::ptree pt; + try + { + boost::property_tree::read_ini(_configPath, pt); + } + catch (const std::exception& e) + { + try + { + std::string defaultPath = "conf/clog.ini"; + boost::property_tree::read_ini(defaultPath, pt); + } + catch (const std::exception& e) + { + std::cerr << "Not found available log config(./clog.ini or ./conf/clog.ini), use " + "the default configuration items" + << std::endl; + } + } + + auto logInitializer = new bcos::BoostLogInitializer(); + logInitializer->initLog(pt, bcos::FileLogger, "cpp_sdk_log"); +} + +const static int DELAY_PERF_MSGTYPE = 9999; + +void workAsClient( + std::string serverIp, uint16_t serverPort, bool disableSsl, uint64_t echoC, uint64_t msgSize) +{ + std::cerr << " ==> boostssl_delay_perf work as client. \n" + << " \t serverIp: " << serverIp << "\n" + << " \t serverPort: " << serverPort << "\n" + << " \t disableSsl: " << disableSsl << "\n" + << " \t echoC: " << echoC << "\n" + << " \t msgSize: " << msgSize << "\n\n\n"; + + auto config = std::make_shared(); + config->setModel(WsModel::Client); + + auto peers = std::make_shared(); + peers->insert(NodeIPEndpoint(serverIp, serverPort)); + config->setConnectPeers(peers); + + config->setThreadPoolSize(4); + config->setDisableSsl(disableSsl); + if (!config->disableSsl()) + { + auto contextConfig = std::make_shared(); + contextConfig->initConfig("./boostssl.ini"); + config->setContextConfig(contextConfig); + } + + auto wsService = std::make_shared("boostssl-delay-perf-client"); + auto wsInitializer = std::make_shared(); + + wsInitializer->setConfig(config); + wsInitializer->initWsService(wsService); + wsService->start(); + + + std::string strMsg(msgSize, 'a'); + auto msg = wsService->messageFactory()->buildMessage(); + msg->setPacketType(DELAY_PERF_MSGTYPE); + msg->setPayload(std::make_shared(strMsg.begin(), strMsg.end())); + // msg->setSeq(wsService->messageFactory()->newSeq()); + + uint64_t nSucC = 0; + uint64_t nFailedC = 0; + + uint64_t i = 0; + uint64_t _10Per = echoC / 10; + auto startPoint = std::chrono::high_resolution_clock::now(); + while (i++ < echoC) + { + std::promise p; + auto f = p.get_future(); + + if (i % _10Per == 0) + { + std::cerr << "\t...process: " << ((double)i / echoC) * 100 << "%" << std::endl; + } + + msg->setSeq(wsService->messageFactory()->newSeq()); + wsService->asyncSendMessage(msg, Options(-1), + [&p, &nFailedC, &nSucC](Error::Ptr _error, std::shared_ptr _msg, + std::shared_ptr _session) { + (void)_error; + (void)_session; + (void)_msg; + if (_error && _error->errorCode() != 0) + { + nFailedC++; + return; + } + p.set_value(true); + + nSucC++; + }); + f.get(); + } + auto endPoint = std::chrono::high_resolution_clock::now(); + + auto totalTime = + std::chrono::duration_cast(endPoint - startPoint).count(); + + std::cerr << std::endl << std::endl; + std::cerr << " ==> boostssl_delay_perf result: " << std::endl; + std::cerr << " \t total time(us): " << totalTime << std::endl; + std::cerr << " \t average time(us): " << ((double)totalTime / echoC) << std::endl; + std::cerr << " \t nSucC: " << nSucC << std::endl; + std::cerr << " \t nFailedC: " << nFailedC << std::endl; +} + +void workAsServer(std::string listenIp, uint16_t listenPort, bool disableSsl) +{ + std::cerr << " ==> boostssl_delay_perf work as server." << std::endl + << " \t listenIp: " << listenIp << std::endl + << " \t listenPort: " << listenPort << std::endl + << " \t disableSsl: " << disableSsl << "\n"; + + auto config = std::make_shared(); + config->setModel(WsModel::Server); + + config->setListenIP(listenIp); + config->setListenPort(listenPort); + config->setThreadPoolSize(4); + config->setDisableSsl(disableSsl); + if (!config->disableSsl()) + { + auto contextConfig = std::make_shared(); + contextConfig->initConfig("./boostssl.ini"); + config->setContextConfig(contextConfig); + } + + auto wsService = std::make_shared("boostssl-delay-perf-server"); + auto wsInitializer = std::make_shared(); + + wsInitializer->setConfig(config); + wsInitializer->initWsService(wsService); + + wsService->registerMsgHandler(DELAY_PERF_MSGTYPE, + [](std::shared_ptr _msg, std::shared_ptr _session) { + _session->asyncSendMessage(_msg); + }); + + wsService->start(); + + int i = 0; + while (true) + { + // std::cerr << " boostssl deplay perf server working ..." << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); + i++; + } +} + +int main(int argc, char** argv) +{ + if (argc < 3) + { + usage(); + } + + std::string workModel = argv[1]; + std::string host = argv[2]; + uint16_t port = atoi(argv[3]); + bool disableSsl = ("true" == std::string(argv[4])) ? true : false; + + initLog(); + + if (workModel == "server") + { + workAsServer(host, port, disableSsl); + } + else if (workModel == "client") + { + uint64_t echoCount = 100; + uint64_t msgSize = 1024; + if (argc > 5) + { + echoCount = std::stoull(std::string(argv[5])); + } + + if (argc > 6) + { + msgSize = std::stoull(std::string(argv[6])); + } + workAsClient(host, port, disableSsl, echoCount, msgSize); + } + else + { + usage(); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-boostssl/test/exec/boostssl_throughput_perf.cpp" "b/BFPL\345\243\271/bcos-boostssl/test/exec/boostssl_throughput_perf.cpp" new file mode 100644 index 00000000..d4e4ccbf --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/test/exec/boostssl_throughput_perf.cpp" @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2021 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 boostssl_throughput_perf.cpp + * @author: octopus + * @date 2022-04-08 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; +using namespace bcos::boostssl::http; +using namespace bcos::boostssl::context; +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +void usage() +{ + std::cerr << "Usage: \n" + << " \t boostssl-throughput-perf server \n " + << " \t boostssl-throughput-perf client " + " \n" + << "Example:\n" + << " \t ./boostssl-throughput-perf server 127.0.0.1 20200 true 16\n" + << " \t ./boostssl-throughput-perf client 127.0.0.1 20200 true 16 1024 1000\n"; + std::exit(0); +} + +void initLog(const std::string& _configPath = "./clog.ini") +{ + boost::property_tree::ptree pt; + try + { + boost::property_tree::read_ini(_configPath, pt); + } + catch (const std::exception& e) + { + try + { + std::string defaultPath = "conf/clog.ini"; + boost::property_tree::read_ini(defaultPath, pt); + } + catch (const std::exception& e) + { + std::cerr << "Not found available log config(./clog.ini or ./conf/clog.ini), use " + "the default configuration items" + << std::endl; + } + } + + auto logInitializer = new bcos::BoostLogInitializer(); + logInitializer->initLog(pt, bcos::FileLogger, "cpp_sdk_log"); +} + +const static int DELAY_PERF_MSGTYPE = 9999; + +void workAsClient(std::string serverIp, uint16_t serverPort, bool disableSsl, uint64_t sendRate, + uint64_t msgSize, uint32_t threadCount) +{ + std::cerr << " boostssl_throughput_perf work as client." << std::endl + << " \t serverIp: " << serverIp << std::endl + << " \t serverPort: " << serverPort << std::endl + << " \t disableSsl: " << disableSsl << std::endl + << " \t sendRate: " << sendRate << std::endl + << " \t msgSize: " << msgSize << std::endl + << " \t threadCount: " << threadCount << "\n\n\n"; + + auto config = std::make_shared(); + config->setModel(WsModel::Client); + + auto peers = std::make_shared(); + peers->insert(NodeIPEndpoint(serverIp, serverPort)); + + config->setConnectPeers(peers); + + config->setThreadPoolSize(threadCount); + config->setDisableSsl(disableSsl); + if (!config->disableSsl()) + { + auto contextConfig = std::make_shared(); + contextConfig->initConfig("./boostssl.ini"); + config->setContextConfig(contextConfig); + } + + auto wsService = std::make_shared("boostssl-th-perf-client"); + auto wsInitializer = std::make_shared(); + + wsInitializer->setConfig(config); + wsInitializer->initWsService(wsService); + wsService->start(); + + std::string strMsg(msgSize, 'a'); + auto msg = wsService->messageFactory()->buildMessage(); + msg->setPacketType(DELAY_PERF_MSGTYPE); + msg->setPayload(std::make_shared(strMsg.begin(), strMsg.end())); + + std::atomic nSucC = 0; + std::atomic nFailedC = 0; + std::atomic nLastSucC = 0; + std::atomic nLastFailedC = 0; + + std::atomic nThisSendCount = 0; + std::atomic nLastSendCount = 0; + + uint64_t sendMsgCountPerMS = sendRate / 1000; + sendMsgCountPerMS = sendMsgCountPerMS > 0 ? sendMsgCountPerMS : 1; + // auto startPoint = std::chrono::high_resolution_clock::now(); + + // report thread; + auto reportThread = std::make_shared( + [&wsService, &nLastSendCount, &nSucC, &nFailedC, &nLastSucC, &nLastFailedC]() { + uint32_t nSleepMS = 1000; + while (true) + { + int64_t nQueueSize = -1; + auto ss = wsService->sessions(); + if (!ss.empty()) + { + nQueueSize = ss[0]->msgQueueSize(); + } + + std::cerr << " boostssl throughput perf working as client: " << std::endl; + std::cerr << " \tnQueueSize: " << nQueueSize << ", nSucC: " << nSucC + << ", nFailedC: " << nFailedC << ", nLastSucC: " << nLastSucC + << ", nLastFailedC: " << nLastFailedC + << ", nLastSendCount: " << nLastSendCount << std::endl; + + nLastFailedC = 0; + nLastSucC = 0; + nLastSendCount = 0; + std::this_thread::sleep_for(std::chrono::milliseconds(nSleepMS)); + } + }); + reportThread->detach(); + + + while (true) + { + nThisSendCount++; + nLastSendCount++; + msg->setSeq(wsService->messageFactory()->newSeq()); + wsService->asyncSendMessage(msg, Options(-1), + [&nFailedC, &nSucC, &nLastFailedC, &nLastSucC](Error::Ptr _error, + std::shared_ptr _msg, std::shared_ptr _session) { + (void)_error; + (void)_session; + (void)_msg; + if (_error && _error->errorCode() != 0) + { + nFailedC++; + nLastFailedC++; + return; + } + + nLastSucC++; + nSucC++; + }); + + if (nThisSendCount > sendMsgCountPerMS) + { + nThisSendCount = 0; + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + } +} + +void workAsServer(std::string listenIp, uint16_t listenPort, bool disableSsl, uint32_t threadCount) +{ + std::cerr << " boostssl_throughput_perf work as server." << std::endl + << " \t listenIp: " << listenIp << std::endl + << " \t listenPort: " << listenPort << std::endl + << " \t disableSsl: " << disableSsl << std::endl + << " \t threadCount: " << threadCount << std::endl; + + auto config = std::make_shared(); + config->setModel(WsModel::Server); + + config->setListenIP(listenIp); + config->setListenPort(listenPort); + config->setThreadPoolSize(threadCount); + config->setDisableSsl(disableSsl); + if (!config->disableSsl()) + { + auto contextConfig = std::make_shared(); + contextConfig->initConfig("./boostssl.ini"); + config->setContextConfig(contextConfig); + } + + auto wsService = std::make_shared("boostssl-th-perf-server"); + auto wsInitializer = std::make_shared(); + + wsInitializer->setConfig(config); + wsInitializer->initWsService(wsService); + + std::atomic totalRecvDataSize = {0}; + std::atomic lastRecvDataCount = {0}; + std::atomic lastSecTotalRecvDataSize = {0}; + wsService->registerMsgHandler(DELAY_PERF_MSGTYPE, + [&totalRecvDataSize, &lastSecTotalRecvDataSize, &lastRecvDataCount]( + std::shared_ptr _msg, std::shared_ptr _session) { + totalRecvDataSize += _msg->payload()->size(); + lastSecTotalRecvDataSize += _msg->payload()->size(); + lastRecvDataCount++; + _session->asyncSendMessage(_msg); + }); + + wsService->start(); + + uint32_t nSleepMS = 1000; + while (true) + { + std::cerr << " boostssl throughput perf working as server: " << std::endl; + std::cerr << " \t ClientCount: " << wsService->sessions().size() + << ", TotalRecvDataSize(Bytes): " << totalRecvDataSize + << ", lastRecvDataCount: " << lastRecvDataCount + << ", LastRecvDataSize(Bytes): " << lastSecTotalRecvDataSize + << ", LastRecvDataRate(MBit/s): " + << (((double)lastSecTotalRecvDataSize * 8 * 1000) / nSleepMS / (1024 * 1024)) + << std::endl; + lastSecTotalRecvDataSize = 0; + lastRecvDataCount = 0; + std::this_thread::sleep_for(std::chrono::milliseconds(nSleepMS)); + } +} + +int main(int argc, char** argv) +{ + if (argc < 5) + { + usage(); + } + + std::string workModel = argv[1]; + std::string host = argv[2]; + uint16_t port = atoi(argv[3]); + bool disableSsl = ("true" == std::string(argv[4])) ? true : false; + uint32_t threadCount = atoi(argv[5]); + + initLog(); + + if (workModel == "server") + { + workAsServer(host, port, disableSsl, threadCount); + } + else if (workModel == "client") + { + uint64_t sendRate = 1000; + uint64_t msgSize = 1024; + + if (argc > 6) + { + msgSize = std::stoull(std::string(argv[6])); + } + + if (argc > 7) + { + sendRate = std::stoull(std::string(argv[7])); + } + + workAsClient(host, port, disableSsl, sendRate, msgSize, threadCount); + } + else + { + usage(); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-boostssl/test/exec/echo_client_sample.cpp" "b/BFPL\345\243\271/bcos-boostssl/test/exec/echo_client_sample.cpp" new file mode 100644 index 00000000..f1245b35 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/test/exec/echo_client_sample.cpp" @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2021 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 echo_client_sample.cpp + * @author: octopus + * @date 2021-10-31 + */ + +#include "bcos-boostssl/websocket/WsInitializer.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; +using namespace bcos::boostssl::http; +using namespace bcos::boostssl::context; + + +#define TEST_LOG(LEVEL, module_name) BCOS_LOG(LEVEL) << LOG_BADGE(module_name) << "[WS][SERVICE]" + +void usage() +{ + std::cerr << "Usage: echo-client-sample qps(MBit/s) payloadSize(KBytes, " + "default is 1MBytes)\n" + << "Example:\n" + << " ./echo-client-sample 127.0.0.1 20200 true 1024 1024\n"; + std::exit(0); +} + +void sendMessage(std::shared_ptr _msg, std::shared_ptr _wsService, + std::shared_ptr _rateLimiter) +{ + while (true) + { + _rateLimiter->acquire(1, true); + auto seq = _wsService->messageFactory()->newSeq(); + _msg->setSeq(seq); + auto startT = utcTime(); + auto msgSize = _msg->payload()->size(); + _wsService->asyncSendMessage(_msg, Options(-1), + [msgSize, startT](Error ::Ptr _error, std::shared_ptr, + std::shared_ptr _session) { + (void)_session; + if (_error && _error->errorCode() != 0) + { + TEST_LOG(WARNING, "TEST_CLIENT_MODULE") + << LOG_BADGE(" [Main] ===>>>> ") << LOG_DESC("callback response error") + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()); + return; + } + BCOS_LOG(INFO) << LOG_DESC("receiveResponse, timecost:") << (utcTime() - startT) + << LOG_KV("msgSize", msgSize); + }); + } +} + +int main(int argc, char** argv) +{ + if (argc < 5) + { + usage(); + } + + std::string host = argv[1]; + uint16_t port = atoi(argv[2]); + + std::string disableSsl = argv[3]; + + uint64_t qps = (atol(argv[4])) * 1024 * 1024; + // default payLoadSize is 1MB + uint64_t payLoadSize = 1024 * 1024; + if (argc > 5) + { + payLoadSize = (atol(argv[5]) * 1024); + } + std::cout << "### payLoad:" << payLoadSize << std::endl; + std::cout << "### qps: " << qps << std::endl; + int64_t packetQPS = (qps) / (payLoadSize * 8); + std::cout << "### packetQPS: " << packetQPS << std::endl; + + auto logInitializer = std::make_shared(); + std::string configFilePath = "config.ini"; + boost::property_tree::ptree pt; + boost::property_tree::read_ini(configFilePath, pt); + logInitializer->initLog(pt); + + std::string test_module_name = "testClient"; + TEST_LOG(INFO, test_module_name) << LOG_DESC("echo-client-sample") << LOG_KV("ip", host) + << LOG_KV("port", port) << LOG_KV("disableSsl", disableSsl); + + auto config = std::make_shared(); + config->setModel(WsModel::Client); + + NodeIPEndpoint endpoint = NodeIPEndpoint(host, port); + + auto peers = std::make_shared(); + peers->insert(endpoint); + config->setConnectPeers(peers); + + config->setThreadPoolSize(8); + config->setMaxMsgSize(100 * 1024 * 1024); + config->setDisableSsl(0 == disableSsl.compare("true")); + if (!config->disableSsl()) + { + auto contextConfig = std::make_shared(); + contextConfig->initConfig("./boostssl.ini"); + config->setContextConfig(contextConfig); + } + config->setModuleName("TEST_CLIENT"); + + auto wsService = std::make_shared(config->moduleName()); + auto wsInitializer = std::make_shared(); + + auto sessionFactory = std::make_shared(); + wsInitializer->setSessionFactory(sessionFactory); + + wsInitializer->setConfig(config); + wsInitializer->initWsService(wsService); + + wsService->start(); + + // construct message + auto msg = std::dynamic_pointer_cast(wsService->messageFactory()->buildMessage()); + msg->setPacketType(999); + std::string randStr(payLoadSize, 'a'); + msg->setPayload(std::make_shared(randStr.begin(), randStr.end())); + auto rateLimiter = std::make_shared(packetQPS); + sendMessage(msg, wsService, rateLimiter); + return EXIT_SUCCESS; +} diff --git "a/BFPL\345\243\271/bcos-boostssl/test/exec/echo_server_sample.cpp" "b/BFPL\345\243\271/bcos-boostssl/test/exec/echo_server_sample.cpp" new file mode 100644 index 00000000..fa52e695 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/test/exec/echo_server_sample.cpp" @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2021 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 echo_server_client.cpp + * @author: octopus + * @date 2021-10-31 + */ + +#include "bcos-boostssl/websocket/WsInitializer.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; +using namespace bcos::boostssl::http; +using namespace bcos::boostssl::context; +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +std::string MODULE_NAME = "DEFAULT"; + +#define TEST_SERVER_LOG(LEVEL, MODULE_NAME) \ + BCOS_LOG(LEVEL) << LOG_BADGE(MODULE_NAME) << "[WS][SERVICE]" + +void usage() +{ + std::cerr << "Usage: echo-server-sample \n" + << "Example:\n" + << " ./echo-server-sample 127.0.0.1 20200 true\n" + << " ./echo-server-sample 127.0.0.1 20200 false\n"; + std::exit(0); +} + + +int main(int argc, char** argv) +{ + if (argc < 3) + { + usage(); + } + + std::string host = argv[1]; + uint16_t port = atoi(argv[2]); + + std::string disableSsl = "true"; + + if (argc > 3) + { + disableSsl = argv[3]; + } + auto logInitializer = std::make_shared(); + std::string configFilePath = "config.ini"; + boost::property_tree::ptree pt; + boost::property_tree::read_ini(configFilePath, pt); + logInitializer->initLog(pt); + MODULE_NAME = "TEST_SERVER_MODULE"; + TEST_SERVER_LOG(INFO, MODULE_NAME) << LOG_DESC("echo-server-sample") << LOG_KV("ip", host) + << LOG_KV("port", port) << LOG_KV("disableSsl", disableSsl); + + auto config = std::make_shared(); + config->setModel(WsModel::Server); + + config->setListenIP(host); + config->setListenPort(port); + config->setThreadPoolSize(8); + config->setMaxMsgSize(100 * 1024 * 1024); + config->setDisableSsl(0 == disableSsl.compare("true")); + if (!config->disableSsl()) + { + auto contextConfig = std::make_shared(); + contextConfig->initConfig("./boostssl.ini"); + config->setContextConfig(contextConfig); + } + config->setModuleName("TEST_SERVER"); + + auto wsService = std::make_shared(config->moduleName()); + auto wsInitializer = std::make_shared(); + + auto sessionFactory = std::make_shared(); + wsInitializer->setSessionFactory(sessionFactory); + + wsInitializer->setConfig(config); + wsInitializer->initWsService(wsService); + + if (!wsService->registerMsgHandler(999, + [](std::shared_ptr _msg, std::shared_ptr _session) { + _msg->setRespPacket(); + + _session->asyncSendMessage(_msg); + })) + { + BCOS_LOG(WARNING) << "registerMsgHandler failed"; + return EXIT_SUCCESS; + } + + auto handler = wsService->getMsgHandler(999); + if (!handler) + { + BCOS_LOG(WARNING) << "msg handler not found"; + return EXIT_SUCCESS; + } + + wsService->start(); + + int i = 0; + while (true) + { + // TEST_SERVER_LOG(INFO, MODULE_NAME) << LOG_BADGE(" [Main] ===>>>> "); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + i++; + } + + return EXIT_SUCCESS; +} diff --git "a/BFPL\345\243\271/bcos-boostssl/test/exec/http_server_sample.cpp" "b/BFPL\345\243\271/bcos-boostssl/test/exec/http_server_sample.cpp" new file mode 100644 index 00000000..8248ce00 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/test/exec/http_server_sample.cpp" @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2021 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 http_server.cpp + * @author: octopus + * @date 2021-10-31 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; +using namespace bcos::boostssl::http; +using namespace bcos::boostssl::context; +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +void usage() +{ + std::cerr << "Usage: http-server-sample \n" + << "Example:\n" + << " ./http-server-sample 127.0.0.1 20200 true\n" + << " ./http-server-sample 127.0.0.1 20200 false\n"; + std::exit(0); +} + + +int main(int argc, char** argv) +{ + if (argc < 3) + { + usage(); + } + + std::string host = argv[1]; + uint16_t port = atoi(argv[2]); + + std::string disableSsl = "true"; + + if (argc > 3) + { + disableSsl = argv[3]; + } + + BCOS_LOG(INFO) << LOG_DESC("http-server-sample") << LOG_KV("ip", host) << LOG_KV("port", port) + << LOG_KV("disableSsl", disableSsl); + + + auto config = std::make_shared(); + config->setModel(WsModel::Server); + + config->setListenIP(host); + config->setListenPort(port); + config->setThreadPoolSize(4); + config->setDisableSsl(0 == disableSsl.compare("true")); + if (!config->disableSsl()) + { + auto contextConfig = std::make_shared(); + contextConfig->initConfig("./boostssl.ini"); + config->setContextConfig(contextConfig); + } + + auto wsService = std::make_shared("TEST"); + auto wsInitializer = std::make_shared(); + + wsInitializer->setConfig(config); + wsInitializer->initWsService(wsService); + + auto server = wsService->httpServer(); + server->setHttpReqHandler( + [](const std::string& _req, std::function _callback) { + BCOS_LOG(INFO) << LOG_BADGE(" [Main] ===>>>> ") << LOG_KV("request", _req); + _callback(_req); + }); + wsService->start(); + + int i = 0; + while (true) + { + BCOS_LOG(INFO) << LOG_BADGE(" [Main] ===>>>> "); + std::this_thread::sleep_for(std::chrono::milliseconds(5000)); + i++; + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-boostssl/test/exec/msg_codec_perf.cpp" "b/BFPL\345\243\271/bcos-boostssl/test/exec/msg_codec_perf.cpp" new file mode 100644 index 00000000..8def658f --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/test/exec/msg_codec_perf.cpp" @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2021 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 msg_codec_perf.cpp + * @author: octopus + * @date 2022-03-24 + */ + +#include "bcos-boostssl/websocket/WsInitializer.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; +using namespace bcos::boostssl::http; +using namespace bcos::boostssl::context; +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +void usage() +{ + std::cerr << "Usage: msg_codec_test payload_length\n" + << "Example:\n" + << " ./msg_codec_test 1024\n"; + std::exit(0); +} + + +int main(int argc, char** argv) +{ + if (argc < 2) + { + usage(); + } + + uint16_t payloadLength = atoi(argv[1]); + + BCOS_LOG(INFO) << LOG_DESC("Msg Codec Test") << LOG_KV("payload length", payloadLength); + + std::string str(payloadLength, 'a'); + auto messageFactory = std::make_shared(); + // construct message + auto msg = std::dynamic_pointer_cast(messageFactory->buildMessage()); + + msg->setPayload(std::make_shared(str.begin(), str.end())); + + auto startPoint = std::chrono::high_resolution_clock::now(); + auto lastReport = std::chrono::high_resolution_clock::now(); + int64_t lastEncodeC = 0; + auto buffer = std::make_shared(); + while (true) + { + msg->encode(*buffer); + lastEncodeC++; + + auto now = std::chrono::high_resolution_clock::now(); + auto lastReportMS = + std::chrono::duration_cast(now - lastReport).count(); + auto totalReportMS = + std::chrono::duration_cast(now - startPoint).count(); + + if (lastReportMS >= 1000) + { + BCOS_LOG(INFO) << LOG_BADGE(" [Main] ===>>>> ") << LOG_KV("interval(ms)", lastReportMS) + << LOG_KV("payload", payloadLength) + << LOG_KV("encodeCount", lastEncodeC); + lastEncodeC = 0; + lastReport = std::chrono::high_resolution_clock::now(); + } + + if (totalReportMS >= 10000) + { + break; + } + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-boostssl/test/unittests/websocket/WsConfigTest.cpp" "b/BFPL\345\243\271/bcos-boostssl/test/unittests/websocket/WsConfigTest.cpp" new file mode 100644 index 00000000..8476ff59 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/test/unittests/websocket/WsConfigTest.cpp" @@ -0,0 +1,91 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for WsConfig + * @file WsConfigTest.cpp + * @author: octopus + * @date 2021-10-04 + */ + +#define BOOST_TEST_MAIN + +#include +#include + +#include +#include +#include + +using namespace bcos; + +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; + +BOOST_AUTO_TEST_SUITE(WsToolsTest) + +BOOST_AUTO_TEST_CASE(test_WsConfigTest) +{ + { + auto config = std::make_shared(); + config->setModel(WsModel::Mixed); + BOOST_CHECK_EQUAL(config->listenIP(), std::string()); + BOOST_CHECK_EQUAL(config->listenPort(), 0); + BOOST_CHECK_EQUAL(config->asClient(), true); + BOOST_CHECK_EQUAL(config->asServer(), true); + + auto peers = std::make_shared(); + config->setConnectPeers(peers); + BOOST_CHECK_EQUAL(config->connectPeers()->size(), 0); + } + + { + auto config = std::make_shared(); + auto model = WsModel::Server; + auto boostsslConfig = std::string("boostssl.ini"); + auto listenIP = std::string("127.0.0.1"); + auto listenPort = 12345; + auto threadPoolSize = 123; + + config->setModel(model); + config->setListenIP(listenIP); + config->setListenPort(listenPort); + config->setThreadPoolSize(threadPoolSize); + + BOOST_CHECK_EQUAL(config->listenIP(), listenIP); + BOOST_CHECK_EQUAL(config->listenPort(), listenPort); + BOOST_CHECK_EQUAL(config->asClient(), false); + BOOST_CHECK_EQUAL(config->asServer(), true); + + auto peers = std::make_shared(); + config->setConnectPeers(peers); + BOOST_CHECK_EQUAL(config->connectPeers()->size(), 0); + } +} + +BOOST_AUTO_TEST_CASE(test_WsToolsTest) +{ + BOOST_CHECK_EQUAL(WsTools::validIP("0.0.0.0"), true); + BOOST_CHECK_EQUAL(WsTools::validIP("123"), false); + BOOST_CHECK_EQUAL(WsTools::validIP("127.0.0.1"), true); + BOOST_CHECK_EQUAL(WsTools::validIP("2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b"), true); + BOOST_CHECK_EQUAL(WsTools::validIP("0:0:0:0:0:0:0:1"), true); + BOOST_CHECK_EQUAL(WsTools::validIP("::1"), true); + + BOOST_CHECK_EQUAL(WsTools::validPort(1111), true); + BOOST_CHECK_EQUAL(WsTools::validPort(10), false); + BOOST_CHECK_EQUAL(WsTools::validPort(65535), true); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git "a/BFPL\345\243\271/bcos-boostssl/test/unittests/websocket/WsConnectorTest.cpp" "b/BFPL\345\243\271/bcos-boostssl/test/unittests/websocket/WsConnectorTest.cpp" new file mode 100644 index 00000000..8ad746b9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/test/unittests/websocket/WsConnectorTest.cpp" @@ -0,0 +1,54 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for WsConnector + * @file WsConnectorTest.cpp + * @author: octopus + * @date 2021-10-04 + */ + +#include + +#include +#include +#include + +using namespace bcos; + +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; + +BOOST_AUTO_TEST_SUITE(WsConnectorTest) + +BOOST_AUTO_TEST_CASE(test_WsConnectorTest) +{ + auto connector = std::make_shared(nullptr); + { + std::string host = "0.0.0.0"; + uint16_t port = 1111; + + std::string endpoint = host + ":" + std::to_string(port); + auto r = connector->insertPendingConns(endpoint); + BOOST_CHECK(r); + r = connector->insertPendingConns(endpoint); + BOOST_CHECK(!r); + r = connector->erasePendingConns(endpoint); + BOOST_CHECK(r); + r = connector->insertPendingConns(endpoint); + BOOST_CHECK(r); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git "a/BFPL\345\243\271/bcos-boostssl/test/unittests/websocket/WsMessageTest.cpp" "b/BFPL\345\243\271/bcos-boostssl/test/unittests/websocket/WsMessageTest.cpp" new file mode 100644 index 00000000..bfe434d7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-boostssl/test/unittests/websocket/WsMessageTest.cpp" @@ -0,0 +1,127 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for WsMessage + * @file WsMessageTest.cpp + * @author: octopus + * @date 2021-07-12 + */ + +#include + +#include + +using namespace bcos; + +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; + +BOOST_AUTO_TEST_SUITE(WsMessageTest) + +BOOST_AUTO_TEST_CASE(test_WsMessage) +{ + auto factory = std::make_shared(); + auto msg = factory->buildMessage(); + auto buffer = std::make_shared(); + auto r = msg->encode(*buffer); + auto seq = msg->seq(); + + BOOST_CHECK(r); + BOOST_CHECK_EQUAL(buffer->size(), WsMessage::MESSAGE_MIN_LENGTH); + + { + auto decodeMsg = factory->buildMessage(); + auto size = decodeMsg->decode(bytesConstRef(buffer->data(), buffer->size())); + BOOST_CHECK(size > 0); + BOOST_CHECK_EQUAL(decodeMsg->payload()->size(), 0); + auto decodeSeq = msg->seq(); + BOOST_CHECK_EQUAL(seq, decodeSeq); + } +} + + +BOOST_AUTO_TEST_CASE(test_buildMessage) +{ + { + int16_t status = 111; + uint16_t type = 222; + std::string data = "HelloWorld."; + auto factory = std::make_shared(); + auto msg = factory->buildMessage(); + auto wsMessage = std::dynamic_pointer_cast(msg); + wsMessage->setStatus(status); + wsMessage->setPacketType(type); + wsMessage->setPayload(std::make_shared(data.begin(), data.end())); + + auto buffer = std::make_shared(); + auto r = msg->encode(*buffer); + auto seq = msg->seq(); + + BOOST_CHECK(r); + BOOST_CHECK_EQUAL(buffer->size(), WsMessage::MESSAGE_MIN_LENGTH + data.length()); + + auto decodeMsg = factory->buildMessage(); + auto size = decodeMsg->decode(bytesConstRef(buffer->data(), buffer->size())); + BOOST_CHECK(size > 0); + auto decodedWsMessge = std::dynamic_pointer_cast(decodeMsg); + BOOST_CHECK_EQUAL(decodedWsMessge->status(), status); + BOOST_CHECK_EQUAL(decodeMsg->packetType(), type); + BOOST_CHECK_EQUAL(decodeMsg->payload()->size(), data.size()); + auto decodeSeq = msg->seq(); + BOOST_CHECK_EQUAL(seq, decodeSeq); + BOOST_CHECK_EQUAL( + data, std::string(decodeMsg->payload()->begin(), decodeMsg->payload()->end())); + } + + { + int16_t status = 222; + uint16_t type = 111; + std::string data = "HelloWorld."; + auto factory = std::make_shared(); + auto msg = factory->buildMessage(type, std::make_shared(data.begin(), data.end())); + auto wsMessage = std::dynamic_pointer_cast(msg); + wsMessage->setStatus(status); + wsMessage->setPacketType(type); + + auto buffer = std::make_shared(); + auto r = msg->encode(*buffer); + auto seq = msg->seq(); + + BOOST_CHECK(r); + BOOST_CHECK_EQUAL(buffer->size(), WsMessage::MESSAGE_MIN_LENGTH + data.length()); + + auto decodeMsg = factory->buildMessage(); + auto size = decodeMsg->decode(bytesConstRef(buffer->data(), buffer->size())); + BOOST_CHECK(size > 0); + auto decodedWsMessge = std::dynamic_pointer_cast(decodeMsg); + BOOST_CHECK_EQUAL(decodedWsMessge->status(), status); + BOOST_CHECK_EQUAL(decodeMsg->packetType(), type); + BOOST_CHECK_EQUAL(decodeMsg->payload()->size(), data.size()); + auto decodeSeq = msg->seq(); + BOOST_CHECK_EQUAL(seq, decodeSeq); + BOOST_CHECK_EQUAL( + data, std::string(decodeMsg->payload()->begin(), decodeMsg->payload()->end())); + } + auto factory = std::make_shared(); + auto msg = factory->buildMessage(); + auto wsMessage = std::dynamic_pointer_cast(msg); + std::string invalidMessage = + "GET / HTTP/1.1\r\nHost: 127.0.0.1:20200\r\nUpgrade: websocket\r\nConnection: " + "upgrade\r\nSec-WebSocket-Key: lkBb9dFFu4tuMNJyXAWIfQ==\r\nSec-WebSocket-Version: " + "13\r\n\r\n"; + auto invalidMsgBytes = bcos::bytes(invalidMessage.begin(), invalidMessage.end()); + BOOST_CHECK_THROW(wsMessage->decode(ref(invalidMsgBytes)), std::out_of_range); +} +BOOST_AUTO_TEST_SUITE_END() diff --git "a/BFPL\345\243\271/bcos-codec/CMakeLists.txt" "b/BFPL\345\243\271/bcos-codec/CMakeLists.txt" new file mode 100644 index 00000000..b2c78659 --- /dev/null +++ "b/BFPL\345\243\271/bcos-codec/CMakeLists.txt" @@ -0,0 +1,54 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for bcos-codec +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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. +# ------------------------------------------------------------------------------ + +cmake_minimum_required(VERSION 3.12) +set(CMAKE_OSX_DEPLOYMENT_TARGET "11.3" CACHE STRING "Minimum OS X deployment version") +include(Version) +project(bcos-codec VERSION ${VERSION}) + +file(GLOB SRC_LIST "*.cpp") +file(GLOB HEADERS "*.h") + +aux_source_directory(bcos-codec/abi SRC_LIST) +aux_source_directory(bcos-codec/scale SRC_LIST) + +find_package(Microsoft.GSL CONFIG REQUIRED) + +add_library(${CODEC_TARGET} ${SRC_LIST} ${HEADERS}) +target_include_directories(${CODEC_TARGET} PUBLIC + $ + $ + $ +) +target_link_libraries(${CODEC_TARGET} PUBLIC bcos-crypto Microsoft.GSL::GSL) + +if (TESTS) + enable_testing() + set(CTEST_OUTPUT_ON_FAILURE True) + add_subdirectory(test) +endif() + +# for code coverage +if (COVERAGE) + include(Coverage) + config_coverage("cov" "'/usr*' '${CMAKE_CURRENT_SOURCE_DIR}/bcos-cmake-scripts*' '${CMAKE_CURRENT_SOURCE_DIR}/test/bcos-test*'") +endif () + +include(GNUInstallDirs) +install(TARGETS ${CODEC_TARGET} EXPORT fiscobcosTargets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") +install(DIRECTORY "bcos-codec" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" FILES_MATCHING PATTERN "*.h") \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-codec/bcos-codec/abi/ContractABICodec.cpp" "b/BFPL\345\243\271/bcos-codec/bcos-codec/abi/ContractABICodec.cpp" new file mode 100644 index 00000000..fe5b7df1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-codec/bcos-codec/abi/ContractABICodec.cpp" @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2021 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. + * + * @brief Contract ABI serialize and deserialize tool. + * @author: octopuswang + * @date: 2019-04-01 + */ + +#include "ContractABICodec.h" + +using namespace std; +using namespace bcos; +using namespace bcos::codec::abi; + +const int ContractABICodec::MAX_BYTE_LENGTH; + +bool ContractABICodec::abiOutByFuncSelector( + bytesConstRef _data, const std::vector& _allTypes, std::vector& _out) +{ + data = _data; + offset = 0; + + for (const std::string& type : _allTypes) + { + if ("int" == type || "int256" == type) + { + s256 s; + deserialize(s, offset); + _out.push_back(toString(s)); + } + else if ("uint" == type || "uint256" == type) + { + u256 u; + deserialize(u, offset); + _out.push_back(toString(u)); + } + else if ("address" == type) + { + Address addr; + deserialize(addr, offset); + _out.push_back(addr.hex()); + } + else if ("string" == type) + { + u256 stringOffset; + deserialize(stringOffset, offset); + + std::string str; + deserialize(str, static_cast(stringOffset)); + _out.push_back(str); + } + else + { // unsupport type + return false; + } + + offset += MAX_BYTE_LENGTH; + } + + return true; +} + +// unsigned integer type uint256. +bytes ContractABICodec::serialise(const u256& _in) +{ + return h256(_in).asBytes(); +} + +// two’s complement signed integer type int256. +bytes ContractABICodec::serialise(const s256& _in) +{ + return h256(_in.convert_to()).asBytes(); +} + +// equivalent to uint8 restricted to the values 0 and 1. For computing the function selector, +// bool is used +bytes ContractABICodec::serialise(const bool& _in) +{ + return h256(u256(_in ? 1 : 0)).asBytes(); +} + +// equivalent to uint160, except for the assumed interpretation and language typing. For +// computing the function selector, address is used. +// bool is used. +bytes ContractABICodec::serialise(const Address& _in) +{ + return bytes(12, 0) + _in.asBytes(); +} + +// binary type of 32 bytes +bytes ContractABICodec::serialise(const string32& _in) +{ + bytes ret(32, 0); + bytesConstRef((byte const*)_in.data(), 32).populate(bytesRef(&ret)); + return ret; +} + +bytes ContractABICodec::serialise(const bytes& _in) +{ + bytes ret; + ret = h256(u256(_in.size())).asBytes(); + ret.resize(ret.size() + (_in.size() + 31) / MAX_BYTE_LENGTH * MAX_BYTE_LENGTH); + bytesConstRef(&_in).populate(bytesRef(&ret).getCroppedData(32)); + return ret; +} + +// dynamic sized unicode string assumed to be UTF-8 encoded. +bytes ContractABICodec::serialise(const std::string& _in) +{ + bytes ret; + ret = h256(u256(_in.size())).asBytes(); + ret.resize(ret.size() + (_in.size() + 31) / MAX_BYTE_LENGTH * MAX_BYTE_LENGTH); + bytesConstRef(&_in).populate(bytesRef(&ret).getCroppedData(32)); + return ret; +} + +void ContractABICodec::deserialize(s256& out, std::size_t _offset) +{ + validOffset(_offset + MAX_BYTE_LENGTH - 1); + + u256 u = fromBigEndian(data.getCroppedData(_offset, MAX_BYTE_LENGTH)); + if (u > u256("0x8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) + { + auto r = + (bcos::u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - u) + + 1; + out = s256("-" + r.str()); + } + else + { + out = u.convert_to(); + } +} + +void ContractABICodec::deserialize(u256& _out, std::size_t _offset) +{ + validOffset(_offset + MAX_BYTE_LENGTH - 1); + + _out = fromBigEndian(data.getCroppedData(_offset, MAX_BYTE_LENGTH)); +} + +void ContractABICodec::deserialize(bool& _out, std::size_t _offset) +{ + validOffset(_offset + MAX_BYTE_LENGTH - 1); + + u256 ret = fromBigEndian(data.getCroppedData(_offset, MAX_BYTE_LENGTH)); + _out = ret > 0 ? true : false; +} + +void ContractABICodec::deserialize(Address& _out, std::size_t _offset) +{ + validOffset(_offset + MAX_BYTE_LENGTH - 1); + + data.getCroppedData(_offset + MAX_BYTE_LENGTH - 20, 20).populate(_out.ref()); +} + +void ContractABICodec::deserialize(string32& _out, std::size_t _offset) +{ + validOffset(_offset + MAX_BYTE_LENGTH - 1); + + data.getCroppedData(_offset, MAX_BYTE_LENGTH) + .populate(bytesRef((byte*)_out.data(), MAX_BYTE_LENGTH)); +} + +void ContractABICodec::deserialize(std::string& _out, std::size_t _offset) +{ + validOffset(_offset + MAX_BYTE_LENGTH - 1); + + u256 len = fromBigEndian(data.getCroppedData(_offset, MAX_BYTE_LENGTH)); + validOffset(_offset + MAX_BYTE_LENGTH + (std::size_t)len - 1); + auto result = data.getCroppedData(_offset + MAX_BYTE_LENGTH, static_cast(len)); + _out.assign((const char*)result.data(), result.size()); +} + +void ContractABICodec::deserialize(bytes& _out, std::size_t _offset) +{ + validOffset(_offset + MAX_BYTE_LENGTH - 1); + + u256 len = fromBigEndian(data.getCroppedData(_offset, MAX_BYTE_LENGTH)); + validOffset(_offset + MAX_BYTE_LENGTH + (std::size_t)len - 1); + _out = data.getCroppedData(_offset + MAX_BYTE_LENGTH, static_cast(len)).toBytes(); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-codec/bcos-codec/abi/ContractABICodec.h" "b/BFPL\345\243\271/bcos-codec/bcos-codec/abi/ContractABICodec.h" new file mode 100644 index 00000000..ace90997 --- /dev/null +++ "b/BFPL\345\243\271/bcos-codec/bcos-codec/abi/ContractABICodec.h" @@ -0,0 +1,744 @@ +/* + * Copyright (C) 2021 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. + * + * @brief Contract ABI serialize and deserialize tool. + * @author: octopuswang + * @date: 2019-04-01 + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace codec +{ +namespace abi +{ +// check if T type of uint256, int256, bool, string, bytes32 +template +struct ABIElementType : std::false_type +{ +}; + +// string +template <> +struct ABIElementType : std::true_type +{ +}; + +template <> +struct ABIElementType : std::false_type +{ +}; + +template <> +struct ABIElementType : std::false_type +{ +}; + +// uint256 +template <> +struct ABIElementType : std::false_type +{ +}; + +// int256 +template <> +struct ABIElementType : std::false_type +{ +}; + +// bool +template <> +struct ABIElementType : std::false_type +{ +}; + +// byte32 +template <> +struct ABIElementType : std::false_type +{ +}; + +template +struct ABIElementType> +{ + static bool constexpr value = ABIElementType::value || ABIElementType::value; +}; + +template +struct ABIElementType> +{ + static bool constexpr value = ABIElementType::value; +}; + +template +struct ABIElementType> +{ + static bool constexpr value = + ABIElementType::value || ABIElementType>::value; +}; + +template +struct ABIElementType> +{ + static bool constexpr value = ABIElementType::value; +}; + +// check if T type of string +template +struct ABIStringType : std::false_type +{ +}; + +template <> +struct ABIStringType : std::true_type +{ +}; + +// check if type of static array +template +struct ABIStaticArray : std::false_type +{ +}; + +// stringN type => bytesN +template +struct ABIStaticArray> : std::false_type +{ +}; + +// a fixed-length array of N elements of type T. +template +struct ABIStaticArray> : std::true_type +{ +}; + +// check if type of dynamic array +template +struct ABIDynamicArray : std::false_type +{ +}; + +// a fixed-length array of N elements of type T. +template +struct ABIDynamicArray> : std::true_type +{ +}; + +template +struct is_tuple : std::false_type +{ +}; + +template +struct is_tuple> : std::true_type +{ +}; + +template +struct ABITuple : std::false_type +{ +}; + +template +struct ABITuple> +{ + static bool constexpr value = ABIElementType::value || ABITuple>::value; +}; + +template +struct ABITuple> +{ + static bool constexpr value = ABIElementType::value; +}; + +// Definition: The following types are called “dynamic”: +// bytes +// string +// T[] for any T +// T[k] for any dynamic T and any k >= 0 +// (T1,...,Tk) if Ti is dynamic for some 1 <= i <= k +template +struct ABIDynamicType : std::false_type +{ +}; + +template +struct remove_dimension +{ + typedef T type; +}; + +template +struct remove_dimension> +{ + typedef typename remove_dimension::type type; +}; + +template +struct ABIDynamicType::type>::value || + ABIDynamicArray::type>::value || + ABITuple::type>::value>::type> + : std::true_type +{ +}; + +// fixed length of type, default 1 except static array type +template +struct Length +{ + enum + { + value = 1 + }; +}; + +// length of static array type +template +struct Length::value && !ABIDynamicType::value>::type> +{ + enum + { + value = std::tuple_size::value * Length::type>::value + }; +}; + +// length of static tuple type +template +struct Length::value && !ABIDynamicType::value>::type> +{ + enum + { + value = std::tuple_size::value + }; +}; + +// static offset for types +template +struct Offset; + +template +struct Offset +{ + enum + { + value = (size_t)Offset::value + (size_t)Offset::value + }; +}; + +template <> +struct Offset<> +{ + enum + { + value = 0 + }; +}; + +template +struct Offset +{ + enum + { + value = Length::value + }; +}; + +/** + * @brief Class for Solidity ABI + * @by octopuswang + * + * Class for serialise and deserialize c++ object in Solidity ABI format. + * @ref https://solidity.readthedocs.io/en/develop/abi-spec.html + */ +class ContractABICodec +{ +public: + explicit ContractABICodec(bcos::crypto::Hash::Ptr _hashImpl) : m_hashImpl(_hashImpl) {} + + template ::value>> + bytes serialise(const T& _t) + { // unsupport type + (void)_t; + static_assert(ABIElementType::value, "ABI not support type."); + return bytes{}; + } + + // template , + // std::enable_if_t::value> = true> + // bytes serialise(const T& _in) + // { + // return serialise(s256(_in)); + // } + + /// FIXME: use template + bytes serialise(const uint8_t& _in) { return serialise(u256(_in)); } + bytes serialise(const uint16_t& _in) { return serialise(u256(_in)); } + bytes serialise(const uint32_t& _in) { return serialise(u256(_in)); } + bytes serialise(const uint64_t& _in) { return serialise(u256(_in)); } + + /// FIXME: use template + bytes serialise(const int8_t& _in) { return serialise(s256(_in)); } + bytes serialise(const int16_t& _in) { return serialise(s256(_in)); } + bytes serialise(const int32_t& _in) { return serialise(s256(_in)); } + bytes serialise(const int64_t& _in) { return serialise(s256(_in)); } + + + // unsigned integer type uint256. + bytes serialise(const u256& _in); + + // two’s complement signed integer type int256. + bytes serialise(const s256& _in); + + // equivalent to uint8 restricted to the values 0 and 1. For computing the function selector, + // bool is used + bytes serialise(const bool& _in); + + // equivalent to uint160, except for the assumed interpretation and language typing. For + // computing the function selector, address is used. + // bool is used. + bytes serialise(const Address& _in); + + // binary type of 32 bytes + bytes serialise(const string32& _in); + + bytes serialise(const bytes& _in); + + // dynamic sized unicode string assumed to be UTF-8 encoded. + bytes serialise(const std::string& _in); + + // static array + template + bytes serialise(const std::array& _in); + // dynamic array + template + bytes serialise(const std::vector& _in); + + // dynamic tuple + template + bytes serialise(const std::tuple& _in); + + template >> + void deserialize(const T& _t, std::size_t _offset) + { // unsupport type + (void)_t; + (void)_offset; + static_assert(ABIElementType::value, "ABI not support type."); + } + + void deserialize(s256& out, std::size_t _offset); + + void deserialize(u256& _out, std::size_t _offset); + + void deserialize(bool& _out, std::size_t _offset); + + /// FIXME: use template + void deserialize(int8_t& _out, std::size_t _offset) + { + s256 out; + deserialize(out, _offset); + _out = out.convert_to(); + } + void deserialize(int16_t& _out, std::size_t _offset) + { + s256 out; + deserialize(out, _offset); + _out = out.convert_to(); + } + void deserialize(int32_t& _out, std::size_t _offset) + { + s256 out; + deserialize(out, _offset); + _out = out.convert_to(); + } + void deserialize(int64_t& _out, std::size_t _offset) + { + s256 out; + deserialize(out, _offset); + _out = out.convert_to(); + } + + /// FIXME: use template + void deserialize(uint8_t& _out, std::size_t _offset) + { + u256 out; + deserialize(out, _offset); + _out = out.convert_to(); + } + void deserialize(uint16_t& _out, std::size_t _offset) + { + u256 out; + deserialize(out, _offset); + _out = out.convert_to(); + } + void deserialize(uint32_t& _out, std::size_t _offset) + { + u256 out; + deserialize(out, _offset); + _out = out.convert_to(); + } + void deserialize(uint64_t& _out, std::size_t _offset) + { + u256 out; + deserialize(out, _offset); + _out = out.convert_to(); + } + + void deserialize(Address& _out, std::size_t _offset); + + void deserialize(string32& _out, std::size_t _offset); + + void deserialize(std::string& _out, std::size_t _offset); + void deserialize(bytes& _out, std::size_t _offset); + + // static array + template + void deserialize(std::array& _out, std::size_t _offset); + // dynamic array + template + void deserialize(std::vector& _out, std::size_t _offset); + + template + void deserialize(std::tuple& out, std::size_t _offset); + +private: + bcos::crypto::Hash::Ptr m_hashImpl; + static const int MAX_BYTE_LENGTH = 32; + // encode or decode offset + std::size_t offset{0}; + // encode temp bytes + bytes fixed; + bytes dynamic; + + // decode data + bytesConstRef data; + +private: + size_t getOffset() { return offset; } + // check if offset valid and std::length_error will be throw + void validOffset(std::size_t _offset) + { + if (_offset >= data.size()) + { + std::stringstream ss; + ss << " deserialize failed, invalid offset , offset is " << _offset << " , length is " + << data.size() << " , data is " << *toHexString(data); + + throw std::length_error(ss.str().c_str()); + } + } + + template + std::string toString(const T& _t) + { + std::stringstream ss; + ss << _t; + return ss.str(); + } + + inline void abiInAux() { return; } + + template + void abiInAux(T const& _t, U const&... _u) + { + bytes out = serialise(_t); + + if (ABIDynamicType::value) + { // dynamic type + dynamic += out; + fixed += serialise((u256)offset); + offset += out.size(); + } + else + { // static type + fixed += out; + } + + abiInAux(_u...); + } + + void abiOutAux() { return; } + + template + void abiOutAux(T& _t, U&... _u) + { + std::size_t _offset = offset; + // dynamic type, offset position + if (ABIDynamicType::value) + { + u256 dynamicOffset; + deserialize(dynamicOffset, offset); + _offset = static_cast(dynamicOffset); + } + + deserialize(_t, _offset); + // update offset + offset = offset + Offset::value * MAX_BYTE_LENGTH; + // decode next element + abiOutAux(_u...); + } + + template + void traverseTuple(std::tuple& tuple, F func, std::index_sequence) + { + return (void(func(std::get(tuple))), ...); + } + + template + void traverseTuple(std::tuple& tuple, F func) + { + traverseTuple(tuple, func, std::make_index_sequence()); + } + +public: + template + bool abiOut(bytesConstRef _data, T&... _t) + { + data = _data; + offset = 0; + try + { + abiOutAux(_t...); + return true; + } + catch (...) + { // error occur + return false; + } + } + + template + bool abiOutHex(const std::string& _data, T&... _t) + { + auto dataFromHex = *fromHexString(_data); + return abiOut(bytesConstRef(&dataFromHex), _t...); + } + + bool abiOutByFuncSelector(bytesConstRef _data, const std::vector& _allTypes, + std::vector& _out); + + template + bytes abiIn(const std::string& _sig, T const&... _t) + { + offset = Offset::value * MAX_BYTE_LENGTH; + fixed.clear(); + dynamic.clear(); + + abiInAux(_t...); + + return _sig.empty() ? + fixed + dynamic : + m_hashImpl->hash(_sig).ref().getCroppedData(0, 4).toBytes() + fixed + dynamic; + } + + template + std::string abiInHex(const std::string& _sig, T const&... _t) + { + return *toHexString(abiIn(_sig, _t...)); + } +}; + +// a fixed-length array of elements of the given type. +template +bytes ContractABICodec::serialise(const std::array& _in) +{ + bytes offset_bytes; + bytes content; + + auto length = N * MAX_BYTE_LENGTH; + + for (const auto& e : _in) + { + bytes out = serialise(e); + content += out; + if (ABIDynamicType::value) + { // dynamic + offset_bytes += serialise(static_cast(length)); + length += out.size(); + } + } + + return offset_bytes + content; +} + +// a variable-length array of elements of the given type. +template +bytes ContractABICodec::serialise(const std::vector& _in) +{ + bytes offset_bytes; + bytes content; + + auto length = _in.size() * MAX_BYTE_LENGTH; + + offset_bytes += serialise(static_cast(_in.size())); + for (const auto& t : _in) + { + bytes out = serialise(t); + content += out; + if (ABIDynamicType::value) + { // dynamic + offset_bytes += serialise(static_cast(length)); + length += out.size(); + } + } + + return offset_bytes + content; +} + +template +bytes ContractABICodec::serialise(const std::tuple& _in) +{ + bytes offsetBytes; + bytes dynamicContent; + auto tupleSize = std::tuple_size::type>::value; + auto length = tupleSize * MAX_BYTE_LENGTH; + + traverseTuple(const_cast&>(_in), [&](auto& _tupleItem) { + bytes out = serialise(_tupleItem); + + if (ABIDynamicType::type>::type>::value) + { + // dynamic + dynamicContent += out; + offsetBytes += serialise(static_cast(length)); + length += out.size(); + } + else + { + // static + offsetBytes += out; + } + }); + return offsetBytes + dynamicContent; +} + +template +void ContractABICodec::deserialize(std::array& _out, std::size_t _offset) +{ + for (std::size_t u = 0; u < N; ++u) + { + auto thisOffset = _offset; + + if (ABIDynamicType< + typename std::remove_const::type>::type>::value) + { // dynamic type + // N element offset + u256 length; + deserialize(length, _offset + u * Offset::value * MAX_BYTE_LENGTH); + thisOffset = thisOffset + static_cast(length); + } + else + { + thisOffset = _offset + u * Offset::value * MAX_BYTE_LENGTH; + } + deserialize(_out[u], thisOffset); + } +} + +template +void ContractABICodec::deserialize(std::vector& _out, std::size_t _offset) +{ + u256 length; + // vector length + deserialize(length, _offset); + _offset += MAX_BYTE_LENGTH; + _out.resize(static_cast(length)); + + for (std::size_t u = 0; u < static_cast(length); ++u) + { + std::size_t thisOffset = _offset; + + if (ABIDynamicType::value) + { // dynamic type + // N element offset + u256 thisEleOffset; + deserialize(thisEleOffset, _offset + u * Offset::value * MAX_BYTE_LENGTH); + thisOffset += static_cast(thisEleOffset); + } + else + { + thisOffset = _offset + u * Offset::value * MAX_BYTE_LENGTH; + } + deserialize(_out[u], thisOffset); + } +} + +template +void ContractABICodec::deserialize(std::tuple& _out, std::size_t _offset) +{ + std::size_t localOffset = _offset; + std::size_t tupleOffset = 0; + traverseTuple(_out, [&](auto& _tupleItem) { + if (ABIDynamicType::type>::type>::value) + { + // dynamic + u256 dynamicOffset; + deserialize(dynamicOffset, _offset + tupleOffset); + localOffset = _offset + static_cast(dynamicOffset); + deserialize(_tupleItem, localOffset); + } + else + { + // static + deserialize(_tupleItem, _offset + tupleOffset); + } + tupleOffset += + Offset::type>::type>::value * + MAX_BYTE_LENGTH; + }); +} +} // namespace abi + +inline string32 toString32(std::string const& _s) +{ + string32 ret; + for (unsigned i = 0; i < 32; ++i) + ret[i] = i < _s.size() ? _s[i] : 0; + return ret; +} + +inline string32 toString32(bcos::h256 const& _hashData) +{ + string32 ret; + for (unsigned i = 0; i < 32; i++) + { + ret[i] = _hashData[i]; + } + return ret; +} + +inline bcos::h256 fromString32(string32 const& _str) +{ + bcos::h256 hashData; + for (unsigned i = 0; i < 32; i++) + { + hashData[i] = _str[i]; + } + return hashData; +} +} // namespace codec +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-codec/bcos-codec/abi/ContractABIType.cpp" "b/BFPL\345\243\271/bcos-codec/bcos-codec/abi/ContractABIType.cpp" new file mode 100644 index 00000000..a5ea8907 --- /dev/null +++ "b/BFPL\345\243\271/bcos-codec/bcos-codec/abi/ContractABIType.cpp" @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2021 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. + * + * @brief Contract ABI function signature parser tool. + * @author: octopuswang + * @date: 2019-04-01 + */ + +#include "ContractABIType.h" +#include + +using namespace std; +using namespace bcos; +using namespace bcos::codec; +using namespace bcos::codec::abi; + +// uint: unsigned integer type of M bits, 0 < M <= 256, M % 8 == 0. e.g. uint32, uint8, +// uint256. +static const std::set setUint{"uint", "uint8", "uint16", "uint24", "uint32", "uint40", + "uint48", "uint56", "uint64", "uint72", "uint80", "uint88", "uint96", "uint104", "uint112", + "uint120", "uint128", "uint136", "uint144", "uint152", "uint160", "uint168", "uint176", + "uint184", "uint192", "uint200", "uint208", "uint216", "uint224", "uint232", "uint240", + "uint248", "uint256"}; + +// int: two’s complement signed integer type of M bits, 0 < M <= 256, M % 8 == 0. +static const std::set setInt{"int", "int8", "int16", "int24", "int32", "int40", + "int48", "int56", "int64", "int72", "int80", "int88", "int96", "int104", "int112", "int120", + "int128", "int136", "int144", "int152", "int160", "int168", "int176", "int184", "int192", + "int200", "int208", "int216", "int224", "int232", "int240", "int248", "int256"}; + +// bytes: binary type of M bytes, 0 < M <= 32. +static const std::set setByteN{"bytes1", "bytes2", "bytes3", "bytes4", "bytes5", + "bytes6", "bytes7", "bytes8", "bytes9", "bytes10", "bytes11", "bytes12", "bytes13", "bytes14", + "bytes15", "bytes16", "bytes17", "bytes18", "bytes19", "bytes20", "bytes21", "bytes22", + "bytes23", "bytes24", "bytes25", "bytes26", "bytes27", "bytes28", "bytes29", "bytes30", + "bytes31", "bytes32"}; + +// bool: equivalent to uint8 restricted to the values 0 and 1. For computing the function +// selector, bool is used. +const std::string strBool = "bool"; +// bytes: dynamic sized byte sequence. +const std::string strBytes = "bytes"; +// bytes: dynamic sized byte sequence. +const std::string strString = "string"; +// address: equivalent to uint160, except for the assumed interpretation and language typing. +// For computing the function selector, address is used. +const std::string strAddr = "address"; + +// Remove the white space characters on both sides +static void trim(std::string& _str) +{ + _str.erase(0, _str.find_first_not_of(" ")); + _str.erase(_str.find_last_not_of(" ") + 1); +} + +ABI_ELEMENTARY_TYPE ABIInType::getEnumType(const std::string& _strType) +{ + auto type = ABI_ELEMENTARY_TYPE::INVALID; + if (_strType == strBool) + { + type = ABI_ELEMENTARY_TYPE::BOOL; + } + else if (_strType == strAddr) + { + type = ABI_ELEMENTARY_TYPE::ADDR; + } + else if (_strType == strString) + { + type = ABI_ELEMENTARY_TYPE::STRING; + } + else if (_strType == strBytes) + { + type = ABI_ELEMENTARY_TYPE::BYTES; + } + else if (setUint.find(_strType) != setUint.end()) + { + type = ABI_ELEMENTARY_TYPE::UINT; + } + else if (setInt.find(_strType) != setInt.end()) + { + type = ABI_ELEMENTARY_TYPE::INT; + } + else if (setByteN.find(_strType) != setByteN.end()) + { + type = ABI_ELEMENTARY_TYPE::BYTESN; + } + + return type; +} + +void ABIInType::clear() +{ + aet = ABI_ELEMENTARY_TYPE::INVALID; + strEleType.clear(); + strType.clear(); + extents.clear(); +} + +bool ABIInType::reset(const std::string& _s) +{ + clear(); + + std::string strType = _s; + // eg: int[1][2][][3] + // trim blank character + trim(strType); + auto firstLeftBracket = strType.find('['); + // int + std::string strEleType = strType.substr(0, firstLeftBracket); + trim(strEleType); + auto t = getEnumType(strEleType); + // invalid solidity abi string + if (t == ABI_ELEMENTARY_TYPE::INVALID) + { + return false; + } + + // eg : [10][2][3][] + std::vector r; + std::string::size_type leftBracket = firstLeftBracket; + std::string::size_type rigthBracket = 0; + std::string::size_type length = strType.size(); + + while (leftBracket < length) + { + auto leftBracketBak = leftBracket; + leftBracket = strType.find('[', leftBracketBak); + rigthBracket = strType.find(']', leftBracketBak); + + if (leftBracket == std::string::npos || rigthBracket == std::string::npos || + leftBracket >= rigthBracket) + { + // invalid format + return false; + } + + std::string digit = strType.substr(leftBracket + 1, rigthBracket - leftBracket - 1); + trim(digit); + bool ok = + std::all_of(digit.begin(), digit.end(), [](char c) { return c >= '0' && c <= '9'; }); + if (!ok) + { + // invalid format + return false; + } + + if (digit.empty()) + { + r.push_back(0); + } + else + { + std::size_t size = strtoul(digit.c_str(), NULL, 10); + r.push_back(size); + } + + leftBracket = rigthBracket + 1; + } + + this->aet = t; + this->extents = r; + this->strType = strType; + this->strEleType = strEleType; + + return true; +} + +bool ABIInType::dynamic() +{ + // string or bytes + if (aet == ABI_ELEMENTARY_TYPE::STRING || aet == ABI_ELEMENTARY_TYPE::BYTES) + { + return true; + } + + // dynamic array + auto length = rank(); + for (std::size_t i = 0; i < length; ++i) + { + if (extent(i + 1) == 0) + { + return true; + } + } + + return false; +} + +// +bool ABIInType::removeExtent() +{ + auto length = rank(); + if (length > 0) + { + extents.resize(length - 1); + return true; + } + + return false; +} + +std::vector ABIFunc::getParamsType() const +{ + std::vector r; + for (auto it = allParamsType.begin(); it != allParamsType.end(); ++it) + { + r.push_back(it->getType()); + } + + return r; +} + +// parser contract abi function signature, eg: transfer(string,string,uint256) +bool ABIFunc::parser(const std::string& _sig) +{ + auto i0 = _sig.find("("); + auto i1 = _sig.find(")"); + // transfer(string,string,uint256) + if ((i0 == std::string::npos) || (i1 == std::string::npos) || (i1 <= i0)) + { + return false; + } + + // function name , eg: transfer + std::string strFuncName = _sig.substr(0, i0); + trim(strFuncName); + // parameters "string,string,uint256" + std::string strTypes = _sig.substr(i0 + 1, i1 - i0 - 1); + + std::string sig = strFuncName + "("; + + std::vector types; + boost::split(types, strTypes, boost::is_any_of(",")); + ABIInType at; + for (std::string& type : types) + { + trim(type); + if (!type.empty()) + { + sig += type; + sig += ","; + auto ok = at.reset(type); + if (!ok) + { + // invalid format + return false; + } + allParamsType.push_back(at); + continue; + } + } + + if (',' == sig.back()) + { + sig.back() = ')'; + } + else + { + sig += ")"; + } + + // set function name + this->strFuncName = strFuncName; + // set function sigature + this->strFuncSignature = sig; + + return true; +} diff --git "a/BFPL\345\243\271/bcos-codec/bcos-codec/abi/ContractABIType.h" "b/BFPL\345\243\271/bcos-codec/bcos-codec/abi/ContractABIType.h" new file mode 100644 index 00000000..e6423689 --- /dev/null +++ "b/BFPL\345\243\271/bcos-codec/bcos-codec/abi/ContractABIType.h" @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2021 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. + * + * @brief Contract ABI function signature parser tool. + * @author: octopuswang + * @date: 2019-04-01 + */ + +#pragma once +#include +#include +#include + +namespace bcos +{ +namespace codec +{ +namespace abi +{ +enum class ABI_ELEMENTARY_TYPE +{ // solidity ABI elementary types + INVALID, // invalid + BOOL, // bool + INT, // int8 ~ int256 + UINT, // uint8 ~ uint256 + BYTESN, // bytesN + ADDR, // address + BYTES, // bytes + STRING, // string + FIXED, // fixed, unsupport + UNFIXED // unfixed, unsupport +}; + +class ABIInType +{ +public: + ABIInType() = default; + + bool reset(const std::string& _str); + void clear(); + +public: + // the number of dimensions of T or zero + std::size_t rank() { return extents.size(); } + // obtains the size of an array type along a specified dimension + std::size_t extent(std::size_t index) { return index > rank() ? 0 : extents[index - 1]; } + bool removeExtent(); + bool dynamic(); + bool valid() { return aet != ABI_ELEMENTARY_TYPE::INVALID; } + std::string getType() const { return strType; } + std::string getEleType() const { return strEleType; } + + // get abi elementary type by string + ABI_ELEMENTARY_TYPE getEnumType(const std::string& _strType); + +private: + ABI_ELEMENTARY_TYPE aet{ABI_ELEMENTARY_TYPE::INVALID}; + std::string strEleType; + std::string strType; + std::vector extents; +}; + +using ABIOutType = ABIInType; + +class ABIFunc +{ +private: + std::string strFuncName; + std::string strFuncSignature; + std::vector allParamsType; + +public: + // parser contract abi function signature, eg: transfer(string,string,uint256) + bool parser(const std::string& _sig); + +public: + std::vector getParamsType() const; + inline std::string getSignature() const { return strFuncSignature; } + inline std::string getFuncName() const { return strFuncName; } +}; + +} // namespace abi +} // namespace codec +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-codec/bcos-codec/scale/Common.h" "b/BFPL\345\243\271/bcos-codec/bcos-codec/scale/Common.h" new file mode 100644 index 00000000..e71a0d02 --- /dev/null +++ "b/BFPL\345\243\271/bcos-codec/bcos-codec/scale/Common.h" @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2021 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. + * + * @brief common functions for scale codec + * @file Common.h + */ +#pragma once +#include "Exceptions.h" +#include +namespace bcos +{ +namespace codec +{ +namespace scale +{ +// represents compact integer value +using CompactInteger = boost::multiprecision::cpp_int; + +// OptionalBool is internal extended bool type +enum class OptionalBool : uint8_t +{ + NoneValue = 0u, + TrueValue = 1u, + FalseValue = 2u +}; + +/// categories of compact encoding +struct EncodingCategoryLimits +{ + // min integer encoded by 2 bytes + constexpr static size_t kMinUint16 = (1ul << 6u); + // min integer encoded by 4 bytes + constexpr static size_t kMinUint32 = (1ul << 14u); + // min integer encoded as multibyte + constexpr static size_t kMinBigInteger = (1ul << 30u); +}; +// calculate number of bytes required +inline size_t countBytes(CompactInteger v) +{ + size_t counter = 0; + do + { + ++counter; + } while ((v >>= 8) != 0); + return counter; +} +// Returns the compact encoded length for the given value. +template , + typename = std::enable_if_t::value>> +uint32_t compactLen(T val) +{ + if (val < EncodingCategoryLimits::kMinUint16) + return 1; + if (val < EncodingCategoryLimits::kMinUint32) + return 2; + if (val < EncodingCategoryLimits::kMinBigInteger) + return 4; + return countBytes(val); +} +} // namespace scale +} // namespace codec +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-codec/bcos-codec/scale/Exceptions.h" "b/BFPL\345\243\271/bcos-codec/bcos-codec/scale/Exceptions.h" new file mode 100644 index 00000000..abe6c4ce --- /dev/null +++ "b/BFPL\345\243\271/bcos-codec/bcos-codec/scale/Exceptions.h" @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2021 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. + * + * @brief Exceptions for scale-codec + * @file Exceptions.h + */ +#pragma once +#include +namespace bcos +{ +namespace codec +{ +namespace scale +{ +DERIVE_BCOS_EXCEPTION(ScaleEncodeException); +DERIVE_BCOS_EXCEPTION(ScaleDecodeException); +} // namespace scale +} // namespace codec +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-codec/bcos-codec/scale/FixedWidthIntegerCodec.h" "b/BFPL\345\243\271/bcos-codec/bcos-codec/scale/FixedWidthIntegerCodec.h" new file mode 100644 index 00000000..82789a5c --- /dev/null +++ "b/BFPL\345\243\271/bcos-codec/bcos-codec/scale/FixedWidthIntegerCodec.h" @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2021 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. + * + * @brief codec for the integer with fixed length + * @file FixedWidthIntegerCodec.h + */ +#pragma once +#include "Common.h" +#include +#include +#include +#include +#include +namespace bcos +{ +namespace codec +{ +namespace scale +{ +/** + * encodeInteger encodes any integer type to little-endian representation + * @tparam T integer type + * @tparam S output stream type + * @param value integer value + * @return byte array representation of value + */ +// Note: rust scale use u128 encoded as FixedWidthInteger(We don't support u128 now) +template , + typename = std::enable_if_t::value>> +void encodeInteger(T value, S& out) +{ // no need to take integers by && + constexpr size_t size = sizeof(I); + constexpr size_t bits = size * 8; + boost::endian::endian_buffer buf{}; + buf = value; // cannot initialize, only assign + for (size_t i = 0; i < size; ++i) + { + out << buf.data()[i]; + } +} + +/** + * @brief decodeInteger function decodes integer from stream + * @tparam T integer type + * @param stream source stream + * @return decoded value or error + */ +template , + typename = std::enable_if_t>> +I decodeInteger(S& stream) +{ + constexpr size_t size = sizeof(I); + static_assert(size <= 8); + // sign bit = 2^(num_bits - 1) + static constexpr std::array sign_bit = { + 0x80, // 1 byte + 0x8000, // 2 bytes + 0x800000, // 3 bytes + 0x80000000, // 4 bytes + 0x8000000000, // 5 bytes + 0x800000000000, // 6 bytes + 0x80000000000000, // 7 bytes + 0x8000000000000000 // 8 bytes + }; + + static constexpr std::array multiplier = { + 0x1, // 2^0 + 0x100, // 2^8 + 0x10000, // 2^16 + 0x1000000, // 2^24 + 0x100000000, // 2^32 + 0x10000000000, // 2^40 + 0x1000000000000, // 2^48 + 0x100000000000000 // 2^56 + }; + if (!stream.hasMore(size)) + { + BOOST_THROW_EXCEPTION(ScaleDecodeException() + << errinfo_comment("decodeInteger exception for NOT_ENOUGH_DATA")); + } + + // get integer as 4 bytes from little-endian stream + // and represent it as native-endian unsigned int eger + uint64_t v = 0u; + + for (size_t i = 0; i < size; ++i) + { + v += multiplier[i] * static_cast(stream.nextByte()); + } + // now we have uint64 native-endian value + // which can be signed or unsigned under the cover + + // if it is unsigned, we know that is not greater than max value for type T + // so static_cast(v) is safe + + // if it is signed, but positive it is also ok + // we can be sure that it is less than max_value/2 + // to check whether is is negative we check if the sign bit present + // in unsigned form it means that value is more than + // a value 2^(bits_number-1) + bool is_positive_signed = v < sign_bit[size - 1]; + if (std::is_unsigned() || is_positive_signed) + { + return static_cast(v); + } + + // T is signed integer type and the value v is negative + // value is negative signed means ( - x ) + // where x is positive unsigned < sign_bits[size-1] + // find this x, safely cast to signed and negate result + // the bitwise negation operation affects higher bits as well + // but it doesn't spoil the result + // static_cast to smaller size cuts them off + T sv = -static_cast((~v) + 1); + + return sv; +} +} // namespace scale +} // namespace codec +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-codec/bcos-codec/scale/Scale.h" "b/BFPL\345\243\271/bcos-codec/bcos-codec/scale/Scale.h" new file mode 100644 index 00000000..7b320e3c --- /dev/null +++ "b/BFPL\345\243\271/bcos-codec/bcos-codec/scale/Scale.h" @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2021 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. + * + * @brief scale codec + * @file Scale.h + */ +#pragma once +#include "Common.h" +#include "ScaleDecoderStream.h" +#include "ScaleEncoderStream.h" +#include +#include +#include +#include + +namespace bcos +{ +namespace codec +{ +namespace scale +{ +/** + * @brief convenience function for encoding primitives data to stream + * @tparam Args primitive types to be encoded + * @param args data to encode + * @return encoded data + */ +template +void encode(std::shared_ptr _encodeData, Args&&... _args) +{ + ScaleEncoderStream s; + (s << ... << std::forward(_args)); + *_encodeData = s.data(); +} + +template +bytes encode(Args&&... _args) +{ + ScaleEncoderStream s; + (s << ... << std::forward(_args)); + return s.data(); +} + +/** + * @brief convenience function for decoding primitives data from stream + * @tparam T primitive type that is decoded from provided span + * @param span of bytes with encoded data + * @return decoded T + */ +template +void decode(T& _decodedObject, gsl::span _span) +{ + ScaleDecoderStream s(_span); + s >> _decodedObject; +} + +template +T decode(gsl::span _span) +{ + T t; + decode(t, _span); + return t; +} +} // namespace scale +} // namespace codec +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-codec/bcos-codec/scale/ScaleDecoderStream.cpp" "b/BFPL\345\243\271/bcos-codec/bcos-codec/scale/ScaleDecoderStream.cpp" new file mode 100644 index 00000000..28bcb130 --- /dev/null +++ "b/BFPL\345\243\271/bcos-codec/bcos-codec/scale/ScaleDecoderStream.cpp" @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2021 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. + * + * @brief scale decoder + * @file ScaleDecoderStream.cpp + */ +#include "ScaleDecoderStream.h" +using namespace bcos; +using namespace bcos::codec::scale; + +ScaleDecoderStream::ScaleDecoderStream(gsl::span _span) + : m_span(_span), m_currentIterator(m_span.begin()), m_currentIndex(0) +{} + +boost::optional ScaleDecoderStream::decodeOptionalBool() +{ + auto byte = nextByte(); + switch (byte) + { + case static_cast(OptionalBool::NoneValue): + return boost::none; + break; + case static_cast(OptionalBool::FalseValue): + return false; + case static_cast(OptionalBool::TrueValue): + return true; + default: + BOOST_THROW_EXCEPTION(ScaleDecodeException() << errinfo_comment( + "decodeOptionalBool exception for unexpected value")); + } +} + +CompactInteger decodeCompactInteger(ScaleDecoderStream& stream) +{ + auto first_byte = stream.nextByte(); + const uint8_t flag = (first_byte)&0b00000011u; + size_t number = 0u; + switch (flag) + { + case 0b00u: + { + number = static_cast(first_byte >> 2u); + break; + } + case 0b01u: + { + auto second_byte = stream.nextByte(); + number = (static_cast((first_byte)&0b11111100u) + + static_cast(second_byte) * 256u) >> + 2u; + break; + } + case 0b10u: + { + number = first_byte; + size_t multiplier = 256u; + if (!stream.hasMore(3u)) + { + // not enough data to decode integer + BOOST_THROW_EXCEPTION(ScaleDecodeException() << errinfo_comment( + "decodeOptionalBool exception for not enough data")); + } + for (auto i = 0u; i < 3u; ++i) + { + // we assured that there are 3 more bytes, + // no need to make checks in a loop + number += (stream.nextByte()) * multiplier; + multiplier = multiplier << 8u; + } + number = number >> 2u; + break; + } + case 0b11: + { + auto bytes_count = ((first_byte) >> 2u) + 4u; + if (!stream.hasMore(bytes_count)) + { + // not enough data to decode integer + BOOST_THROW_EXCEPTION(ScaleDecodeException() << errinfo_comment( + "decodeCompactInteger exception for not enough data")); + } + + CompactInteger multiplier{1u}; + CompactInteger value = 0; + // we assured that there are m more bytes, + // no need to make checks in a loop + for (auto i = 0u; i < bytes_count; ++i) + { + value += (stream.nextByte()) * multiplier; + multiplier *= 256u; + } + + return value; // special case + } + default: + BOOST_THROW_EXCEPTION( + ScaleDecodeException() << errinfo_comment( + "decodeCompactInteger exception for not supported flag " + std::to_string(flag))); + } + + return CompactInteger{number}; +} + + +bool ScaleDecoderStream::decodeBool() +{ + auto byte = nextByte(); + switch (byte) + { + case 0u: + return false; + case 1u: + return true; + default: + BOOST_THROW_EXCEPTION( + ScaleDecodeException() << errinfo_comment("decodeBool exception for UNEXPECTED_VALUE")); + } +} + +ScaleDecoderStream& ScaleDecoderStream::operator>>(CompactInteger& v) +{ + v = decodeCompactInteger(*this); + return *this; +} + +ScaleDecoderStream& ScaleDecoderStream::operator>>(std::string& v) +{ + std::vector collection; + *this >> collection; + v.clear(); + v.append(collection.begin(), collection.end()); + return *this; +} + +bool ScaleDecoderStream::hasMore(uint64_t n) const +{ + return static_cast(m_currentIndex + n) <= m_span.size(); +} + +ScaleDecoderStream& ScaleDecoderStream::operator>>(u256& v) +{ + bytes decodedBigEndianData; + byte size = 32; + decodedBigEndianData.resize(size); + decodedBigEndianData.assign(m_currentIterator, m_currentIterator + size); + m_currentIterator += size; + m_currentIndex += size; + v = fromBigEndian(decodedBigEndianData); + return *this; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-codec/bcos-codec/scale/ScaleDecoderStream.h" "b/BFPL\345\243\271/bcos-codec/bcos-codec/scale/ScaleDecoderStream.h" new file mode 100644 index 00000000..03c30cd0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-codec/bcos-codec/scale/ScaleDecoderStream.h" @@ -0,0 +1,431 @@ +/* + * Copyright (C) 2021 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. + * + * @brief scale decoder + * @file ScaleDecoderStream.cpp + */ +#pragma once +#include "Common.h" +#include "FixedWidthIntegerCodec.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace codec +{ +namespace scale +{ +class ScaleDecoderStream +{ +public: + // special tag to differentiate decoding streams from others + static constexpr auto is_decoder_stream = true; + explicit ScaleDecoderStream(gsl::span span); + + /** + * @brief scale-decodes pair of values + * @tparam F first value type + * @tparam S second value type + * @param p pair of values to decode + * @return reference to stream + */ + template + ScaleDecoderStream& operator>>(std::pair& p) + { + static_assert(!std::is_reference_v && !std::is_reference_v); + return *this >> const_cast&>(p.first) // NOLINT + >> const_cast&>(p.second); // NOLINT + } + + /** + * @brief scale-decoding of tuple + * @tparam T enumeration of tuples types + * @param v reference to tuple + * @return reference to stream + */ + template + ScaleDecoderStream& operator>>(std::tuple& v) + { + if constexpr (sizeof...(T) > 0) + { + decodeElementOfTuple<0>(v); + } + return *this; + } + + /** + * @brief scale-decoding of variant + * @tparam T enumeration of various types + * @param v reference to variant + * @return reference to stream + */ + template + ScaleDecoderStream& operator>>(boost::variant& v) + { + // first byte means type index + uint8_t type_index = 0u; + *this >> type_index; // decode type index + + // ensure that index is in [0, types_count) + if (type_index >= sizeof...(Ts)) + { + BOOST_THROW_EXCEPTION( + ScaleDecodeException() << errinfo_comment("exception for WRONG_TYPE_INDEX")); + } + + tryDecodeAsOneOfVariant<0>(v, type_index); + return *this; + } + + /** + * @brief scale-decodes shared_ptr value + * @tparam T value type + * @param v value to decode + * @return reference to stream + */ + template + ScaleDecoderStream& operator>>(std::shared_ptr& v) + { + using mutableT = std::remove_const_t; + + static_assert(std::is_default_constructible_v); + + v = std::make_shared(); + return *this >> const_cast(*v); // NOLINT + } + + /** + * @brief scale-decodes unique_ptr value + * @tparam T value type + * @param v value to decode + * @return reference to stream + */ + template + ScaleDecoderStream& operator>>(std::unique_ptr& v) + { + using mutableT = std::remove_const_t; + + static_assert(std::is_default_constructible_v); + + v = std::make_unique(); + return *this >> const_cast(*v); // NOLINT + } + + /** + * @brief scale-encodes any integral type including bool + * @tparam T integral type + * @param v value of integral type + * @return reference to stream + */ + template , + typename = std::enable_if_t::value>> + ScaleDecoderStream& operator>>(T& v) + { + // check bool + if constexpr (std::is_same::value) + { + v = decodeBool(); + return *this; + } + // check byte + if constexpr (sizeof(T) == 1u) + { + v = nextByte(); + return *this; + } + // decode any other integer + v = decodeInteger(*this); + return *this; + } + + /** + * @brief scale-decodes any optional value + * @tparam T type of optional value + * @param v optional value reference + * @return reference to stream + */ + template + ScaleDecoderStream& operator>>(boost::optional& v) + { + using mutableT = std::remove_const_t; + + static_assert(std::is_default_constructible_v); + + // optional bool is special case of optional values + // it is encoded as one byte instead of two + // as described in specification + if constexpr (std::is_same::value) + { + v = decodeOptionalBool(); + return *this; + } + // detect if optional has value + bool has_value = false; + *this >> has_value; + if (!has_value) + { + v.reset(); + return *this; + } + // decode value + v.emplace(); + return *this >> const_cast(*v); // NOLINT + } + + ScaleDecoderStream& operator>>(u256& v); + /** + * @brief scale-decodes compact integer value + * @param v compact integer reference + * @return + */ + ScaleDecoderStream& operator>>(CompactInteger& v); + ScaleDecoderStream& operator>>(s256& v) + { + u256 unsignedValue; + *this >> unsignedValue; + v = u2s(unsignedValue); + return *this; + } + + template + ScaleDecoderStream& operator>>(FixedBytes& fixedData) + { + for (unsigned i = 0; i < N; ++i) + { + *this >> fixedData[i]; + } + return *this; + } + /** + * @brief decodes vector of items + * @tparam T item type + * @param v reference to vector + * @return reference to stream + */ + template + ScaleDecoderStream& operator>>(std::vector& v) + { + using mutableT = std::remove_const_t; + using size_type = typename std::list::size_type; + + static_assert(std::is_default_constructible_v); + + CompactInteger size{0u}; + *this >> size; + + auto item_count = size.convert_to(); + std::vector vec; + try + { + vec.resize(item_count); + } + catch (const std::bad_alloc&) + { + BOOST_THROW_EXCEPTION( + ScaleDecodeException() + << errinfo_comment("exception for TOO_MANY_ITEMS: " + std::to_string(item_count))); + } + if constexpr (sizeof(T) == 1u) + { + vec.assign(m_currentIterator, m_currentIterator + item_count); + m_currentIterator += item_count; + m_currentIndex += item_count; + } + else + { + for (size_type i = 0u; i < item_count; ++i) + { + *this >> vec[i]; + } + } + v = std::move(vec); + return *this; + } + + /** + * @brief decodes map of pairs + * @tparam T item type + * @tparam F item type + * @param m reference to map + * @return reference to stream + */ + template + ScaleDecoderStream& operator>>(std::map& m) + { + using mutableT = std::remove_const_t; + static_assert(std::is_default_constructible_v); + using mutableF = std::remove_const_t; + static_assert(std::is_default_constructible_v); + + using size_type = typename std::map::size_type; + + CompactInteger size{0u}; + *this >> size; + + auto item_count = size.convert_to(); + std::map map; + for (size_type i = 0u; i < item_count; ++i) + { + std::pair p; + *this >> p; + map.emplace(std::move(p)); + } + m = std::move(map); + return *this; + } + + /** + * @brief decodes collection of items + * @tparam T item type + * @param v reference to collection + * @return reference to stream + */ + template + ScaleDecoderStream& operator>>(std::list& v) + { + using mutableT = std::remove_const_t; + using size_type = typename std::list::size_type; + + static_assert(std::is_default_constructible_v); + + CompactInteger size{0u}; + *this >> size; + + auto item_count = size.convert_to(); + + std::list lst; + try + { + lst.reserve(item_count); + } + catch (const std::bad_alloc&) + { + BOOST_THROW_EXCEPTION(ScaleDecodeException() << errinfo_comment( + "exception for TOO_MANY_ITEMS" + std::to_string(item_count))); + } + + for (size_type i = 0u; i < item_count; ++i) + { + lst.emplace_back(); + *this >> lst.back(); + } + v = std::move(lst); + return *this; + } + + /** + * @brief decodes array of items + * @tparam T item type + * @tparam size of the array + * @param a reference to the array + * @return reference to stream + */ + template + ScaleDecoderStream& operator>>(std::array& a) + { + using mutableT = std::remove_const_t; + for(auto it = a.begin(); it != a.end(); ++it) + { + *this >> const_cast(*it); // NOLINT + } + return *this; + } + + /** + * @brief decodes string from stream + * @param v value to decode + * @return reference to stream + */ + ScaleDecoderStream& operator>>(std::string& v); + + /** + * @brief hasMore Checks whether n more bytes are available + * @param n Number of bytes to check + * @return True if n more bytes are available and false otherwise + */ + bool hasMore(uint64_t n) const; + + /** + * @brief takes one byte from stream and + * advances current byte iterator by one + * @return current byte + */ + uint8_t nextByte() + { + if (!hasMore(1)) + { + BOOST_THROW_EXCEPTION(ScaleDecodeException() + << errinfo_comment("nextByte exception for NOT_ENOUGH_DATA")); + } + ++m_currentIndex; + return *m_currentIterator++; + } + using SizeType = gsl::span::size_type; + + gsl::span span() const { return m_span; } + SizeType currentIndex() const { return m_currentIndex; } + +private: + bool decodeBool(); + /** + * @brief special case of optional values as described in specification + * @return boost::optional value + */ + boost::optional decodeOptionalBool(); + + template + void decodeElementOfTuple(std::tuple& v) + { + using T = std::remove_const_t>>; + *this >> const_cast(std::get(v)); // NOLINT + if constexpr (sizeof...(Ts) > I + 1) + { + decodeElementOfTuple(v); + } + } + + template + void tryDecodeAsOneOfVariant(boost::variant& v, size_t i) + { + using T = std::remove_const_t>>; + static_assert(std::is_default_constructible_v); + if (I == i) + { + T val; + *this >> val; + v = std::forward(val); + return; + } + if constexpr (sizeof...(Ts) > I + 1) + { + tryDecodeAsOneOfVariant(v, i); + } + } + +private: + gsl::span m_span; + gsl::span::iterator m_currentIterator; + SizeType m_currentIndex; +}; +} // namespace scale +} // namespace codec +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-codec/bcos-codec/scale/ScaleEncoderStream.cpp" "b/BFPL\345\243\271/bcos-codec/bcos-codec/scale/ScaleEncoderStream.cpp" new file mode 100644 index 00000000..064da74a --- /dev/null +++ "b/BFPL\345\243\271/bcos-codec/bcos-codec/scale/ScaleEncoderStream.cpp" @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2021 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. + * + * @brief scale encoder + * @file ScaleEncoderStream.h + */ +#include "ScaleEncoderStream.h" +#include "Common.h" + +using namespace bcos; +using namespace bcos::codec::scale; + +// must not use these functions outside encodeInteger +inline void encodeFirstCategory(uint8_t value, ScaleEncoderStream& out) +{ + // only values from [0, kMinUint16) can be put here + out << static_cast(value << 2u); +} + +inline void encodeSecondCategory(uint16_t value, ScaleEncoderStream& out) +{ + // only values from [kMinUint16, kMinUint32) can be put here + auto v = value; + v <<= 2u; // v *= 4 + v += 1u; // set 0b01 flag + auto minor_byte = static_cast(v & 0xFFu); + v >>= 8u; + auto major_byte = static_cast(v & 0xFFu); + + out << minor_byte << major_byte; +} + +inline void encodeThirdCategory(uint32_t value, ScaleEncoderStream& out) +{ + // only values from [kMinUint32, kMinBigInteger) can be put here + uint32_t v = (value << 2u) + 2; + encodeInteger(v, out); +} + +/** + * @brief compact-encodes CompactInteger + * @param value source CompactInteger value + */ +void encodeCompactInteger(const CompactInteger& value, ScaleEncoderStream& out) +{ + // cannot encode negative numbers + // there is no description how to encode compact negative numbers + if (value < 0) + { + BOOST_THROW_EXCEPTION(ScaleEncodeException() << errinfo_comment( + "encodeCompactInteger exception for NEGATIVE_COMPACT_INTEGER")); + } + + if (value < EncodingCategoryLimits::kMinUint16) + { + encodeFirstCategory(value.convert_to(), out); + return; + } + + if (value < EncodingCategoryLimits::kMinUint32) + { + encodeSecondCategory(value.convert_to(), out); + return; + } + + if (value < EncodingCategoryLimits::kMinBigInteger) + { + encodeThirdCategory(value.convert_to(), out); + return; + } + + // number of bytes required to represent value + size_t bigIntLength = countBytes(value); + // number of bytes to scale-encode value + // 1 byte is reserved for header + size_t requiredLength = 1 + bigIntLength; + if (bigIntLength > 67) + { + BOOST_THROW_EXCEPTION(ScaleEncodeException() << errinfo_comment( + "encodeCompactInteger exception for COMPACT_INTEGER_TOO_BIG")); + } + + bytes result; + result.reserve(requiredLength); + /* The value stored in 6 major bits of header is used + * to encode number of bytes for storing big integer. + * Value formed by 6 bits varies from 0 to 63 == 2^6 - 1, + * However big integer byte count starts from 4, + * so to store this number we should decrease this value by 4. + * And the range of bytes number for storing big integer + * becomes 4 .. 67. To form resulting header we need to move + * those bits representing bytes count to the left by 2 positions + * by means of multiplying by 4. + * Minor 2 bits store encoding option, in our case it is 0b11 == 3 + * We just add 3 to the result of operations above + */ + uint8_t header = (bigIntLength - 4) * 4 + 3; + result.push_back(header); + CompactInteger v{value}; + for (size_t i = 0; i < bigIntLength; ++i) + { + result.push_back(static_cast(v & 0xFF)); // push back least significant byte + v >>= 8; + } + for (const uint8_t c : result) + { + out << c; + } +} + +ScaleEncoderStream& ScaleEncoderStream::operator<<(const u256& _value) +{ + // convert u256 to big-edian bytes(Note: must be 32bytes) + bytes bigEndianData = toBigEndian(_value); + m_stream.insert(m_stream.end(), bigEndianData.begin(), bigEndianData.end()); + return *this; +} + +bytes ScaleEncoderStream::data() const +{ + bytes buffer(m_stream.size(), 0u); + buffer.assign(m_stream.begin(), m_stream.end()); + return buffer; +} + +ScaleEncoderStream& ScaleEncoderStream::operator<<(const CompactInteger& v) +{ + encodeCompactInteger(v, *this); + return *this; +} + +ScaleEncoderStream& ScaleEncoderStream::encodeOptionalBool(const boost::optional& v) +{ + auto result = OptionalBool::TrueValue; + if (!v.has_value()) + { + result = OptionalBool::NoneValue; + } + else if (!*v) + { + result = OptionalBool::FalseValue; + } + return putByte(static_cast(result)); +} diff --git "a/BFPL\345\243\271/bcos-codec/bcos-codec/scale/ScaleEncoderStream.h" "b/BFPL\345\243\271/bcos-codec/bcos-codec/scale/ScaleEncoderStream.h" new file mode 100644 index 00000000..1c5615d9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-codec/bcos-codec/scale/ScaleEncoderStream.h" @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2021 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. + * + * @brief scale encoder + * @file ScaleEncoderStream.cpp + */ +#pragma once +#include "FixedWidthIntegerCodec.h" +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace codec +{ +namespace scale +{ +class ScaleEncoderStream +{ +public: + // special tag to differentiate encoding streams from others + static constexpr auto is_encoder_stream = true; + + // get the encoded data + bytes data() const; + + /** + * @brief scale-encodes pair of values + * @tparam F first value type + * @tparam S second value type + * @param p pair of values to encode + * @return reference to stream + */ + template + ScaleEncoderStream& operator<<(const std::pair& p) + { + return *this << p.first << p.second; + } + + /** + * @brief scale-encodes tuple + * @tparam T enumeration of types + * @param v tuple + * @return reference to stream + */ + template + ScaleEncoderStream& operator<<(const std::tuple& v) + { + if constexpr (sizeof...(Ts) > 0) + { + encodeElementOfTuple<0>(v); + } + return *this; + } + + /** + * @brief scale-encodes variant value + * @tparam T type list + * @param v value to encode + * @return reference to stream + */ + template + ScaleEncoderStream& operator<<(const boost::variant& v) + { + tryEncodeAsOneOfVariant<0>(v); + return *this; + } + + /** + * @brief scale-encodes sharead_ptr value + * @tparam T type list + * @param v value to encode + * @return reference to stream + */ + template + ScaleEncoderStream& operator<<(const std::shared_ptr& v) + { + if (v == nullptr) + { + BOOST_THROW_EXCEPTION(ScaleEncodeException() + << errinfo_comment("encode exception for DEREF_NULLPOINTER")); + } + return *this << *v; + } + + /** + * @brief scale-encodes unique_ptr value + * @tparam T type list + * @param v value to encode + * @return reference to stream + */ + template + ScaleEncoderStream& operator<<(const std::unique_ptr& v) + { + if (v == nullptr) + { + BOOST_THROW_EXCEPTION(ScaleEncodeException() + << errinfo_comment("encode exception for DEREF_NULLPOINTER")); + } + return *this << *v; + } + + template + ScaleEncoderStream& operator<<(const FixedBytes& fixedData) + { + for (auto it = fixedData.begin(); it != fixedData.end(); ++it) + { + *this << *it; + } + return *this; + } + + /** + * @brief scale-encodes collection of same time items + * @tparam T type of item + * @param c collection to encode + * @return reference to stream + */ + template + ScaleEncoderStream& operator<<(const std::vector& c) + { + return encodeCollection(c.size(), c.begin(), c.end()); + } + + /** + * @brief scale-encodes collection of same time items + * @tparam T type of item + * @param c collection to encode + * @return reference to stream + */ + template + ScaleEncoderStream& operator<<(const std::list& c) + { + return encodeCollection(c.size(), c.begin(), c.end()); + } + + /** + * @brief scale-encodes collection of map + * @tparam T type of item + * @tparam F type of item + * @param c collection to encode + * @return reference to stream + */ + template + ScaleEncoderStream& operator<<(const std::map& c) + { + return encodeCollection(c.size(), c.begin(), c.end()); + } + + /** + * @brief scale-encodes optional value + * @tparam T value type + * @param v value to encode + * @return reference to stream + */ + template + ScaleEncoderStream& operator<<(const boost::optional& v) + { + // optional bool is a special case of optional values + // it should be encoded using one byte instead of two + // as described in specification + if constexpr (std::is_same::value) + { + return encodeOptionalBool(v); + } + if (!v.has_value()) + { + return putByte(0u); + } + return putByte(1u) << *v; + } + + /** + * @brief appends sequence of bytes + * @param v bytes sequence + * @return reference to stream + */ + template + ScaleEncoderStream& operator<<(const gsl::span& v) + { + return encodeCollection(v.size(), v.begin(), v.end()); + } + + /** + * @brief scale-encodes array of items + * @tparam T item type + * @tparam size of the array + * @param a reference to the array + * @return reference to stream + */ + template + ScaleEncoderStream& operator<<(const std::array& a) + { + for (const auto& e : a) + { + *this << e; + } + return *this; + } + + /** + * @brief scale-encodes std::reference_wrapper of a type + * @tparam T underlying type + * @param v value to encode + * @return reference to stream; + */ + template + ScaleEncoderStream& operator<<(const std::reference_wrapper& v) + { + return *this << static_cast(v); + } + + /** + * @brief scale-encodes a string view + * @param sv string_view item + * @return reference to stream + */ + ScaleEncoderStream& operator<<(std::string_view sv) + { + return encodeCollection(sv.size(), sv.begin(), sv.end()); + } + + /** + * @brief scale-encodes any integral type including bool + * @tparam T integral type + * @param v value of integral type + * @return reference to stream + */ + template , + typename = std::enable_if_t::value>> + ScaleEncoderStream& operator<<(T&& v) + { + // encode bool + if constexpr (std::is_same::value) + { + uint8_t byte = (v ? 1u : 0u); + return putByte(byte); + } + // put byte + if constexpr (sizeof(T) == 1u) + { +// to avoid infinite recursion +#if __GNUC__ >= 10 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + return putByte(static_cast(v)); +#if __GNUC__ >= 10 +#pragma GCC diagnostic pop +#endif + } +// encode any other integer +#if __GNUC__ >= 10 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + encodeInteger(v, *this); +#if __GNUC__ >= 10 +#pragma GCC diagnostic pop +#endif + return *this; + } + + /** + * @brief scale-encodes CompactInteger value as compact integer + * @param v value to encode + * @return reference to stream + */ + ScaleEncoderStream& operator<<(const CompactInteger& v); + + ScaleEncoderStream& operator<<(s256 const& v) + { + u256 unsignedValue = s2u(v); + return *this << unsignedValue; + } + + ScaleEncoderStream& operator<<(const u256& v); + +protected: + template + void encodeElementOfTuple(const std::tuple& v) + { + *this << std::get(v); + if constexpr (sizeof...(Ts) > I + 1) + { + encodeElementOfTuple(v); + } + } + + template + void tryEncodeAsOneOfVariant(const boost::variant& v) + { + using T = std::tuple_element_t>; + if (v.type() == typeid(T)) + { + *this << I << boost::get(v); + return; + } + if constexpr (sizeof...(Ts) > I + 1) + { + tryEncodeAsOneOfVariant(v); + } + } + + /** + * @brief scale-encodes any collection + * @tparam It iterator over collection of bytes + * @param size size of the collection + * @param begin iterator pointing to the begin of collection + * @param end iterator pointing to the end of collection + * @return reference to stream + */ + template + ScaleEncoderStream& encodeCollection(const CompactInteger& size, It&& begin, It&& end) + { + *this << size; + for (auto&& it = begin; it != end; ++it) + { + *this << *it; + } + return *this; + } + + /** + * @brief puts a byte to buffer + * @param v byte value + * @return reference to stream + */ + ScaleEncoderStream& putByte(uint8_t v) + { + m_stream.emplace_back(v); + return *this; + } + +private: + ScaleEncoderStream& encodeOptionalBool(const boost::optional& v); + // std::deque m_stream; + std::deque m_stream; +}; +} // namespace scale +} // namespace codec +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-codec/bcos-codec/wrapper/CodecWrapper.h" "b/BFPL\345\243\271/bcos-codec/bcos-codec/wrapper/CodecWrapper.h" new file mode 100644 index 00000000..9a46c9c3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-codec/bcos-codec/wrapper/CodecWrapper.h" @@ -0,0 +1,125 @@ +/** + * Copyright (C) 2021 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 CodecWrapper.h + * @author: kyonRay + * @date 2021-06-02 + */ + +#pragma once + +#include "bcos-codec/abi/ContractABICodec.h" +#include "bcos-codec/scale/Scale.h" + +namespace bcos +{ +enum VMType +{ + EVM, + WASM, + UNDEFINED +}; +class CodecWrapper +{ +public: + using Ptr = std::shared_ptr; + CodecWrapper(crypto::Hash::Ptr _hash, bool _isWasm) : m_type(_isWasm ? VMType::WASM : VMType::EVM), m_hash(std::move(_hash)) + {} + template + bytes encode(Args&&... _args) const + { + assert(m_type != VMType::UNDEFINED); + if (m_type == VMType::EVM) + { + // Note: the codec is not thread-safe, so we can't share this object + codec::abi::ContractABICodec abi(m_hash); + return abi.abiIn("", _args...); + } + else + { + codec::scale::ScaleEncoderStream s; + (s << ... << std::forward(_args)); + return s.data(); + } + } + template + bytes encodeWithSig(const std::string& _sig, Args&&... _args) const + { + assert(m_type != VMType::UNDEFINED); + if (m_type == VMType::EVM) + { + // Note: the codec is not thread-safe, so we can't share this object + codec::abi::ContractABICodec abi(m_hash); + return abi.abiIn(_sig, _args...); + } + else + { + codec::scale::ScaleEncoderStream s; + (s << ... << std::forward(_args)); + return m_hash->hash(_sig).ref().getCroppedData(0, 4).toBytes() + s.data(); + } + } + + bytes encodeWithSig(const std::string& _sig) const + { + assert(m_type != VMType::UNDEFINED); + if (m_type == VMType::EVM) + { + // Note: the codec is not thread-safe, so we can't share this object + codec::abi::ContractABICodec abi(m_hash); + return abi.abiIn(_sig); + } + else + { + codec::scale::ScaleEncoderStream s; + return m_hash->hash(_sig).ref().getCroppedData(0, 4).toBytes() + s.data(); + } + } + + template + void decode(bytesConstRef _data, T&... _t) const + { + assert(m_type != VMType::UNDEFINED); + if (m_type == VMType::EVM) + { + codec::abi::ContractABICodec abi(m_hash); + abi.abiOut(_data, _t...); + } + else if (m_type == VMType::WASM) + { + auto&& t = _data.toBytes(); + codec::scale::ScaleDecoderStream stream(gsl::make_span(t)); + decodeScale(stream, _t...); + } + } + template + void decodeScale(codec::scale::ScaleDecoderStream& _s, T& _t, U&... _u) const + { + _s >> _t; + decodeScale(_s, _u...); + } + template + void decodeScale(codec::scale::ScaleDecoderStream& _s, T& _t) const + { + _s >> _t; + } + + void decodeScale(codec::scale::ScaleDecoderStream&) const { return; } + +private: + VMType m_type = VMType::UNDEFINED; + crypto::Hash::Ptr m_hash; +}; +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-codec/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-codec/test/CMakeLists.txt" new file mode 100644 index 00000000..4f888e0c --- /dev/null +++ "b/BFPL\345\243\271/bcos-codec/test/CMakeLists.txt" @@ -0,0 +1,28 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of bcos-codec +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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(GLOB_RECURSE SOURCES "*.cpp" "*.h" "*.sol") +# cmake settings +set(TEST_BINARY_NAME test-bcos-codec) + +add_executable(${TEST_BINARY_NAME} ${SOURCES}) +target_include_directories(${TEST_BINARY_NAME} PRIVATE . ${CMAKE_SOURCE_DIR}) + +find_package(Boost REQUIRED unit_test_framework) + +target_link_libraries(${TEST_BINARY_NAME} ${CODEC_TARGET} Boost::unit_test_framework) +add_test(NAME test-codec WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-codec/test/unittests/ContractABICodecTest.cpp" "b/BFPL\345\243\271/bcos-codec/test/unittests/ContractABICodecTest.cpp" new file mode 100644 index 00000000..ab0a3b64 --- /dev/null +++ "b/BFPL\345\243\271/bcos-codec/test/unittests/ContractABICodecTest.cpp" @@ -0,0 +1,1111 @@ +/* + * Copyright (C) 2021 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. + * + * @brief unittest for ContractABICodec + * @author: octopuswang + * @date: 2019-04-01 + */ +#include "bcos-codec/abi/ContractABICodec.h" +#include "bcos-codec/abi/ContractABIType.h" +#include "bcos-codec/wrapper/CodecWrapper.h" +#include +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::codec::abi; +using namespace bcos::codec; +using namespace bcos::crypto; +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(ABITest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(ContractABIType_func0) +{ + auto hashImpl = std::make_shared(); + ContractABICodec ct(hashImpl); + u256 a("0x123"); + std::vector b; + u256 u0("0x456"); + u256 u1("0x789"); + b.push_back(u0); + b.push_back(u1); + string32 c = toString32("1234567890"); + std::string d = "Hello, world!"; + + auto r = ct.abiInHex("", a, b, c, d); + auto rb = ct.abiIn("", a, b, c, d); + BOOST_CHECK(r == *toHexString(rb)); + + BOOST_CHECK_EQUAL( + r, std::string("0000000000000000000000000000000000000000000000000000000000000123" + "0000000000000000000000000000000000000000000000000000000000000080" + "3132333435363738393000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000e0" + "0000000000000000000000000000000000000000000000000000000000000002" + "0000000000000000000000000000000000000000000000000000000000000456" + "0000000000000000000000000000000000000000000000000000000000000789" + "000000000000000000000000000000000000000000000000000000000000000d" + "48656c6c6f2c20776f726c642100000000000000000000000000000000000000")); + + u256 outA; + std::vector outB; + string32 outC; + std::string outD; + auto Ok = ct.abiOutHex(r, outA, outB, outC, outD); + BOOST_CHECK(Ok == true); + BOOST_CHECK(a == outA); + BOOST_CHECK(b == outB); + BOOST_CHECK(c == outC); + BOOST_CHECK(d == outD); +} + +BOOST_AUTO_TEST_CASE(ContractABIType_func1) +{ + auto hashImpl = std::make_shared(); + ContractABICodec ct(hashImpl); + std::string a("dave"); + bool b(true); + std::vector c{1, 2, 3}; + + auto rb = ct.abiIn("", a, b, c); + auto r = *toHexString(rb); + BOOST_CHECK_EQUAL( + r, std::string("0000000000000000000000000000000000000000000000000000000000000060" + "0000000000000000000000000000000000000000000000000000000000000001" + "00000000000000000000000000000000000000000000000000000000000000a0" + "0000000000000000000000000000000000000000000000000000000000000004" + "6461766500000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000003" + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000002" + "0000000000000000000000000000000000000000000000000000000000000003")); + + std::string outA; + bool outB; + std::vector outC; + auto Ok = ct.abiOutHex(r, outA, outB, outC); + BOOST_CHECK(Ok == true); + BOOST_CHECK(a == outA); + BOOST_CHECK(b == outB); + BOOST_CHECK(c == outC); +} + +BOOST_AUTO_TEST_CASE(ContractABIType_func2) +{ + auto hashImpl = std::make_shared(); + ContractABICodec ct(hashImpl); + std::string a("daslfjaklfdaskl"); + u256 b = 1111; + std::array c{1, 2, 3, 4, 5, 6}; + std::vector d{1, 2, 3, 4, 5, 6}; + bool e = false; + Address f("0x692a70d2e424a56d2c6c27aa97d1a86395877b3a"); + + auto rb = ct.abiIn("", a, b, c, d, e, f); + auto r = *toHexString(rb); + + std::string outA; + u256 outB; + std::array outC; + std::vector outD; + bool outE = true; + Address outF; + + auto Ok = ct.abiOutHex(r, outA, outB, outC, outD, outE, outF); + BOOST_CHECK(Ok == true); + BOOST_CHECK(a == outA); + BOOST_CHECK(b == outB); + BOOST_CHECK(c == outC); + BOOST_CHECK(d == outD); + BOOST_CHECK(e == outE); + BOOST_CHECK(f == outF); +} + + +BOOST_AUTO_TEST_CASE(ContractABIType_func3) +{ + auto hashImpl = std::make_shared(); + ContractABICodec ct(hashImpl); + std::string a("aaafadsfsfadsfdasf"); + Address b("0x35ef07393b57464e93deb59175ff72e6499450cf"); + u256 c = 11111; + int d = -11111; + + auto rb = ct.abiIn("", a, b, c, d); + auto r = *toHexString(rb); + + BOOST_CHECK_EQUAL( + r, std::string( + "00000000000000000000000000000000000000000000000000000000000000800000000" + "0000000000000000035ef07393b57464e93deb59175ff72e6499450cf000000000000000000000000" + "0000000000000000000000000000000000002b67fffffffffffffffffffffffffffffffffffffffff" + "fffffffffffffffffffd4990000000000000000000000000000000000000000000000000000000000" + "0000126161616661647366736661647366646173660000000000000000000000000000")); + + std::string outA; + Address outB; + u256 outC; + s256 outD; + + auto Ok = ct.abiOutHex(r, outA, outB, outC, outD); + BOOST_CHECK(Ok == true); + BOOST_CHECK(a == outA); + BOOST_CHECK(b == outB); + BOOST_CHECK(c == outC); + BOOST_CHECK(d == outD); +} + + +BOOST_AUTO_TEST_CASE(ContractABIType_func4) +{ + auto hashImpl = std::make_shared(); + ContractABICodec ct(hashImpl); + std::string a; + u256 b; + s256 c; + string32 d; + std::array e; + std::vector> f(3); + + auto rb = ct.abiIn("", a, b, c, d, e, f); + auto r = *toHexString(rb); + + std::string outA = "HelloWorld"; + u256 outB = 11111; + s256 outC = -1111; + string32 outD = toString32("aa"); + std::array outE; + std::vector> outF; + + auto Ok = ct.abiOutHex(r, outA, outB, outC, outD, outE, outF); + BOOST_CHECK(Ok == true); + BOOST_CHECK(a == outA); + BOOST_CHECK(b == outB); + BOOST_CHECK(c == outC); + BOOST_CHECK(d == outD); + BOOST_CHECK(e == outE); + BOOST_CHECK(f == outF); +} + +BOOST_AUTO_TEST_CASE(ContractABIType_u256) +{ + auto hashImpl = std::make_shared(); + ContractABICodec ct(hashImpl); + + u256 x = 0; + std::string r = *toHexString(ct.serialise(x)); + BOOST_CHECK(r == "0000000000000000000000000000000000000000000000000000000000000000"); + + u256 y("0x7fffffffffffffff"); + r = *toHexString(ct.serialise(y)); + BOOST_CHECK(r == "0000000000000000000000000000000000000000000000007fffffffffffffff"); + + u256 z("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + r = *toHexString(ct.serialise(z)); + BOOST_CHECK(r == "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + + u256 u("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"); + r = *toHexString(ct.serialise(u)); + BOOST_CHECK(r == "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"); +} + +BOOST_AUTO_TEST_CASE(ContractABIType_s256) +{ + auto hashImpl = std::make_shared(); + ContractABICodec ct(hashImpl); + + s256 x = 0; + std::string r = *toHexString(ct.serialise(x)); + BOOST_CHECK_EQUAL(r, "0000000000000000000000000000000000000000000000000000000000000000"); + + s256 y("0x7fffffffffffffff"); + r = *toHexString(ct.serialise(y)); + BOOST_CHECK_EQUAL(r, "0000000000000000000000000000000000000000000000007fffffffffffffff"); + + s256 z = -1; + r = *toHexString(ct.serialise(z)); + BOOST_CHECK_EQUAL(r, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + + s256 s = 1000; + r = *toHexString(ct.serialise(s)); + BOOST_CHECK_EQUAL(r, "00000000000000000000000000000000000000000000000000000000000003e8"); +} + +BOOST_AUTO_TEST_CASE(ContractABIType_integer) +{ + auto hashImpl = std::make_shared(); + auto codec = CodecWrapper(hashImpl, false); + + // int8 + { + int8_t i8 = INT8_MAX; + auto b = codec.encode(i8); + std::string r = *toHexString(b); + BOOST_CHECK_EQUAL(r, "000000000000000000000000000000000000000000000000000000000000007f"); + int8_t ri8; + codec.decode(ref(b), ri8); + BOOST_CHECK(i8 == ri8); + + i8 = INT8_MIN; + b = codec.encode(i8); + r = *toHexString(b); + BOOST_CHECK_EQUAL(r, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80"); + codec.decode(ref(b), ri8); + BOOST_CHECK(i8 == ri8); + } + + // int16 + { + int16_t i16 = INT16_MAX; + auto b = codec.encode(i16); + std::string r = *toHexString(b); + BOOST_CHECK_EQUAL(r, "0000000000000000000000000000000000000000000000000000000000007fff"); + int16_t ri16; + codec.decode(ref(b), ri16); + BOOST_CHECK(i16 == ri16); + + i16 = INT16_MIN; + b = codec.encode(i16); + r = *toHexString(b); + BOOST_CHECK_EQUAL(r, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8000"); + codec.decode(ref(b), ri16); + BOOST_CHECK(i16 == ri16); + } + + // int32 + { + int32_t i32 = INT32_MAX; + auto b = codec.encode(i32); + std::string r = *toHexString(b); + BOOST_CHECK_EQUAL(r, "000000000000000000000000000000000000000000000000000000007fffffff"); + int32_t ri32; + codec.decode(ref(b), ri32); + BOOST_CHECK(i32 == ri32); + + i32 = INT32_MIN; + b = codec.encode(i32); + r = *toHexString(b); + BOOST_CHECK_EQUAL(r, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000"); + codec.decode(ref(b), ri32); + BOOST_CHECK(i32 == ri32); + } + + // int64 + { + int64_t i64 = INT64_MAX; + auto b = codec.encode(i64); + std::string r = *toHexString(b); + BOOST_CHECK_EQUAL(r, "0000000000000000000000000000000000000000000000007fffffffffffffff"); + int64_t ri64; + codec.decode(ref(b), ri64); + BOOST_CHECK(i64 == ri64); + + i64 = INT64_MIN; + b = codec.encode(i64); + r = *toHexString(b); + BOOST_CHECK_EQUAL(r, "ffffffffffffffffffffffffffffffffffffffffffffffff8000000000000000"); + codec.decode(ref(b), ri64); + BOOST_CHECK(i64 == ri64); + } + + // uint8 + { + uint8_t u8 = UINT8_MAX; + auto b = codec.encode(u8); + std::string r = *toHexString(b); + BOOST_CHECK_EQUAL(r, "00000000000000000000000000000000000000000000000000000000000000ff"); + uint8_t ru8; + codec.decode(ref(b), ru8); + BOOST_CHECK(u8 == ru8); + + u8 = 0; + b = codec.encode(u8); + r = *toHexString(b); + BOOST_CHECK_EQUAL(r, "0000000000000000000000000000000000000000000000000000000000000000"); + codec.decode(ref(b), ru8); + BOOST_CHECK(u8 == ru8); + } + + // uint16 + { + uint16_t u16 = UINT16_MAX; + auto b = codec.encode(u16); + std::string r = *toHexString(b); + BOOST_CHECK_EQUAL(r, "000000000000000000000000000000000000000000000000000000000000ffff"); + uint16_t ru16; + codec.decode(ref(b), ru16); + BOOST_CHECK(u16 == ru16); + + u16 = 0; + b = codec.encode(u16); + r = *toHexString(b); + BOOST_CHECK_EQUAL(r, "0000000000000000000000000000000000000000000000000000000000000000"); + codec.decode(ref(b), ru16); + BOOST_CHECK(u16 == ru16); + } + + // uint32 + { + uint32_t u32 = UINT32_MAX; + auto b = codec.encode(u32); + std::string r = *toHexString(b); + BOOST_CHECK_EQUAL(r, "00000000000000000000000000000000000000000000000000000000ffffffff"); + uint32_t ru32; + codec.decode(ref(b), ru32); + BOOST_CHECK(u32 == ru32); + + u32 = 0; + b = codec.encode(u32); + r = *toHexString(b); + BOOST_CHECK_EQUAL(r, "0000000000000000000000000000000000000000000000000000000000000000"); + codec.decode(ref(b), ru32); + BOOST_CHECK(u32 == ru32); + } + + // uint64 + { + uint64_t u64 = UINT64_MAX; + auto b = codec.encode(u64); + std::string r = *toHexString(b); + BOOST_CHECK_EQUAL(r, "000000000000000000000000000000000000000000000000ffffffffffffffff"); + uint64_t ru64; + codec.decode(ref(b), ru64); + BOOST_CHECK(u64 == ru64); + + u64 = 0; + b = codec.encode(u64); + r = *toHexString(b); + BOOST_CHECK_EQUAL(r, "0000000000000000000000000000000000000000000000000000000000000000"); + codec.decode(ref(b), ru64); + BOOST_CHECK(u64 == ru64); + } +} + +BOOST_AUTO_TEST_CASE(ContractABIType_bool) +{ + auto hashImpl = std::make_shared(); + ContractABICodec ct(hashImpl); + + bool x = true; + std::string r = *toHexString(ct.serialise(x)); + BOOST_CHECK_EQUAL(r, "0000000000000000000000000000000000000000000000000000000000000001"); + + + bool y = false; + r = *toHexString(ct.serialise(y)); + BOOST_CHECK_EQUAL(r, "0000000000000000000000000000000000000000000000000000000000000000"); +} + +BOOST_AUTO_TEST_CASE(ContractABIType_addr) +{ + auto hashImpl = std::make_shared(); + ContractABICodec ct(hashImpl); + + Address x; + std::string r = *toHexString(ct.serialise(x)); + BOOST_CHECK_EQUAL(r, "0000000000000000000000000000000000000000000000000000000000000000"); + + + Address y("0xbe5422d15f39373eb0a97ff8c10fbd0e40e29338"); + r = *toHexString(ct.serialise(y)); + BOOST_CHECK_EQUAL(r, "000000000000000000000000be5422d15f39373eb0a97ff8c10fbd0e40e29338"); +} + +BOOST_AUTO_TEST_CASE(ContractABIType_string) +{ + auto hashImpl = std::make_shared(); + ContractABICodec ct(hashImpl); + + std::string x("Hello, world!"); + std::string r = *toHexString(ct.serialise(x)); + BOOST_CHECK_EQUAL( + r, std::string("000000000000000000000000000000000000000000000000000000000000000d48656c6c6" + "f2c20776f726c642100000000000000000000000000000000000000")); + + std::string y(""); + r = *toHexString(ct.serialise(y)); + BOOST_CHECK_EQUAL( + r, std::string("0000000000000000000000000000000000000000000000000000000000000000")); +} + +BOOST_AUTO_TEST_CASE(ContractABIType_array_uint256) +{ + auto hashImpl = std::make_shared(); + ContractABICodec ct(hashImpl); + + std::array x{1, 2, 3}; + std::string r = *toHexString(ct.serialise(x)); + BOOST_CHECK_EQUAL( + r, std::string("00000000000000000000000000000000000000000000000000000000000000010" + "0000000000000000000000000" + "00000000000000000000000000000000000002000000000000000000000000000" + "0000000000000000000000000" + "000000000003")); + + std::vector y{1, 2, 3}; + r = *toHexString(ct.serialise(y)); + BOOST_CHECK_EQUAL( + r, std::string("000000000000000000000000000000000000000000000000000000000000000300000000000" + "000000000000000" + "0000000000000000000000000000000000000100000000000000000000000000" + "000000000000000000000000000000000000020000000000000000000000000000000000000" + "000000000000000" + "000000000003")); +} + +BOOST_AUTO_TEST_CASE(ContractABITest0) +{ + auto hashImpl = std::make_shared(); + ContractABICodec ct(hashImpl); + + u256 a = 12345; + s256 b = -67890; + string c("xxxsxxxsxxs"); + string32 d = toString32(std::string("adsggsakjffl;kajsdf")); + + auto rb = ct.abiIn("", a, b, c, d); + auto r = *toHexString(rb); + + u256 outA; + s256 outB; + string outC; + string32 outD; + + bool Ok = ct.abiOutHex(r, outA, outB, outC, outD); + BOOST_CHECK(Ok == true); + BOOST_CHECK(a == outA); + BOOST_CHECK(b == outB); + BOOST_CHECK(c == outC); + BOOST_CHECK(d == outD); +} + +BOOST_AUTO_TEST_CASE(ContractABITest1) +{ + u256 a = 100; + s256 b = -100; + std::string c = "abc"; + std::vector d = {"abc", "abc", "abc"}; + std::array e{"abc", "abc", "abc"}; + + std::string expect = + "0000000000000000000000000000000000000000000000000000000000000064ffffffffffffffffffffffffff" + "ffffffffffffffffffffffffffffffffffff9c0000000000000000000000000000000000000000000000000000" + "0000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000" + "000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000" + "000000000000000000000003616263000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000" + "000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000" + "00000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000" + "000000000000000000000000000000000000000000000003616263000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000036162" + "630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000361626300000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000" + "00000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000" + "000000000000000000e00000000000000000000000000000000000000000000000000000000000000003616263" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000036162630000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000361626300000000000000" + "00000000000000000000000000000000000000000000"; + + auto hashImpl = std::make_shared(); + ContractABICodec ct(hashImpl); + auto rb = ct.abiIn("", a, b, c, d, e); + auto r = *toHexString(rb); + BOOST_CHECK_EQUAL(r, expect); + + u256 outA; + s256 outB; + std::string outC; + std::vector outD; + std::array outE; + + bool Ok = ct.abiOutHex(r, outA, outB, outC, outD, outE); + + BOOST_CHECK_EQUAL(Ok, true); + BOOST_CHECK(a == outA); + BOOST_CHECK(b == outB); + BOOST_CHECK(c == outC); + BOOST_CHECK(d == outD); + BOOST_CHECK(e == outE); +} + +BOOST_AUTO_TEST_CASE(ContractABITest2) +{ + std::array, 3> a{ + std::vector{1}, std::vector{2, 3}, std::vector{4, 5, 6}}; + + std::string expect = + "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000" + "000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000" + "0000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000" + "000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000" + "000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200" + "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000" + "000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000" + "000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000" + "000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000" + "0000000000000000000006"; + + auto hashImpl = std::make_shared(); + ContractABICodec ct(hashImpl); + auto rb = ct.abiIn("", a); + auto r = *toHexString(rb); + + BOOST_CHECK(r == expect); + + std::array, 3> outA; + bool Ok = ct.abiOutHex(r, outA); + + BOOST_CHECK_EQUAL(Ok, true); + BOOST_CHECK(a == outA); +} + +BOOST_AUTO_TEST_CASE(ContractABITest3) +{ + auto hashImpl = std::make_shared(); + ContractABICodec ct(hashImpl); + u256 a = 123; + Address b("0x692a70d2e424a56d2c6c27aa97d1a86395877b3a"); + std::string c = "string c"; + std::vector d{1, 2, 3}; + std::array e = {4, 5, 6}; + + std::vector f{"abc", "def", "ghi"}; + std::array g{"abc", "def", "ghi"}; + + std::vector> h{{1, 1, 1}, {2, 2, 2}, {3, 3, 3}}; + std::vector> i{{4, 4, 4}, {5, 5, 5}}; + + auto rb = ct.abiIn("", a, b, c, d, e, f, g, h, i); + auto r = *toHexString(rb); + + std::string expect = + "000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000069" + "2a70d2e424a56d2c6c27aa97d1a86395877b3a0000000000000000000000000000000000000000000000000000" + "00000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000" + "000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000" + "000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600" + "000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000" + "000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000" + "000000048000000000000000000000000000000000000000000000000000000000000006800000000000000000" + "000000000000000000000000000000000000000000000008737472696e67206300000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000" + "000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000" + "000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000" + "000000030000000000000000000000000000000000000000000000000000000000000003000000000000000000" + "000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000" + "000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000" + "000000000000000000000000000000000000000000000000000000000361626300000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000003646566000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000036768690000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000" + "000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000" + "0000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000" + "000361626300000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000003646566000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000036768690000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000" + "6000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000" + "000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000" + "000000000000030000000000000000000000000000000000000000000000000000000000000001000000000000" + "000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000" + "000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003" + "000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000" + "000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000" + "000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000" + "000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000" + "000000000000000000000003000000000000000000000000000000000000000000000000000000000000000300" + "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000" + "000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000" + "000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000" + "000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000" + "00000000000000000000050000000000000000000000000000000000000000000000000000000000000005"; + + + BOOST_CHECK_EQUAL(r, expect); + + u256 outA; + Address outB; + std::string outC; + std::vector outD; + std::array outE; + + std::vector outF; + std::array outG; + + std::vector> outH; + std::vector> outI; + bool Ok = ct.abiOutHex(r, outA, outB, outC, outD, outE, outF, outG, outH, outI); + + BOOST_CHECK(Ok == true); + BOOST_CHECK(a == outA); + BOOST_CHECK(b == outB); + BOOST_CHECK(c == outC); + BOOST_CHECK(d == outD); + BOOST_CHECK(e == outE); + BOOST_CHECK(f == outF); + BOOST_CHECK(g == outG); + BOOST_CHECK(h == outH); + BOOST_CHECK(i == outI); +} + +BOOST_AUTO_TEST_CASE(ContractABI_ABIType) +{ + std::string s = "string"; + ABIInType at; + auto ok = at.reset(s); + BOOST_CHECK(ok == true); + BOOST_CHECK(at.getType() == "string"); + BOOST_CHECK(at.dynamic() == true); + BOOST_CHECK(at.getEleType() == "string"); + BOOST_CHECK(at.rank() == 0); + + ok = at.reset("adf"); + BOOST_CHECK(ok == false); + + ok = at.reset("uint256"); + BOOST_CHECK(ok == true); + BOOST_CHECK(at.getType() == "uint256"); + BOOST_CHECK(at.dynamic() == false); + BOOST_CHECK(at.getEleType() == "uint256"); + BOOST_CHECK(at.rank() == 0); + + + ok = at.reset("bool"); + BOOST_CHECK(ok == true); + BOOST_CHECK(at.getType() == "bool"); + BOOST_CHECK(at.dynamic() == false); + BOOST_CHECK(at.getEleType() == "bool"); + BOOST_CHECK(at.rank() == 0); + + ok = at.reset("bool[]"); + BOOST_CHECK(ok == true); + BOOST_CHECK(at.getType() == "bool[]"); + BOOST_CHECK(at.dynamic() == true); + BOOST_CHECK(at.getEleType() == "bool"); + BOOST_CHECK(at.rank() == 1); + + ok = at.reset("bool[10]"); + BOOST_CHECK(ok == true); + BOOST_CHECK(at.getType() == "bool[10]"); + BOOST_CHECK(at.dynamic() == false); + BOOST_CHECK(at.getEleType() == "bool"); + BOOST_CHECK(at.extent(1) == 10); + BOOST_CHECK(at.rank() == 1); + + ok = at.reset("string[10][][20]"); + BOOST_CHECK(ok == true); + BOOST_CHECK(at.getType() == "string[10][][20]"); + BOOST_CHECK(at.dynamic() == true); + BOOST_CHECK(at.getEleType() == "string"); + BOOST_CHECK(at.extent(1) == 10); + BOOST_CHECK(at.extent(2) == 0); + BOOST_CHECK(at.extent(3) == 20); + BOOST_CHECK(at.rank() == 3); + + at.removeExtent(); + BOOST_CHECK(at.extent(1) == 10); + BOOST_CHECK(at.extent(2) == 0); + BOOST_CHECK(at.rank() == 2); + + at.removeExtent(); + BOOST_CHECK(at.extent(1) == 10); + BOOST_CHECK(at.rank() == 1); + + at.removeExtent(); + BOOST_CHECK(at.rank() == 0); +} + +BOOST_AUTO_TEST_CASE(ContractABI_ABIFunc0) +{ + std::string s = "transfer (string, uint256, int256, string[])"; + ABIFunc afunc; + auto ok = afunc.parser(s); + BOOST_CHECK(ok == true); + BOOST_CHECK(afunc.getFuncName() == "transfer"); + BOOST_CHECK(afunc.getSignature() == "transfer(string,uint256,int256,string[])"); + std::vector exp{"string", "uint256", "int256", "string[]"}; + BOOST_CHECK(afunc.getParamsType() == exp); +} + +BOOST_AUTO_TEST_CASE(ContractABI_ABIFunc1) +{ + std::string s0 = "register(string,uint25)"; + ABIFunc afunc0; + auto ok = afunc0.parser(s0); + BOOST_CHECK(ok == false); + + + std::string s1 = "f()"; + ABIFunc afunc1; + ok = afunc1.parser(s1); + BOOST_CHECK(ok == true); + BOOST_CHECK(afunc1.getFuncName() == "f"); + BOOST_CHECK(afunc1.getSignature() == "f()"); +} + +BOOST_AUTO_TEST_CASE(ContractABI_ABIFunc2) +{ + std::string s = "trans(string,uint256)"; + + ABIFunc afunc; + auto ok = afunc.parser(s); + BOOST_CHECK(ok == true); + + BOOST_CHECK(afunc.getFuncName() == "trans"); + BOOST_CHECK(afunc.getSignature() == "trans(string,uint256)"); + std::vector exp{"string", "uint256"}; + BOOST_CHECK(afunc.getParamsType() == exp); +} + +BOOST_AUTO_TEST_CASE(ContractABI_AbiOutString0) +{ + u256 u = 111111111; + std::string s = "test string"; + auto hashImpl = std::make_shared(); + ContractABICodec ct(hashImpl); + auto in = ct.abiIn("", u, s); + + ABIFunc afunc; + auto ok = afunc.parser("test(uint256,string)"); + BOOST_CHECK(ok == true); + + auto allTypes = afunc.getParamsType(); + + BOOST_CHECK(allTypes.size() == 2); + BOOST_CHECK(allTypes[0] == "uint256"); + BOOST_CHECK(allTypes[1] == "string"); + + std::vector allOut; + ct.abiOutByFuncSelector(bytesConstRef(&in), allTypes, allOut); + BOOST_CHECK(allOut.size() == 2); + BOOST_CHECK(allOut[0] == "111111111"); + BOOST_CHECK(allOut[1] == "test string"); +} + +BOOST_AUTO_TEST_CASE(ContractABI_AbiOutString1) +{ + u256 u = 111111111; + s256 i = -11111111; + std::string s = "aaaaaaa"; + auto hashImpl = std::make_shared(); + ContractABICodec ct(hashImpl); + auto in = ct.abiIn("", s, u, i); + + ABIFunc afunc; + auto ok = afunc.parser("f(string,uint256,int256)"); + BOOST_CHECK(ok == true); + + auto allTypes = afunc.getParamsType(); + + BOOST_CHECK(allTypes.size() == 3); + BOOST_CHECK(allTypes[0] == "string"); + BOOST_CHECK(allTypes[1] == "uint256"); + BOOST_CHECK(allTypes[2] == "int256"); + + std::vector allOut; + ct.abiOutByFuncSelector(bytesConstRef(&in), allTypes, allOut); + BOOST_CHECK(allOut.size() == 3); + BOOST_CHECK(allOut[1] == "111111111"); + BOOST_CHECK(allOut[2] == "-11111111"); + BOOST_CHECK(allOut[0] == "aaaaaaa"); +} + +BOOST_AUTO_TEST_CASE(testABIOutBytes) +{ + // test byte32 + std::string hashStr = "1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b"; + h256 hashData = h256(hashStr); + std::string plainText = "test"; + bytes plainBytes = *fromHexString(*toHexString(plainText)); + ABIFunc afunc; + auto ok = afunc.parser("f(bytes32,bytes,bytes32)"); + BOOST_CHECK(ok == true); + auto allTypes = afunc.getParamsType(); + + BOOST_CHECK(allTypes.size() == 3); + BOOST_CHECK(allTypes[0] == "bytes32"); + BOOST_CHECK(allTypes[1] == "bytes"); + BOOST_CHECK(allTypes[2] == "bytes32"); + + auto hashImpl = std::make_shared(); + ContractABICodec abi(hashImpl); + auto paramData = abi.abiIn("", toString32(hashData), plainBytes, toString32(hashData)); + string32 decodedParam1; + bytes decodedParam2; + string32 decodedParam3; + abi.abiOut(ref(paramData), decodedParam1, decodedParam2, decodedParam3); + + BOOST_CHECK(*toHexString(fromString32(decodedParam1)) == hashStr); + BOOST_CHECK(*toHexString(fromString32(decodedParam3)) == hashStr); + std::cout << "decodedParam1: " << *toHexString(decodedParam1) << std::endl; + std::cout << "decodedParam2: " << *toHexString(decodedParam2) << std::endl; + std::cout << "decodedParam3: " << *toHexString(decodedParam3) << std::endl; + BOOST_CHECK(*toHexString(decodedParam2) == *toHexString(plainText)); + + // test bytes + plainText = "testabcxxd"; + bytesConstRef refPlainBytes(plainText); + paramData = abi.abiIn("", refPlainBytes.toBytes()); + bytes decodedParam; + abi.abiOut(ref(paramData), decodedParam); + BOOST_CHECK(*toHexString(decodedParam) == *toHexString(plainText)); +} + +BOOST_AUTO_TEST_CASE(testABITuple) +{ + auto hashImpl = std::make_shared(); + ContractABICodec abi(hashImpl); + + // static tuple(uint32,uint32) + { + auto staticTuple = std::make_tuple(uint32_t(0), uint32_t(10)); + auto dynamicTuple = std::make_tuple(uint8_t(0), std::string("990")); + auto tupleV = std::vector({dynamicTuple}); + + auto encodedBytes = abi.abiIn("", tupleV, staticTuple); + auto s = toHexStringWithPrefix(encodedBytes); + BOOST_CHECK( + s == + "0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000a0000000000000000000000000000000000000000000000000000000000000001" + "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000040000000000000000000000000000000000000000000000000000000000000000339" + "39300000000000000000000000000000000000000000000000000000000000"); + + decltype(tupleV) resultV; + decltype(staticTuple) resultStatic; + abi.abiOut(ref(encodedBytes), resultV, resultStatic); + BOOST_CHECK(resultV.size() == 1); + BOOST_CHECK(std::get<0>(resultV.at(0)) == uint8_t(0)); + BOOST_CHECK(std::get<1>(resultV.at(0)) == std::string("990")); + BOOST_CHECK(std::get<0>(resultStatic) == uint32_t(0)); + BOOST_CHECK(std::get<1>(resultStatic) == uint32_t(10)); + } + + // static tuple(uint32,uint32,int8) + { + auto staticTuple = std::make_tuple(uint32_t(0), uint32_t(10), int8_t(-1)); + auto dynamicTuple1 = std::make_tuple(uint8_t(0), std::string("990")); + auto dynamicTuple2 = std::make_tuple(uint8_t(1), std::string("991")); + auto tupleV = std::vector({dynamicTuple1, dynamicTuple2}); + + auto encodedBytes = abi.abiIn("", tupleV, staticTuple, tupleV); + auto s = toHexStringWithPrefix(encodedBytes); + BOOST_CHECK( + s == + "0x00000000000000000000000000000000000000000000000000000000000000a000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000affffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "00000000000000000000000000000000000000000000000000000000000002000000000000000000000000" + "00000000000000000000000000000000000000000200000000000000000000000000000000000000000000" + "0000000000000000004000000000000000000000000000000000000000000000000000000000000000c000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000400000000000000000000000000000000000000000000000" + "00000000000000000339393000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000100000000000000000000000000" + "00000000000000000000000000000000000040000000000000000000000000000000000000000000000000" + "00000000000000033939310000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000020000000000000000000000000000" + "00000000000000000000000000000000004000000000000000000000000000000000000000000000000000" + "000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000040000000000000000000000000000000" + "00000000000000000000000000000000033939300000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000010000000000" + "00000000000000000000000000000000000000000000000000004000000000000000000000000000000000" + "00000000000000000000000000000003393931000000000000000000000000000000000000000000000000" + "0000000000"); + + decltype(tupleV) resultV1, resultV2; + decltype(staticTuple) resultStatic; + abi.abiOut(ref(encodedBytes), resultV1, resultStatic, resultV2); + BOOST_CHECK(resultV1.size() == 2); + BOOST_CHECK(std::get<0>(resultV1.at(0)) == uint8_t(0)); + BOOST_CHECK(std::get<1>(resultV1.at(0)) == std::string("990")); + BOOST_CHECK(std::get<0>(resultV1.at(1)) == uint8_t(1)); + BOOST_CHECK(std::get<1>(resultV1.at(1)) == std::string("991")); + + BOOST_CHECK(std::get<0>(resultStatic) == uint32_t(0)); + BOOST_CHECK(std::get<1>(resultStatic) == uint32_t(10)); + BOOST_CHECK(std::get<2>(resultStatic) == int8_t(-1)); + + BOOST_CHECK(resultV2.size() == 2); + BOOST_CHECK(std::get<0>(resultV2.at(0)) == uint8_t(0)); + BOOST_CHECK(std::get<1>(resultV2.at(0)) == std::string("990")); + BOOST_CHECK(std::get<0>(resultV2.at(1)) == uint8_t(1)); + BOOST_CHECK(std::get<1>(resultV2.at(1)) == std::string("991")); + } + + // tuple(vector(tuple(u256,string,string,u256))) + { + auto tuple1 = std::make_tuple(u256(1), std::string("id1"), std::string("test1"), u256(2)); + auto tuple2 = std::make_tuple(u256(1), std::string("id2"), std::string("test2"), u256(2)); + auto testEncode = std::make_tuple(std::vector{tuple1, tuple2}); + auto encodedBytes = abi.abiIn("", std::string("t_test"), testEncode); + // solc 0.6.12 + // solidity struct A{ enum,string,string,enum } struct B {A[]} + // B(A[]({1,"id1","test1",2},{1,"id2","test2",2})) + std::string bytesString = + "00000000000000000000000000000000000000000000000000000000000000400000000000000000000000" + "0000" + "00000000000000000000000000000000000080000000000000000000000000000000000000000000000000" + "0000" + "000000000006745f7465737400000000000000000000000000000000000000000000000000000000000000" + "0000" + "00000000000000000000000000000000000000000000000020000000000000000000000000000000000000" + "0000" + "00000000000000000000000200000000000000000000000000000000000000000000000000000000000000" + "4000" + "00000000000000000000000000000000000000000000000000000000000140000000000000000000000000" + "0000" + "00000000000000000000000000000000000100000000000000000000000000000000000000000000000000" + "0000" + "000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000" + "0000" + "00000000000000000000000000000000000000000000000200000000000000000000000000000000000000" + "0000" + "00000000000000000000036964310000000000000000000000000000000000000000000000000000000000" + "0000" + "00000000000000000000000000000000000000000000000000000000000574657374310000000000000000" + "0000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000" + "00000001000000000000000000000000000000000000000000000000000000000000008000000000000000" + "0000" + "00000000000000000000000000000000000000000000c00000000000000000000000000000000000000000" + "0000" + "00000000000000000002000000000000000000000000000000000000000000000000000000000000000369" + "6432" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000" + "00000000000000000000000000000005746573743200000000000000000000000000000000000000000000" + "0000" + "000000"; + std::string hexString = *toHexString(encodedBytes); + BOOST_CHECK(hexString == bytesString); + + decltype(testEncode) testDecode; + std::string testString; + abi.abiOut(ref(encodedBytes), testString, testDecode); + + BOOST_CHECK(std::get<0>(testDecode).size() == 2); + auto tupleDecode1 = std::get<0>(testDecode).at(0); + auto tupleDecode2 = std::get<0>(testDecode).at(1); + BOOST_CHECK(std::get<0>(tupleDecode1) == std::get<0>(tuple1)); + BOOST_CHECK(std::get<1>(tupleDecode1) == std::get<1>(tuple1)); + BOOST_CHECK(std::get<2>(tupleDecode1) == std::get<2>(tuple1)); + BOOST_CHECK(std::get<3>(tupleDecode1) == std::get<3>(tuple1)); + + BOOST_CHECK(std::get<0>(tupleDecode2) == std::get<0>(tuple2)); + BOOST_CHECK(std::get<1>(tupleDecode2) == std::get<1>(tuple2)); + BOOST_CHECK(std::get<2>(tupleDecode2) == std::get<2>(tuple2)); + BOOST_CHECK(std::get<3>(tupleDecode2) == std::get<3>(tuple2)); + } + + // simple tuple + { + auto test1 = std::make_tuple(std::string("123"), s256(-1), + Address("0x420f853b49838bd3e9466c85a4cc3428c960dde2"), + *fromHexString("420f853b49838bd3e9466c85a4cc3428c960dde2"), + std::vector({"123", "456"})); + auto test1Encode = abi.abiIn("", test1); + decltype(test1) test1Decode; + abi.abiOut(ref(test1Encode), test1Decode); + + BOOST_CHECK(std::get<0>(test1) == std::get<0>(test1Decode)); + BOOST_CHECK(std::get<1>(test1) == std::get<1>(test1Decode)); + BOOST_CHECK(std::get<2>(test1) == std::get<2>(test1Decode)); + BOOST_CHECK(std::get<3>(test1) == std::get<3>(test1Decode)); + BOOST_CHECK(std::get<4>(test1)[0] == std::get<4>(test1Decode)[0]); + BOOST_CHECK(std::get<4>(test1)[1] == std::get<4>(test1Decode)[1]); + } + + // tuple(u256,bool,vector(tuple(string,s256,Address,bytes,vector)),vector(tuple(string,s256,Address,bytes,vector))) + { + auto tuple1 = std::make_tuple(std::string("123"), s256(-1), + Address("0x420f853b49838bd3e9466c85a4cc3428c960dde2"), + *fromHexString("420f853b49838bd3e9466c85a4cc3428c960dde2"), + std::vector({"123", "456"})); + + auto tuple2 = std::make_tuple(std::string("456"), s256(-123), + Address("0x420f853b49838bd3e9466c85a4cc3428c360dde2"), + *fromHexString("420f853b49838bd3e9466c85a4cc3528c960dde2"), + std::vector({"321", "33333"})); + + std::vector tupleV1{tuple1, tuple2}; + std::vector tupleV2{tuple2, tuple1}; + + auto tupleTest1 = std::make_tuple(u256(321321441), true, tupleV1, tupleV2); + auto test1Encode = abi.abiIn("", tuple1, tupleTest1); + decltype(tuple1) tuple1Decode; + decltype(tupleTest1) test1Decode; + abi.abiOut(ref(test1Encode), tuple1Decode, test1Decode); + + BOOST_CHECK(std::get<0>(tuple1Decode) == std::get<0>(tuple1)); + BOOST_CHECK(std::get<1>(tuple1Decode) == std::get<1>(tuple1)); + BOOST_CHECK(std::get<2>(tuple1Decode) == std::get<2>(tuple1)); + BOOST_CHECK(std::get<3>(tuple1Decode) == std::get<3>(tuple1)); + BOOST_CHECK(std::get<4>(tuple1Decode)[0] == std::get<4>(tuple1)[0]); + BOOST_CHECK(std::get<4>(tuple1Decode)[1] == std::get<4>(tuple1)[1]); + + BOOST_CHECK(std::get<0>(test1Decode) == std::get<0>(tupleTest1)); + BOOST_CHECK(std::get<1>(test1Decode) == std::get<1>(tupleTest1)); + auto test1DecodeTupleV1 = std::get<2>(test1Decode); + auto test1DecodeTupleV2 = std::get<3>(test1Decode); + + BOOST_CHECK(std::get<0>(test1DecodeTupleV1[0]) == std::get<0>(tuple1)); + BOOST_CHECK(std::get<1>(test1DecodeTupleV1[0]) == std::get<1>(tuple1)); + BOOST_CHECK(std::get<2>(test1DecodeTupleV1[0]) == std::get<2>(tuple1)); + BOOST_CHECK(std::get<3>(test1DecodeTupleV1[0]) == std::get<3>(tuple1)); + BOOST_CHECK(std::get<4>(test1DecodeTupleV1[0])[0] == std::get<4>(tuple1)[0]); + BOOST_CHECK(std::get<4>(test1DecodeTupleV1[0])[1] == std::get<4>(tuple1)[1]); + + BOOST_CHECK(std::get<0>(test1DecodeTupleV1[1]) == std::get<0>(tuple2)); + BOOST_CHECK(std::get<1>(test1DecodeTupleV1[1]) == std::get<1>(tuple2)); + BOOST_CHECK(std::get<2>(test1DecodeTupleV1[1]) == std::get<2>(tuple2)); + BOOST_CHECK(std::get<3>(test1DecodeTupleV1[1]) == std::get<3>(tuple2)); + BOOST_CHECK(std::get<4>(test1DecodeTupleV1[1])[0] == std::get<4>(tuple2)[0]); + BOOST_CHECK(std::get<4>(test1DecodeTupleV1[1])[1] == std::get<4>(tuple2)[1]); + + BOOST_CHECK(std::get<0>(test1DecodeTupleV2[0]) == std::get<0>(tuple2)); + BOOST_CHECK(std::get<1>(test1DecodeTupleV2[0]) == std::get<1>(tuple2)); + BOOST_CHECK(std::get<2>(test1DecodeTupleV2[0]) == std::get<2>(tuple2)); + BOOST_CHECK(std::get<3>(test1DecodeTupleV2[0]) == std::get<3>(tuple2)); + BOOST_CHECK(std::get<4>(test1DecodeTupleV2[0])[0] == std::get<4>(tuple2)[0]); + BOOST_CHECK(std::get<4>(test1DecodeTupleV2[0])[1] == std::get<4>(tuple2)[1]); + + BOOST_CHECK(std::get<0>(test1DecodeTupleV2[1]) == std::get<0>(tuple1)); + BOOST_CHECK(std::get<1>(test1DecodeTupleV2[1]) == std::get<1>(tuple1)); + BOOST_CHECK(std::get<2>(test1DecodeTupleV2[1]) == std::get<2>(tuple1)); + BOOST_CHECK(std::get<3>(test1DecodeTupleV2[1]) == std::get<3>(tuple1)); + BOOST_CHECK(std::get<4>(test1DecodeTupleV2[1])[0] == std::get<4>(tuple1)[0]); + BOOST_CHECK(std::get<4>(test1DecodeTupleV2[1])[1] == std::get<4>(tuple1)[1]); + } +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-codec/test/unittests/ScaleCodecTest.cpp" "b/BFPL\345\243\271/bcos-codec/test/unittests/ScaleCodecTest.cpp" new file mode 100644 index 00000000..fa5da060 --- /dev/null +++ "b/BFPL\345\243\271/bcos-codec/test/unittests/ScaleCodecTest.cpp" @@ -0,0 +1,1149 @@ +/* + * Copyright (C) 2021 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. + */ +#include "bcos-codec/scale/Scale.h" +#include "bcos-codec/scale/ScaleDecoderStream.h" +#include "bcos-codec/scale/ScaleEncoderStream.h" +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::codec::scale; + +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(ScaleTest, TestPromptFixture) +/** + * @given collection of N of type uint8_t + * @when encode array and decode back + * @then given equal array + */ +template > +void testArray() +{ + for (auto value : + {0b0000'0000, 0b0011'0011, 0b0101'0101, 0b1010'1010, 0b1100'1100, 0b1111'1111}) + { + Array testee; + std::fill(testee.begin(), testee.end(), value); + + auto data = encode(testee); + auto decoded_res = decode((data)); + BOOST_CHECK(testee == decoded_res); + } +} +BOOST_AUTO_TEST_CASE(ArrayTest) +{ + testArray<0>(); + testArray<127>(); + testArray<128>(); + testArray<255>(); + testArray<256>(); + testArray<999>(); +} + +struct ThreeBooleans +{ + bool b1 = false; + bool b2 = false; + bool b3 = false; +}; + +template > +Stream& operator>>(Stream& s, ThreeBooleans& v) +{ + return s >> v.b1 >> v.b2 >> v.b3; +} +BOOST_AUTO_TEST_CASE(BoolTest) +{ + ScaleEncoderStream s1; + s1 << true; + std::vector data = {0x1}; + BOOST_CHECK(s1.data() == data); + ScaleEncoderStream s2; + s2 << false; + data = {0x0}; + BOOST_CHECK(s2.data() == data); + + // exception case + data = bytes{0, 1, 2}; + BOOST_CHECK_THROW(decode((data)), ScaleDecodeException); + + data = bytes{0, 1, 0}; + auto result = decode((data)); + BOOST_CHECK(result.b1 == false); + BOOST_CHECK(result.b2 == true); + BOOST_CHECK(result.b3 == false); +} +BOOST_AUTO_TEST_CASE(pairTest) +{ + uint8_t v1 = 1; + uint32_t v2 = 2; + ScaleEncoderStream s; + s << std::make_pair(v1, v2); + bytes data = {1, 2, 0, 0, 0}; + BOOST_CHECK(s.data() == data); + + data = {1, 2, 0, 0, 0}; + ScaleDecoderStream s2((data)); + using pair_type = std::pair; + pair_type pair{}; + s2 >> pair; + BOOST_CHECK(pair.first == 1); + BOOST_CHECK(pair.second == 2); +} + +template +void testMap(std::vector& t_v, std::vector& f_v, size_t _size = 1) +{ + std::map m; + for (size_t i = 0; i < _size; i++) + { + m.insert(std::make_pair(t_v[i], f_v[i])); + } + ScaleEncoderStream s; + s << m; + bytes encodeBytes = s.data(); + + ScaleDecoderStream s2((encodeBytes)); + std::map newMap; + s2 >> newMap; + + BOOST_CHECK(_size == newMap.size()); + bool allEqual = std::equal(m.begin(), m.end(), newMap.begin(), [&](auto& pair1, auto& pair2) { + return pair1.first == pair2.first && pair1.second == pair2.second; + }); + BOOST_CHECK(allEqual); +} + +BOOST_AUTO_TEST_CASE(mapTest) +{ + // string 2 string + { + std::vector v1{"test1", "test2"}; + std::vector v2{"test3", "test4"}; + testMap(v1, v2, v1.size()); + } + // string 2 bytes + { + std::vector v1{"test1", "test2"}; + std::vector v2{asBytes("test3"), asBytes("test4")}; + testMap(v1, v2, v1.size()); + } + // s256 2 u256 + { + std::vector v1{0, -1, -256123}; + std::vector v2{0, 1, 256123}; + testMap(v1, v2, v1.size()); + } + + // bool 2 vector + { + std::vector v1{true, false}; + std::vector v2_1 = {asBytes("test1"), asBytes("test2")}; + std::vector v2_2 = {asBytes("test3"), asBytes("test4")}; + std::vector> v2{v2_1, v2_2}; + testMap>(v1, v2, v1.size()); + } + + // bytes 2 map + { + std::vector v1{asBytes("test1"), asBytes("test2")}; + + std::map v2_1; + v2_1.insert(std::make_pair(Address("0x420f853b49838bd3e9466c85a4cc3428c960dde2"), true)); + v2_1.insert(std::make_pair(Address("0x120f853b49838bd3e9466c85a4cc3428c960dde2"), true)); + std::map v2_2; + v2_2.insert(std::make_pair(Address("0x420f853b49838bd3e9466c85a4cc3428c960d123"), false)); + v2_2.insert(std::make_pair(Address("0x420f853b49838bd3e9466c85a4cc3428c960d456"), true)); + std::vector> v2{v2_1, v2_2}; + testMap>(v1, v2, v1.size()); + } + + // map 2 map + { + std::map v1_1; + v1_1.insert(std::make_pair(123, asBytes("test1"))); + std::map v1_2; + v1_2.insert(std::make_pair(456, asBytes("test2"))); + std::vector> v1{v1_1, v1_2}; + + std::map v2_1; + v2_1.insert(std::make_pair(-789, asBytes("test3"))); + std::map v2_2; + v2_2.insert(std::make_pair(-987, asBytes("test4"))); + std::vector> v2{v2_1, v2_2}; + testMap, std::map>(v1, v2, v1.size()); + } +} + +BOOST_AUTO_TEST_CASE(testString) +{ + std::string v = "asdadad"; + ScaleEncoderStream s{}; + s << v; + bytes data = {28, 'a', 's', 'd', 'a', 'd', 'a', 'd'}; + BOOST_CHECK(s.data() == data); + + std::string v2 = "asdadad"; + ScaleEncoderStream s2; + s2 << v2; + data = {28, 'a', 's', 'd', 'a', 'd', 'a', 'd'}; + BOOST_CHECK(s.data() == data); + + data = {28, 'a', 's', 'd', 'a', 'd', 'a', 'd'}; + ScaleDecoderStream s3((data)); + s3 >> v; + BOOST_CHECK(v == "asdadad"); +} + + +BOOST_AUTO_TEST_CASE(testBytes1) +{ + std::string v = "0x1"; + FixedBytes<1> fb1(v); + ScaleEncoderStream s{}; + s << fb1; + bytes data = {1}; + BOOST_CHECK(s.data() == data); + FixedBytes<1> fb2; + ScaleDecoderStream sd((data)); + sd >> fb2; + BOOST_CHECK(fb1 == fb2); +} + +BOOST_AUTO_TEST_CASE(testBytes2) +{ + std::string v = "0x0102"; + FixedBytes<2> fb2_i(v); + ScaleEncoderStream s{}; + s << fb2_i; + bytes data = {1, 2}; + BOOST_CHECK(s.data() == data); + FixedBytes<2> fb2_o; + ScaleDecoderStream sd((data)); + sd >> fb2_o; + BOOST_CHECK(fb2_i == fb2_o); +} + +BOOST_AUTO_TEST_CASE(testBytes16) +{ + std::string v = "0x000102030405060708090a0b0c0d0e0f"; + FixedBytes<16> fb2_i(v); + ScaleEncoderStream s{}; + s << fb2_i; + bytes data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + BOOST_CHECK(s.data() == data); + FixedBytes<16> fb2_o; + ScaleDecoderStream sd((data)); + sd >> fb2_o; + BOOST_CHECK(fb2_i == fb2_o); +} + +BOOST_AUTO_TEST_CASE(testBytes22) +{ + std::string v = "0x000102030405060708090a0b0c0d0e0f10111213141516"; + FixedBytes<22> fb2_i(v); + ScaleEncoderStream s{}; + s << fb2_i; + bytes data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21}; + BOOST_CHECK(s.data() == data); + FixedBytes<22> fb2_o; + ScaleDecoderStream sd((data)); + sd >> fb2_o; + BOOST_CHECK(fb2_i == fb2_o); +} + +BOOST_AUTO_TEST_CASE(testBytes32) +{ + std::string v = "0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"; + FixedBytes<32> fb2_i(v); + ScaleEncoderStream s{}; + s << fb2_i; + bytes data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31}; + BOOST_CHECK(s.data() == data); + FixedBytes<32> fb2_o; + ScaleDecoderStream sd((data)); + sd >> fb2_o; + BOOST_CHECK(fb2_i == fb2_o); +} + +BOOST_AUTO_TEST_CASE(testTuple) +{ + { + uint8_t v1 = 1; + uint32_t v2 = 2; + uint8_t v3 = 3; + bytes expectedBytes = {1, 2, 0, 0, 0, 3}; + ScaleEncoderStream s; + s << std::make_tuple(v1, v2, v3); + BOOST_CHECK(s.data() == expectedBytes); + } + + { + bytes data = {1, 2, 0, 0, 0, 3}; + ScaleDecoderStream s2((data)); + using tuple_type = std::tuple; + tuple_type tuple{}; + s2 >> tuple; + auto&& [v1, v2, v3] = tuple; + BOOST_CHECK(v1 == 1); + BOOST_CHECK(v2 == 2); + BOOST_CHECK(v3 == 3); + } + + using tuple_type_t = std::tuple; + tuple_type_t tuple = std::make_tuple(uint8_t(1), uint16_t(3), uint8_t(2), uint32_t(4)); + auto encodeBytes = encode(tuple); + auto decodResult = decode((encodeBytes)); + BOOST_CHECK(decodResult == tuple); +} + +struct TestStruct +{ + std::string a; + int b; + inline bool operator==(const TestStruct& rhs) const { return a == rhs.a && b == rhs.b; } +}; +template > +Stream& operator<<(Stream& s, const TestStruct& test_struct) +{ + return s << test_struct.a << test_struct.b; +} + +template > +Stream& operator>>(Stream& s, TestStruct& test_struct) +{ + return s >> test_struct.a >> test_struct.b; +} + +BOOST_AUTO_TEST_CASE(ScaleConvenienceFuncsTest) +{ + TestStruct s1{"some_string", 42}; + auto encodedData = encode((s1)); + auto decodedResult = decode((encodedData)); + BOOST_CHECK(decodedResult == s1); + + std::string expectedString = "some_string"; + int expectedInt = 42; + encodedData = encode(expectedString, expectedInt); + decodedResult = decode(encodedData); + BOOST_CHECK(decodedResult.a == expectedString); + BOOST_CHECK(decodedResult.b == expectedInt); +} + +BOOST_AUTO_TEST_CASE(EncodeOptionalTest) +{ + // most simple case + { + ScaleEncoderStream s; + s << boost::optional{boost::none}; + BOOST_CHECK(s.data() == (bytes{0})); + } + // encode existing uint8_t + { + ScaleEncoderStream s; + s << boost::optional{1}; + BOOST_CHECK(s.data() == (bytes{1, 1})); + } + + { + // encode negative int8_t + ScaleEncoderStream s; + s << boost::optional{-1}; + BOOST_CHECK(s.data() == (bytes{1, 255})); + } + + // encode non-existing uint16_t + { + ScaleEncoderStream s; + s << boost::optional{boost::none}; + BOOST_CHECK(s.data() == (bytes{0})); + } + // encode existing uint16_t + { + ScaleEncoderStream s; + s << boost::optional{511}; + BOOST_CHECK(s.data() == (bytes{1, 255, 1})); + } + // encode existing uint32_t + { + ScaleEncoderStream s; + s << boost::optional{67305985}; + BOOST_CHECK(s.data() == (bytes{1, 1, 2, 3, 4})); + } +} + +BOOST_AUTO_TEST_CASE(DecodeOptionalTest) +{ + auto data = bytes{0, // first value + 1, 1, // second value + 1, 255, // third value + 0, // fourth value + 1, 255, 1, // fifth value + 1, 1, 2, 3, 4}; // sixth value + + auto stream = ScaleDecoderStream{data}; + // decode nullopt uint8_t + { + boost::optional opt; + stream >> opt; + BOOST_CHECK(opt.has_value() == false); + } + // decode optional uint8_t + { + boost::optional opt; + stream >> opt; + BOOST_CHECK(opt.has_value() == true); + BOOST_CHECK(*opt == 1); + } + // decode optional negative int8_t + { + boost::optional opt; + stream >> opt; + BOOST_CHECK(opt.has_value() == true); + BOOST_CHECK(*opt == -1); + } + // decode nullopt uint16_t + // it requires 1 zero byte just like any other nullopt + { + boost::optional opt; + stream >> opt; + BOOST_CHECK(opt.has_value() == false); + } + // decode optional uint16_t + { + boost::optional opt; + stream >> opt; + BOOST_CHECK(opt.has_value() == true); + BOOST_CHECK(*opt == 511); + } + // decode optional uint32_t + { + boost::optional opt; + stream >> opt; + BOOST_CHECK(opt.has_value() == true); + BOOST_CHECK(*opt == 67305985); + } +} + +struct FourOptBools +{ + boost::optional b1; + boost::optional b2; + boost::optional b3; + boost::optional b4; +}; +template > +Stream& operator>>(Stream& s, FourOptBools& v) +{ + return s >> v.b1 >> v.b2 >> v.b3 >> v.b4; +} + +BOOST_AUTO_TEST_CASE(encodeOptionalBoolSuccessTest) +{ + std::vector> values = {true, false, boost::none}; + ScaleEncoderStream s; + for (auto&& v : values) + { + s << v; + } + BOOST_CHECK(s.data() == (bytes{1, 2, 0})); + auto data = bytes{0, 1, 2, 3}; + BOOST_CHECK_THROW(decode(data), ScaleDecodeException); + data = bytes{0, 1, 2, 1}; + using optbool = boost::optional; + auto res = decode(data); + BOOST_CHECK(res.b1 == boost::none); + BOOST_CHECK(res.b2 == optbool(true)); + BOOST_CHECK(res.b3 == optbool(false)); + BOOST_CHECK(res.b4 == optbool(true)); +} + +BOOST_AUTO_TEST_CASE(scaleDecodeStreamTest) +{ + auto data = bytes{0, 1, 2}; + auto stream = ScaleDecoderStream{data}; + + for (size_t i = 0; i < data.size(); i++) + { + uint8_t byteData = 255u; + byteData = stream.nextByte(); + BOOST_CHECK(byteData == data.at(i)); + } + BOOST_CHECK_THROW(stream.nextByte(), std::exception); + data = bytes{0, 1}; + auto stream2 = ScaleDecoderStream{data}; + BOOST_CHECK(stream2.hasMore(0) == true); + BOOST_CHECK(stream2.hasMore(1) == true); + BOOST_CHECK(stream2.hasMore(2) == true); + BOOST_CHECK(stream2.hasMore(3) == false); + stream2.nextByte(); + BOOST_CHECK(stream2.hasMore(1) == true); + BOOST_CHECK(stream2.hasMore(2) == false); + stream2.nextByte(); + BOOST_CHECK(stream2.hasMore(1) == false); + BOOST_CHECK_THROW(stream2.nextByte(), std::exception); +} + + +std::pair makeCompactPair(CompactInteger v, bytes m) +{ + return std::make_pair(CompactInteger(std::move(v)), std::move(m)); +} + +void testCompactEncodeAndDecode(std::pair _compactData) +{ + ScaleEncoderStream s; + // encode + const auto& [value, match] = _compactData; + s << value; + BOOST_CHECK(s.data() == match); + // decode + ScaleDecoderStream s2(gsl::make_span(match)); + CompactInteger v{}; + s2 >> v; + BOOST_CHECK(v == value); +} + +BOOST_AUTO_TEST_CASE(scaleCompactTest) +{ + auto compactPair = makeCompactPair(0, {0}); + testCompactEncodeAndDecode(compactPair); + compactPair = makeCompactPair(1, {4}); + testCompactEncodeAndDecode(compactPair); + compactPair = makeCompactPair(63, {252}); + testCompactEncodeAndDecode(compactPair); + compactPair = makeCompactPair(64, {1, 1}); + testCompactEncodeAndDecode(compactPair); + compactPair = makeCompactPair(255, {253, 3}); + testCompactEncodeAndDecode(compactPair); + compactPair = makeCompactPair(511, {253, 7}); + testCompactEncodeAndDecode(compactPair); + compactPair = makeCompactPair(16383, {253, 255}); + testCompactEncodeAndDecode(compactPair); + compactPair = makeCompactPair(16384, {2, 0, 1, 0}); + testCompactEncodeAndDecode(compactPair); + compactPair = makeCompactPair(65535, {254, 255, 3, 0}); + testCompactEncodeAndDecode(compactPair); + compactPair = makeCompactPair(1073741823ul, {254, 255, 255, 255}); + testCompactEncodeAndDecode(compactPair); + compactPair = makeCompactPair(CompactInteger("1234567890123456789012345678901234567890"), + {0b110111, 210, 10, 63, 206, 150, 95, 188, 172, 184, 243, 219, 192, 117, 32, 201, 160, 3}); + testCompactEncodeAndDecode(compactPair); + compactPair = makeCompactPair(1073741824, {3, 0, 0, 0, 64}); + testCompactEncodeAndDecode(compactPair); + compactPair = + makeCompactPair(CompactInteger("224945689727159819140526925384299092943484855915095831" + "655037778630591879033574393515952034305194542857496045" + "531676044756160413302774714984450425759043258192756735"), + *fromHexString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFF")); + testCompactEncodeAndDecode(compactPair); +} + +BOOST_AUTO_TEST_CASE(testScaleEncodeFail) +{ + CompactInteger v(-1); + ScaleEncoderStream out{}; + BOOST_CHECK_THROW((out << v), std::exception); + BOOST_CHECK(out.data().size() == 0); // nothing was written to buffer + + CompactInteger v2( + "224945689727159819140526925384299092943484855915095831" + "655037778630591879033574393515952034305194542857496045" + "531676044756160413302774714984450425759043258192756736"); // 2^536 + + ScaleEncoderStream out2; + BOOST_CHECK_THROW((out2 << v2), std::exception); // value is too big, it is not encoded + BOOST_CHECK(out2.data().size() == 0); // nothing was written to buffer + + auto data = bytes{255, 255, 255, 255}; + BOOST_CHECK_THROW(decode(data), ScaleDecodeException); +} + +std::pair, bytes> makeVariantPair( + boost::variant v, bytes m) +{ + return std::pair, bytes>(std::move(v), std::move(m)); +} + +BOOST_AUTO_TEST_CASE(testScaleVariant) +{ + // encode uint8_t + ScaleEncoderStream s; + auto variantPair = makeVariantPair(uint8_t(1), {0, 1}); + const auto& [value, match] = variantPair; + s << value; + BOOST_CHECK(s.data() == match); + // decode uint8_t + ScaleDecoderStream s2(match); + boost::variant val{}; + s2 >> val; + BOOST_CHECK(boost::get(val) == 1); + + // encode uint32_t + variantPair = makeVariantPair(uint32_t(2), {1, 2, 0, 0, 0}); + ScaleEncoderStream s3; + const auto& [value2, match2] = variantPair; + s3 << value2; + BOOST_CHECK(s3.data() == match2); + ScaleDecoderStream s4(match2); + s4 >> val; + BOOST_CHECK(boost::get(val) == 2); +} + + +template +std::pair makeMatchPair(T value, const bytes& match) +{ + return std::make_pair(value, match); +} + +template +void testFixedWidthInteger(std::pair const& _matchPair, bool _check = true) +{ + auto [value, match] = _matchPair; + ScaleEncoderStream s; + s << value; + if (_check) + { + BOOST_CHECK(s.data() == match); + ScaleDecoderStream s2(match); + T v; + s2 >> v; + BOOST_CHECK(v == value); + } + std::cout << "##### value:" << std::to_string(value) << ", data:" << *toHexString(s.data()) + << std::endl; +} + +BOOST_AUTO_TEST_CASE(testFixedWidthIntegerCase) +{ + // Int8Test + std::cout << "##### int8_t test" << std::endl; + auto fixedWidthIntegerInt8 = makeMatchPair(0, {0}); + testFixedWidthInteger(fixedWidthIntegerInt8); + fixedWidthIntegerInt8 = makeMatchPair(-1, {255}); + testFixedWidthInteger(fixedWidthIntegerInt8); + fixedWidthIntegerInt8 = makeMatchPair(-128, {128}); + testFixedWidthInteger(fixedWidthIntegerInt8); + fixedWidthIntegerInt8 = makeMatchPair(-127, {129}); + testFixedWidthInteger(fixedWidthIntegerInt8); + fixedWidthIntegerInt8 = makeMatchPair(123, {123}); + testFixedWidthInteger(fixedWidthIntegerInt8); + fixedWidthIntegerInt8 = makeMatchPair(-15, {241}); + testFixedWidthInteger(fixedWidthIntegerInt8); + // UInt8Test + std::cout << "##### uint8_t test" << std::endl; + auto fixedWidthIntegerUInt8 = makeMatchPair(0, {0}); + testFixedWidthInteger(fixedWidthIntegerUInt8); + fixedWidthIntegerUInt8 = makeMatchPair(234, {234}); + testFixedWidthInteger(fixedWidthIntegerUInt8); + fixedWidthIntegerUInt8 = makeMatchPair(255, {255}); + testFixedWidthInteger(fixedWidthIntegerUInt8); + fixedWidthIntegerUInt8 = makeMatchPair(129, {255}); + testFixedWidthInteger(fixedWidthIntegerUInt8, false); + fixedWidthIntegerUInt8 = makeMatchPair(132, {255}); + testFixedWidthInteger(fixedWidthIntegerUInt8, false); + fixedWidthIntegerUInt8 = makeMatchPair(128, {255}); + testFixedWidthInteger(fixedWidthIntegerUInt8, false); + fixedWidthIntegerUInt8 = makeMatchPair(244, {255}); + testFixedWidthInteger(fixedWidthIntegerUInt8, false); + // Int16Test + std::cout << "##### int16_t test" << std::endl; + auto fixedWidthIntegerInt16 = makeMatchPair(0, {}); + testFixedWidthInteger(fixedWidthIntegerInt16, false); + fixedWidthIntegerInt16 = makeMatchPair(-32767, {1, 128}); + testFixedWidthInteger(fixedWidthIntegerInt16); + fixedWidthIntegerInt16 = makeMatchPair(-3, {}); + testFixedWidthInteger(fixedWidthIntegerInt16, false); + fixedWidthIntegerInt16 = makeMatchPair(3, {}); + testFixedWidthInteger(fixedWidthIntegerInt16, false); + fixedWidthIntegerInt16 = makeMatchPair(128, {}); + testFixedWidthInteger(fixedWidthIntegerInt16, false); + fixedWidthIntegerInt16 = makeMatchPair(-128, {}); + testFixedWidthInteger(fixedWidthIntegerInt16, false); + + fixedWidthIntegerInt16 = makeMatchPair(-1, {255, 255}); + testFixedWidthInteger(fixedWidthIntegerInt16); + fixedWidthIntegerInt16 = makeMatchPair(32767, {255, 127}); + testFixedWidthInteger(fixedWidthIntegerInt16); + fixedWidthIntegerInt16 = makeMatchPair(12345, {57, 48}); + testFixedWidthInteger(fixedWidthIntegerInt16); + fixedWidthIntegerInt16 = makeMatchPair(-12345, {199, 207}); + testFixedWidthInteger(fixedWidthIntegerInt16); + fixedWidthIntegerInt16 = makeMatchPair(255, {}); + testFixedWidthInteger(fixedWidthIntegerInt16, false); + fixedWidthIntegerInt16 = makeMatchPair(252, {}); + testFixedWidthInteger(fixedWidthIntegerInt16, false); + fixedWidthIntegerInt16 = makeMatchPair(244, {}); + testFixedWidthInteger(fixedWidthIntegerInt16, false); + // UInt16Test + std::cout << "##### uint16_t test" << std::endl; + auto fixedWidthIntegerUInt16 = makeMatchPair(32767, {255, 127}); + testFixedWidthInteger(fixedWidthIntegerUInt16); + fixedWidthIntegerUInt16 = makeMatchPair(65535, {}); + testFixedWidthInteger(fixedWidthIntegerUInt16, false); + fixedWidthIntegerUInt16 = makeMatchPair(0, {}); + testFixedWidthInteger(fixedWidthIntegerUInt16, false); + fixedWidthIntegerUInt16 = makeMatchPair(1, {}); + testFixedWidthInteger(fixedWidthIntegerUInt16, false); + + fixedWidthIntegerUInt16 = makeMatchPair(128, {}); + testFixedWidthInteger(fixedWidthIntegerUInt16, false); + fixedWidthIntegerUInt16 = makeMatchPair(255, {}); + testFixedWidthInteger(fixedWidthIntegerUInt16, false); + fixedWidthIntegerUInt16 = makeMatchPair(256, {}); + testFixedWidthInteger(fixedWidthIntegerUInt16, false); + fixedWidthIntegerUInt16 = makeMatchPair(12345, {57, 48}); + testFixedWidthInteger(fixedWidthIntegerUInt16); + // Int32Test + + std::cout << "##### int32_t test" << std::endl; + auto fixedWidthIntegerInt32 = makeMatchPair(2147483647l, {255, 255, 255, 127}); + testFixedWidthInteger(fixedWidthIntegerInt32); + fixedWidthIntegerInt32 = makeMatchPair(0, {}); + testFixedWidthInteger(fixedWidthIntegerInt32, false); + fixedWidthIntegerInt32 = makeMatchPair(-3, {}); + testFixedWidthInteger(fixedWidthIntegerInt32, false); + fixedWidthIntegerInt32 = makeMatchPair(3, {}); + testFixedWidthInteger(fixedWidthIntegerInt32, false); + fixedWidthIntegerInt32 = makeMatchPair(252, {}); + testFixedWidthInteger(fixedWidthIntegerInt32, false); + fixedWidthIntegerInt32 = makeMatchPair(-252, {}); + testFixedWidthInteger(fixedWidthIntegerInt32, false); + fixedWidthIntegerInt32 = makeMatchPair(255, {}); + testFixedWidthInteger(fixedWidthIntegerInt32, false); + fixedWidthIntegerInt32 = makeMatchPair(-255, {}); + testFixedWidthInteger(fixedWidthIntegerInt32, false); + fixedWidthIntegerInt32 = makeMatchPair(256, {}); + testFixedWidthInteger(fixedWidthIntegerInt32, false); + fixedWidthIntegerInt32 = makeMatchPair(-256, {}); + testFixedWidthInteger(fixedWidthIntegerInt32, false); + fixedWidthIntegerInt32 = makeMatchPair(257, {}); + testFixedWidthInteger(fixedWidthIntegerInt32, false); + fixedWidthIntegerInt32 = makeMatchPair(-257, {}); + testFixedWidthInteger(fixedWidthIntegerInt32, false); + fixedWidthIntegerInt32 = makeMatchPair(65535, {}); + testFixedWidthInteger(fixedWidthIntegerInt32, false); + fixedWidthIntegerInt32 = makeMatchPair(-65535, {}); + testFixedWidthInteger(fixedWidthIntegerInt32, false); + fixedWidthIntegerInt32 = makeMatchPair(-1, {255, 255, 255, 255}); + testFixedWidthInteger(fixedWidthIntegerInt32); + fixedWidthIntegerInt32 = makeMatchPair(1, {1, 0, 0, 0}); + testFixedWidthInteger(fixedWidthIntegerInt32); + // Uint32Test + std::cout << "##### uint32_t test" << std::endl; + auto fixedWidthIntegerUInt32 = makeMatchPair(16909060ul, {4, 3, 2, 1}); + testFixedWidthInteger(fixedWidthIntegerUInt32); + fixedWidthIntegerUInt32 = makeMatchPair(0, {}); + testFixedWidthInteger(fixedWidthIntegerUInt32, false); + fixedWidthIntegerUInt32 = makeMatchPair(1, {}); + testFixedWidthInteger(fixedWidthIntegerUInt32, false); + fixedWidthIntegerUInt32 = makeMatchPair(2, {}); + testFixedWidthInteger(fixedWidthIntegerUInt32, false); + fixedWidthIntegerUInt32 = makeMatchPair(127, {}); + testFixedWidthInteger(fixedWidthIntegerUInt32, false); + fixedWidthIntegerUInt32 = makeMatchPair(128, {}); + testFixedWidthInteger(fixedWidthIntegerUInt32, false); + fixedWidthIntegerUInt32 = makeMatchPair(129, {}); + testFixedWidthInteger(fixedWidthIntegerUInt32, false); + fixedWidthIntegerUInt32 = makeMatchPair(255, {}); + testFixedWidthInteger(fixedWidthIntegerUInt32, false); + fixedWidthIntegerUInt32 = makeMatchPair(256, {}); + testFixedWidthInteger(fixedWidthIntegerUInt32, false); + fixedWidthIntegerUInt32 = makeMatchPair(257, {}); + testFixedWidthInteger(fixedWidthIntegerUInt32, false); + fixedWidthIntegerUInt32 = makeMatchPair(65535, {}); + testFixedWidthInteger(fixedWidthIntegerUInt32, false); + fixedWidthIntegerUInt32 = makeMatchPair(65536, {}); + testFixedWidthInteger(fixedWidthIntegerUInt32, false); + fixedWidthIntegerUInt32 = makeMatchPair(65537, {}); + testFixedWidthInteger(fixedWidthIntegerUInt32, false); + fixedWidthIntegerUInt32 = makeMatchPair(2147483647ul, {}); + testFixedWidthInteger(fixedWidthIntegerUInt32, false); + + fixedWidthIntegerUInt32 = makeMatchPair(67305985, {1, 2, 3, 4}); + testFixedWidthInteger(fixedWidthIntegerUInt32); + // Int64Test + std::cout << "##### int64_t test" << std::endl; + auto fixedWidthIntegerInt64 = + makeMatchPair(578437695752307201ll, {1, 2, 3, 4, 5, 6, 7, 8}); + testFixedWidthInteger(fixedWidthIntegerInt64); + fixedWidthIntegerInt64 = makeMatchPair(-1, {255, 255, 255, 255, 255, 255, 255, 255}); + testFixedWidthInteger(fixedWidthIntegerInt64); + + fixedWidthIntegerInt64 = makeMatchPair(-1, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(1, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(-127, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(127, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(-128, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(128, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(129, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(-129, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(-255, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(255, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(256, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(-256, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(257, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(-257, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(65535, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(-65535, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + + fixedWidthIntegerInt64 = makeMatchPair(65536, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(-65536, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(65537, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(-65537, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(-2147483647, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(2147483647, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(-2147483648, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(2147483648, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(-2147483649, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(2147483649, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(67305985, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(-67305985, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + fixedWidthIntegerInt64 = makeMatchPair(0, {}); + testFixedWidthInteger(fixedWidthIntegerInt64, false); + + + // UInt64Test + std::cout << "##### uint64_t test" << std::endl; + auto fixedWidthIntegerUInt64 = + makeMatchPair(578437695752307201ull, {1, 2, 3, 4, 5, 6, 7, 8}); + testFixedWidthInteger(fixedWidthIntegerUInt64); + fixedWidthIntegerUInt64 = makeMatchPair(0, {}); + testFixedWidthInteger(fixedWidthIntegerUInt64, false); + fixedWidthIntegerUInt64 = makeMatchPair(1, {}); + testFixedWidthInteger(fixedWidthIntegerUInt64, false); + fixedWidthIntegerUInt64 = makeMatchPair(127, {}); + testFixedWidthInteger(fixedWidthIntegerUInt64, false); + fixedWidthIntegerUInt64 = makeMatchPair(128, {}); + testFixedWidthInteger(fixedWidthIntegerUInt64, false); + fixedWidthIntegerUInt64 = makeMatchPair(129, {}); + testFixedWidthInteger(fixedWidthIntegerUInt64, false); + fixedWidthIntegerUInt64 = makeMatchPair(255, {}); + testFixedWidthInteger(fixedWidthIntegerUInt64, false); + fixedWidthIntegerUInt64 = makeMatchPair(256, {}); + testFixedWidthInteger(fixedWidthIntegerUInt64, false); + fixedWidthIntegerUInt64 = makeMatchPair(257, {}); + testFixedWidthInteger(fixedWidthIntegerUInt64, false); + fixedWidthIntegerUInt64 = makeMatchPair(65535, {}); + testFixedWidthInteger(fixedWidthIntegerUInt64, false); + fixedWidthIntegerUInt64 = makeMatchPair(65536, {}); + testFixedWidthInteger(fixedWidthIntegerUInt64, false); + fixedWidthIntegerUInt64 = makeMatchPair(65537, {}); + testFixedWidthInteger(fixedWidthIntegerUInt64, false); + fixedWidthIntegerUInt64 = makeMatchPair(2147483647, {}); + testFixedWidthInteger(fixedWidthIntegerUInt64, false); + fixedWidthIntegerUInt64 = makeMatchPair(2147483648, {}); + testFixedWidthInteger(fixedWidthIntegerUInt64, false); + fixedWidthIntegerUInt64 = makeMatchPair(2147483649, {}); + testFixedWidthInteger(fixedWidthIntegerUInt64, false); +} + +BOOST_AUTO_TEST_CASE(testCollections) +{ + { + // 80 items of value 1 + bytes collection(80, 1); + auto match = bytes{65, 1}; // header + match.insert(match.end(), collection.begin(), collection.end()); + ScaleEncoderStream s; + s << collection; + auto&& out = s.data(); + BOOST_CHECK(out.size() == 82); + BOOST_CHECK(out == match); + } + + { + // test uint16_t collections + std::vector collectionInt16 = {1, 2, 3, 4}; + ScaleEncoderStream s; + s << collectionInt16; + auto&& out = s.data(); + // clang-format off + BOOST_CHECK(out == + (bytes{ + 16, // header + 1, 0, // first item + 2, 0, // second item + 3, 0, // third item + 4, 0 // fourth item + })); +} +{ + // test uint32_t collections + std::vector collectionUint32 = {50462976, 117835012, 185207048, + 252579084}; + ScaleEncoderStream s; + s << collectionUint32; + auto &&out = s.data(); + // clang-format off + BOOST_CHECK(out == + (bytes{ + 16, // header + 0, 1, 2, 3, // first item + 4, 5, 6, 7, // second item + 8, 9, 0xA, 0xB, // third item + 0xC, 0xD, 0xE, 0xF // fourth item + })); +} +{ + // test uint64_t collections +std::vector collection = {506097522914230528ull, + 1084818905618843912ull}; + ScaleEncoderStream s; + s << collection; + auto &&out = s.data(); + // clang-format off + BOOST_CHECK(out == + (bytes{ + 8, // header + 0, 1, 2, 3, // first item + 4, 5, 6, 7, // second item + 8, 9, 0xA, 0xB, // third item + 0xC, 0xD, 0xE, 0xF // fourth item + })); +} + +// test uint16_t collections +{ +std::vector collection; + auto length = 16384; + collection.reserve(length); + for (auto i = 0; i < length; ++i) { + collection.push_back(i % 256); + } + ScaleEncoderStream s; + s << collection; + auto &&out = s.data(); + BOOST_CHECK((size_t)out.size() == (size_t)(length * 2 + 4)); + // header takes 4 byte, + // first 4 bytes represent le-encoded value 2^16 + 2 + // which is compact-encoded value 2^14 = 16384 + auto stream = ScaleDecoderStream(gsl::make_span(out)); + CompactInteger res; + stream >> res; + BOOST_CHECK(res == 16384); + // now only 32768 bytes left in stream + BOOST_CHECK(stream.hasMore(32768)== true); + BOOST_CHECK(stream.hasMore(32769)== false); + for (auto i = 0; i < length; ++i) { + uint8_t data = 0u; + stream >> data; + BOOST_CHECK(data == i % 256); + stream >> data; + BOOST_CHECK(data == 0); + } + BOOST_CHECK(stream.hasMore(1) == false); +} + +{ +// test very long collections +/** + * @given very long collection of items of type uint8_t containing 2^20 items + * this number takes ~ 1 Mb of data + * where collection[i] == i % 256 + * @when encodeCollection is applied + * @then obtain byte array of length 1048576 + 4 bytes (header) bytes + * where first bytes repreent header, other are data itself + * where each byte after header == i%256 + */ +auto length = 1048576; // 2^20 + std::vector collection; + collection.reserve(length); + + for (auto i = 0; i < length; ++i) { + collection.push_back(i % 256); + } + ScaleEncoderStream s; + s << collection; + auto &&out = s.data(); + BOOST_CHECK((size_t)out.size() == (size_t)(length + 4)); + // header takes 4 bytes, + // first byte == (4-4) + 3 = 3, + // which means that number of items requires 4 bytes + // 3 next bytes are 0, and the last 4-th == 2^6 == 64 + // which is compact-encoded value 2^14 = 16384 + auto stream = ScaleDecoderStream(gsl::make_span(out)); + CompactInteger bi; + stream >> bi; + BOOST_CHECK(bi == 1048576); + + // now only 1048576 bytes left in stream + BOOST_CHECK(stream.hasMore(1048576) == true); + BOOST_CHECK(stream.hasMore(1048576 + 1) == false); + + for (auto i = 0; i < length; ++i) { + uint8_t data{0u}; + stream >> data; + BOOST_CHECK(data == i % 256); + } + BOOST_CHECK(stream.hasMore(1) ==false); +} +} + +template +void printData(T const& _data) +{ + ScaleEncoderStream encoder; + encoder << _data; + auto &&out = encoder.data(); + T decodedNumber; + ScaleDecoderStream decoder(gsl::make_span(out)); + decoder >> decodedNumber; + BOOST_CHECK(_data == decodedNumber); + std::cout << "#### value:" << _data << ", encoded:" << *toHexString(encoder.data()) << std::endl; +} +BOOST_AUTO_TEST_CASE(testU256) +{ + u256 number = 3453456346534; + ScaleEncoderStream encoder; + // encode + encoder << number; + // decode + u256 decodedNumber; + auto &&out = encoder.data(); + ScaleDecoderStream decoder(gsl::make_span(out)); + decoder >> decodedNumber; + std::cout << "#### number:" << number << ", decodedNumber:" << decodedNumber << std::endl; + BOOST_CHECK(number == decodedNumber); + + CompactInteger number2("123"); + ScaleEncoderStream encoder2; + encoder2 << number2; + auto &&out2 = encoder2.data(); + ScaleDecoderStream decoder2(gsl::make_span(out2)); + CompactInteger decodedNumber2; + decoder2 >> decodedNumber2; + std::cout << "#### number2:" << number2 << ", decodedNumber2:" << decodedNumber2 << std::endl; + BOOST_CHECK(number2 == decodedNumber2); + + std::cout << "##### u256 test" << std::endl; + printData((u256)0); + printData((u256)1); + printData((u256)127); + printData((u256)128); + printData((u256)129); + printData((u256)255); + printData((u256)256); + printData((u256)257); + printData((u256)65535); + printData((u256)65536); + printData((u256)65537); + printData((u256)2147483647); + printData((u256)2147483648); + printData((u256)2147483649); + printData((u256)123123122147483649); + std::cout << "##### u256 test end" << std::endl; +} +BOOST_AUTO_TEST_CASE(tests256) +{ + s256 number = 3453456346534; + ScaleEncoderStream encoder; + // encode + encoder << number; + // decode + s256 decodedNumber; + auto &&out = encoder.data(); + ScaleDecoderStream decoder(gsl::make_span(out)); + decoder >> decodedNumber; + std::cout << "#### number:" << number << ", decodedNumber:" << decodedNumber << std::endl; + BOOST_CHECK(number == decodedNumber); + + + s256 number2 = -3453456346534; + ScaleEncoderStream encoder2; + // encode + encoder2 << number2; + // decode + s256 decodedNumber2; + auto &&out2 = encoder2.data(); + ScaleDecoderStream decoder2(gsl::make_span(out2)); + decoder2 >> decodedNumber2; + std::cout << "#### number2:" << number2 << ", decodedNumber2:" << decodedNumber2 << std::endl; + BOOST_CHECK(number2 == decodedNumber2); + + std::cout << "##### s256 test" << std::endl; + printData((s256)0); + printData((s256)1); + printData((s256)127); + printData((s256)128); + printData((s256)129); + printData((s256)255); + printData((s256)256); + printData((s256)257); + printData((s256)65535); + printData((s256)65536); + printData((s256)65537); + printData((s256)2147483647); + printData((s256)2147483648); + printData((s256)2147483649); + printData((s256)123123122147483649); + + printData((s256)-1); + printData((s256)-127); + printData((s256)-128); + printData((s256)-129); + printData((s256)-255); + printData((s256)-256); + printData((s256)-257); + printData((s256)-65535); + printData((s256)-65536); + printData((s256)-65537); + printData((s256)-2147483647); + printData((s256)-2147483648); + printData((s256)-2147483649); + printData((s256)-123123122147483649); + std::cout << "##### s256 test end" << std::endl; +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-codec/test/unittests/main/main.cpp" "b/BFPL\345\243\271/bcos-codec/test/unittests/main/main.cpp" new file mode 100644 index 00000000..5029377e --- /dev/null +++ "b/BFPL\345\243\271/bcos-codec/test/unittests/main/main.cpp" @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 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 main.cpp + * @author: yujiechen, jimmyshi + * @date 2021-02-24 + */ +#define BOOST_TEST_MODULE FISCO_BCOS_Tests +#define BOOST_TEST_MAIN + +#include +#include diff --git "a/BFPL\345\243\271/bcos-crypto/CMakeLists.txt" "b/BFPL\345\243\271/bcos-crypto/CMakeLists.txt" new file mode 100644 index 00000000..b7ed28ed --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/CMakeLists.txt" @@ -0,0 +1,24 @@ +include(Version) +project(bcos-crypto VERSION ${VERSION}) + +file(GLOB_RECURSE SRCS bcos-crypto/*.cpp) + +find_package(OpenSSL REQUIRED) +find_package(TBB REQUIRED) +find_package(wedprcrypto REQUIRED) + +add_library(bcos-crypto STATIC ${SRCS}) +target_include_directories(bcos-crypto PUBLIC + $ + $) +target_link_libraries(bcos-crypto PUBLIC OpenSSL::SSL OpenSSL::Crypto wedprcrypto::crypto wedprcrypto::zkp bcos-utilities bcos-concepts TBB::tbb) + +if(TESTS) + enable_testing() + add_subdirectory(test) + add_subdirectory(demo) +endif() + +include(GNUInstallDirs) +install(TARGETS bcos-crypto EXPORT fiscobcosTargets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") +install(DIRECTORY "bcos-crypto" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" FILES_MATCHING PATTERN "*.h") \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/ChecksumAddress.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/ChecksumAddress.h" new file mode 100644 index 00000000..13697877 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/ChecksumAddress.h" @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2021 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 ChecksumAddress.h + * @author: xingqiangbai + * @date: 2021-07-30 + */ + +#pragma once + +#include +#include +#include +#include + +namespace bcos +{ +inline void toChecksumAddress(std::string& _addr, const std::string_view& addressHashHex) +{ + auto convertHexCharToInt = [](char byte) { + int ret = 0; + if (byte >= '0' && byte <= '9') + { + ret = byte - '0'; + } + else if (byte >= 'a' && byte <= 'f') + { + ret = byte - 'a' + 10; + } + else if (byte >= 'A' && byte <= 'F') + { + ret = byte - 'A' + 10; + } + return ret; + }; + for (size_t i = 0; i < _addr.size(); ++i) + { + if (isdigit(_addr[i])) + { + continue; + } + if (convertHexCharToInt(addressHashHex[i]) >= 8) + { + _addr[i] = toupper(_addr[i]); + } + } +} + +inline void toCheckSumAddress(std::string& _hexAddress, crypto::Hash::Ptr _hashImpl) +{ + boost::algorithm::to_lower(_hexAddress); + toChecksumAddress(_hexAddress, _hashImpl->hash(_hexAddress).hex()); +} + +inline void toAddress(std::string& _hexAddress, [[maybe_unused]] crypto::Hash::Ptr _hashImpl) +{ + boost::algorithm::to_lower(_hexAddress); + // toChecksumAddress(_hexAddress, _hashImpl->hash(_hexAddress).hex()); notice : + // toChecksumAddress must be used before rpc return +} + + +inline std::string toChecksumAddressFromBytes( + const std::string_view& _AddressBytes, crypto::Hash::Ptr _hashImpl) +{ + auto hexAddress = *toHexString(_AddressBytes); + toAddress(hexAddress, _hashImpl); + return hexAddress; +} + +inline std::string newEVMAddress( + bcos::crypto::Hash::Ptr _hashImpl, int64_t blockNumber, int64_t contextID, int64_t seq) +{ + auto hash = _hashImpl->hash(boost::lexical_cast(blockNumber) + "_" + + boost::lexical_cast(contextID) + "_" + + boost::lexical_cast(seq)); + + std::string hexAddress; + hexAddress.reserve(40); + boost::algorithm::hex(hash.data(), hash.data() + 20, std::back_inserter(hexAddress)); + + toAddress(hexAddress, _hashImpl); + + return hexAddress; +} + + +inline std::string newEVMAddress(bcos::crypto::Hash::Ptr _hashImpl, const std::string_view& _sender, + bytesConstRef _init, u256 const& _salt) +{ + auto hash = + _hashImpl->hash(bytes{0xff} + _sender + toBigEndian(_salt) + _hashImpl->hash(_init)); + + std::string hexAddress; + hexAddress.reserve(40); + boost::algorithm::hex(hash.data(), hash.data() + 20, std::back_inserter(hexAddress)); + + toAddress(hexAddress, _hashImpl); + + return hexAddress; +} + +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/TrivialObject.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/TrivialObject.h" new file mode 100644 index 00000000..90261e30 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/TrivialObject.h" @@ -0,0 +1,99 @@ +/** + * Copyright (C) 2021 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. + * + * @brief concepts for crypto + * @file Concepts.h + * @author: ancelmo + * @date 2022-05-06 + */ +#pragma once + +#include +#include +#include +#include + +namespace bcos::crypto::trivial +{ + +template +concept Value = std::is_trivial_v> && + !std::is_pointer_v>; + +template +concept Range = RANGES::contiguous_range> && + std::is_trivial_v>>; + +template +concept Object = Value || Range; + +constexpr size_t size(Object auto&& obj) +{ + auto view = toView(std::forward(obj)); + return view.size(); +} + +constexpr auto toView(trivial::Object auto&& object) +{ + using RawType = std::remove_cvref_t; + + if constexpr (trivial::Value) + { + using ByteType = + std::conditional_t>, + std::byte const, std::byte>; + std::span view{(ByteType*)&object, sizeof(object)}; + + return view; + } + else if constexpr (trivial::Range) + { + using ByteType = std::conditional_t< + std::is_const_v>>, + std::byte const, std::byte>; + std::span view{(ByteType*)std::data(object), + sizeof(std::remove_cvref_t>) * RANGES::size(object)}; + + return view; + } + else + { + static_assert(!sizeof(object), "Unsupported type!"); + } +} + +template +concept DynamicRange = requires(Range range, size_t newSize) +{ + RANGES::range; + range.resize(newSize); + range.reserve(newSize); +}; + +void resizeTo(RANGES::range auto& out, size_t size) +{ + if ((size_t)RANGES::size(out) < size) + { + if constexpr (DynamicRange>) + { + out.resize(size); + return; + } + + BOOST_THROW_EXCEPTION(std::runtime_error("Not enough output space!")); + } +} + +} // namespace bcos::crypto::trivial \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/digestsign/DigestSign.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/digestsign/DigestSign.h" new file mode 100644 index 00000000..e8327009 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/digestsign/DigestSign.h" @@ -0,0 +1,19 @@ +#pragma once +#include "../TrivialObject.h" +#include +#include +#include + +namespace bcos::crypto::digestsign +{ + +template +concept DigestSign = requires(DigestSignType digestSign) +{ + typename DigestSignType::Key; + typename DigestSignType::Sign; + + digestSign.sign(typename DigestSignType::Key const& key, trivial::Object auto const& hash); +}; + +} // namespace bcos::crypto::digestsign \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/digestsign/OpenSSLDigestSign.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/digestsign/OpenSSLDigestSign.h" new file mode 100644 index 00000000..ba8fc2f2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/digestsign/OpenSSLDigestSign.h" @@ -0,0 +1,101 @@ +#pragma once + +#include "DigestSign.h" +#include +#include +#include +#include + +namespace bcos::crypto::digestsign::openssl +{ + +enum DigestSignType +{ + SM2 +}; + +template +class OpenSSLDigestSign +{ +private: + constexpr static int id() + { + if constexpr (digestSignType == SM2) { return EVP_PKEY_SM2; } + else { static_assert(!sizeof(OpenSSLDigestSign), "Unknown DigestSignType!"); } + } + + struct EVPContext + { + EVPContext() : m_context(EVP_PKEY_CTX_new_id(id(), nullptr)) {} + + EVP_PKEY_CTX* context() { return m_context.get(); } + struct Deleter + { + void operator()(EVP_PKEY_CTX* p) const { EVP_PKEY_CTX_free(p); } + }; + std::unique_ptr m_context; + }; + +public: + constexpr static size_t KEY_SIZE = 32; + constexpr static size_t SIGN_SIZE = 32; + + class Key + { + public: + Key() : m_key{EVP_PKEY_new()} {}; + + Key(const Key&) = delete; + Key(Key&&) noexcept = default; + Key& operator=(const Key&) = delete; + Key& operator=(Key&&) noexcept = default; + ~Key() = default; + + private: + Key(EVP_PKEY* pkey) : m_key{pkey} {}; + + const EVP_PKEY* pkey() const { return m_key.get(); } + EVP_PKEY* pkey() { return m_key.get(); } + + void setPKey(EVP_PKEY* pkey) { m_key.reset(pkey); } + + struct Deleter + { + void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); } + }; + std::unique_ptr m_key; + }; + + struct Sign : public std::array + { + using std::array::array; + }; + + class KeyGenerator + { + public: + void gen(Key& key) + { + if (!EVP_PKEY_keygen_init(m_context.context())) [[unlikely]] + BOOST_THROW_EXCEPTION(std::runtime_error{"EVP_PKEY_keygen_init error!"}); + + EVP_PKEY** ppkey = nullptr; + if (!EVP_PKEY_keygen(m_context.context(), ppkey)) [[unlikely]] + BOOST_THROW_EXCEPTION(std::runtime_error{"EVP_PKEY_keygen error!"}); + + key.setPKey(*ppkey); + } + + private: + EVPContext m_context; + }; + + class Encrypter + { + }; +}; + +using OpenSSL_SM2_DigestSign = OpenSSLDigestSign; + +static_assert(DigestSign, "Assert OpenSSLHasher type"); +} // namespace bcos::crypto::digestsign::openssl \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/AESCrypto.cpp" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/AESCrypto.cpp" new file mode 100644 index 00000000..7d691d0f --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/AESCrypto.cpp" @@ -0,0 +1,76 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for AES encryption/decryption + * @file AESCrypto.cpp + * @date 2021.04.03 + * @author yujiechen + */ +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::crypto; + +bytesPointer bcos::crypto::AESEncrypt(const unsigned char* _plainData, size_t _plainDataSize, + const unsigned char* _key, size_t _keySize, const unsigned char* _ivData, size_t _ivDataSize) +{ + CInputBuffer plainText{(const char*)_plainData, _plainDataSize}; + + FixedBytes fixedKeyData(_key, _keySize); + CInputBuffer key{(const char*)fixedKeyData.data(), AES_KEY_SIZE}; + + FixedBytes FixedIVData(_ivData, _ivDataSize); + CInputBuffer ivData{(const char*)FixedIVData.data(), AES_IV_DATA_SIZE}; + + auto encryptedData = std::make_shared(); + size_t ciperDataSize = _plainDataSize + AES_MAX_PADDING_SIZE; + encryptedData->resize(ciperDataSize); + COutputBuffer encryptResult{(char*)encryptedData->data(), ciperDataSize}; + + if (wedpr_aes256_encrypt(&plainText, &key, &ivData, &encryptResult) == WEDPR_ERROR) + { + BOOST_THROW_EXCEPTION(EncryptException() << errinfo_comment("AES encrypt exception")); + } + encryptedData->resize(encryptResult.len); + return encryptedData; +} + +bytesPointer bcos::crypto::AESDecrypt(const unsigned char* _cipherData, size_t _cipherDataSize, + const unsigned char* _key, size_t _keySize, const unsigned char* _ivData, size_t _ivDataSize) +{ + CInputBuffer ciper{(const char*)_cipherData, _cipherDataSize}; + + FixedBytes fixedKeyData(_key, _keySize); + CInputBuffer key{(const char*)fixedKeyData.data(), AES_KEY_SIZE}; + + FixedBytes fixedIVData(_ivData, _ivDataSize); + CInputBuffer iv{(const char*)fixedIVData.data(), AES_IV_DATA_SIZE}; + + auto decodedData = std::make_shared(); + auto plainDataSize = _cipherDataSize; + decodedData->resize(plainDataSize); + COutputBuffer decodedResult{(char*)decodedData->data(), plainDataSize}; + + if (wedpr_aes256_decrypt(&ciper, &key, &iv, &decodedResult) == WEDPR_ERROR) + { + BOOST_THROW_EXCEPTION(DecryptException() << errinfo_comment("AES decrypt exception")); + } + decodedData->resize(decodedResult.len); + + return decodedData; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/AESCrypto.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/AESCrypto.h" new file mode 100644 index 00000000..0dba9715 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/AESCrypto.h" @@ -0,0 +1,67 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for AES encryption/decryption + * @file AESCrypto.h + * @date 2021.04.03 + * @author yujiechen + */ +#pragma once +#include + +namespace bcos +{ +namespace crypto +{ +const int AES_MAX_PADDING_SIZE = 26; +const int AES_KEY_SIZE = 32; +const int AES_IV_DATA_SIZE = 16; +bytesPointer AESEncrypt(const unsigned char* _plainData, size_t _plainDataSize, + const unsigned char* _key, size_t _keySize, const unsigned char* _ivData, size_t _ivDataSize); +bytesPointer AESDecrypt(const unsigned char* _cipherData, size_t _cipherDataSize, + const unsigned char* _key, size_t _keySize, const unsigned char* _ivData, size_t _ivDataSize); +class AESCrypto : public SymmetricEncryption +{ +public: + using Ptr = std::shared_ptr; + AESCrypto() = default; + ~AESCrypto() override {} + bytesPointer symmetricEncrypt(const unsigned char* _plainData, size_t _plainDataSize, + const unsigned char* _key, size_t _keySize) override + { + return symmetricEncrypt(_plainData, _plainDataSize, _key, _keySize, _key, AES_IV_DATA_SIZE); + } + bytesPointer symmetricDecrypt(const unsigned char* _cipherData, size_t _cipherDataSize, + const unsigned char* _key, size_t _keySize) override + { + return symmetricDecrypt( + _cipherData, _cipherDataSize, _key, _keySize, _key, AES_IV_DATA_SIZE); + } + + bytesPointer symmetricEncrypt(const unsigned char* _plainData, size_t _plainDataSize, + const unsigned char* _key, size_t _keySize, const unsigned char* _ivData, + size_t _ivDataSize) override + { + return AESEncrypt(_plainData, _plainDataSize, _key, _keySize, _ivData, _ivDataSize); + } + bytesPointer symmetricDecrypt(const unsigned char* _cipherData, size_t _cipherDataSize, + const unsigned char* _key, size_t _keySize, const unsigned char* _ivData, + size_t _ivDataSize) override + { + return AESDecrypt(_cipherData, _cipherDataSize, _key, _keySize, _ivData, _ivDataSize); + } +}; +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/Exceptions.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/Exceptions.h" new file mode 100644 index 00000000..9dd9a251 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/Exceptions.h" @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2021 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. + * + * @brief define the exceptions for encryption/decryption algorithm of bcos-crypto + * @file Execptions.h + * @date 2021.04.05 + * @author yujiechen + */ +#pragma once +#include + +namespace bcos +{ +namespace crypto +{ +DERIVE_BCOS_EXCEPTION(EncryptException); +DERIVE_BCOS_EXCEPTION(DecryptException); +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/SM4Crypto.cpp" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/SM4Crypto.cpp" new file mode 100644 index 00000000..f3e580a6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/SM4Crypto.cpp" @@ -0,0 +1,73 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for SM4 encryption/decryption + * @file SM4Crypto.cpp + * @date 2021.04.03 + * @author yujiechen + */ +#include +#include +#include +#include +using namespace bcos; +using namespace bcos::crypto; + +bytesPointer bcos::crypto::SM4Encrypt(const unsigned char* _plainData, size_t _plainDataSize, + const unsigned char* _key, size_t _keySize, const unsigned char* _ivData, size_t _ivDataSize) +{ + CInputBuffer plain{(const char*)_plainData, _plainDataSize}; + + FixedBytes fixedKeyData(_key, _keySize); + CInputBuffer key{(const char*)fixedKeyData.data(), SM4_KEY_SIZE}; + + FixedBytes fixedIVData(_ivData, _ivDataSize); + CInputBuffer iv{(const char*)fixedIVData.data(), SM4_IV_SIZE}; + + auto encryptedData = std::make_shared(); + encryptedData->resize(_plainDataSize + SM4_MAX_PADDING_LEN); + COutputBuffer ciper{(char*)encryptedData->data(), encryptedData->size()}; + + if (wedpr_sm4_encrypt(&plain, &key, &iv, &ciper) == WEDPR_ERROR) + { + BOOST_THROW_EXCEPTION(EncryptException() << errinfo_comment("SM4 encrypt exception")); + } + encryptedData->resize(ciper.len); + return encryptedData; +} + + +bytesPointer bcos::crypto::SM4Decrypt(const unsigned char* _cipherData, size_t _cipherDataSize, + const unsigned char* _key, size_t _keySize, const unsigned char* _ivData, size_t _ivDataSize) +{ + CInputBuffer cipher{(const char*)_cipherData, _cipherDataSize}; + + FixedBytes fixedKeyData(_key, _keySize); + CInputBuffer key{(const char*)fixedKeyData.data(), SM4_KEY_SIZE}; + + FixedBytes fixedIVData(_ivData, _ivDataSize); + CInputBuffer iv{(const char*)fixedIVData.data(), SM4_IV_SIZE}; + + auto decryptedData = std::make_shared(); + decryptedData->resize(_cipherDataSize); + COutputBuffer plain{(char*)decryptedData->data(), decryptedData->size()}; + + if (wedpr_sm4_decrypt(&cipher, &key, &iv, &plain) == WEDPR_ERROR) + { + BOOST_THROW_EXCEPTION(DecryptException() << errinfo_comment("SM4 decrypt exception")); + } + decryptedData->resize(plain.len); + return decryptedData; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/SM4Crypto.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/SM4Crypto.h" new file mode 100644 index 00000000..06cf5992 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/encrypt/SM4Crypto.h" @@ -0,0 +1,66 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for sm4 encryption/decryption + * @file SM4Crypto.h + * @date 2021.04.03 + * @author yujiechen + */ +#pragma once +#include + +namespace bcos +{ +namespace crypto +{ +const int SM4_MAX_PADDING_LEN = 26; +const int SM4_KEY_SIZE = 16; +const int SM4_IV_SIZE = 16; +bytesPointer SM4Encrypt(const unsigned char* _plainData, size_t _plainDataSize, + const unsigned char* _key, size_t _keySize, const unsigned char* _ivData, size_t _ivDataSize); +bytesPointer SM4Decrypt(const unsigned char* _cipherData, size_t _cipherDataSize, + const unsigned char* _key, size_t _keySize, const unsigned char* _ivData, size_t _ivDataSize); +class SM4Crypto : public SymmetricEncryption +{ +public: + using Ptr = std::shared_ptr; + SM4Crypto() = default; + ~SM4Crypto() override {} + bytesPointer symmetricEncrypt(const unsigned char* _plainData, size_t _plainDataSize, + const unsigned char* _key, size_t _keySize) override + { + return symmetricEncrypt(_plainData, _plainDataSize, _key, _keySize, _key, 16); + } + bytesPointer symmetricDecrypt(const unsigned char* _cipherData, size_t _cipherDataSize, + const unsigned char* _key, size_t _keySize) override + { + return symmetricDecrypt(_cipherData, _cipherDataSize, _key, _keySize, _key, 16); + } + + bytesPointer symmetricEncrypt(const unsigned char* _plainData, size_t _plainDataSize, + const unsigned char* _key, size_t _keySize, const unsigned char* _ivData, + size_t _ivDataSize) override + { + return SM4Encrypt(_plainData, _plainDataSize, _key, _keySize, _ivData, _ivDataSize); + } + bytesPointer symmetricDecrypt(const unsigned char* _cipherData, size_t _cipherDataSize, + const unsigned char* _key, size_t _keySize, const unsigned char* _ivData, + size_t _ivDataSize) override + { + return SM4Decrypt(_cipherData, _cipherDataSize, _key, _keySize, _ivData, _ivDataSize); + } +}; +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/hash/Keccak256.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/hash/Keccak256.h" new file mode 100644 index 00000000..4ca4e622 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/hash/Keccak256.h" @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Hash algorithm of keccak256 + * @file Keccak256.h + * @date 2021.03.04 + * @author yujiechen + */ +#pragma once + +#include +#include + +namespace bcos +{ +namespace crypto +{ + +inline HashType keccak256Hash(bytesConstRef _data) +{ + bcos::crypto::hasher::openssl::OpenSSL_Keccak256_Hasher hasher; + hasher.update(_data); + + HashType out; + hasher.final(out); + return out; +} + +class Keccak256 : public Hash +{ +public: + using Ptr = std::shared_ptr; + Keccak256() { setHashImplType(HashImplType::Keccak256Hash); } + ~Keccak256() override {} + HashType hash(bytesConstRef _data) override { return keccak256Hash(_data); } + bcos::crypto::hasher::AnyHasher hasher() override + { + return bcos::crypto::hasher::AnyHasher{ + bcos::crypto::hasher::openssl::OpenSSL_Keccak256_Hasher{}}; + } +}; +} // namespace crypto +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/hash/SM3.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/hash/SM3.h" new file mode 100644 index 00000000..a3a70063 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/hash/SM3.h" @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Hash algorithm of sm3 + * @file SM3.h + * @date 2021.03.04 + * @author yujiechen + */ +#pragma once +#include +#include + +namespace bcos +{ +namespace crypto +{ +HashType inline sm3Hash(bytesConstRef _data) +{ + hasher::openssl::OpenSSL_SM3_Hasher hasher; + hasher.update(_data); + + HashType out; + hasher.final(out); + return out; +} +class SM3 : public Hash +{ +public: + using Ptr = std::shared_ptr; + SM3() { setHashImplType(HashImplType::Sm3Hash); } + virtual ~SM3() {} + HashType hash(bytesConstRef _data) override { return sm3Hash(_data); } + + bcos::crypto::hasher::AnyHasher hasher() override + { + return bcos::crypto::hasher::AnyHasher{bcos::crypto::hasher::openssl::OpenSSL_SM3_Hasher{}}; + }; +}; +} // namespace crypto +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/hash/Sha256.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/hash/Sha256.h" new file mode 100644 index 00000000..0273add2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/hash/Sha256.h" @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Hash algorithm of sha3 + * @file Sha3.h + * @date 2021.04.01 + * @author yujiechen + */ +#pragma once +#include + +namespace bcos +{ +namespace crypto +{ +HashType inline sha256Hash(bytesConstRef _data) +{ + hasher::openssl::OpenSSL_SHA2_256_Hasher hasher; + hasher.update(_data); + + HashType out; + hasher.final(out); + return out; +} +class Sha256 : public Hash +{ +public: + using Ptr = std::shared_ptr; + Sha256() { setHashImplType(HashImplType::Sha3); } + virtual ~Sha256() {} + HashType hash(bytesConstRef _data) override { return sha256Hash(_data); } + bcos::crypto::hasher::AnyHasher hasher() override + { + return bcos::crypto::hasher::AnyHasher{ + bcos::crypto::hasher::openssl::OpenSSL_SHA3_256_Hasher{}}; + }; +}; +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/hash/Sha3.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/hash/Sha3.h" new file mode 100644 index 00000000..04aae8a9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/hash/Sha3.h" @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Hash algorithm of sha3 + * @file Sha3.h + * @date 2021.04.01 + * @author yujiechen + */ +#pragma once +#include + +namespace bcos +{ +namespace crypto +{ +HashType inline sha3Hash(bytesConstRef _data) +{ + hasher::openssl::OpenSSL_SHA3_256_Hasher hasher; + hasher.update(_data); + + HashType out; + hasher.final(out); + return out; +} + +class Sha3 : public Hash +{ +public: + using Ptr = std::shared_ptr; + Sha3() { setHashImplType(HashImplType::Sha3); } + virtual ~Sha3() {} + HashType hash(bytesConstRef _data) override { return sha3Hash(_data); } + bcos::crypto::hasher::AnyHasher hasher() override + { + return bcos::crypto::hasher::AnyHasher{ + bcos::crypto::hasher::openssl::OpenSSL_SHA3_256_Hasher{}}; + }; +}; +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/hasher/AnyHasher.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/hasher/AnyHasher.h" new file mode 100644 index 00000000..add67a69 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/hasher/AnyHasher.h" @@ -0,0 +1,12 @@ +#pragma once +#include "OpenSSLHasher.h" +#include + +namespace bcos::crypto::hasher +{ + +// Type erasure hasher +using AnyHasher = std::variant; + +} // namespace bcos::crypto::hasher \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/hasher/Hasher.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/hasher/Hasher.h" new file mode 100644 index 00000000..f9a0af8d --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/hasher/Hasher.h" @@ -0,0 +1,26 @@ +#pragma once +#include +#include +#include +#include + +namespace bcos::crypto::hasher +{ + +template +concept Hasher = requires(HasherType hasher, std::span out) +{ + HasherType{}; + HasherType::HASH_SIZE > 0; + hasher.update(std::span{}); + hasher.final(out); +}; + +auto final(Hasher auto& hasher) +{ + std::array::HASH_SIZE> out; + hasher.final(out); + return out; +} + +} // namespace bcos::crypto::hasher \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/hasher/IPPCryptoHasher.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/hasher/IPPCryptoHasher.h" new file mode 100644 index 00000000..2c4d9297 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/hasher/IPPCryptoHasher.h" @@ -0,0 +1,98 @@ +#ifdef ENABLE_IPPCRYPTO +#pragma once + +#include "Hasher.h" +#include "ippcp.h" +#include +#include +#include + +namespace bcos::crypto::ippcrypto +{ + +enum HasherType +{ + SM3_256, + SHA3_256, + SHA2_256, + Keccak256, +}; + +template +class IPPCryptoHasher : public bcos::crypto::HasherBase> +{ +public: + IPPCryptoHasher() + { + int hashStateSize; + if (ippsHashGetSize_rmf(&hashStateSize) != ippStsNoErr) [[unlikely]] + { + BOOST_THROW_EXCEPTION(std::runtime_error{"ippsHashGetSize_rmf error!"}); + } + + const IppsHashMethod* hashMethod; + if constexpr (hasherType == SM3_256) + { + hashMethod = ippsHashMethod_SM3(); + } + else if constexpr (hasherType == SHA2_256) + { + hashMethod = ippsHashMethod_SHA256_TT(); + } + else + { + static_assert(!sizeof(*this), "Unknown Hasher Type!"); + } + + m_hashState.reset(new std::byte[hashStateSize]); + if (ippsHashInit_rmf(hashState(), hashMethod) != ippStsNoErr) [[unlikely]] + { + BOOST_THROW_EXCEPTION(std::runtime_error{"ippsHashInit_rmf error!"}); + } + } + + void impl_update(std::span in) + { + if (ippsHashUpdate_rmf(reinterpret_cast(in.data()), + static_cast(in.size()), hashState()) != ippStsNoErr) [[unlikely]] + { + BOOST_THROW_EXCEPTION(std::runtime_error{"ippsHashUpdate_rmf error!"}); + } + } + + void impl_final(std::span out) + { + if (out.size() < HASH_SIZE) [[unlikely]] + { + BOOST_THROW_EXCEPTION(std::invalid_argument{"Output size too short!"}); + } + + if (ippsHashFinal_rmf(reinterpret_cast(out.data()), hashState()) != ippStsNoErr) + [[unlikely]] + { + BOOST_THROW_EXCEPTION(std::runtime_error{"ippsHashFinal_rmf error!"}); + } + } + + constexpr static size_t impl_hashSize() noexcept { return HASH_SIZE; } + +private: + constexpr static size_t HASH_SIZE = 32; + + IppsHashState_rmf* hashState() + { + return reinterpret_cast(m_hashState.get()); + } + + std::unique_ptr m_hashState; +}; + +using IPPCrypto_SM3_256_Hasher = IPPCryptoHasher; +using IPPCrypto_SHA2_256_Hasher = IPPCryptoHasher; + +static_assert(Hasher, "Assert Hasher type"); +static_assert(Hasher, "Assert Hasher type"); + +} // namespace bcos::crypto::ippcrypto + +#endif \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/hasher/OpenSSLHasher.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/hasher/OpenSSLHasher.h" new file mode 100644 index 00000000..4a97ae05 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/hasher/OpenSSLHasher.h" @@ -0,0 +1,150 @@ +#pragma once + +#include "../TrivialObject.h" +#include "Hasher.h" +#include +#include +#include +#include +#include +#include + +namespace bcos::crypto::hasher::openssl +{ +enum HasherType +{ + SM3, + SHA3_256, + SHA2_256, + Keccak256 +}; + +template +class OpenSSLHasher +{ +public: + OpenSSLHasher() : m_mdCtx(EVP_MD_CTX_new()), m_init(false) + { + if (!m_mdCtx) [[unlikely]] + { + BOOST_THROW_EXCEPTION(std::runtime_error{"EVP_MD_CTX_new error!"}); + } + } + + OpenSSLHasher(const OpenSSLHasher&) = delete; + OpenSSLHasher(OpenSSLHasher&&) = default; + OpenSSLHasher& operator=(const OpenSSLHasher&) = delete; + OpenSSLHasher& operator=(OpenSSLHasher&&) = default; + ~OpenSSLHasher() = default; + + constexpr static size_t HASH_SIZE = 32; + + void init() + { + auto md = chooseMD(); + + if (!EVP_DigestInit(m_mdCtx.get(), md)) [[unlikely]] + { + BOOST_THROW_EXCEPTION(std::runtime_error{"EVP_DigestInit error!"}); + } + + // Keccak256 special padding + if constexpr (hasherType == Keccak256) + { + struct KECCAK1600_CTX + { + uint64_t A[5][5]; + size_t block_size; + size_t md_size; + size_t num; + unsigned char buf[1600 / 8 - 32]; + unsigned char pad; + }; + + struct EVP_MD_CTX_Keccak256 + { + const EVP_MD* digest; + ENGINE* engine; + unsigned long flags; + + KECCAK1600_CTX* md_data; + }; + + auto keccak256 = reinterpret_cast(m_mdCtx.get()); + if (!keccak256->md_data || keccak256->md_data->pad != 0x06) // The sha3 origin pad + { + BOOST_THROW_EXCEPTION(std::runtime_error{ + "OpenSSL KECCAK1600_CTX layout error! Maybe untested openssl version"}); + } + keccak256->md_data->pad = 0x01; + } + } + + void update(bcos::crypto::trivial::Object auto const& in) + { + if (!m_init) + { + init(); + m_init = true; + } + auto view = bcos::crypto::trivial::toView(std::forward(in)); + + if (!EVP_DigestUpdate(m_mdCtx.get(), view.data(), view.size())) [[unlikely]] + { + BOOST_THROW_EXCEPTION(std::runtime_error{"EVP_DigestUpdate error!"}); + } + } + + void final(bcos::crypto::trivial::Object auto& out) + { + m_init = false; + bcos::crypto::trivial::resizeTo(out, HASH_SIZE); + auto view = bcos::crypto::trivial::toView(std::forward(out)); + + if (!EVP_DigestFinal(m_mdCtx.get(), reinterpret_cast(view.data()), nullptr)) + [[unlikely]] + { + BOOST_THROW_EXCEPTION(std::runtime_error{"EVP_DigestFinal error!"}); + } + } + + constexpr const EVP_MD* chooseMD() + { + if constexpr (hasherType == SM3) + { + return EVP_sm3(); + } + else if constexpr (hasherType == SHA3_256 || hasherType == Keccak256) + { + return EVP_sha3_256(); + } + else if constexpr (hasherType == SHA2_256) + { + return EVP_sha256(); + } + else + { + static_assert(!sizeof(*this), "Unknown EVP Type!"); + } + } + + struct Deleter + { + void operator()(EVP_MD_CTX* p) const { EVP_MD_CTX_free(p); } + }; + + std::unique_ptr m_mdCtx; + bool m_init; +}; + +using OpenSSL_SHA3_256_Hasher = OpenSSLHasher; +using OpenSSL_SHA2_256_Hasher = OpenSSLHasher; +using OpenSSL_SM3_Hasher = OpenSSLHasher; +using OpenSSL_Keccak256_Hasher = OpenSSLHasher; + +static_assert(Hasher, "Assert OpenSSLHasher type"); +static_assert(Hasher, "Assert OpenSSLHasher type"); +static_assert(Hasher, "Assert OpenSSLHasher type"); +static_assert(Hasher, "Assert OpenSSLHasher type"); + +} // namespace bcos::crypto::hasher::openssl diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/CommonType.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/CommonType.h" new file mode 100644 index 00000000..0f94009b --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/CommonType.h" @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2021 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. + * + * @brief common types for crypto + * @file CommonType.h + * @author: yujiechen + * @date 2021-04-01 + */ +#pragma once +#include +#include + +#define CRYPTO_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("CRYPTO") +namespace bcos::crypto +{ +using HashType = h256; +using HashList = std::vector; +using HashListPtr = std::shared_ptr; + +} // namespace bcos::crypto \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/CryptoSuite.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/CryptoSuite.h" new file mode 100644 index 00000000..478884e5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/CryptoSuite.h" @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2021 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. + * + * @brief crypto suite toolkit + * @file CryptoSuite.h + * @author: yujiechen + * @date 2021-03-03 + */ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace crypto +{ +class CryptoSuite +{ +public: + using Ptr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + + CryptoSuite(Hash::Ptr _hashImpl, SignatureCrypto::Ptr _signatureImpl, + SymmetricEncryption::Ptr _symmetricEncryptionHandler) + : m_hashImpl(_hashImpl), + m_signatureImpl(_signatureImpl), + m_symmetricEncryptionHandler(_symmetricEncryptionHandler) + {} + virtual ~CryptoSuite() {} + Hash::Ptr hashImpl() { return m_hashImpl; } + SignatureCrypto::Ptr signatureImpl() { return m_signatureImpl; } + SymmetricEncryption::Ptr symmetricEncryptionHandler() { return m_symmetricEncryptionHandler; } + + template + HashType hash(T&& _data) + { + return m_hashImpl->hash(_data); + } + + virtual Address calculateAddress(PublicPtr _public) + { + return right160(m_hashImpl->hash(_public)); + } + + virtual void setKeyFactory(KeyFactory::Ptr _keyFactory) { m_keyFactory = _keyFactory; } + virtual KeyFactory::Ptr keyFactory() { return m_keyFactory; } + +private: + Hash::Ptr m_hashImpl; + SignatureCrypto::Ptr m_signatureImpl; + SymmetricEncryption::Ptr m_symmetricEncryptionHandler; + KeyFactory::Ptr m_keyFactory; +}; +} // namespace crypto +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/Hash.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/Hash.h" new file mode 100644 index 00000000..658620d8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/Hash.h" @@ -0,0 +1,78 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interfaces for Hash + * @file Hash.h + * @author: yujiechen + * @date 2021-03-03 + */ +#pragma once +#include "../../hasher/AnyHasher.h" +#include +#include +#include +#include +namespace bcos +{ +namespace crypto +{ +enum HashImplType : int +{ + Keccak256Hash, + Sm3Hash, + Sha3 +}; +class Hash +{ +public: + using Ptr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + Hash() = default; + virtual ~Hash() {} + virtual HashType hash(bytesConstRef _data) = 0; + virtual HashType emptyHash() + { + if (HashType() == m_emptyHash) + { + m_emptyHash = hash(bytesConstRef()); + } + return m_emptyHash; + } + virtual HashType hash(bytes const& _data) + { + return hash(bytesConstRef(_data.data(), _data.size())); + } + virtual HashType hash(std::string const& _data) { return hash(bytesConstRef(_data)); } + + template + inline HashType hash(FixedBytes const& _input) + { + return hash(_input.ref()); + } + + inline HashType hash(PublicPtr _public) { return hash(_public->data()); } + + inline void setHashImplType(HashImplType _type) { m_type = _type; } + + inline HashImplType getHashImplType() const { return m_type; } + + virtual bcos::crypto::hasher::AnyHasher hasher() = 0; + +private: + HashType m_emptyHash = HashType(); + HashImplType m_type = Keccak256Hash; +}; +} // namespace crypto +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/KeyFactory.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/KeyFactory.h" new file mode 100644 index 00000000..ff7dcff5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/KeyFactory.h" @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for Public/Secret key creation + * @file KeyFactory.h + * @author: yujiechen + * @date 2021-05-10 + */ +#pragma once +#include +namespace bcos +{ +namespace crypto +{ +class KeyFactory +{ +public: + using Ptr = std::shared_ptr; + KeyFactory() = default; + virtual ~KeyFactory() {} + + virtual KeyInterface::Ptr createKey(bytesConstRef _keyData) = 0; + virtual KeyInterface::Ptr createKey(bytes const& _keyData) = 0; +}; +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/KeyInterface.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/KeyInterface.h" new file mode 100644 index 00000000..a4450724 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/KeyInterface.h" @@ -0,0 +1,66 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for Public/Secret key + * @file KeyInterface.h + * @author: yujiechen + * @date 2021-04-02 + */ +#pragma once +#include +#include +namespace bcos +{ +namespace crypto +{ +class KeyInterface +{ +public: + using Ptr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + KeyInterface() = default; + virtual ~KeyInterface() {} + virtual const bytes& data() const = 0; + virtual size_t size() const = 0; + virtual char* mutableData() = 0; + virtual const char* constData() const = 0; + virtual std::shared_ptr encode() const = 0; + virtual void decode(bytesConstRef _data) = 0; + virtual void decode(bytes&& _data) = 0; + + virtual std::string shortHex() = 0; + virtual std::string hex() = 0; +}; +using Public = KeyInterface; +using Secret = KeyInterface; +using PublicPtr = KeyInterface::Ptr; +using SecretPtr = KeyInterface::Ptr; +using NodeIDPtr = KeyInterface::Ptr; +using NodeIDs = std::vector; +using NodeIDListPtr = std::shared_ptr; + +struct KeyCompare +{ +public: + bool operator()(KeyInterface::Ptr const& _first, KeyInterface::Ptr const& _second) const + { + // increase order + return _first->data() < _second->data(); + } +}; +using NodeIDSet = std::set; +using NodeIDSetPtr = std::shared_ptr; +} // namespace crypto +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/KeyPairFactory.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/KeyPairFactory.h" new file mode 100644 index 00000000..90857e61 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/KeyPairFactory.h" @@ -0,0 +1,37 @@ +/** + * 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 KeyPairFactory.h + * @author: yujiechen + * @date 2022-01-18 + */ +#pragma once +#include +#include +namespace bcos +{ +namespace crypto +{ +class KeyPairFactory +{ +public: + using Ptr = std::shared_ptr; + KeyPairFactory() = default; + virtual ~KeyPairFactory() {} + virtual KeyPairInterface::UniquePtr createKeyPair(SecretPtr _secretKey) = 0; + virtual KeyPairInterface::UniquePtr generateKeyPair() = 0; +}; +} // namespace crypto +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/KeyPairInterface.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/KeyPairInterface.h" new file mode 100644 index 00000000..a686e1ba --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/KeyPairInterface.h" @@ -0,0 +1,50 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interfaces for KeyPair + * @file KeyPairInterface.h + * @author: yujiechen + * @date 2021-04-02 + */ +#pragma once +#include +#include +#include +namespace bcos +{ +namespace crypto +{ +enum class KeyPairType : int +{ + Secp256K1 = 0, + SM2 = 1, + Ed25519 = 2 +}; +class KeyPairInterface +{ +public: + using Ptr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + + KeyPairInterface() = default; + virtual ~KeyPairInterface() {} + + virtual SecretPtr secretKey() const = 0; + virtual PublicPtr publicKey() const = 0; + virtual Address address(Hash::Ptr _hashImpl) = 0; + virtual KeyPairType keyPairType() const = 0; +}; +} // namespace crypto +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/Signature.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/Signature.h" new file mode 100644 index 00000000..944521a0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/Signature.h" @@ -0,0 +1,60 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interfaces for Signature + * @file Signature.h + * @author: yujiechen + * @date 2021-03-03 + */ +#pragma once +#include +#include +#include +#include +#include +namespace bcos +{ +namespace crypto +{ +class SignatureCrypto +{ +public: + using Ptr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + SignatureCrypto() = default; + virtual ~SignatureCrypto() {} + + // sign returns a signature of a given hash + virtual std::shared_ptr sign(const KeyPairInterface& _keyPair, const HashType& _hash, + bool _signatureWithPub = false) = 0; + + // verify checks whether a signature is calculated from a given hash + virtual bool verify(PublicPtr _pubKey, const HashType& _hash, bytesConstRef _signatureData) = 0; + virtual bool verify(std::shared_ptr _pubKeyBytes, const HashType& _hash, + bytesConstRef _signatureData) = 0; + + // recover recovers the public key from the given signature + virtual PublicPtr recover(const HashType& _hash, bytesConstRef _signatureData) = 0; + + // generateKeyPair generates keyPair + virtual KeyPairInterface::UniquePtr generateKeyPair() = 0; + + // recoverAddress recovers address from a signature(for precompiled) + virtual std::pair recoverAddress(Hash::Ptr _hashImpl, bytesConstRef _in) = 0; + + virtual KeyPairInterface::UniquePtr createKeyPair(SecretPtr _secretKey) = 0; +}; +} // namespace crypto +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/SymmetricEncryption.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/SymmetricEncryption.h" new file mode 100644 index 00000000..b4475184 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/interfaces/crypto/SymmetricEncryption.h" @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interfaces for Symmetric encryption + * @file SymmetricEncryption.h + * @author: yujiechen + * @date 2021-03-03 + */ + +#pragma once +#include +#include +#include + +namespace bcos +{ +namespace crypto +{ +class SymmetricEncryption +{ +public: + using Ptr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + SymmetricEncryption() = default; + virtual ~SymmetricEncryption() {} + + // symmetricEncrypt encrypts plain data with default ivData + virtual bytesPointer symmetricEncrypt(const unsigned char* _plainData, size_t _plainDataSize, + const unsigned char* _key, size_t _keySize) = 0; + // symmetricDecrypt encrypts plain data with default ivData + virtual bytesPointer symmetricDecrypt(const unsigned char* _cipherData, size_t _cipherDataSize, + const unsigned char* _key, size_t _keySize) = 0; + + // symmetricEncrypt encrypts plain data with given ivData + virtual bytesPointer symmetricEncrypt(const unsigned char* _plainData, size_t _plainDataSize, + const unsigned char* _key, size_t _keySize, const unsigned char* _ivData, + size_t _ivDataSize) = 0; + // symmetricDecrypt encrypts plain data with given ivData + virtual bytesPointer symmetricDecrypt(const unsigned char* _cipherData, size_t _cipherDataSize, + const unsigned char* _key, size_t _keySize, const unsigned char* _ivData, + size_t _ivDataSize) = 0; +}; +} // namespace crypto +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/merkle/Merkle.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/merkle/Merkle.h" new file mode 100644 index 00000000..3f70d2ad --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/merkle/Merkle.h" @@ -0,0 +1,231 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::crypto::merkle +{ + +template +concept HashRange = RANGES::random_access_range && + bcos::concepts::bytebuffer::ByteBuffer>>; + +template +concept MerkleRange = HashRange; + +template +concept ProofRange = bcos::concepts::DynamicRange && + bcos::concepts::bytebuffer::ByteBuffer>>; + +template +class Merkle +{ + static_assert(width >= 2, "Width too short, at least 2"); + static_assert(HasherType::HASH_SIZE >= 4, "Hash size too short!"); + + using HashType = std::array; + +public: + bool verifyMerkleProof(ProofRange auto const& proof, bcos::concepts::bytebuffer::Hash auto hash, + bcos::concepts::bytebuffer::Hash auto const& root) + { + if (RANGES::empty(proof)) [[unlikely]] + BOOST_THROW_EXCEPTION(std::invalid_argument{"Empty input proof!"}); + + if (RANGES::size(proof) > 1) + { + auto it = RANGES::begin(proof); + + while (it != RANGES::end(proof)) + { + auto count = getNumberFromHash(*(it++)); + auto range = RANGES::subrange{it, it + count}; + + if (RANGES::find(range, hash) == RANGES::end(range)) [[unlikely]] + return false; + + HasherType hasher; + for (auto& merkleHash : range) + { + hasher.update(merkleHash); + } + hasher.final(hash); + + std::advance(it, count); + } + } + + if (hash != root) [[unlikely]] + return false; + + return true; + } + + void generateMerkleProof(HashRange auto const& originHashes, MerkleRange auto const& merkle, + bcos::concepts::bytebuffer::Hash auto const& hash, ProofRange auto& out) const + { + // Find the hash in originHashes first + auto it = RANGES::find(originHashes, hash); + if (it == RANGES::end(originHashes)) [[unlikely]] + BOOST_THROW_EXCEPTION(std::invalid_argument{"Not found hash!"}); + + generateMerkleProof( + originHashes, merkle, RANGES::distance(RANGES::begin(originHashes), it), out); + } + + void generateMerkleProof(HashRange auto const& originHashes, MerkleRange auto const& merkle, + std::integral auto index, ProofRange auto& out) const + { + if ((size_t)index >= (size_t)RANGES::size(originHashes)) [[unlikely]] + BOOST_THROW_EXCEPTION(std::invalid_argument{"Out of range!"}); + + if (RANGES::size(originHashes) == 1) + { + concepts::resizeTo(out, 1); + bcos::concepts::bytebuffer::assignTo(*RANGES::begin(merkle), *RANGES::begin(out)); + return; + } + + auto [merkleNodes, merkleLevels] = getMerkleSize(RANGES::size(originHashes)); + if (merkleNodes != RANGES::size(merkle)) + BOOST_THROW_EXCEPTION(std::invalid_argument{"Merkle size mismitch!"}); + + index = indexAlign(index); + auto count = std::min((size_t)(RANGES::size(originHashes) - index), (size_t)width); + + setNumberToHash(count, out.emplace_back()); + + for (auto it = RANGES::begin(originHashes) + index; + it < RANGES::begin(originHashes) + index + count; ++it) + { + bcos::concepts::bytebuffer::assignTo(*it, out.emplace_back()); + } + + // Query next level hashes + auto inputIt = RANGES::begin(merkle); + while (inputIt != RANGES::end(merkle)) + { + index = indexAlign(index / width); + auto levelLength = getNumberFromHash(*(inputIt++)); + assert(index < levelLength); + if (levelLength == 1) // Ignore merkle root + break; + + auto count = std::min((size_t)(levelLength - index), (size_t)width); + + setNumberToHash(count, out.emplace_back()); + for (auto it = inputIt + index; it < inputIt + index + count; ++it) + { + bcos::concepts::bytebuffer::assignTo(*it, out.emplace_back()); + } + RANGES::advance(inputIt, levelLength); + } + } + + void generateMerkle(HashRange auto const& originHashes, MerkleRange auto& out) const + { + if (RANGES::empty(originHashes)) [[unlikely]] + BOOST_THROW_EXCEPTION(std::invalid_argument{"Empty input"}); + + if (RANGES::size(originHashes) == 1) + { + bcos::concepts::resizeTo(out, 1); + bcos::concepts::bytebuffer::assignTo(*RANGES::begin(originHashes), *RANGES::begin(out)); + return; + } + + [[maybe_unused]] auto [merkleNodes, merkleLevels] = + getMerkleSize(RANGES::size(originHashes)); + bcos::concepts::resizeTo(out, merkleNodes); + + // Calculate first level from originHashes + auto it = RANGES::begin(out); + auto nextNodes = getNextLevelSize(RANGES::size(originHashes)); + setNumberToHash(nextNodes, *(it++)); + auto outputRange = RANGES::subrange(it, it + nextNodes); + calculateLevelHashes(originHashes, outputRange); + + while (nextNodes > 1) // Calculate next level from out, ignore only root + { + auto inputRange = RANGES::subrange(it, it + nextNodes); + + RANGES::advance(it, nextNodes); + nextNodes = getNextLevelSize(nextNodes); + + setNumberToHash(nextNodes, *(it++)); + auto outputRange = RANGES::subrange(it, it + nextNodes); + calculateLevelHashes(inputRange, outputRange); + + assert(it <= RANGES::end(out)); + } + } + +private: + auto indexAlign(std::integral auto index) const { return index - ((index + width) % width); } + + void setNumberToHash(uint32_t number, bcos::concepts::bytebuffer::Hash auto& output) const + { + bcos::concepts::resizeTo(output, sizeof(uint32_t)); + *((uint32_t*)output.data()) = boost::endian::native_to_big(number); + } + + uint32_t getNumberFromHash(bcos::concepts::bytebuffer::Hash auto const& input) const + { + return boost::endian::big_to_native(*((uint32_t*)input.data())); + } + + std::tuple getMerkleSize(std::integral auto inputSize) const + { + auto nodeSize = 0u; + auto levels = 0u; + while (inputSize > 1) + { + inputSize = getNextLevelSize(inputSize); + nodeSize += (inputSize + 1); // Extra 1 for length record + ++levels; + } + + return std::make_tuple(nodeSize, levels); + } + + auto getNextLevelSize(std::integral auto inputSize) const + { + return (inputSize + (width - 1)) / width; + } + + void calculateLevelHashes(HashRange auto const& input, HashRange auto& output) const + { + assert(RANGES::size(input) > 0); + + auto outputSize = RANGES::size(output); + tbb::parallel_for(tbb::blocked_range(0, outputSize), + [&input, &output](const tbb::blocked_range& range) { + HasherType hasher; + + for (auto i = range.begin(); i < range.end(); ++i) + { + for (auto j = i * width; j < (i + 1) * width && j < RANGES::size(input); ++j) + { + hasher.update(input[j]); + } + hasher.final(output[i]); + } + }); + } +}; + +} // namespace bcos::crypto::merkle \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/Exceptions.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/Exceptions.h" new file mode 100644 index 00000000..0e3133ad --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/Exceptions.h" @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2021 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. + * + * @brief define the exceptions for signature of bcos-crypto + * @file Execptions.h + * @date 2021.03.05 + * @author yujiechen + */ +#pragma once +#include + +namespace bcos +{ +namespace crypto +{ +DERIVE_BCOS_EXCEPTION(PriToPublicKeyException); +DERIVE_BCOS_EXCEPTION(SignException); +DERIVE_BCOS_EXCEPTION(InvalidSignature); +DERIVE_BCOS_EXCEPTION(InvalidSignatureData); +DERIVE_BCOS_EXCEPTION(InvalidKey); +DERIVE_BCOS_EXCEPTION(GenerateKeyPairException); +DERIVE_BCOS_EXCEPTION(InvalidSecretKey); +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/codec/SignatureData.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/codec/SignatureData.h" new file mode 100644 index 00000000..5e653e4b --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/codec/SignatureData.h" @@ -0,0 +1,70 @@ +/** + * Copyright (C) 2021 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. + * + * @brief codec for signature data + * @file SignatureData.h + * @date 2021.03.10 + * @author yujiechen + */ + +#pragma once +#include +#include +namespace bcos +{ +namespace crypto +{ +class SignatureData +{ +public: + using Ptr = std::shared_ptr; + SignatureData() = default; + SignatureData(h256 const& _r, h256 const& _s) : m_r(_r), m_s(_s) {} + virtual ~SignatureData() {} + virtual bytesPointer encode() const = 0; + virtual void decode(bytesConstRef _signatureData) = 0; + + h256 const& r() const { return m_r; } + h256 const& s() const { return m_s; } + +protected: + virtual void decodeCommonFields(bytesConstRef _signatureData) + { + if (_signatureData.size() < m_signatureLen) + { + BOOST_THROW_EXCEPTION( + InvalidSignatureData() << errinfo_comment( + "InvalidSignatureData: the signature data size must be at least " + + std::to_string(m_signatureLen))); + } + m_r = h256(_signatureData.data(), h256::ConstructorType::FromPointer); + m_s = h256(_signatureData.data() + 32, h256::ConstructorType::FromPointer); + } + virtual void encodeCommonFields(bytesPointer _signatureData) const + { + _signatureData->resize(64); + memcpy(_signatureData->data(), m_r.data(), 32); + memcpy(_signatureData->data() + 32, m_s.data(), 32); + } + +protected: + size_t m_signatureLen = 64; + +private: + h256 m_r; + h256 m_s; +}; +} // namespace crypto +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/codec/SignatureDataWithPub.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/codec/SignatureDataWithPub.h" new file mode 100644 index 00000000..8f6e67fa --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/codec/SignatureDataWithPub.h" @@ -0,0 +1,70 @@ +/** + * Copyright (C) 2021 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. + * + * @brief codec for signature data with pub + * @file SignatureDataWithPub.h + * @date 2021.03.10 + * @author yujiechen + */ +#pragma once +#include +#include +namespace bcos +{ +namespace crypto +{ +class SignatureDataWithPub : public SignatureData +{ +public: + using Ptr = std::shared_ptr; + explicit SignatureDataWithPub(bytesConstRef _data) : m_pub(std::make_shared()) + { + decode(_data); + } + SignatureDataWithPub(h256 const& _r, h256 const& _s, bytesConstRef _pub) + : SignatureData(_r, _s), m_pub(std::make_shared(_pub.begin(), _pub.end())) + {} + + SignatureDataWithPub(h256 const& _r, h256 const& _s, bytesPointer _pub) + : SignatureData(_r, _s), m_pub(_pub) + {} + + ~SignatureDataWithPub() override {} + + bytesPointer pub() const { return m_pub; } + bytesPointer encode() const override + { + auto encodedData = std::make_shared(); + encodeCommonFields(encodedData); + encodedData->insert(encodedData->end(), m_pub->begin(), m_pub->end()); + return encodedData; + } + + void decode(bytesConstRef _signatureData) override + { + m_pub->clear(); + decodeCommonFields(_signatureData); + if (_signatureData.size() > m_signatureLen) + { + m_pub->insert( + m_pub->end(), _signatureData.data() + m_signatureLen, _signatureData.end()); + } + } + +private: + bytesPointer m_pub; +}; +} // namespace crypto +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/codec/SignatureDataWithV.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/codec/SignatureDataWithV.h" new file mode 100644 index 00000000..0485e09d --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/codec/SignatureDataWithV.h" @@ -0,0 +1,67 @@ +/** + * Copyright (C) 2021 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. + * + * @brief codec for signature data with v (for secp256k1) + * @file SignatureDataWithV.h + * @date 2021.03.10 + * @author yujiechen + */ + +#pragma once +#include +#include + +namespace bcos +{ +namespace crypto +{ +class SignatureDataWithV : public SignatureData +{ +public: + using Ptr = std::shared_ptr; + explicit SignatureDataWithV(bytesConstRef _data) { decode(_data); } + SignatureDataWithV(h256 const& _r, h256 const& _s, byte const& _v) + : SignatureData(_r, _s), m_v(_v) + {} + + ~SignatureDataWithV() override {} + + byte const& v() { return m_v; } + + bytesPointer encode() const override + { + auto encodedData = std::make_shared(); + encodeCommonFields(encodedData); + encodedData->emplace_back(m_v); + return encodedData; + } + void decode(bytesConstRef _signatureData) override + { + if (_signatureData.size() < m_signatureLen + 1) + { + BOOST_THROW_EXCEPTION( + InvalidSignatureData() << errinfo_comment( + "InvalidSignatureData: the signature data size must be at least " + + std::to_string(m_signatureLen + 1))); + } + decodeCommonFields(_signatureData); + m_v = (byte)(_signatureData[m_signatureLen]); + } + +private: + byte m_v; +}; +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/ed25519/Ed25519Crypto.cpp" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/ed25519/Ed25519Crypto.cpp" new file mode 100644 index 00000000..57f4e07c --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/ed25519/Ed25519Crypto.cpp" @@ -0,0 +1,139 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for ed25519 signature algorithm + * @file Ed25519Crypto.cpp + * @date 2021.04.01 + * @author yujiechen + */ +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::crypto; +std::shared_ptr bcos::crypto::ed25519Sign( + const KeyPairInterface& _keyPair, const HashType& _messageHash, bool _signatureWithPub) +{ + CInputBuffer privateKey{_keyPair.secretKey()->constData(), _keyPair.secretKey()->size()}; + CInputBuffer messagHash{(const char*)_messageHash.data(), HashType::SIZE}; + FixedBytes signatureArray; + COutputBuffer signatureResult{(char*)signatureArray.data(), ED25519_SIGNATURE_LEN}; + auto retCode = wedpr_ed25519_sign(&privateKey, &messagHash, &signatureResult); + if (retCode != WEDPR_SUCCESS) + { + BOOST_THROW_EXCEPTION(SignException() << errinfo_comment( + "secp256k1Sign exception, messageHash: " + _messageHash.hex())); + } + auto signatureData = std::make_shared(); + *signatureData = signatureArray.asBytes(); + if (_signatureWithPub) + { + signatureData->insert(signatureData->end(), _keyPair.publicKey()->mutableData(), + _keyPair.publicKey()->mutableData() + _keyPair.publicKey()->size()); + } + return signatureData; +} + +KeyPairInterface::UniquePtr bcos::crypto::ed25519GenerateKeyPair() +{ + auto ed25519KeyPair = std::make_unique(); + COutputBuffer publicKey{ + ed25519KeyPair->publicKey()->mutableData(), ed25519KeyPair->publicKey()->size()}; + COutputBuffer privateKey{ + ed25519KeyPair->secretKey()->mutableData(), ed25519KeyPair->secretKey()->size()}; + if (wedpr_ed25519_gen_key_pair(&publicKey, &privateKey) != WEDPR_SUCCESS) + { + BOOST_THROW_EXCEPTION( + GenerateKeyPairException() << errinfo_comment("ed25519GenerateKeyPair exception")); + } + return ed25519KeyPair; +} + +bool bcos::crypto::ed25519Verify( + PublicPtr _pubKey, const HashType& _messageHash, bytesConstRef _signatureData) +{ + CInputBuffer publicKey{_pubKey->constData(), _pubKey->size()}; + CInputBuffer msgHash{(const char*)_messageHash.data(), HashType::SIZE}; + + auto signatureWithoutPub = bytesConstRef(_signatureData.data(), ED25519_SIGNATURE_LEN); + CInputBuffer signatureData{(const char*)signatureWithoutPub.data(), signatureWithoutPub.size()}; + if (wedpr_ed25519_verify(&publicKey, &msgHash, &signatureData) != WEDPR_SUCCESS) + { + return false; + } + return true; +} + +PublicPtr bcos::crypto::ed25519Recover(const HashType& _messageHash, bytesConstRef _signatureData) +{ + auto signature = std::make_shared(_signatureData); + auto ed25519Pub = std::make_shared(ED25519_PUBLIC_LEN, signature->pub()); + if (!ed25519Verify(ed25519Pub, _messageHash, _signatureData)) + { + BOOST_THROW_EXCEPTION( + InvalidSignature() << errinfo_comment( + "invalid signature: ed25519 recover failed, msgHash : " + _messageHash.hex() + + ", signature:" + *toHexString(_signatureData))); + } + return ed25519Pub; +} + +std::pair bcos::crypto::ed25519Recover(Hash::Ptr _hashImpl, bytesConstRef _input) +{ + struct + { + HashType hash; + h256 pub; + h256 r; + h256 s; + } in; + memcpy(&in, _input.data(), std::min(_input.size(), sizeof(_input))); + // verify the signature + auto signatureData = std::make_shared(in.r, in.s, in.pub.ref()); + try + { + auto encodedData = signatureData->encode(); + auto ed25519Pub = std::make_shared(ED25519_PUBLIC_LEN, signatureData->pub()); + if (ed25519Verify( + ed25519Pub, in.hash, bytesConstRef(encodedData->data(), encodedData->size()))) + { + auto address = calculateAddress(_hashImpl, ed25519Pub); + return {true, address.asBytes()}; + } + } + catch (const std::exception& e) + { + CRYPTO_LOG(WARNING) << LOG_DESC("ed25519Recover failed") + << LOG_KV("error", boost::diagnostic_information(e)); + } + return {false, {}}; +} + + +bool Ed25519Crypto::verify( + std::shared_ptr _pubKeyBytes, const HashType& _hash, bytesConstRef _signatureData) +{ + return ed25519Verify( + std::make_shared(ED25519_PUBLIC_LEN, _pubKeyBytes), _hash, _signatureData); +} + +KeyPairInterface::UniquePtr Ed25519Crypto::createKeyPair(SecretPtr _secretKey) +{ + return std::make_unique(_secretKey); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/ed25519/Ed25519Crypto.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/ed25519/Ed25519Crypto.h" new file mode 100644 index 00000000..a4c964c5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/ed25519/Ed25519Crypto.h" @@ -0,0 +1,73 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for ed25519 signature algorithm + * @file Ed25519Crypto.h + * @date 2021.04.01 + * @author yujiechen + */ +#pragma once +#include +namespace bcos +{ +namespace crypto +{ +const int ED25519_SIGNATURE_LEN = 64; + +std::shared_ptr ed25519Sign( + const KeyPairInterface& _keyPair, const HashType& _messageHash, bool _signatureWithPub = false); +KeyPairInterface::UniquePtr ed25519GenerateKeyPair(); +bool ed25519Verify(PublicPtr _pubKey, const HashType& _messageHash, bytesConstRef _signatureData); +PublicPtr ed25519Recover(const HashType& _messageHash, bytesConstRef _signatureData); + +std::pair ed25519Recover(Hash::Ptr _hashImpl, bytesConstRef _in); + +class Ed25519Crypto : public SignatureCrypto +{ +public: + using Ptr = std::shared_ptr; + Ed25519Crypto() = default; + ~Ed25519Crypto() override {} + std::shared_ptr sign(const KeyPairInterface& _keyPair, const HashType& _messageHash, + bool _signatureWithPub = false) override + { + return ed25519Sign(_keyPair, _messageHash, _signatureWithPub); + } + + bool verify( + PublicPtr _pubKey, const HashType& _messageHash, bytesConstRef _signatureData) override + { + return ed25519Verify(_pubKey, _messageHash, _signatureData); + } + + bool verify(std::shared_ptr _pubKeyBytes, const HashType& _hash, + bytesConstRef _signatureData) override; + + PublicPtr recover(const HashType& _messageHash, bytesConstRef _signatureData) override + { + return ed25519Recover(_messageHash, _signatureData); + } + + KeyPairInterface::UniquePtr generateKeyPair() override { return ed25519GenerateKeyPair(); } + + std::pair recoverAddress(Hash::Ptr _hashImpl, bytesConstRef _in) override + { + return ed25519Recover(_hashImpl, _in); + } + + KeyPairInterface::UniquePtr createKeyPair(SecretPtr _secretKey) override; +}; +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/ed25519/Ed25519KeyPair.cpp" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/ed25519/Ed25519KeyPair.cpp" new file mode 100644 index 00000000..78ebac6b --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/ed25519/Ed25519KeyPair.cpp" @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for ed25519 keyPair algorithm + * @file Ed25519KeyPair.cpp + * @date 2021.04.01 + * @author yujiechen + */ + +#include +#include +#include + +using namespace bcos; +using namespace bcos::crypto; + +PublicPtr bcos::crypto::ed25519PriToPub(SecretPtr _secretKey) +{ + CInputBuffer privateKey{_secretKey->constData(), _secretKey->size()}; + auto pub = std::make_shared(ED25519_PUBLIC_LEN); + COutputBuffer publicKey{pub->mutableData(), pub->size()}; + // get public key + if (wedpr_ed25519_derive_public_key(&privateKey, &publicKey) != WEDPR_SUCCESS) + { + BOOST_THROW_EXCEPTION( + PriToPublicKeyException() << errinfo_comment("ed25519PriToPub exception")); + } + return pub; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/ed25519/Ed25519KeyPair.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/ed25519/Ed25519KeyPair.h" new file mode 100644 index 00000000..4a430103 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/ed25519/Ed25519KeyPair.h" @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for ed25519 keyPair algorithm + * @file Ed25519KeyPair.h + * @date 2021.04.01 + * @author yujiechen + */ +#pragma once +#include +#include + +namespace bcos +{ +namespace crypto +{ +const int ED25519_PUBLIC_LEN = 32; +const int ED25519_PRIVATE_LEN = 32; + +PublicPtr ed25519PriToPub(SecretPtr _secretKey); +class Ed25519KeyPair : public KeyPair +{ +public: + Ed25519KeyPair() : KeyPair(ED25519_PUBLIC_LEN, ED25519_PRIVATE_LEN, KeyPairType::Ed25519) {} + explicit Ed25519KeyPair(SecretPtr _secretKey) : Ed25519KeyPair() + { + m_secretKey = _secretKey; + m_publicKey = priToPub(_secretKey); + m_type = KeyPairType::Ed25519; + } + ~Ed25519KeyPair() override {} + virtual PublicPtr priToPub(SecretPtr _secretKey) { return ed25519PriToPub(_secretKey); } +}; +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/FastSM2Crypto.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/FastSM2Crypto.h" new file mode 100644 index 00000000..ede367dc --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/FastSM2Crypto.h" @@ -0,0 +1,48 @@ +/** + * 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 FastSM2Crypto.h + * @date 2022.01.17 + * @author yujiechen + */ + +#pragma once +#include +#include +#include +#include + +#ifdef WITH_SM2_OPTIMIZE + +namespace bcos +{ +namespace crypto +{ +class FastSM2Crypto : public SM2Crypto +{ +public: + using Ptr = std::shared_ptr; + FastSM2Crypto() : SM2Crypto() + { + m_signer = fast_sm2_sign; + m_verifier = fast_sm2_verify; + m_keyPairFactory = std::make_shared(); + } + virtual ~FastSM2Crypto() {} +}; +} // namespace crypto +} // namespace bcos + +#endif \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/FastSM2KeyPair.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/FastSM2KeyPair.h" new file mode 100644 index 00000000..e30a57f3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/FastSM2KeyPair.h" @@ -0,0 +1,41 @@ +/** + * 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. + * + * @brief implementation for fast-sm2 keyPair + * @file FastSM2KeyPair.h + * @date 2022.01.17 + * @author yujiechen + */ +#pragma once +#include +#include + +#ifdef WITH_SM2_OPTIMIZE + +namespace bcos +{ +namespace crypto +{ +class FastSM2KeyPair : public SM2KeyPair +{ +public: + using Ptr = std::shared_ptr; + FastSM2KeyPair() : SM2KeyPair() { m_publicKeyDeriver = fast_sm2_derive_public_key; } + explicit FastSM2KeyPair(SecretPtr _secretKey) : SM2KeyPair(_secretKey) {} +}; +} // namespace crypto +} // namespace bcos + +#endif \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/FastSM2KeyPairFactory.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/FastSM2KeyPairFactory.h" new file mode 100644 index 00000000..b10b020a --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/FastSM2KeyPairFactory.h" @@ -0,0 +1,53 @@ +/** + * 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. + * + * @brief implementation for fast-sm2 keyPairFactory + * @file FastSM2KeyPairFactory.h + * @date 2022.01.17 + * @author yujiechen + */ +#pragma once +#include +#include +#include +#include + +#ifdef WITH_SM2_OPTIMIZE + +namespace bcos +{ +namespace crypto +{ +class FastSM2KeyPairFactory : public SM2KeyPairFactory +{ +public: + using Ptr = std::shared_ptr; + FastSM2KeyPairFactory() : SM2KeyPairFactory() {} + ~FastSM2KeyPairFactory() override {} + + KeyPairInterface::UniquePtr createKeyPair() override + { + return std::make_unique(); + } + + KeyPairInterface::UniquePtr createKeyPair(SecretPtr _secretKey) override + { + return std::make_unique(_secretKey); + } +}; +} // namespace crypto +} // namespace bcos + +#endif \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/fast_sm2.cpp" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/fast_sm2.cpp" new file mode 100644 index 00000000..ca7ad5c6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/fast_sm2.cpp" @@ -0,0 +1,307 @@ +/** + * 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. + * + * @brief implementation for ed25519 keyPair algorithm + * @file fast_sm2.h + * @date 2022.01.17 + * @author yujiechen + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WITH_SM2_OPTIMIZE +using namespace bcos; +using namespace bcos::crypto; + +const char* c_userId = "1234567812345678"; +const int c_R_FIELD_LEN = 32; +const int c_S_FIELD_LEN = 32; +const int c_PUBLICKEY_LEN = 64; +// Note: EC_GROUP_new_by_curve_name cost performance, So only one copy is created globally to +// improve performance +EC_GROUP* sm2Group = EC_GROUP_new_by_curve_name(NID_sm2); + +// C interface for 'fast_sm2_sign'. +int8_t bcos::crypto::fast_sm2_sign(const CInputBuffer* raw_private_key, + const CInputBuffer* raw_public_key, const CInputBuffer* raw_message_hash, + COutputBuffer* output_signature) +{ + auto hexPubKey = + toHexString(raw_public_key->data, raw_public_key->data + raw_public_key->len, "04"); + int len = 0; + // create EC_GROUP + EC_KEY* sm2Key = NULL; + ECDSA_SIG* sig = NULL; + EC_POINT* publicKey = NULL; + BIGNUM* privateKey = NULL; + int8_t ret = WEDPR_ERROR; + + // load privateKey + privateKey = BN_bin2bn((const unsigned char*)raw_private_key->data, raw_private_key->len, NULL); + if (privateKey == NULL) + { + CRYPTO_LOG(ERROR) << LOG_DESC("sm2: fast_sm2_sign: error of BN_bin2bn for privateKey"); + goto done; + } + publicKey = EC_POINT_hex2point(sm2Group, hexPubKey->data(), NULL, NULL); + if (publicKey == NULL) + { + CRYPTO_LOG(ERROR) << LOG_DESC("sm2: fast_sm2_sign: error of BN_bin2bn for publicKey"); + goto done; + } + sm2Key = EC_KEY_new(); + if (sm2Key == NULL) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_sign: error of EC_KEY_new"; + goto done; + } + if (!EC_KEY_set_group(sm2Key, sm2Group)) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_sign: error of EC_KEY_set_group"; + goto done; + } + // set the private key + if (!EC_KEY_set_private_key(sm2Key, privateKey)) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_sign: error of EC_KEY_set_private_key"; + goto done; + } + // set the public key + if (!EC_KEY_set_public_key(sm2Key, publicKey)) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_sign: error of EC_KEY_set_public_key"; + goto done; + } + sig = sm2_do_sign(sm2Key, EVP_sm3(), (const uint8_t*)c_userId, (size_t)strlen(c_userId), + (const uint8_t*)raw_message_hash->data, raw_message_hash->len); + if (sig == NULL) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_sign: error of sm2_do_sign"; + goto done; + } + // set (r, s) to output_signature + len = BN_bn2bin(ECDSA_SIG_get0_r(sig), (unsigned char*)output_signature->data); + if (len < c_R_FIELD_LEN) + { + // padding zero to the r field + memmove(output_signature->data + (c_R_FIELD_LEN - len), output_signature->data, len); + memset(output_signature->data, 0, (c_R_FIELD_LEN - len)); + } + // get s filed + len = + BN_bn2bin(ECDSA_SIG_get0_s(sig), (unsigned char*)(output_signature->data + c_R_FIELD_LEN)); + if (len < c_S_FIELD_LEN) + { + auto startPointer = output_signature->data + c_R_FIELD_LEN; + // padding zero to the s field + memmove(startPointer + (c_S_FIELD_LEN - len), startPointer, len); + memset(startPointer, 0, (c_S_FIELD_LEN - len)); + } + ret = WEDPR_SUCCESS; +done: + if (sm2Key) + { + EC_KEY_free(sm2Key); + } + if (sig) + { + ECDSA_SIG_free(sig); + } + if (privateKey) + { + BN_free(privateKey); + } + if (publicKey) + { + EC_POINT_free(publicKey); + } + return ret; +} + +int8_t bcos::crypto::fast_sm2_verify(const CInputBuffer* raw_public_key, + const CInputBuffer* raw_message_hash, const CInputBuffer* raw_signature) +{ + auto hexPubKey = + toHexString(raw_public_key->data, raw_public_key->data + raw_public_key->len, "04"); + EC_KEY* sm2Key = NULL; + EC_POINT* point = NULL; + BIGNUM* r = NULL; + BIGNUM* s = NULL; + ECDSA_SIG* signData = NULL; + int8_t ret = WEDPR_ERROR; + point = EC_POINT_new(sm2Group); + if (point == NULL) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_verify: error of EC_POINT_new"; + goto done; + } + if (!EC_POINT_hex2point(sm2Group, hexPubKey->data(), point, NULL)) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_verify: error of EC_POINT_bin2point"; + goto done; + } + + sm2Key = EC_KEY_new(); + if (sm2Key == NULL) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_verify: error of EC_KEY_new"; + goto done; + } + + if (!EC_KEY_set_group(sm2Key, sm2Group)) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_verify: error of EC_KEY_set_group"; + goto done; + } + if (!EC_KEY_set_public_key(sm2Key, point)) + { + CRYPTO_LOG(ERROR) << "EC_KEY_set_public_key of EC_KEY_set_public_key"; + goto done; + } + r = BN_bin2bn((const unsigned char*)raw_signature->data, c_R_FIELD_LEN, NULL); + if (r == NULL) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_verify: error of BN_bin2bn for r"; + goto done; + } + s = BN_bin2bn((const unsigned char*)(raw_signature->data + c_R_FIELD_LEN), c_S_FIELD_LEN, NULL); + if (s == NULL) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_verify: error of BN_bin2bn for s"; + goto done; + } + signData = ECDSA_SIG_new(); + if (signData == NULL) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_verify: error of ECDSA_SIG_new"; + goto done; + } + // takes ownership of r and s + if (!ECDSA_SIG_set0(signData, r, s)) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_verify: error of ECDSA_SIG_set0"; + goto done; + } + if (sm2_do_verify(sm2Key, EVP_sm3(), signData, (const uint8_t*)c_userId, strlen(c_userId), + (const uint8_t*)raw_message_hash->data, raw_message_hash->len)) + { + ret = WEDPR_SUCCESS; + } +done: + if (sm2Key) + { + EC_KEY_free(sm2Key); + } + if (point) + { + EC_POINT_free(point); + } + if (signData == NULL) + { + BN_free(r); + BN_free(s); + } + if (signData) + { + ECDSA_SIG_free(signData); + } + return ret; +} + +// C interface for 'fast_sm2_derive_public_key'. +int8_t bcos::crypto::fast_sm2_derive_public_key( + const CInputBuffer* raw_private_key, COutputBuffer* output_public_key) +{ + int8_t ret = WEDPR_ERROR; + EC_KEY* sm2Key = NULL; + EC_POINT* pubPoint = NULL; + BN_CTX* ctx = NULL; + char* publicKey = NULL; + BIGNUM* privateKey = + BN_bin2bn((const unsigned char*)raw_private_key->data, raw_private_key->len, NULL); + if (!privateKey) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_derive_public_key: error of BN_bin2bn for privateKey"; + goto done; + } + ctx = BN_CTX_new(); + if (!ctx) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_derive_public_key: error of BN_CTX_new"; + goto done; + } + sm2Key = EC_KEY_new_by_curve_name(NID_sm2); + if (!sm2Key) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_derive_public_key: error of EC_KEY_new_by_curve_name"; + goto done; + } + if (!EC_KEY_set_private_key(sm2Key, privateKey)) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_derive_public_key: error EC_KEY_set_private_key"; + goto done; + } + pubPoint = EC_POINT_new(sm2Group); + if (!pubPoint) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_derive_public_key: error EC_POINT_new"; + goto done; + } + if (!EC_POINT_mul(sm2Group, pubPoint, privateKey, NULL, NULL, NULL)) + { + CRYPTO_LOG(ERROR) << "sm2: fast_sm2_derive_public_key: error of EC_POINT_mul"; + goto done; + } + if (!EC_POINT_point2buf( + sm2Group, pubPoint, POINT_CONVERSION_UNCOMPRESSED, (unsigned char**)&publicKey, ctx)) + { + CRYPTO_LOG(ERROR) + << "sm2: fast_sm2_derive_public_key: error of EC_POINT_point2bin for publicKey"; + goto done; + } + // remove the prefix 04 + memcpy(output_public_key->data, publicKey + 1, c_PUBLICKEY_LEN); + ret = WEDPR_SUCCESS; +done: + if (sm2Key) + { + EC_KEY_free(sm2Key); + } + if (pubPoint) + { + EC_POINT_free(pubPoint); + } + if (ctx) + { + BN_CTX_free(ctx); + } + if (privateKey) + { + BN_free(privateKey); + } + if (publicKey) + { + OPENSSL_free(publicKey); + } + return ret; +} + +#endif \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/fast_sm2.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/fast_sm2.h" new file mode 100644 index 00000000..8a79ba58 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/fastsm2/fast_sm2.h" @@ -0,0 +1,43 @@ +/** + * 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. + * + * @brief implementation for ed25519 keyPair algorithm + * @file fast_sm2.h + * @date 2022.01.17 + * @author yujiechen + */ +#pragma once + +#ifdef WITH_SM2_OPTIMIZE +#include + +namespace bcos +{ +namespace crypto +{ +// C interface for 'fast_sm2_sign'. +int8_t fast_sm2_sign(const CInputBuffer* raw_private_key, const CInputBuffer* raw_public_key, + const CInputBuffer* raw_message_hash, COutputBuffer* output_signature); + +// C interface for 'fast_sm2_verify'. +int8_t fast_sm2_verify(const CInputBuffer* raw_public_key, const CInputBuffer* raw_message_hash, + const CInputBuffer* raw_signature); + +// C interface for 'fast_sm2_verify'. +int8_t fast_sm2_derive_public_key( + const CInputBuffer* raw_private_key, COutputBuffer* output_public_key); +} // namespace crypto +} // namespace bcos +#endif \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/key/KeyFactoryImpl.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/key/KeyFactoryImpl.h" new file mode 100644 index 00000000..b181006a --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/key/KeyFactoryImpl.h" @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for KeyFactory + * @file KeyFactoryImpl.h + * @date 2021.05.10 + * @author yujiechen + */ +#pragma once +#include +#include + +namespace bcos +{ +namespace crypto +{ +class KeyFactoryImpl : public KeyFactory +{ +public: + using Ptr = std::shared_ptr; + KeyFactoryImpl() = default; + ~KeyFactoryImpl() override {} + + KeyInterface::Ptr createKey(bytesConstRef _keyData) override + { + return std::make_shared(_keyData); + } + + KeyInterface::Ptr createKey(bytes const& _keyData) override + { + return std::make_shared(_keyData); + } +}; +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/key/KeyImpl.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/key/KeyImpl.h" new file mode 100644 index 00000000..61614f62 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/key/KeyImpl.h" @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for public key/private key + * @file KeyImpl.h + * @date 2021.04.01 + * @author yujiechen + */ +#pragma once +#include +#include +#include +namespace bcos +{ +namespace crypto +{ +class KeyImpl : public KeyInterface +{ +public: + using Ptr = std::shared_ptr; + explicit KeyImpl(size_t _keySize) : m_keyData(std::make_shared(_keySize)) {} + explicit KeyImpl(bytesConstRef _data) : m_keyData(std::make_shared()) { decode(_data); } + explicit KeyImpl(bytes const& _data) : KeyImpl(ref(_data)) {} + explicit KeyImpl(size_t _keySize, std::shared_ptr _data) + : m_keyData(std::make_shared()) + { + if (_data->size() < _keySize) + { + BOOST_THROW_EXCEPTION(InvalidKey() << errinfo_comment( + "invalidKey, the key size: " + std::to_string(_data->size()) + + ", expected size:" + std::to_string(_keySize))); + } + *m_keyData = *_data; + } + + bool operator==(KeyImpl const& _comparedKey) { return (*m_keyData == _comparedKey.data()); } + bool operator!=(KeyImpl const& _comparedKey) { return !operator==(_comparedKey); } + + ~KeyImpl() override {} + + const bytes& data() const override { return *m_keyData; } + size_t size() const override { return m_keyData->size(); } + char* mutableData() override { return (char*)m_keyData->data(); } + const char* constData() const override { return (const char*)m_keyData->data(); } + std::shared_ptr encode() const override { return m_keyData; } + void decode(bytesConstRef _data) override { *m_keyData = _data.toBytes(); } + + void decode(bytes&& _data) override { *m_keyData = std::move(_data); } + + std::string shortHex() override + { + auto startIt = m_keyData->begin(); + auto endIt = m_keyData->end(); + if (m_keyData->size() > 4) + { + endIt = startIt + 4 * sizeof(byte); + } + return *toHexString(startIt, endIt) + "..."; + } + + std::string hex() override { return *toHexString(*m_keyData); } + +private: + std::shared_ptr m_keyData; +}; +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/key/KeyPair.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/key/KeyPair.h" new file mode 100644 index 00000000..7160d24d --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/key/KeyPair.h" @@ -0,0 +1,67 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Basic implementation of KeyPairInterface + * @file KeyPair.h + * @date 2021.4.2 + * @author yujiechen + */ +#pragma once +#include +#include +#include + +namespace bcos +{ +namespace crypto +{ +Address inline calculateAddress(Hash::Ptr _hashImpl, PublicPtr _publicKey) +{ + return right160(_hashImpl->hash(_publicKey)); +} + +class KeyPair : public KeyPairInterface +{ +public: + using Ptr = std::shared_ptr; + KeyPair(int _pubKeyLen, int _secretLen, KeyPairType _type) + : m_publicKey(std::make_shared(_pubKeyLen)), + m_secretKey(std::make_shared(_secretLen)), + m_type(_type) + {} + + KeyPair(Public const& _publicKey, Secret const& _secretKey, KeyPairType _type) + : m_publicKey(std::make_shared(_publicKey.data())), + m_secretKey(std::make_shared(_secretKey.data())), + m_type(_type) + {} + + ~KeyPair() override {} + KeyPairType keyPairType() const override { return m_type; } + SecretPtr secretKey() const override { return m_secretKey; } + PublicPtr publicKey() const override { return m_publicKey; } + + Address address(Hash::Ptr _hashImpl) override + { + return calculateAddress(_hashImpl, m_publicKey); + } + +protected: + PublicPtr m_publicKey; + SecretPtr m_secretKey; + KeyPairType m_type; +}; +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1Crypto.cpp" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1Crypto.cpp" new file mode 100644 index 00000000..95918629 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1Crypto.cpp" @@ -0,0 +1,136 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for secp256k1 signature algorithm + * @file Secp256k1Signature.cpp + * @date 2021.03.05 + * @author yujiechen + */ + +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::crypto; + +std::shared_ptr bcos::crypto::secp256k1Sign( + const KeyPairInterface& _keyPair, const HashType& _hash) +{ + FixedBytes signatureDataArray; + CInputBuffer privateKey{_keyPair.secretKey()->constData(), _keyPair.secretKey()->size()}; + CInputBuffer msgHash{(const char*)_hash.data(), HashType::SIZE}; + COutputBuffer secp256k1SignatureResult{ + (char*)signatureDataArray.data(), SECP256K1_SIGNATURE_LEN}; + auto retCode = wedpr_secp256k1_sign(&privateKey, &msgHash, &secp256k1SignatureResult); + if (retCode != WEDPR_SUCCESS) + { + BOOST_THROW_EXCEPTION(SignException() << errinfo_comment( + "secp256k1Sign exception, raw data: " + _hash.hex())); + } + std::shared_ptr signatureData = std::make_shared(); + *signatureData = signatureDataArray.asBytes(); + return signatureData; +} + +bool bcos::crypto::secp256k1Verify( + PublicPtr _pubKey, const HashType& _hash, bytesConstRef _signatureData) +{ + CInputBuffer publicKey{_pubKey->constData(), _pubKey->size()}; + CInputBuffer msgHash{(const char*)_hash.data(), HashType::SIZE}; + CInputBuffer signature{(const char*)_signatureData.data(), _signatureData.size()}; + auto verifyResult = wedpr_secp256k1_verify(&publicKey, &msgHash, &signature); + if (verifyResult == WEDPR_SUCCESS) + { + return true; + } + return false; +} + +KeyPairInterface::UniquePtr bcos::crypto::secp256k1GenerateKeyPair() +{ + auto keyPair = std::make_unique(); + COutputBuffer publicKey{keyPair->publicKey()->mutableData(), keyPair->publicKey()->size()}; + COutputBuffer privateKey{keyPair->secretKey()->mutableData(), keyPair->secretKey()->size()}; + auto retCode = wedpr_secp256k1_gen_key_pair(&publicKey, &privateKey); + if (retCode != WEDPR_SUCCESS) + { + BOOST_THROW_EXCEPTION( + GenerateKeyPairException() << errinfo_comment("secp256k1GenerateKeyPair exception")); + } + return keyPair; +} + +PublicPtr bcos::crypto::secp256k1Recover(const HashType& _hash, bytesConstRef _signatureData) +{ + CInputBuffer msgHash{(const char*)_hash.data(), HashType::SIZE}; + CInputBuffer signature{(const char*)_signatureData.data(), _signatureData.size()}; + auto pubKey = std::make_shared(SECP256K1_PUBLIC_LEN); + COutputBuffer publicKeyResult{pubKey->mutableData(), pubKey->size()}; + auto retCode = wedpr_secp256k1_recover_public_key(&msgHash, &signature, &publicKeyResult); + if (retCode != WEDPR_SUCCESS) + { + BOOST_THROW_EXCEPTION(InvalidSignature() << errinfo_comment( + "invalid signature: secp256k1Recover failed, msgHash : " + + _hash.hex() + ", signData:" + *toHexString(_signatureData))); + } + return pubKey; +} + +std::pair bcos::crypto::secp256k1Recover(Hash::Ptr _hashImpl, bytesConstRef _input) +{ + struct + { + HashType hash; + h256 v; + h256 r; + h256 s; + } in; + memcpy(&in, _input.data(), std::min(_input.size(), sizeof(_input))); + u256 v = (u256)in.v; + if (v >= 27 && v <= 28) + { + auto signatureData = std::make_shared(in.r, in.s, (byte)((int)v - 27)); + try + { + auto encodedBytes = signatureData->encode(); + auto publicKey = secp256k1Recover( + in.hash, bytesConstRef(encodedBytes->data(), encodedBytes->size())); + auto address = calculateAddress(_hashImpl, publicKey); + return {true, address.asBytes()}; + } + catch (const std::exception& e) + { + CRYPTO_LOG(WARNING) << LOG_DESC("secp256k1Recover failed") + << LOG_KV("error", boost::diagnostic_information(e)); + } + } + return {false, {}}; +} + +bool Secp256k1Crypto::verify( + std::shared_ptr _pubKeyBytes, const HashType& _hash, bytesConstRef _signatureData) +{ + return secp256k1Verify( + std::make_shared(SECP256K1_PUBLIC_LEN, _pubKeyBytes), _hash, _signatureData); +} + +KeyPairInterface::UniquePtr Secp256k1Crypto::createKeyPair(SecretPtr _secretKey) +{ + return std::make_unique(_secretKey); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1Crypto.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1Crypto.h" new file mode 100644 index 00000000..9c08e694 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1Crypto.h" @@ -0,0 +1,70 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for secp256k1 signature algorithm + * @file Secp256k1Crypto.h + * @date 2021.03.05 + * @author yujiechen + */ + +#pragma once +#include + +namespace bcos +{ +namespace crypto +{ +const int SECP256K1_SIGNATURE_LEN = 65; +std::shared_ptr secp256k1Sign(const KeyPairInterface& _keyPair, const HashType& _hash); +bool secp256k1Verify(PublicPtr _pubKey, const HashType& _hash, bytesConstRef _signatureData); +KeyPairInterface::UniquePtr secp256k1GenerateKeyPair(); + +PublicPtr secp256k1Recover(const HashType& _hash, bytesConstRef _signatureData); +std::pair secp256k1Recover(Hash::Ptr _hashImpl, bytesConstRef _in); + +class Secp256k1Crypto : public SignatureCrypto +{ +public: + using Ptr = std::shared_ptr; + Secp256k1Crypto() = default; + ~Secp256k1Crypto() override {} + std::shared_ptr sign( + const KeyPairInterface& _keyPair, const HashType& _hash, bool) override + { + return secp256k1Sign(_keyPair, _hash); + } + bool verify(PublicPtr _pubKey, const HashType& _hash, bytesConstRef _signatureData) override + { + return secp256k1Verify(_pubKey, _hash, _signatureData); + } + + bool verify(std::shared_ptr _pubKeyBytes, const HashType& _hash, + bytesConstRef _signatureData) override; + + PublicPtr recover(const HashType& _hash, bytesConstRef _signatureData) override + { + return secp256k1Recover(_hash, _signatureData); + } + KeyPairInterface::UniquePtr generateKeyPair() override { return secp256k1GenerateKeyPair(); } + + std::pair recoverAddress(Hash::Ptr _hashImpl, bytesConstRef _in) override + { + return secp256k1Recover(_hashImpl, _in); + } + + KeyPairInterface::UniquePtr createKeyPair(SecretPtr _secretKey) override; +}; +} // namespace crypto +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1KeyPair.cpp" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1KeyPair.cpp" new file mode 100644 index 00000000..1883564c --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1KeyPair.cpp" @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for secp256k1 KeyPair + * @file Secp256k1KeyPair.cpp + * @date 2021.03.05 + * @author yujiechen + */ +#include +#include +#include + +bcos::crypto::PublicPtr bcos::crypto::secp256k1PriToPub(bcos::crypto::SecretPtr _secret) +{ + CInputBuffer privateKey{_secret->constData(), _secret->size()}; + + PublicPtr pubKey = std::make_shared(SECP256K1_PUBLIC_LEN); + COutputBuffer publicKey{pubKey->mutableData(), pubKey->size()}; + auto retCode = wedpr_secp256k1_derive_public_key(&privateKey, &publicKey); + if (retCode != WEDPR_SUCCESS) + { + BOOST_THROW_EXCEPTION( + PriToPublicKeyException() << errinfo_comment("secp256k1PriToPub exception")); + } + return pubKey; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1KeyPair.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1KeyPair.h" new file mode 100644 index 00000000..3589362e --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/secp256k1/Secp256k1KeyPair.h" @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for secp256k1 KeyPair + * @file Secp256k1KeyPair.h + * @date 2021.03.05 + * @author yujiechen + */ +#pragma once +#include +#include + +namespace bcos +{ +namespace crypto +{ +const int SECP256K1_PUBLIC_LEN = 64; +const int SECP256K1_PRIVATE_LEN = 32; + +PublicPtr secp256k1PriToPub(SecretPtr _secret); +class Secp256k1KeyPair : public KeyPair +{ +public: + using Ptr = std::shared_ptr; + Secp256k1KeyPair() + : KeyPair(SECP256K1_PUBLIC_LEN, SECP256K1_PRIVATE_LEN, KeyPairType::Secp256K1) + {} + explicit Secp256k1KeyPair(SecretPtr _secretKey) : Secp256k1KeyPair() + { + m_secretKey = _secretKey; + m_publicKey = priToPub(_secretKey); + m_type = KeyPairType::Secp256K1; + } + virtual PublicPtr priToPub(SecretPtr _secret) { return secp256k1PriToPub(_secret); } +}; +} // namespace crypto +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2.cpp" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2.cpp" new file mode 100644 index 00000000..12a9f9f1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2.cpp" @@ -0,0 +1,68 @@ +/** + * 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. + * + * @brief implementation for sm2 algorithm + * @file sm2.cpp + * @date 2022.01.17 + * @author yujiechen + */ +#include +#include +#include +#include + +#if WITH_SM2_OPTIMIZE +#include "fastsm2/FastSM2Crypto.h" +bcos::crypto::FastSM2Crypto c_sm2Crypto; +bcos::crypto::FastSM2KeyPair c_sm2KeyPair; +#else +bcos::crypto::SM2Crypto c_sm2Crypto; +bcos::crypto::SM2KeyPair c_sm2KeyPair; +#endif + + +using namespace bcos; +using namespace bcos::crypto; + +PublicPtr bcos::crypto::sm2PriToPub(SecretPtr _secretKey) +{ + return c_sm2KeyPair.priToPub(_secretKey); +} + +std::shared_ptr bcos::crypto::sm2Sign( + const KeyPairInterface& _keyPair, const HashType& _hash, bool _signatureWithPub) +{ + return c_sm2Crypto.sign(_keyPair, _hash, _signatureWithPub); +} + +KeyPairInterface::UniquePtr bcos::crypto::sm2GenerateKeyPair() +{ + return c_sm2Crypto.generateKeyPair(); +} + +bool bcos::crypto::sm2Verify(PublicPtr _pubKey, const HashType& _hash, bytesConstRef _signatureData) +{ + return c_sm2Crypto.verify(_pubKey, _hash, _signatureData); +} + +PublicPtr bcos::crypto::sm2Recover(const HashType& _hash, bytesConstRef _signData) +{ + return c_sm2Crypto.recover(_hash, _signData); +} + +std::pair bcos::crypto::sm2Recover(Hash::Ptr _hashImpl, bytesConstRef _input) +{ + return c_sm2Crypto.recoverAddress(_hashImpl, _input); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2.h" new file mode 100644 index 00000000..4f8a04a0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2.h" @@ -0,0 +1,39 @@ +/** + * 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. + * + * @brief implementation for sm2 algorithm + * @file sm2.h + * @date 2022.01.17 + * @author yujiechen + */ +#pragma once +#include +#include +#include +#include +namespace bcos +{ +namespace crypto +{ +std::shared_ptr sm2Sign( + const KeyPairInterface& _keyPair, const HashType& _hash, bool _signatureWithPub = false); +bool sm2Verify(PublicPtr _pubKey, const HashType& _hash, bytesConstRef _signatureData); +KeyPairInterface::UniquePtr sm2GenerateKeyPair(); +PublicPtr sm2Recover(const HashType& _hash, bytesConstRef _signData); + +std::pair sm2Recover(Hash::Ptr _hashImpl, bytesConstRef _in); +PublicPtr sm2PriToPub(SecretPtr _secret); +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2Crypto.cpp" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2Crypto.cpp" new file mode 100644 index 00000000..aef2411e --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2Crypto.cpp" @@ -0,0 +1,127 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for sm2 signature + * @file SM2Crypto.cpp + * @date 2021.03.10 + * @author yujiechen + */ +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::crypto; +bool SM2Crypto::verify( + std::shared_ptr _pubKeyBytes, const HashType& _hash, bytesConstRef _signatureData) +{ + return verify( + std::make_shared(SM2_PUBLIC_KEY_LEN, _pubKeyBytes), _hash, _signatureData); +} + +KeyPairInterface::UniquePtr SM2Crypto::createKeyPair(SecretPtr _secretKey) +{ + return m_keyPairFactory->createKeyPair(_secretKey); +} + +std::shared_ptr SM2Crypto::sign( + const KeyPairInterface& _keyPair, const HashType& _hash, bool _signatureWithPub) +{ + FixedBytes signatureDataArray; + CInputBuffer rawPrivateKey{_keyPair.secretKey()->constData(), _keyPair.secretKey()->size()}; + CInputBuffer rawPublicKey{_keyPair.publicKey()->constData(), _keyPair.publicKey()->size()}; + CInputBuffer rawMsgHash{(const char*)_hash.data(), HashType::SIZE}; + COutputBuffer sm2SignatureResult{(char*)signatureDataArray.data(), SM2_SIGNATURE_LEN}; + auto retCode = m_signer(&rawPrivateKey, &rawPublicKey, &rawMsgHash, &sm2SignatureResult); + if (retCode != WEDPR_SUCCESS) + { + BOOST_THROW_EXCEPTION( + SignException() << errinfo_comment("sm2Sign failed, raw data: " + _hash.hex())); + } + std::shared_ptr signatureData = std::make_shared(); + *signatureData = signatureDataArray.asBytes(); + // append the public key + if (_signatureWithPub) + { + signatureData->insert(signatureData->end(), _keyPair.publicKey()->mutableData(), + _keyPair.publicKey()->mutableData() + _keyPair.publicKey()->size()); + } + return signatureData; +} + +bool SM2Crypto::verify(PublicPtr _pubKey, const HashType& _hash, bytesConstRef _signatureData) +{ + CInputBuffer publicKey{_pubKey->constData(), _pubKey->size()}; + CInputBuffer messageHash{(const char*)_hash.data(), HashType::SIZE}; + + auto signatureWithoutPub = bytesConstRef(_signatureData.data(), SM2_SIGNATURE_LEN); + CInputBuffer signature{(const char*)signatureWithoutPub.data(), signatureWithoutPub.size()}; + auto verifyResult = m_verifier(&publicKey, &messageHash, &signature); + if (verifyResult == WEDPR_SUCCESS) + { + return true; + } + return false; +} + +PublicPtr SM2Crypto::recover(const HashType& _hash, bytesConstRef _signData) +{ + auto signatureStruct = std::make_shared(_signData); + auto sm2Pub = std::make_shared(SM2_PUBLIC_KEY_LEN, signatureStruct->pub()); + if (verify(sm2Pub, _hash, _signData)) + { + return sm2Pub; + } + BOOST_THROW_EXCEPTION(InvalidSignature() << errinfo_comment( + "invalid signature: sm2 recover public key failed, msgHash : " + + _hash.hex() + ", signature:" + *toHexString(_signData))); +} + +std::pair SM2Crypto::recoverAddress(Hash::Ptr _hashImpl, bytesConstRef _input) +{ + struct + { + HashType hash; + h512 pub; + h256 r; + h256 s; + } in; + memcpy(&in, _input.data(), std::min(_input.size(), sizeof(_input))); + // verify the signature + auto signatureData = std::make_shared(in.r, in.s, in.pub.ref()); + try + { + auto encodedData = signatureData->encode(); + auto sm2Pub = std::make_shared(SM2_PUBLIC_KEY_LEN, signatureData->pub()); + if (verify(sm2Pub, in.hash, bytesConstRef(encodedData->data(), encodedData->size()))) + { + auto address = calculateAddress(_hashImpl, sm2Pub); + return {true, address.asBytes()}; + } + } + catch (const std::exception& e) + { + CRYPTO_LOG(WARNING) << LOG_DESC("sm2Recover failed") + << LOG_KV("error", boost::diagnostic_information(e)); + } + return {false, {}}; +} + +KeyPairInterface::UniquePtr SM2Crypto::generateKeyPair() +{ + return m_keyPairFactory->generateKeyPair(); +} diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2Crypto.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2Crypto.h" new file mode 100644 index 00000000..4b67b7dc --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2Crypto.h" @@ -0,0 +1,69 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for sm2 signature + * @file SM2Crypto.h + * @date 2021.03.10 + * @author yujiechen + */ +#pragma once +#include +#include +#include +#include + +namespace bcos +{ +namespace crypto +{ +const int SM2_SIGNATURE_LEN = 64; +class SM2Crypto : public SignatureCrypto +{ +public: + using Ptr = std::shared_ptr; + SM2Crypto() + { + m_signer = wedpr_sm2_sign_fast; + m_verifier = wedpr_sm2_verify; + m_keyPairFactory = std::make_shared(); + } + virtual ~SM2Crypto() {} + std::shared_ptr sign(const KeyPairInterface& _keyPair, const HashType& _hash, + bool _signatureWithPub = false) override; + + bool verify(PublicPtr _pubKey, const HashType& _hash, bytesConstRef _signatureData) override; + + bool verify(std::shared_ptr _pubKeyBytes, const HashType& _hash, + bytesConstRef _signatureData) override; + + PublicPtr recover(const HashType& _hash, bytesConstRef _signatureData) override; + KeyPairInterface::UniquePtr generateKeyPair() override; + + std::pair recoverAddress(Hash::Ptr _hashImpl, bytesConstRef _in) override; + + KeyPairInterface::UniquePtr createKeyPair(SecretPtr _secretKey) override; + +protected: + std::function + m_signer; + + std::function + m_verifier; + KeyPairFactory::Ptr m_keyPairFactory; +}; +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2KeyPair.cpp" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2KeyPair.cpp" new file mode 100644 index 00000000..863e97d1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2KeyPair.cpp" @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for SM2KeyPair + * @file SM2KeyPair.cpp + * @date 2021.03.10 + * @author yujiechen + */ +#include +#include +#include + +using namespace bcos; +using namespace bcos::crypto; +SM2KeyPair::SM2KeyPair(SecretPtr _secretKey) : SM2KeyPair() +{ + if (_secretKey->size() != SM2_PRIVATE_KEY_LEN) + { + BOOST_THROW_EXCEPTION(GenerateKeyPairException() << errinfo_comment( + "Invalid InvalidSecretKey, sm2 secret length must be " + + std::to_string(SM2_PRIVATE_KEY_LEN))); + } + m_secretKey = _secretKey; + m_publicKey = priToPub(_secretKey); +} + +PublicPtr SM2KeyPair::priToPub(SecretPtr _secretKey) +{ + CInputBuffer privateKey{_secretKey->constData(), _secretKey->size()}; + auto pubKey = std::make_shared(SM2_PUBLIC_KEY_LEN); + COutputBuffer publicKey{pubKey->mutableData(), pubKey->size()}; + auto retCode = m_publicKeyDeriver(&privateKey, &publicKey); + if (retCode != WEDPR_SUCCESS) + { + BOOST_THROW_EXCEPTION( + PriToPublicKeyException() << errinfo_comment("sm2PriToPub exception")); + } + return pubKey; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2KeyPair.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2KeyPair.h" new file mode 100644 index 00000000..8ced522a --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2KeyPair.h" @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for SM2KeyPair + * @file SM2KeyPair.h + * @date 2021.03.10 + * @author yujiechen + */ +#pragma once +#include +#include +#include + +namespace bcos +{ +namespace crypto +{ +const int SM2_PRIVATE_KEY_LEN = 32; +const int SM2_PUBLIC_KEY_LEN = 64; +PublicPtr sm2PriToPub(SecretPtr _secret); +class SM2KeyPair : public KeyPair +{ +public: + SM2KeyPair() : KeyPair(SM2_PUBLIC_KEY_LEN, SM2_PRIVATE_KEY_LEN, KeyPairType::SM2) + { + m_publicKeyDeriver = wedpr_sm2_derive_public_key; + } + explicit SM2KeyPair(SecretPtr _secretKey); + ~SM2KeyPair() override {} + virtual PublicPtr priToPub(SecretPtr _secretKey); + +protected: + std::function + m_publicKeyDeriver; +}; +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2KeyPairFactory.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2KeyPairFactory.h" new file mode 100644 index 00000000..886e4337 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/signature/sm2/SM2KeyPairFactory.h" @@ -0,0 +1,62 @@ +/** + * 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. + * + * @brief implementation for sm2 keyPairFactory algorithm + * @file SM2KeyPairFactory.h + * @date 2022.01.17 + * @author yujiechen + */ +#pragma once +#include +#include +#include +#include +namespace bcos +{ +namespace crypto +{ +class SM2KeyPairFactory : public KeyPairFactory +{ +public: + using Ptr = std::shared_ptr; + SM2KeyPairFactory() { m_keyPairGenerator = wedpr_sm2_gen_key_pair; } + ~SM2KeyPairFactory() override {} + + virtual KeyPairInterface::UniquePtr createKeyPair() { return std::make_unique(); } + KeyPairInterface::UniquePtr createKeyPair(SecretPtr _secretKey) override + { + return std::make_unique(_secretKey); + } + + KeyPairInterface::UniquePtr generateKeyPair() override + { + auto keyPair = createKeyPair(); + COutputBuffer publicKey{keyPair->publicKey()->mutableData(), keyPair->publicKey()->size()}; + COutputBuffer privateKey{keyPair->secretKey()->mutableData(), keyPair->secretKey()->size()}; + auto retCode = m_keyPairGenerator(&publicKey, &privateKey); + if (retCode != WEDPR_SUCCESS) + { + BOOST_THROW_EXCEPTION( + GenerateKeyPairException() << errinfo_comment("generateKeyPair exception")); + } + return keyPair; + } + +protected: + std::function + m_keyPairGenerator; +}; +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/zkp/Common.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/zkp/Common.h" new file mode 100644 index 00000000..9d515ea0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/zkp/Common.h" @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2021 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. + * + * @brief define the exceptions for zkp of bcos-crypto + * @file Common.h + * @date 2022.07.18 + * @author yujiechen + */ +#pragma once +#include "Exceptions.h" +#include +#include + +namespace bcos +{ +namespace crypto +{ +inline CInputBuffer bytesToInputBuffer(bytes const& data, size_t _length) +{ + if (data.size() < _length) + { + BOOST_THROW_EXCEPTION( + InvalidInputInput() << errinfo_comment( + "InvalidInputInput: the data size must be at least " + std::to_string(_length))); + } + return CInputBuffer{(const char*)data.data(), _length}; +} +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/zkp/Exceptions.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/zkp/Exceptions.h" new file mode 100644 index 00000000..45409d95 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/zkp/Exceptions.h" @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2021 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. + * + * @brief define the exceptions for zkp of bcos-crypto + * @file Execptions.h + * @date 2022.07.18 + * @author yujiechen + */ +#pragma once +#include + +namespace bcos +{ +namespace crypto +{ +DERIVE_BCOS_EXCEPTION(ZkpExcetpion); +DERIVE_BCOS_EXCEPTION(InvalidInputInput); +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/zkp/discretezkp/DiscreteLogarithmZkp.cpp" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/zkp/discretezkp/DiscreteLogarithmZkp.cpp" new file mode 100644 index 00000000..704fa448 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/zkp/discretezkp/DiscreteLogarithmZkp.cpp" @@ -0,0 +1,161 @@ +/** + * 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. + * + * @brief implementation for discrete-logarithm-proof + * @file DiscreteLogarithmZkp.cpp + * @date 2022.01.17 + * @author yujiechen + */ +#include "DiscreteLogarithmZkp.h" +#include "bcos-crypto/zkp/Common.h" + +using namespace bcos; +using namespace bcos::crypto; + +// wedpr_verify_either_equality_relationship_proof +bool DiscreteLogarithmZkp::verifyEitherEqualityProof(bytes const& c1PointData, + bytes const& c2PointData, bytes const& c3PointData, bytes const& equalityProofData, + bytes const& basePointData, bytes const& blindingBasePointData) +{ + auto c1Point = bytesToInputBuffer(c1PointData, m_pointLen); + auto c2Point = bytesToInputBuffer(c2PointData, m_pointLen); + auto c3Point = bytesToInputBuffer(c3PointData, m_pointLen); + CInputBuffer proof{(const char*)equalityProofData.data(), equalityProofData.size()}; + auto basePoint = bytesToInputBuffer(basePointData, m_pointLen); + auto blindingBasePoint = bytesToInputBuffer(blindingBasePointData, m_pointLen); + auto ret = wedpr_verify_either_equality_relationship_proof( + &c1Point, &c2Point, &c3Point, &proof, &basePoint, &blindingBasePoint); + if (ret == WEDPR_SUCCESS) + { + return true; + } + return false; +} + +// wedpr_verify_knowledge_proof +bool DiscreteLogarithmZkp::verifyKnowledgeProof(bytes const& pointData, + bytes const& knowledgeProofData, bytes const& basePointData, bytes const& blindingBasePointData) +{ + auto point = bytesToInputBuffer(pointData, m_pointLen); + CInputBuffer proof{(const char*)knowledgeProofData.data(), knowledgeProofData.size()}; + auto basePoint = bytesToInputBuffer(basePointData, m_pointLen); + auto blindingBasePoint = bytesToInputBuffer(blindingBasePointData, m_pointLen); + + auto ret = wedpr_verify_knowledge_proof(&point, &proof, &basePoint, &blindingBasePoint); + if (ret == WEDPR_SUCCESS) + { + return true; + } + return false; +} + +// wedpr_verify_format_proof +bool DiscreteLogarithmZkp::verifyFormatProof(bytes const& c1Point, bytes const& c2Point, + bytes const& formatProofData, bytes const& c1BasePointData, bytes const& c2BasePointData, + bytes const& blindingBasePointData) +{ + auto c1 = bytesToInputBuffer(c1Point, m_pointLen); + auto c2 = bytesToInputBuffer(c2Point, m_pointLen); + CInputBuffer proof{(const char*)formatProofData.data(), formatProofData.size()}; + auto c1BasePoint = bytesToInputBuffer(c1BasePointData, m_pointLen); + auto c2BasePoint = bytesToInputBuffer(c2BasePointData, m_pointLen); + auto blindingBasePoint = bytesToInputBuffer(blindingBasePointData, m_pointLen); + auto ret = + wedpr_verify_format_proof(&c1, &c2, &proof, &c1BasePoint, &c2BasePoint, &blindingBasePoint); + if (ret == WEDPR_SUCCESS) + { + return true; + } + return false; +} + +// wedpr_verify_sum_relationship +bool DiscreteLogarithmZkp::verifySumProof(bytes const& c1Point, bytes const& c2Point, + bytes const& c3Point, bytes const& arithmeticProofData, bytes const& valueBasePointData, + bytes const& blindingBasePointData) +{ + auto c1 = bytesToInputBuffer(c1Point, m_pointLen); + auto c2 = bytesToInputBuffer(c2Point, m_pointLen); + auto c3 = bytesToInputBuffer(c3Point, m_pointLen); + CInputBuffer proof{(const char*)arithmeticProofData.data(), arithmeticProofData.size()}; + auto valueBasePoint = bytesToInputBuffer(valueBasePointData, m_pointLen); + auto blindingBasePoint = bytesToInputBuffer(blindingBasePointData, m_pointLen); + auto ret = + wedpr_verify_sum_relationship(&c1, &c2, &c3, &proof, &valueBasePoint, &blindingBasePoint); + if (ret == WEDPR_SUCCESS) + { + return true; + } + return false; +} + +// wedpr_verify_product_relationship +bool DiscreteLogarithmZkp::verifyProductProof(bytes const& c1Point, bytes const& c2Point, + bytes const& c3Point, bytes const& arithmeticProofData, bytes const& valueBasePointData, + bytes const& blindingBasePointData) +{ + auto c1 = bytesToInputBuffer(c1Point, m_pointLen); + auto c2 = bytesToInputBuffer(c2Point, m_pointLen); + auto c3 = bytesToInputBuffer(c3Point, m_pointLen); + CInputBuffer proof{(const char*)arithmeticProofData.data(), arithmeticProofData.size()}; + auto valueBasePoint = bytesToInputBuffer(valueBasePointData, m_pointLen); + auto blindingBasePoint = bytesToInputBuffer(blindingBasePointData, m_pointLen); + auto ret = wedpr_verify_product_relationship( + &c1, &c2, &c3, &proof, &valueBasePoint, &blindingBasePoint); + if (ret == WEDPR_SUCCESS) + { + return true; + } + return false; +} + +// wedpr_verify_equality_relationship_proof +bool DiscreteLogarithmZkp::verifyEqualityProof(bytes const& c1Point, bytes const c2Point, + bytes const& equalityProofData, bytes const& basePoint1Data, bytes const& basePoint2Data) +{ + auto c1 = bytesToInputBuffer(c1Point, m_pointLen); + auto c2 = bytesToInputBuffer(c2Point, m_pointLen); + CInputBuffer proof{(const char*)equalityProofData.data(), equalityProofData.size()}; + + auto basePoint1 = bytesToInputBuffer(basePoint1Data, m_pointLen); + auto basePoint2 = bytesToInputBuffer(basePoint2Data, m_pointLen); + auto ret = wedpr_verify_equality_relationship_proof(&c1, &c2, &proof, &basePoint1, &basePoint2); + if (ret == WEDPR_SUCCESS) + { + return true; + } + return false; +} + +// wedpr_aggregate_ristretto_point +bytes DiscreteLogarithmZkp::aggregateRistrettoPoint(bytes const& pointSum, bytes const& pointShare) +{ + // with empty pointShare to aggregate + if (pointShare.size() == 0) + { + return pointSum; + } + auto pointSumInput = bytesToInputBuffer(pointSum, m_pointLen); + auto pointShareInput = bytesToInputBuffer(pointShare, m_pointLen); + bytes aggregatedResult; + aggregatedResult.resize(m_pointLen); + COutputBuffer result{(char*)aggregatedResult.data(), m_pointLen}; + auto ret = wedpr_aggregate_ristretto_point(&pointSumInput, &pointShareInput, &result); + if (ret == WEDPR_SUCCESS) + { + return aggregatedResult; + } + BOOST_THROW_EXCEPTION(ZkpExcetpion() << errinfo_comment("aggregateRistrettoPoint error")); +} diff --git "a/BFPL\345\243\271/bcos-crypto/bcos-crypto/zkp/discretezkp/DiscreteLogarithmZkp.h" "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/zkp/discretezkp/DiscreteLogarithmZkp.h" new file mode 100644 index 00000000..fc102c06 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/bcos-crypto/zkp/discretezkp/DiscreteLogarithmZkp.h" @@ -0,0 +1,69 @@ +/** + * 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. + * + * @brief implementation for discrete-logarithm-proof + * @file DiscreteLogarithmZkp.h + * @date 2022.01.17 + * @author yujiechen + */ +#pragma once +#include +#include +#include +namespace bcos +{ +namespace crypto +{ +class DiscreteLogarithmZkp +{ +public: + using Ptr = std::shared_ptr; + DiscreteLogarithmZkp(size_t _pointLen) : m_pointLen(_pointLen) {} + DiscreteLogarithmZkp() {} + + virtual ~DiscreteLogarithmZkp() {} + + // wedpr_verify_either_equality_relationship_proof + bool verifyEitherEqualityProof(bytes const& c1Point, bytes const& c2Point, bytes const& c3Point, + bytes const& equalityProof, bytes const& basePoint, bytes const& blindingBasePoint); + + // wedpr_verify_knowledge_proof + bool verifyKnowledgeProof(bytes const& pointData, bytes const& knowledgeProof, + bytes const& basePointData, bytes const& blindingBasePoint); + + // wedpr_verify_format_proof + bool verifyFormatProof(bytes const& c1Point, bytes const& c2Point, bytes const& formatProof, + bytes const& c1BasePoint, bytes const& c2BasePoint, bytes const& blindingBasePoint); + + // wedpr_verify_sum_relationship + bool verifySumProof(bytes const& c1Point, bytes const& c2Point, bytes const& c3Point, + bytes const& arithmeticProof, bytes const& valueBasePoint, bytes const& blindingBasePoint); + + // wedpr_verify_product_relationship + bool verifyProductProof(bytes const& c1Point, bytes const& c2Point, bytes const& c3Point, + bytes const& arithmeticProof, bytes const& valueBasePoint, bytes const& blindingBasePoint); + + // wedpr_verify_equality_relationship_proof + bool verifyEqualityProof(bytes const& c1Point, bytes const c2Point, bytes const& equalityProof, + bytes const& basePoint1, bytes const& basePoint2); + + // wedpr_aggregate_ristretto_point + bytes aggregateRistrettoPoint(bytes const& pointSum, bytes const& pointShare); + +private: + size_t m_pointLen = 32; +}; +} // namespace crypto +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/demo/CMakeLists.txt" "b/BFPL\345\243\271/bcos-crypto/demo/CMakeLists.txt" new file mode 100644 index 00000000..844d57f8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/demo/CMakeLists.txt" @@ -0,0 +1,15 @@ +find_package(ZLIB REQUIRED) + +add_executable(perf_demo perf_demo.cpp) +target_link_libraries(perf_demo PRIVATE bcos-crypto ZLIB::ZLIB) + +if(ENABLE_IPPCRYPTO) + find_package(IPPCrypto REQUIRED MODULE) + if (NOT IPPCRYPTO_FOUND) + message(FATAL_ERROR "No Intel IPP Cryptography library found on the system. To build bcos-crypto, please specify -DIPPCRYPTO_ROOT_DIR= option, where is the path to directory that contains include/ and lib/ folders of Intel IPP Cryptography product.") + endif() + + add_executable(hasher_test hasher_test.cpp) + target_include_directories(hasher_test PUBLIC ${IPPCRYPTO_INCLUDE_DIRS}) + target_link_libraries(hasher_test PUBLIC ${BCOS_CRYPTO_TARGET} Boost::thread ${IPPCRYPTO_LIBRARIES} ZLIB::ZLIB) +endif() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/demo/hasher_test.cpp" "b/BFPL\345\243\271/bcos-crypto/demo/hasher_test.cpp" new file mode 100644 index 00000000..11309b48 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/demo/hasher_test.cpp" @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::crypto::hasher; + +template +auto hasherTest(std::string_view name, RANGES::random_access_range auto&& input, + size_t totalSize, bool multiThread) +{ + std::vector> results{RANGES::size(input)}; + + std::cout << " " << name; + auto begin = std::chrono::high_resolution_clock::now(); + +#pragma omp parallel if (multiThread) + { + HasherType hasher; + +#pragma omp for + for (RANGES::range_size_t i = 0; i < RANGES::size(input); ++i) + { + hasher.update(input[i]); + results[i] = hasher.final(); + } + } + + auto duration = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - begin) + .count(); + auto throughput = ((double)totalSize / ((double)duration / 1000.0)) / 1024.0 / 1024.0; + std::cout << " duration: " << duration << "ms throughput: " << throughput << "MB/s" + << std::endl; + + return results; +} + +template +void testHash(size_t count, bool multiThread) +{ + constexpr auto valueSize = blockSize * 1024; + std::vector> inputData; + inputData.resize(count); + + // Generate random data +#pragma omp parallel + { + std::mt19937 random(std::random_device{}()); + +#pragma omp for + for (typename decltype(inputData)::size_type i = 0; i < inputData.size(); ++i) + { + auto& output = inputData[i]; + std::span view{ + (unsigned int*)output.data(), output.size() / sizeof(unsigned int)}; + for (auto& num : view) + { + num = random(); + } + } + } + + hasherTest( + "OpenSSL_SHA2_256", inputData, valueSize * count, multiThread); + hasherTest( + "OpenSSL_SM3", inputData, valueSize * count, multiThread); + + hasherTest( + "IPPCrypto_SHA2_256", inputData, valueSize * count, multiThread); + hasherTest( + "IPPCrypto_SM3", inputData, valueSize * count, multiThread); +} + +int main(int argc, char* argv[]) +{ + boost::ignore_unused(argc, argv); + if (argc < 2) + { + std::cout << "Usage: " << argv[0] << " [count] [enable multithread(0/1)]" << std::endl; + return 1; + } + + auto count = boost::lexical_cast(argv[1]); + auto multiThread = boost::lexical_cast(argv[2]); + + std::cout << "Testing 1k chunk..." << std::endl; + testHash<1>(count, multiThread); + std::cout << "Testing 4k chunk..." << std::endl; + testHash<4>(count, multiThread); + std::cout << "Testing 8k chunk..." << std::endl; + testHash<8>(count, multiThread); + std::cout << "Testing 16k chunk..." << std::endl; + testHash<16>(count, multiThread); + + return 0; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/demo/perf_demo.cpp" "b/BFPL\345\243\271/bcos-crypto/demo/perf_demo.cpp" new file mode 100644 index 00000000..a7addce3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/demo/perf_demo.cpp" @@ -0,0 +1,369 @@ +/** + * Copyright (C) 2021 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. + * + * @brief perf for bcos-crypto + * @file perf_demo.cpp + * @date 2022.06.04 + * @author yujiechen ancelmo + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::crypto; + +const std::string HASH_CMD = "hash"; +const std::string SIGN_CMD = "sign"; +const std::string ENCRYPT_CMD = "enc"; + +void Usage(std::string const& _appName) +{ + std::cout << _appName << " [" << HASH_CMD << "/" << SIGN_CMD << "/" << ENCRYPT_CMD << "] count" + << std::endl; +} + +double getTPS(int64_t _endT, int64_t _startT, size_t _count) +{ + return (1000.0 * (double)_count) / (double)(_endT - _startT); +} + +template +std::vector hashPerf( + Hash& _hash, std::string_view _hashName, std::string_view _inputData, size_t _count) +{ + std::vector result(_count); + + std::cout << std::endl; + std::cout << "----------- " << _hashName << " perf start -----------" << std::endl; + auto startT = utcTime(); + for (size_t i = 0; i < _count; i++) + { + result[i] = _hash->hash(bytesConstRef((byte const*)_inputData.data(), _inputData.size())); + } + std::cout << "input data size: " << (double)_inputData.size() / 1000.0 + << "KB, loops: " << _count << ", timeCost: " << utcTime() - startT << std::endl; + std::cout << "TPS of " << _hashName << ": " + << getTPS(utcTime(), startT, _count) * (double)_inputData.size() / (1024 * 1024) + << " MB/s" << std::endl; + std::cout << "----------- " << _hashName << " perf end -----------" << std::endl; + std::cout << std::endl; + + return result; +} + +std::vector hashingPerf( + bcos::crypto::hasher::Hasher auto& hasher, std::string_view _inputData, size_t _count) +{ + std::vector result(_count); + + std::string hashName = std::string{"New Hashing Test: "} + typeid(hasher).name(); + std::cout << std::endl; + std::cout << "----------- " << hashName << " perf start -----------" << std::endl; + auto startT = utcTime(); + + for (size_t i = 0; i < _count; i++) + { + hasher.update(_inputData); + hasher.final(result[i]); + } + + std::cout << "input data size: " << (double)_inputData.size() / 1000.0 + << "KB, loops: " << _count << ", timeCost: " << utcTime() - startT << std::endl; + std::cout << "TPS of " << hashName << ": " + << getTPS(utcTime(), startT, _count) * (double)_inputData.size() / (1024 * 1024) + << " MB/s" << std::endl; + std::cout << "----------- " << hashName << " perf end -----------" << std::endl; + std::cout << std::endl; + + return result; +} + +void stTest(std::string_view inputData, size_t _count) +{ + // keccak256 perf + // auto hashImpl = std::make_shared(); + // auto keccak256Old = hashPerf(hashImpl, "Keccak256", inputData, _count); + + // openssl::OPENSSL_Keccak256_Hasher hasherKeccak256; + // auto keccak256New = hashingPerf(hasherKeccak256, inputData, _count); + // if (keccak256Old[0] != keccak256New[0]) + // { + // std::cout << "Wrong keccak256 hash result! old: " << keccak256Old[0] + // << " new: " << keccak256New[0] << std::endl; + // } + + // sha3 perf + auto hashImpl2 = std::make_shared(); + auto sha3Old = hashPerf(hashImpl2, "SHA3", inputData, _count); + + hasher::openssl::OpenSSL_SHA3_256_Hasher hasherSHA3; + auto sha3New = hashingPerf(hasherSHA3, inputData, _count); + + for (size_t i = 0; i < _count; ++i) + { + if (sha3Old[i] != sha3New[i]) + { + std::cout << "Wrong sha3 hash result! old: " << sha3Old[i] << " new: " << sha3New[i] + << std::endl; + break; + } + } + + // sm3 perf + auto hashImpl3 = std::make_shared(); + auto sm3Old = hashPerf(hashImpl3, "SM3", inputData, _count); + + hasher::openssl::OpenSSL_SM3_Hasher hasherSM3; + auto sm3New = hashingPerf(hasherSM3, inputData, _count); + + for (size_t i = 0; i < _count; ++i) + { + if (sm3Old[i] != sm3New[i]) + { + std::cout << "Wrong sm3 hash result! old: " << sm3Old[i] << " new: " << sm3New[i] + << std::endl; + break; + } + } + + auto hashImpl4 = std::make_shared(); + auto sha256Old = hashPerf(hashImpl4, "SHA256", inputData, _count); + + hasher::openssl::OpenSSL_SHA2_256_Hasher hasherSHA2; + auto sha256New = hashingPerf(hasherSHA2, inputData, _count); + + for (size_t i = 0; i < _count; ++i) + { + if (sha256Old[i] != sha256New[i]) + { + std::cout << "Wrong sha256 hash result! old: " << sha256Old[i] + << " new: " << sha256New[i] << std::endl; + break; + } + } +} + +void signaturePerf(SignatureCrypto::Ptr _signatureImpl, HashType const& _msgHash, + std::string const& _signatureName, size_t _count) +{ + std::cout << std::endl; + std::cout << "----------- " << _signatureName << " perf test start -----------" << std::endl; + + auto keyPair = _signatureImpl->generateKeyPair(); + // sign + std::shared_ptr signedData; + auto startT = utcTime(); + for (size_t i = 0; i < _count; i++) + { + signedData = _signatureImpl->sign(*keyPair, _msgHash, false); + } + std::cout << "TPS of " << _signatureName << " sign:" << getTPS(utcTime(), startT, _count) + << std::endl; + + // verify + startT = utcTime(); + for (size_t i = 0; i < _count; i++) + { + assert(_signatureImpl->verify(keyPair->publicKey(), _msgHash, ref(*signedData))); + } + std::cout << "TPS of " << _signatureName << " verify:" << getTPS(utcTime(), startT, _count) + << std::endl; + + // recover + signedData = _signatureImpl->sign(*keyPair, _msgHash, true); + startT = utcTime(); + for (size_t i = 0; i < _count; i++) + { + _signatureImpl->recover(_msgHash, ref(*signedData)); + } + std::cout << "TPS of " << _signatureName << " recover:" << getTPS(utcTime(), startT, _count) + << std::endl; + std::cout << "----------- " << _signatureName << " perf test end -----------" << std::endl; + std::cout << std::endl; +} + +void derivePublicKeyPerf(SignatureCrypto::Ptr _signatureImpl, std::string const& _signatureName, + const KeyPairInterface& _keyPair, size_t _count) +{ + std::cout << std::endl; + std::cout << "----------- " << _signatureName << " derivePublicKeyPerf test start -----------" + << std::endl; + auto startT = utcTime(); + KeyPairInterface::Ptr keyPair; + for (size_t i = 0; i < _count; i++) + { + keyPair = _signatureImpl->createKeyPair(_keyPair.secretKey()); + assert(keyPair->secretKey()->data() == _keyPair.secretKey()->data()); + assert(keyPair->publicKey()->data() == _keyPair.publicKey()->data()); + } + std::cout << "TPS of " << _signatureName + << " derivePublicKeyPerf:" << getTPS(utcTime(), startT, _count) << std::endl; + std::cout << "----------- " << _signatureName << " derivePublicKeyPerf test end -----------" + << std::endl; +} + +void derivePublicKeyPerf(size_t _count) +{ + SignatureCrypto::Ptr signatureImpl = nullptr; + signatureImpl = std::make_shared(); + auto keyPair = signatureImpl->generateKeyPair(); + derivePublicKeyPerf(signatureImpl, "secp256k1", *keyPair, _count); + + signatureImpl = std::make_shared(); + keyPair = signatureImpl->generateKeyPair(); + derivePublicKeyPerf(signatureImpl, "SM2", *keyPair, _count); + +#if SM2_OPTIMIZE + signatureImpl = std::make_shared(); + keyPair = signatureImpl->generateKeyPair(); + derivePublicKeyPerf(signatureImpl, "FastSM2", *keyPair, _count); +#endif + + signatureImpl = std::make_shared(); + keyPair = signatureImpl->generateKeyPair(); + derivePublicKeyPerf(signatureImpl, "Ed25519Crypto", *keyPair, _count); +} +void signaturePerf(size_t _count) +{ + std::string inputData = "signature perf test"; + auto msgHash = keccak256Hash(bytesConstRef((byte const*)inputData.c_str(), inputData.size())); + // secp256k1 perf + SignatureCrypto::Ptr signatureImpl = std::make_shared(); + signaturePerf(signatureImpl, msgHash, "secp256k1", _count); + + // sm2 perf + signatureImpl = std::make_shared(); + signaturePerf(signatureImpl, msgHash, "SM2", _count); + +#if SM2_OPTIMIZE + // fastsm2 perf + signatureImpl = std::make_shared(); + signaturePerf(signatureImpl, msgHash, "FastSM2", _count); +#endif + + // ed25519 perf + signatureImpl = std::make_shared(); + signaturePerf(signatureImpl, msgHash, "Ed25519", _count); +} + +void encryptPerf(SymmetricEncryption::Ptr _encryptor, std::string const& _inputData, + const std::string& _encryptorName, size_t _count) +{ + std::cout << std::endl; + std::cout << "----------- " << _encryptorName << " perf test start -----------" << std::endl; + std::string key = "abcdefgwerelkewrwerw"; + // encrypt + bytesPointer encryptedData; + auto startT = utcTime(); + for (size_t i = 0; i < _count; i++) + { + encryptedData = _encryptor->symmetricEncrypt((const unsigned char*)_inputData.c_str(), + _inputData.size(), (const unsigned char*)key.c_str(), key.size()); + } + std::cout << "PlainData size:" << (double)_inputData.size() / 1000.0 << " KB, loops: " << _count + << ", timeCost: " << utcTime() - startT << " ms" << std::endl; + std::cout << "TPS of " << _encryptorName << " encrypt:" + << (getTPS(utcTime(), startT, _count) * (double)(_inputData.size())) / 1000.0 + << "KB/s" << std::endl; + std::cout << std::endl; + // decrypt + startT = utcTime(); + bytesPointer decryptedData; + for (size_t i = 0; i < _count; i++) + { + decryptedData = _encryptor->symmetricDecrypt((const unsigned char*)encryptedData->data(), + encryptedData->size(), (const unsigned char*)key.c_str(), key.size()); + } + std::cout << "CiperData size:" << (double)encryptedData->size() / 1000.0 + << " KB, loops: " << _count << ", timeCost:" << utcTime() - startT << " ms" + << std::endl; + std::cout << "TPS of " << _encryptorName << " decrypt:" + << (getTPS(utcTime(), startT, _count) * (double)_inputData.size()) / 1000.0 << "KB/s" + << std::endl; + bytes plainBytes(_inputData.begin(), _inputData.end()); + assert(plainBytes == *decryptedData); + + std::cout << "----------- " << _encryptorName << " perf test end -----------" << std::endl; + std::cout << std::endl; +} + +void encryptPerf(size_t _count) +{ + std::string inputData = "w3rwerk2-304swlerkjewlrjoiur4kslfjsd,fmnsdlfjlwerlwerjw;erwe;rewrew"; + std::string deltaData = inputData; + for (int i = 0; i < 100; i++) + { + inputData += deltaData; + } + // AES + SymmetricEncryption::Ptr encryptor = std::make_shared(); + encryptPerf(encryptor, inputData, "AES", _count); + // SM4 + encryptor = std::make_shared(); + encryptPerf(encryptor, inputData, "SM4", _count); +} + +int main(int argc, char* argv[]) +{ + if (argc < 3) + { + Usage(argv[0]); + return -1; + } + auto cmd = argv[1]; + size_t count = atoi(argv[2]); + + std::string data; + data.reserve(16 * 1024); + for (size_t i = 0; i < 16 * 1024; ++i) + { + char c = i; + data.push_back(c); + } + + + if (HASH_CMD == cmd) + { + stTest(data, count); + } + else if (SIGN_CMD == cmd) + { + signaturePerf(count); + derivePublicKeyPerf(count); + } + else if (ENCRYPT_CMD == cmd) + { + encryptPerf(count); + } + else + { + std::cout << "Invalid subcommand \"" << cmd << "\"" << std::endl; + Usage(argv[0]); + } + return 0; +} diff --git "a/BFPL\345\243\271/bcos-crypto/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-crypto/test/CMakeLists.txt" new file mode 100644 index 00000000..57373dda --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/test/CMakeLists.txt" @@ -0,0 +1,29 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of bcos-framework +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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(GLOB_RECURSE SOURCES "*.cpp" "*.h" "*.sol") + +# cmake settings +set(TEST_BINARY_NAME test-bcos-crypto) + +find_package(Boost REQUIRED COMPONENTS unit_test_framework) +find_package(ZLIB REQUIRED) + +add_executable(${TEST_BINARY_NAME} ${SOURCES}) +target_include_directories(${TEST_BINARY_NAME} PRIVATE ..) +target_link_libraries(${TEST_BINARY_NAME} Boost::unit_test_framework bcos-utilities bcos-crypto ZLIB::ZLIB) +add_test(NAME test-crypto WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/test/unittests/EncryptionTest.cpp" "b/BFPL\345\243\271/bcos-crypto/test/unittests/EncryptionTest.cpp" new file mode 100644 index 00000000..4353f8df --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/test/unittests/EncryptionTest.cpp" @@ -0,0 +1,106 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test cases for AES/sm4 + * @file SignatureTest.h + * @date 2021.03.06 + */ +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::crypto; + +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(EncryptionTest, TestPromptFixture) + +void testEncryption(SymmetricEncryption::Ptr _encrypt) +{ + std::string plainData = "testAESx%$234sdfjl234129p"; + std::string key = "abcd"; + // encrypt + auto ciperData = _encrypt->symmetricEncrypt((const unsigned char*)plainData.c_str(), + plainData.size(), (const unsigned char*)key.c_str(), key.size()); + // decrypt + auto decryptedData = _encrypt->symmetricDecrypt((const unsigned char*)ciperData->data(), + ciperData->size(), (const unsigned char*)key.c_str(), key.size()); + bytes plainDataBytes(plainData.begin(), plainData.end()); + BOOST_CHECK(*decryptedData == plainDataBytes); + + // invalid key + std::string invalidKey = "ABCDCD"; + try + { + _encrypt->symmetricDecrypt((const unsigned char*)ciperData->data(), ciperData->size(), + (const unsigned char*)invalidKey.c_str(), invalidKey.size()); + BOOST_CHECK(std::string_view((const char*)ciperData->data(), ciperData->size()) != + std::string_view(plainData)); + } + catch (std::exception& e) + { + // We only need to throw DecryptException, if there is another exception, + // dynamic_cast will throw exception + // So we check no throw here. + BOOST_CHECK_NO_THROW(auto dee = dynamic_cast(e)); + } + + // invalid ciper + (*ciperData)[0] += 10; + decryptedData = _encrypt->symmetricDecrypt((const unsigned char*)ciperData->data(), + ciperData->size(), (const unsigned char*)key.c_str(), key.size()); + plainDataBytes = bytes(plainData.begin(), plainData.end()); + BOOST_CHECK(*decryptedData != plainDataBytes); + // test encrypt/decrypt with given ivData + std::string ivData = "adfwerivswerwerwerpi9werlwerwasdfa234523423dsfa"; + key = "werwlerewkrjewwwwwwwr4234981034%wer23423&3453453453465646778)7897678"; + plainData += "testAESx%$234sdfjl234129p" + ivData + key; + ciperData = _encrypt->symmetricEncrypt((const unsigned char*)plainData.c_str(), + plainData.size(), (const unsigned char*)key.c_str(), key.size(), + (const unsigned char*)ivData.c_str(), ivData.size()); + + decryptedData = _encrypt->symmetricDecrypt((const unsigned char*)ciperData->data(), + ciperData->size(), (const unsigned char*)key.c_str(), key.size(), + (const unsigned char*)ivData.c_str(), ivData.size()); + plainDataBytes = bytes(plainData.begin(), plainData.end()); + BOOST_CHECK(*decryptedData == plainDataBytes); + + // invalid ivData + std::string invalidIVData = "bdfwerivswerwerwerpi9werlwerwasdfa234523423dsf"; + decryptedData = _encrypt->symmetricDecrypt((const unsigned char*)ciperData->data(), + ciperData->size(), (const unsigned char*)key.c_str(), key.size(), + (const unsigned char*)invalidIVData.c_str(), invalidIVData.size()); + plainDataBytes = bytes(plainData.begin(), plainData.end()); + BOOST_CHECK(*decryptedData != plainDataBytes); +} +BOOST_AUTO_TEST_CASE(testAES) +{ + auto aes = std::make_shared(); + testEncryption(aes); +} + +BOOST_AUTO_TEST_CASE(testSM4) +{ + auto sm4 = std::make_shared(); + testEncryption(sm4); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-crypto/test/unittests/HashTest.cpp" "b/BFPL\345\243\271/bcos-crypto/test/unittests/HashTest.cpp" new file mode 100644 index 00000000..77f41a8e --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/test/unittests/HashTest.cpp" @@ -0,0 +1,126 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test cases for keccak256/sm3 + * @file HashTest.h + * @date 2021.03.04 + */ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace crypto; +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(HashTest, TestPromptFixture) +BOOST_AUTO_TEST_CASE(testKeccak256) +{ + auto keccak256 = std::make_shared(); + auto cryptoSuite = std::make_shared(keccak256, nullptr, nullptr); + // test multiple-thread + auto worker = std::make_shared("testHash", 8); + std::atomic totolCount = 1000; + worker->enqueue([&]() { + if (totolCount <= 0) + { + return; + } + while (totolCount > 0) + { + totolCount -= 1; + auto data = "abcde" + std::to_string(totolCount.load()); + keccak256->hash(data); + } + }); + while (totolCount > 0) + { + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + std::string ts = keccak256->emptyHash().hex(); + BOOST_CHECK_EQUAL( + ts, std::string("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); + + std::string hashData = "abcde"; + + ts = keccak256->hash(hashData).hex(); + BOOST_CHECK_EQUAL( + ts, std::string("6377c7e66081cb65e473c1b95db5195a27d04a7108b468890224bedbe1a8a6eb")); + + h256 emptyKeccak256( + *fromHexString("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); + BOOST_REQUIRE_EQUAL(emptyKeccak256, keccak256->emptyHash()); + + BOOST_REQUIRE_EQUAL(cryptoSuite->hash(""), + h256("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); + BOOST_REQUIRE_EQUAL(cryptoSuite->hash("hello"), + h256("1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8")); +} +BOOST_AUTO_TEST_CASE(testSM3) +{ + auto sm3 = std::make_shared(); + auto cryptoSuite = std::make_shared(sm3, nullptr, nullptr); + std::string ts = sm3->emptyHash().hex(); + BOOST_CHECK_EQUAL( + ts, std::string("1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b")); + + std::string hashData = "abcde"; + ts = sm3->hash(hashData).hex(); + BOOST_CHECK_EQUAL( + ts, std::string("afe4ccac5ab7d52bcae36373676215368baf52d3905e1fecbe369cc120e97628")); + + h256 emptySM3( + *fromHexString("1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b")); + BOOST_REQUIRE_EQUAL(emptySM3, sm3->emptyHash()); + + BOOST_REQUIRE_EQUAL(cryptoSuite->hash(""), + h256("1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b")); + + BOOST_REQUIRE_EQUAL(cryptoSuite->hash("hello"), + h256("becbbfaae6548b8bf0cfcad5a27183cd1be6093b1cceccc303d9c61d0a645268")); +} +BOOST_AUTO_TEST_CASE(testSha3) +{ + auto sha3 = std::make_shared(); + auto cryptoSuite = std::make_shared(sha3, nullptr, nullptr); + std::string ts = sha3->emptyHash().hex(); + BOOST_CHECK_EQUAL( + ts, std::string("a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a")); + + std::string hashData = "abcde"; + ts = sha3->hash(hashData).hex(); + BOOST_CHECK_EQUAL( + ts, std::string("d716ec61e18904a8f58679b71cb065d4d5db72e0e0c3f155a4feff7add0e58eb")); + + h256 emptySha3( + *fromHexString("a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a")); + BOOST_REQUIRE_EQUAL(emptySha3, sha3->emptyHash()); + + BOOST_REQUIRE_EQUAL(cryptoSuite->hash(""), + h256("a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a")); + + BOOST_REQUIRE_EQUAL(cryptoSuite->hash("hello"), + h256("3338be694f50c5f338814986cdf0686453a888b84f424d792af4b9202398f392")); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-crypto/test/unittests/HasherTest.cpp" "b/BFPL\345\243\271/bcos-crypto/test/unittests/HasherTest.cpp" new file mode 100644 index 00000000..0e04936e --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/test/unittests/HasherTest.cpp" @@ -0,0 +1,129 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test cases for hasher256 + * @file HasherTest.h + * @date 2022.04.19 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos::crypto::hasher; + +namespace bcos::test +{ + +using HashType = std::array; + +BOOST_FIXTURE_TEST_SUITE(HasherTest, TestPromptFixture) BOOST_AUTO_TEST_CASE(testSHA256) +{ + std::string a = "arg"; + + openssl::OpenSSL_SHA3_256_Hasher hash1; + hash1.update(a); + hash1.update("abcdefg"); + hash1.update(100); + auto h1 = final(hash1); + + openssl::OpenSSL_SHA3_256_Hasher hash2; + hash2.update(a); + char s[] = "abcdefg"; + hash2.update(s); + auto b = 100; + hash2.update(b); + auto h2 = final(hash2); + + BOOST_CHECK_EQUAL(h1, h2); +} + +BOOST_AUTO_TEST_CASE(opensslSHA3) +{ + std::string a = "str123456789012345678901234567890"; + std::string_view view = a; + bcos::h256 h(100); + std::span hView(h.data(), h.SIZE); + + openssl::OpenSSL_SHA3_256_Hasher hasher1; + hasher1.update(100); + hasher1.update(a); + hasher1.update(view); + hasher1.update(hView); + hasher1.update("bbbc"); + + auto hash = final(hasher1); + + decltype(hash) emptyHash; + emptyHash.fill(std::byte('0')); + BOOST_CHECK_NE(hash, emptyHash); + + std::string a1 = "str123456789012345678901234567890"; + view = a1; + char by[] = "bbbc"; + int be = 100; + + openssl::OpenSSL_SHA3_256_Hasher hasher2; + hasher2.update(be); + hasher2.update(a1); + hasher2.update(view); + hasher2.update(std::span((const byte*)h.data(), h.SIZE)); + hasher2.update(by); + + auto hash2 = final(hasher2); + + BOOST_CHECK_EQUAL(hash, hash2); +} + +BOOST_AUTO_TEST_CASE(dynamicRange) +{ + bcos::h256 hash; + + int a = 10; + openssl::OpenSSL_SHA3_256_Hasher hasher; + hasher.update(a); + BOOST_CHECK_NO_THROW(hasher.final(hash)); + + std::vector hashVec; + hasher.update(a); + BOOST_CHECK_NO_THROW(hasher.final(hashVec)); + + auto b = bcos::crypto::trivial::DynamicRange>; +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test + +namespace std +{ + +template +::std::ostream& operator<<(::std::ostream& stream, const std::array& hash) +{ + std::string str; + str.reserve(hash.size() * 2); + std::span view{(char*)hash.data(), hash.size()}; + + boost::algorithm::hex_lower(view.begin(), view.end(), std::back_inserter(str)); + stream << str; + return stream; +} +} // namespace std \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/test/unittests/SignatureTest.cpp" "b/BFPL\345\243\271/bcos-crypto/test/unittests/SignatureTest.cpp" new file mode 100644 index 00000000..fdeda248 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/test/unittests/SignatureTest.cpp" @@ -0,0 +1,392 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test cases for secp256k1/sm2/ed25519 + * @file SignatureTest.h + * @date 2021.03.06 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if SM2_OPTIMIZE +#include +#endif +using namespace bcos; +using namespace bcos::crypto; + +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(SignatureTest, TestPromptFixture) +BOOST_AUTO_TEST_CASE(testSecp256k1KeyPair) +{ + // BOOST_CHECK(Secret::size == 32); + // BOOST_CHECK(Public::size == 64); + // check secret->public + auto fixedSec1 = h256( + "bcec428d5205abe0f0cc8a7340839" + "08d9eb8563e31f943d760786edf42ad67dd"); + auto sec1 = std::make_shared(fixedSec1.asBytes()); + auto pub1 = secp256k1PriToPub(sec1); + for (int i = 0; i < 10; i++) + { + pub1 = secp256k1PriToPub(sec1); + BOOST_CHECK_EQUAL(*toHexString(pub1->data()), + "3378c2b7bcdce20357eb3dbb62590b88d4711dae74e1ea47dd4207441734d2fc7cf6df92fd8c0a3368ba5a" + "1f5f9c3318d19a3f00ba2f2bd9f508b953be299fb5"); + } + + auto signatureImpl = std::make_shared(); + auto keyPair1 = signatureImpl->createKeyPair(sec1); + BOOST_CHECK(pub1->data() == keyPair1->publicKey()->data()); + BOOST_CHECK(sec1->data() == keyPair1->secretKey()->data()); + + auto fixedSec2 = h256("bcec428d5205abe0"); + auto sec2 = std::make_shared(fixedSec2.asBytes()); + auto pub2 = secp256k1PriToPub(sec2); + auto keyPair2 = signatureImpl->createKeyPair(sec2); + BOOST_CHECK(pub2->data() == keyPair2->publicKey()->data()); + BOOST_CHECK(sec2->data() == keyPair2->secretKey()->data()); + BOOST_CHECK(pub1->data() != pub2->data()); + + // check address + auto hashImpl = std::make_shared(); + auto secp256k1KeyPair1 = std::make_shared(sec1); + auto secp256k1KeyPair2 = std::make_shared(sec2); + + Address address1 = secp256k1KeyPair1->address(hashImpl); + Address address2 = secp256k1KeyPair2->address(hashImpl); + BOOST_CHECK(address1 != address2); + + // test calculateAddress + BOOST_CHECK(secp256k1KeyPair1->address(hashImpl) == calculateAddress(hashImpl, pub1)); + BOOST_CHECK(secp256k1KeyPair2->address(hashImpl) == calculateAddress(hashImpl, pub2)); + // test privateKeyToPublicKey + auto derivedPub1 = secp256k1KeyPair1->priToPub(secp256k1KeyPair1->secretKey()); + auto derivedPub2 = secp256k1KeyPair2->priToPub(secp256k1KeyPair2->secretKey()); + BOOST_CHECK(derivedPub1->data() == pub1->data()); + BOOST_CHECK(derivedPub2->data() == pub2->data()); + + // create KeyPair + auto keyPair = secp256k1GenerateKeyPair(); + BOOST_CHECK(keyPair->secretKey()); + BOOST_CHECK(keyPair->publicKey()); + auto testPub = secp256k1PriToPub(keyPair->secretKey()); + BOOST_CHECK(keyPair->publicKey()->data() == testPub->data()); + SecretPtr emptySecret = std::make_shared(SECP256K1_PRIVATE_LEN); + BOOST_CHECK_THROW(Secp256k1KeyPair emptyKeyPair(emptySecret), PriToPublicKeyException); +} + +BOOST_AUTO_TEST_CASE(testSecp256k1SignAndVerify) +{ + auto keyPair = secp256k1GenerateKeyPair(); + auto hashData = keccak256Hash((std::string)("abcd")); + std::cout << "### hashData:" << *toHexString(hashData) << std::endl; + std::cout << "#### publicKey:" << keyPair->publicKey()->hex() << std::endl; + std::cout << "#### publicKey shortHex:" << keyPair->publicKey()->shortHex() << std::endl; + /// normal check + // sign + auto signData = secp256k1Sign(*keyPair, hashData); + std::cout << "### signData:" << *toHexString(*signData) << std::endl; + // verify + bool result = secp256k1Verify( + keyPair->publicKey(), hashData, bytesConstRef(signData->data(), signData->size())); + BOOST_CHECK(result == true); + std::cout << "### verify result:" << result << std::endl; + + // recover + auto pub = secp256k1Recover(hashData, bytesConstRef(signData->data(), signData->size())); + std::cout << "### secp256k1Recover begin, publicKey:" + << *toHexString(keyPair->publicKey()->data()) << std::endl; + std::cout << "#### recoverd publicKey:" << *toHexString(pub->data()) << std::endl; + BOOST_CHECK(pub->data() == keyPair->publicKey()->data()); + /// exception check: + // check1: invalid payload(hash) + h256 invalidHash = keccak256Hash((std::string)("abce")); + result = secp256k1Verify( + keyPair->publicKey(), invalidHash, bytesConstRef(signData->data(), signData->size())); + BOOST_CHECK(result == false); + + PublicPtr invalidPub = std::make_shared(SECP256K1_PUBLIC_LEN); + invalidPub = secp256k1Recover(invalidHash, bytesConstRef(signData->data(), signData->size())); + BOOST_CHECK(invalidPub->data() != keyPair->publicKey()->data()); + + // check2: invalid sig + auto anotherSig(secp256k1Sign(*keyPair, invalidHash)); + result = secp256k1Verify( + keyPair->publicKey(), hashData, bytesConstRef(anotherSig->data(), anotherSig->size())); + BOOST_CHECK(result == false); + + invalidPub = secp256k1Recover(hashData, bytesConstRef(anotherSig->data(), anotherSig->size())); + BOOST_CHECK(invalidPub->data() != keyPair->publicKey()->data()); + + // check3: invalid keyPair + auto keyPair2 = secp256k1GenerateKeyPair(); + result = secp256k1Verify( + keyPair2->publicKey(), hashData, bytesConstRef(signData->data(), signData->size())); + BOOST_CHECK(result == false); + + h256 r(keccak256Hash(std::string("+++"))); + h256 s(keccak256Hash(std::string("24324"))); + byte v = 4; + auto signatureData = std::make_shared(r, s, v); + auto secp256k1Crypto = std::make_shared(); + auto encodedData = signatureData->encode(); + BOOST_CHECK_THROW(secp256k1Crypto->recover(hashData, ref(*encodedData)), InvalidSignature); + + + // test signatureData encode and decode + encodedData = signatureData->encode(); + auto signatureData2 = std::make_shared( + bytesConstRef(encodedData->data(), encodedData->size())); + BOOST_CHECK(signatureData2->r() == signatureData->r()); + BOOST_CHECK(signatureData2->s() == signatureData->s()); + BOOST_CHECK(signatureData2->v() == signatureData->v()); + + auto signatureData3 = + std::make_shared(bytesConstRef(signData->data(), signData->size())); + encodedData = signatureData3->encode(); + BOOST_CHECK(*signData == *encodedData); + auto publicKey = secp256k1Crypto->recover(hashData, ref(*encodedData)); + BOOST_CHECK(publicKey->data() == keyPair->publicKey()->data()); +} + +BOOST_AUTO_TEST_CASE(testSM2KeyPair) +{ + // check secret->public + h256 fixedSec1( + "bcec428d5205abe0f0cc8a7340839" + "08d9eb8563e31f943d760786edf42ad67dd"); + auto sec1 = std::make_shared(fixedSec1.asBytes()); + auto keyFactory = std::make_shared(); + auto secCreated = keyFactory->createKey(fixedSec1.asBytes()); + BOOST_CHECK(sec1->data() == secCreated->data()); + + auto pub1 = sm2PriToPub(sec1); + auto keyPair1 = std::make_shared(sec1); + + auto signatureImpl = std::make_shared(); + auto keyPairTest = signatureImpl->createKeyPair(sec1); + BOOST_CHECK(keyPairTest->publicKey()->data() == keyPair1->publicKey()->data()); + BOOST_CHECK(keyPairTest->secretKey()->data() == keyPair1->secretKey()->data()); + + h256 fixedSec2("bcec428d5205abe0"); + auto sec2 = std::make_shared(fixedSec2.asBytes()); + auto pub2 = sm2PriToPub(sec2); + auto keyPair2 = std::make_shared(sec2); + + BOOST_CHECK(pub1->data() != pub2->data()); + + + // check public to address + auto hashImpl = std::make_shared(); + Address address1 = calculateAddress(hashImpl, pub1); + Address address2 = calculateAddress(hashImpl, pub2); + BOOST_CHECK(address1 != address2); + + // check secret to address + Address addressSec1 = keyPair1->address(hashImpl); + Address addressSec2 = keyPair2->address(hashImpl); + BOOST_CHECK(addressSec1 != addressSec2); + BOOST_CHECK(address1 == addressSec1); + BOOST_CHECK(address2 == addressSec2); + + // create keyPair + auto keyPair = sm2GenerateKeyPair(); + BOOST_CHECK(keyPair->publicKey()); + BOOST_CHECK(keyPair->secretKey()); + pub1 = sm2PriToPub(keyPair->secretKey()); + BOOST_CHECK(keyPair->publicKey()->data() == pub1->data()); + + // empty case + auto emptySecret = std::make_shared(SM2_PRIVATE_KEY_LEN); + BOOST_CHECK_THROW(SM2KeyPair sm2KeyPair(emptySecret), PriToPublicKeyException); +} + +inline void SM2SignAndVerifyTest(SM2Crypto::Ptr _smCrypto) +{ + auto hashCrypto = std::make_shared(); + auto hashData = hashCrypto->hash(std::string("abcd")); + + h256 secret("ca508b2b49c1d2dc46cbd5a011686fdc19937dbc704afe6c547a862b3e2b6c69"); + auto sec = std::make_shared(secret.asBytes()); + auto keyPair = _smCrypto->createKeyPair(sec); + BOOST_CHECK(keyPair->publicKey()->data() == + *(fromHexString("f7dee65e76603ed7cd4c598d53cabe875c459e0fae4c6fd7b858189fd4741081e9" + "70bca0d5cb571a7ac30586aec71b23187d4b25e59143812f74a2744604d42b"))); + auto signatureData = fromHexString( + "cd39bf939d999ca710576a629c962edfc28608701a3a7b61c971daeac5a1399cf4a7272fa80783e171c7fd5b03" + "8a3af4521f681ebe9fd44db3b60e750c438293f7dee65e76603ed7cd4c598d53cabe875c459e0fae4c6fd7b858" + "189fd4741081e970bca0d5cb571a7ac30586aec71b23187d4b25e59143812f74a2744604d42b"); + // check verify + bool result = _smCrypto->verify(keyPair->publicKey(), hashData, + bytesConstRef(signatureData->data(), signatureData->size())); + BOOST_CHECK(result == true); + + keyPair = _smCrypto->generateKeyPair(); + // sign + auto sig = _smCrypto->sign(*keyPair, hashData, true); + // verify + result = + _smCrypto->verify(keyPair->publicKey(), hashData, bytesConstRef(sig->data(), sig->size())); + + std::cout << "#### privateKey:" << *toHexString(keyPair->secretKey()->data()) << std::endl; + std::cout << "#### phase 1, signatureData:" << *toHexString(*sig) << std::endl; + BOOST_CHECK(result == true); + // recover + auto pub = _smCrypto->recover(hashData, bytesConstRef(sig->data(), sig->size())); + std::cout << "#### phase 2" << std::endl; + BOOST_CHECK(pub->data() == keyPair->publicKey()->data()); + + + // exception case + // invalid payload(hash) + auto invalidHash = hashCrypto->hash(std::string("abce")); + result = _smCrypto->verify( + keyPair->publicKey(), invalidHash, bytesConstRef(sig->data(), sig->size())); + BOOST_CHECK(result == false); + // recover + BOOST_CHECK_THROW( + _smCrypto->recover(invalidHash, bytesConstRef(sig->data(), sig->size())), InvalidSignature); + + // invalid signature + auto anotherSig = _smCrypto->sign(*keyPair, invalidHash, true); + result = _smCrypto->verify( + keyPair->publicKey(), hashData, bytesConstRef(anotherSig->data(), anotherSig->size())); + BOOST_CHECK(result == false); + BOOST_CHECK_THROW( + _smCrypto->recover(hashData, bytesConstRef(anotherSig->data(), anotherSig->size())), + InvalidSignature); + + // invalid sig + auto keyPair2 = _smCrypto->generateKeyPair(); + result = + _smCrypto->verify(keyPair2->publicKey(), hashData, bytesConstRef(sig->data(), sig->size())); + BOOST_CHECK(result == false); + auto signatureStruct = + std::make_shared(bytesConstRef(sig->data(), sig->size())); + auto r = signatureStruct->r(); + auto s = signatureStruct->s(); + + auto signatureStruct2 = std::make_shared(r, s, signatureStruct->pub()); + auto encodedData = signatureStruct2->encode(); + auto recoverKey = + _smCrypto->recover(hashData, bytesConstRef(encodedData->data(), encodedData->size())); + BOOST_CHECK(recoverKey->data() == keyPair->publicKey()->data()); + + // test padding + unsigned fieldLen = 32; + std::shared_ptr data = std::make_shared(fieldLen, 0); + auto binData = fromHexString("508b2b49c1d2dc46cbd5a011686fdc19937dbc704afe6c547a862b3e2b6c69"); + memcpy(data->data(), binData->data(), binData->size()); + // padding zero to the r field + memmove(data->data() + (fieldLen - binData->size()), data->data(), binData->size()); + memset(data->data(), 0, (fieldLen - binData->size())); + std::cout << "#### data:" << *toHexString(*data) << std::endl; + std::cout << "#### binData:" << *toHexString(*binData) << std::endl; + std::cout << "### data 0:" << int((*data)[0]) << std::endl; + std::cout << "### data 1:" << int((*data)[1]) << std::endl; +} + +BOOST_AUTO_TEST_CASE(testSM2SignAndVerify) +{ + std::cout << "### testSM2SignAndVerify: SM2Crypto" << std::endl; + auto signatureCrypto = std::make_shared(); + SM2SignAndVerifyTest(signatureCrypto); +#if SM2_OPTIMIZE + std::cout << "### testSM2SignAndVerify: FastSM2Crypto" << std::endl; + auto fastCrypto = std::make_shared(); + SM2SignAndVerifyTest(fastCrypto); +#endif +} + + +BOOST_AUTO_TEST_CASE(testED25519SignAndVerify) +{ + auto signatureCrypto = std::make_shared(); + auto hashCrypto = std::make_shared(); + auto keyPair = signatureCrypto->generateKeyPair(); + auto hashData = hashCrypto->hash(std::string("abcd")); + // sign + auto sig = signatureCrypto->sign(*keyPair, hashData, true); + // verify + bool result = signatureCrypto->verify( + keyPair->publicKey(), hashData, bytesConstRef(sig->data(), sig->size())); + std::cout << "#### phase 1, signatureData:" << *toHexString(*sig) << std::endl; + std::cout << "#### keyPair->publicKey():" << *toHexString(keyPair->publicKey()->data()) + << std::endl; + BOOST_CHECK(result == true); + // recover + auto pub = signatureCrypto->recover(hashData, bytesConstRef(sig->data(), sig->size())); + std::cout << "#### phase 2" << std::endl; + BOOST_CHECK(pub->data() == keyPair->publicKey()->data()); + + // exception case + // invalid payload(hash) + auto invalidHash = hashCrypto->hash(std::string("abce")); + result = signatureCrypto->verify( + keyPair->publicKey(), invalidHash, bytesConstRef(sig->data(), sig->size())); + BOOST_CHECK(result == false); + + // recover + BOOST_CHECK_THROW( + signatureCrypto->recover(invalidHash, bytesConstRef(sig->data(), sig->size())), + InvalidSignature); + + // invalid signature + auto anotherSig = signatureCrypto->sign(*keyPair, invalidHash, true); + result = signatureCrypto->verify( + keyPair->publicKey(), hashData, bytesConstRef(anotherSig->data(), anotherSig->size())); + BOOST_CHECK(result == false); + BOOST_CHECK_THROW( + signatureCrypto->recover(hashData, bytesConstRef(anotherSig->data(), anotherSig->size())), + InvalidSignature); + + // invalid sig + auto keyPair2 = signatureCrypto->generateKeyPair(); + result = signatureCrypto->verify( + keyPair2->publicKey(), hashData, bytesConstRef(sig->data(), sig->size())); + BOOST_CHECK(result == false); + + auto signatureStruct = + std::make_shared(bytesConstRef(sig->data(), sig->size())); + auto r = signatureStruct->r(); + auto s = signatureStruct->s(); + + auto signatureStruct2 = std::make_shared(r, s, signatureStruct->pub()); + auto encodedData = signatureStruct2->encode(); + auto recoverKey = + signatureCrypto->recover(hashData, bytesConstRef(encodedData->data(), encodedData->size())); + BOOST_CHECK(recoverKey->data() == keyPair->publicKey()->data()); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/test/unittests/ZkpTest.cpp" "b/BFPL\345\243\271/bcos-crypto/test/unittests/ZkpTest.cpp" new file mode 100644 index 00000000..4ad12397 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/test/unittests/ZkpTest.cpp" @@ -0,0 +1,221 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test cases for secp256k1/sm2/ed25519 + * @file SignatureTest.h + * @date 2021.03.06 + */ +#include "bcos-crypto/zkp/discretezkp/DiscreteLogarithmZkp.h" +#include +#include +#include + +using namespace bcos; +using namespace bcos::crypto; + +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(ZkpTest, TestPromptFixture) +// aggregateRistrettoPoint +BOOST_AUTO_TEST_CASE(testAggregateRistrettoPoint) +{ + std::string point1 = "5ede6177a14b66389ab5fe5785d7c14adf2a3973aa7c3070878954937e47746f"; + std::string point2 = "d21b3e0fde82da359493b96b8f3633f9ba39bbb8d8d72c951c173639c750c248"; + auto zkpImpl = std::make_shared(); + + auto result = zkpImpl->aggregateRistrettoPoint(*fromHexString(point1), *fromHexString(point2)); + BOOST_CHECK( + *toHexString(result) == "327ea1e62fd6c6a3fc8fb203618fbf2f7924bbfbdec52a9324bd6964c4c0ca12"); + + point1 = "786058ceffe85198ef127b6a9781b07adbe3b2d21de87a26804e6a1b43cd4135"; + point2 = "18df09397eed2a614062649be865da8e26cbdd8848fdbed7e8ae8b0e67745d52"; + result = zkpImpl->aggregateRistrettoPoint(*fromHexString(point1), *fromHexString(point2)); + BOOST_CHECK( + *toHexString(result) == "36f238ce7215e9704a60c631ac0d576a5d816baf6fc1092798d3bd6ae260f540"); + + point2 = "c25d4eed8b636edaa06e2ee0b3a16d1ea7058a3fd455702180cc9bdb14ce8016"; + result = zkpImpl->aggregateRistrettoPoint(*fromHexString(point1), *fromHexString(point2)); + BOOST_CHECK( + *toHexString(result) != "36f238ce7215e9704a60c631ac0d576a5d816baf6fc1092798d3bd6ae260f540"); +} +// verifyEitherEqualityProof +BOOST_AUTO_TEST_CASE(testVerifyEitherEqualityProof) +{ + std::string c1_point = "e0ad69e116f7aa07e7ce1d9e55eec9be69baa310ec4616fbf7e11756a3ff3627"; + std::string c2_point = "8020353cff25729feb2d057330434b423b5b01fc6325b9c5aae49df56dceff7b"; + std::string c3_point = "aac55c73d60521c0978394872db2a8a9ee21b67ea990e6cd1301f71e6f449216"; + std::string basepoint = "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"; + std::string blinding_basepoint = + "8c9240b456a9e6dc65c377a1048d745f94a08cdb7f44cbcd7b46f34048871134"; + std::string proof = + "d13f92f8ff02ed667f318f90e63db8f7c6cc5504bca4d30ba5ba98ee27762500c97cabd65e0d0f383e2a86ded2" + "95e8a6b5474c59455802a1d48e480de1b2be08fb48782b96d8090c9b4b619a8998f8cc8999d12edb7e0fbdb394" + "6c7373b41509807b2a1b88febf3058f843d73e72f73841093be86e20cb0c16f28ee5ffd3750aa90efe06abee0e" + "893f6af3dbd214a66d312ae4dd1253dd3bcb70dfc5f9821e03a92492bd5a18e05e64c98165b8a433f068c39796" + "703d8069b6c3fae2b4406d06f54a46f05c5d321c4ed01f1c72c9a89c3d3ba7adbd807da14148dd4c77b41c07c4" + "3d026dd14fa7c025ad3a06a9a891149d6a25268b49f79cc016c6c49bafd107"; + auto zkpImpl = std::make_shared(); + auto result = zkpImpl->verifyEitherEqualityProof(*fromHexString(c1_point), + *fromHexString(c2_point), *fromHexString(c3_point), *fromHexString(proof), + *fromHexString(basepoint), *fromHexString(blinding_basepoint)); + BOOST_CHECK(result == true); + + // invalid case + result = zkpImpl->verifyEitherEqualityProof(*fromHexString(c2_point), *fromHexString(c2_point), + *fromHexString(c1_point), *fromHexString(proof), *fromHexString(basepoint), + *fromHexString(blinding_basepoint)); + BOOST_CHECK(result == false); + + // case equal to zeror + c1_point = "ced7f98f75ab04635c7acd0600a67067b5c3475a82911aa02cdd92864a273118"; + proof = + "4bdc1283354b39dbb901b5bd96ee1de30601bf6321c2348f3c594b2a0fe8b306eba04833e1b817c25f50103b95" + "7ae8657f8e3497b92d993ffd040ad4a4f28a0e831601c9efde53fd3252d6e70aa0f10ecd6852739ae03fc20b4a" + "aeb922fc3c0895be82ed5be7012dbb25615e41b6184f76f39ecc01a3696c9d5dd8acb79d1b0fcf8db3cc437612" + "a333d67298562f994857b73cc241cc550cb8ec19057c669e0b7535f5c0b3f73d6778a6d6f9d762cd35be7d6603" + "a04767c24de80b6544577306bf42cc0f703fddfefffc50b88a1f275c31ceed85cbf0de458fc570ddf4ba570185" + "d6c0a0b18c136fd2fc16e7bdde856e90ab7488b44ed829ca305d861df1e30e"; + result = zkpImpl->verifyEitherEqualityProof(*fromHexString(c1_point), *fromHexString(c2_point), + *fromHexString(c3_point), *fromHexString(proof), *fromHexString(basepoint), + *fromHexString(blinding_basepoint)); + BOOST_CHECK(result == true); +} + +// verifyKnowledgeProof +BOOST_AUTO_TEST_CASE(testVerifyKnowledgeProof) +{ + auto zkpImpl = std::make_shared(); + bytes point = + *fromHexString("c2e63cef83875e81ea26e00546102cfbbca50ec21a92d077df9100d1bc3a461e"); + bytes proof = *fromHexString( + "5466f3449a00af0d922670b9f80295c6a685c23713349a91a36e4c082a3e282f9aa835448523cfc62b527d7533" + "0845f4e889c2e70e844c35177a9f0647e3d00b0d17ac6a70acbf14f9a7d838a0b4fe251ba59dc00404cb7d243d" + "d4cba1832d0f"); + bytes base_point = + *fromHexString("e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"); + bytes blinding_base_point = + *fromHexString("625c50529218ebb9f80e296886f4ac5bb55e06416db27b901c552d3e06ec4871"); + auto ret = zkpImpl->verifyKnowledgeProof(point, proof, base_point, blinding_base_point); + BOOST_CHECK(ret == true); + + // invalid case + ret = zkpImpl->verifyKnowledgeProof(base_point, proof, point, blinding_base_point); + BOOST_CHECK(ret == false); +} + +// verifyFormatProof +BOOST_AUTO_TEST_CASE(testVerifyFormatProof) +{ + auto zkpImpl = std::make_shared(); + bytes c1 = *fromHexString("febd4c0d856502a95986a7181c42c51768846755147f178c3646a12834b4e63d"); + bytes c2 = *fromHexString("ce8c95f13b202defc2bd0e3ddf62dddeb478b693b5f20ce9cd28b64c473aef0f"); + bytes proof = *fromHexString( + "80b7cb7fe7f4aa86b86b7d375d15242cc33336e8b2b735807d9a786be353f0544e99b6d416ce1f1b8d97894f33" + "d2c94dbe4987dea483ac68cf44b2abf7c6594dc12255a9babc8bd275fe3103ea44d0a33cc3cad8f2d0ef4ca5fc" + "9257464ecd0405bde6b3bb5e612b14c738e6221940a3f07e82344693867bcd6c85360bbff40e"); + bytes c1_basepoint = + *fromHexString("e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"); + bytes c2_basepoint = + *fromHexString("8c9240b456a9e6dc65c377a1048d745f94a08cdb7f44cbcd7b46f34048871134"); + bytes blinding_base_point = + *fromHexString("c2b614cc98c793bff0298b0c0881fa78261f0bc6d5f826484257b34d3480c22e"); + auto ret = + zkpImpl->verifyFormatProof(c1, c2, proof, c1_basepoint, c2_basepoint, blinding_base_point); + BOOST_CHECK(ret == true); + + // invalid case + ret = + zkpImpl->verifyFormatProof(c2, c1, proof, c1_basepoint, c2_basepoint, blinding_base_point); + BOOST_CHECK(ret == false); +} + +// verifySumProof +BOOST_AUTO_TEST_CASE(testVerifySumProof) +{ + auto zkpImpl = std::make_shared(); + bytes c1 = *fromHexString("d4cc1326224453213b6a8d758a87bda4612cfabc7992127580e39cddba6ca234"); + bytes c2 = *fromHexString("6839477d311c001e605001c6f270b99429b0b2a3fc7e8e0ae70fe77c4c203a1e"); + bytes c3 = *fromHexString("b050a9a8baae17bdaa7d0cac3f3f2fbb2634a02ca0a86962f2ece308b6fffa3e"); + bytes proof = *fromHexString( + "fca6e4c20eaa0c167aecd92579b726851369cd993b2204aef6d48064522b2f17849ad5218b1caf6857db0df2f9" + "db348f981e928f61b26aa4b83d2f834332237bf6a293eb4e1161db741fe77c4c22e4109cb4d9fc2baada7c46d1" + "2a567fe1114cc24be6612d716b0ff984a98d52a6b0215ffd3ae05afb1e3ce2ea326bb1f66f0ed805efec8c1b73" + "b87541f05bb28fa8bc4a52c1a0505a420f4b55b088900e75000e49b79e6039f17d9f8f01cf6090a3e8b3946113" + "897e090e46671b6272ecde05412f27c2cd4f06d738473a561bf23cb6bf96960db30a6b414660977417ae580d44" + "8123c5f895f563adf7bc56fe7ca7a42149dc0ac4a4203ccc3b68daa5cc1504"); + bytes value_basepoint = + *fromHexString("e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"); + bytes blinding_base_point = + *fromHexString("8c9240b456a9e6dc65c377a1048d745f94a08cdb7f44cbcd7b46f34048871134"); + auto ret = zkpImpl->verifySumProof(c1, c2, c3, proof, value_basepoint, blinding_base_point); + BOOST_CHECK(ret == true); + + // invalid case + ret = zkpImpl->verifySumProof(c2, c1, c3, proof, value_basepoint, blinding_base_point); + BOOST_CHECK(ret == false); +} + +// verifyProductProof +BOOST_AUTO_TEST_CASE(testVerifyProductProof) +{ + auto zkpImpl = std::make_shared(); + bytes c1 = *fromHexString("54a33c1e4a2f7c9201b888b2260aade604a546e5acf49c63bd016fc442270640"); + bytes c2 = *fromHexString("80c99be28493d198e4c2780446d54701a7773ef0d5c4f511d04a832047c62971"); + bytes c3 = *fromHexString("66b290292161da6e43fcd32ef103a674fa9535155443bcba462455a838bac71f"); + bytes proof = *fromHexString( + "687f1bed4716b0e1b0ab3c855594bf82e119b48f71f0f2c42cdb43725bbe2d39b0ed1666b78c87b785e2c7bbd0" + "69981be08634a8c9ee712bd94cf30153f63e2974a00e328f42b3992fce13f66a44adb0a5b8814a3bc34d3da406" + "f3d90014bc679cd0e0456ae03f1cf34156aec93d1b0c81f2e14de374c4d90540562181073500512781dc90f142" + "30e152e313c8571c9406a9497480d7b71c8b38564dd4c2dd051acc1d1db1acd4528a2ef9911ab487b192abf7d5" + "3ea94cbd5ed83ca23d7c760d70231328c4a4008cc58de545712dae4f1d336b8562ec59468b737c30b76938002f" + "79a5923855cf48c6399fa25cebeea13502768ed584dc04b2821b35fb32540d"); + bytes value_basepoint = + *fromHexString("e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"); + bytes blinding_base_point = + *fromHexString("8c9240b456a9e6dc65c377a1048d745f94a08cdb7f44cbcd7b46f34048871134"); + auto ret = zkpImpl->verifyProductProof(c1, c2, c3, proof, value_basepoint, blinding_base_point); + BOOST_CHECK(ret == true); + + // invalid case + ret = zkpImpl->verifyProductProof(c2, c1, c3, proof, value_basepoint, blinding_base_point); + BOOST_CHECK(ret == false); +} + +// verifyEqualityProof +BOOST_AUTO_TEST_CASE(testVerifyEqualityProof) +{ + auto zkpImpl = std::make_shared(); + bytes c1 = *fromHexString("62bf13dcf116499f3970b8497120741e9dc66fe1d9c3a34274b5e7e851398f40"); + bytes c2 = *fromHexString("6c07f2bd045bd4058e2f8c15b618193229a749827784c6e0b62ee175a987c510"); + bytes proof = *fromHexString( + "a782e114de54fd1460081bae2b05edfc157b6ff31cb55c85d81c130556fb5b03e2569d3ec8e78be9732e73a6c5" + "e9c0e231aff1171ae87453747cabaaf2cfdc1de474d1c45b0ac1454e5b3ba860d73d5938b2536f06a13b321fe9" + "664acfc0c013"); + bytes basepoint1 = + *fromHexString("e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"); + bytes basepoint2 = + *fromHexString("8c9240b456a9e6dc65c377a1048d745f94a08cdb7f44cbcd7b46f34048871134"); + auto ret = zkpImpl->verifyEqualityProof(c1, c2, proof, basepoint1, basepoint2); + BOOST_CHECK(ret == true); + + // invalid case + ret = zkpImpl->verifyEqualityProof(c2, c1, proof, basepoint1, basepoint2); + BOOST_CHECK(ret == false); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/test/unittests/main.cpp" "b/BFPL\345\243\271/bcos-crypto/test/unittests/main.cpp" new file mode 100644 index 00000000..7f0f1937 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/test/unittests/main.cpp" @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 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 main.cpp + * @author: yujiechen, jimmyshi + * @date 2021-02-24 + */ +#define BOOST_TEST_MODULE FISCO_BCOS_Tests +#define BOOST_TEST_MAIN + +#include \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-crypto/test/unittests/testMerkle.cpp" "b/BFPL\345\243\271/bcos-crypto/test/unittests/testMerkle.cpp" new file mode 100644 index 00000000..e5b31cc7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-crypto/test/unittests/testMerkle.cpp" @@ -0,0 +1,143 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using HashType = std::array; + +namespace std +{ +std::ostream& operator<<(std::ostream& stream, const HashType& hash) +{ + std::string hex; + boost::algorithm::hex_lower( + (char*)hash.data(), (char*)hash.data() + hash.size(), std::back_inserter(hex)); + std::string_view view{hex.data(), 8}; + stream << view; + return stream; +} +} // namespace std + +namespace bcos::test +{ + +using namespace bcos::crypto::merkle; + +struct TestBinaryMerkleTrieFixture +{ + std::array hashes; + + TestBinaryMerkleTrieFixture() + { + crypto::hasher::openssl::OpenSSL_SHA3_256_Hasher hasher; + std::mt19937 prng(std::random_device{}()); + + for (auto& element : hashes) + { + hasher.update(prng()); + hasher.final(element); + } + } +}; + +BOOST_FIXTURE_TEST_SUITE(TestBinaryMerkleTrie, TestBinaryMerkleTrieFixture) + +template +void testFixedWidthMerkle(bcos::crypto::merkle::HashRange auto const& inputHashes) +{ + HashType emptyHash; + emptyHash.fill(std::byte(0)); + auto seed = std::random_device{}(); + + for (auto count = 0lu; count < RANGES::size(inputHashes); ++count) + { + std::span hashes(inputHashes.data(), count); + + bcos::crypto::merkle::Merkle + trie; + std::vector out; + BOOST_CHECK_THROW(trie.generateMerkle(std::vector{}, out), + boost::wrapexcept); + + if (count == 0) + { + BOOST_CHECK_THROW(trie.generateMerkle(std::as_const(hashes), out), + boost::wrapexcept); + } + else + { + std::vector outMerkle; + BOOST_CHECK_NO_THROW(trie.generateMerkle(std::as_const(hashes), outMerkle)); + std::cout << "Merkle: " << std::endl; + std::cout << outMerkle << std::endl; + + std::vector outProof; + BOOST_CHECK_THROW(trie.generateMerkleProof(hashes, outMerkle, emptyHash, outProof), + boost::wrapexcept); + BOOST_CHECK_THROW( + trie.generateMerkleProof(hashes, outMerkle, RANGES::size(hashes), outProof), + boost::wrapexcept); + + for (auto& hash : hashes) + { + trie.generateMerkleProof(hashes, outMerkle, hash, outProof); + + std::cout << "Width: " << width << " Root: " << *outMerkle.rbegin() + << " Hash: " << hash << std::endl; + + std::cout << "Proof: " << std::endl; + std::cout << outProof << std::endl; + BOOST_CHECK(trie.verifyMerkleProof(outProof, hash, *(outMerkle.rbegin()))); + BOOST_CHECK(!trie.verifyMerkleProof(outProof, emptyHash, *(outMerkle.rbegin()))); + + auto dis = std::uniform_int_distribution(0lu, outProof.size() - 1); + std::mt19937 prng{seed}; + outProof[dis(prng)] = emptyHash; + + if (outProof.size() > 1) + { + BOOST_CHECK(!trie.verifyMerkleProof(outProof, hash, *(outMerkle.rbegin()))); + } + + outProof.clear(); + BOOST_CHECK_THROW(trie.verifyMerkleProof(outProof, hash, *(outMerkle.rbegin())), + boost::wrapexcept); + } + } + } +} + +template +constexpr void loopWidthTest(bcos::crypto::merkle::HashRange auto const& inputHashes) +{ + testFixedWidthMerkle(inputHashes); + + if constexpr (i > 2) + { + loopWidthTest(inputHashes); + } +} + +BOOST_AUTO_TEST_CASE(merkle) +{ + constexpr static size_t testCount = 16; + loopWidthTest(hashes); +} + +BOOST_AUTO_TEST_CASE(performance) {} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/CMakeLists.txt" "b/BFPL\345\243\271/bcos-executor/CMakeLists.txt" new file mode 100644 index 00000000..cfbf61f7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/CMakeLists.txt" @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.14) +set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0" CACHE STRING "Minimum OS X deployment version") + +include(Version) +project(bcos-executor VERSION ${VERSION}) + +find_package(TBB CONFIG REQUIRED) +find_package(wedprcrypto REQUIRED) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + +find_package(evmc REQUIRED) +find_package(evmone REQUIRED) +find_package(intx REQUIRED) +find_package(ethash REQUIRED) +find_package(Boost REQUIRED serialization program_options context) +find_package(jsoncpp REQUIRED) + +file(GLOB_RECURSE SRCS src/*.cpp) +add_library(${EXECUTOR_TARGET} ${SRCS}) +target_link_libraries(${EXECUTOR_TARGET} PUBLIC jsoncpp_static ${CODEC_TARGET} ${CRYPTO_TARGET} ${TABLE_TARGET} wedprcrypto::fisco-bcos bcos-protocol + Boost::context evmone fbwasm evmc::loader evmc::instructions wabt GroupSig) + +# TODO: EXTEND_LIB not support now! + +# add_subdirectory(test/trie-test) +# add_subdirectory(test/flow-graph) + +if (TOOLS) + add_subdirectory(tools) +endif() + +if (TESTS) + enable_testing() + set(ENV{CTEST_OUTPUT_ON_FAILURE} True) + add_subdirectory(test/unittest) +endif() diff --git "a/BFPL\345\243\271/bcos-executor/src/CallParameters.h" "b/BFPL\345\243\271/bcos-executor/src/CallParameters.h" new file mode 100644 index 00000000..92b610ae --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/CallParameters.h" @@ -0,0 +1,125 @@ +#pragma once + +#include +#include +#include +#include + +namespace bcos::executor +{ +struct CallParameters +{ + using UniquePtr = std::unique_ptr; + using UniqueConstPtr = std::unique_ptr; + + enum Type : int8_t + { + MESSAGE = 0, + KEY_LOCK = 1, + FINISHED = 2, + REVERT = 3, + }; + + explicit CallParameters(Type _type) : type(_type) {} + + CallParameters(const CallParameters&) = delete; + CallParameters& operator=(const CallParameters&) = delete; + + CallParameters(CallParameters&&) = delete; + CallParameters(const CallParameters&&) = delete; + + int64_t contextID = 0; + int64_t seq = 0; + + std::string senderAddress; // common field, readable format + std::string codeAddress; // common field, readable format + std::string receiveAddress; // common field, readable format + std::string origin; // common field, readable format + + /// WARNING: gasLeft, be cautious to assign value + int64_t gas = 0; // common field + bcos::bytes data; // common field, transaction data, binary format + std::string abi; // common field, contract abi, json format + + std::vector keyLocks; // common field + std::string acquireKeyLock; // by response + + std::string message; // by response, readable format + std::vector logEntries; // by response + std::optional createSalt; // by response + std::string newEVMContractAddress; // by response, readable format + + int32_t status = 0; // by response + int32_t evmStatus = 0; + Type type; + bool staticCall = false; // common field + bool create = false; // by request, is creation + bool internalCreate = false; // by internal precompiled request, is creation + /** + * Internal precompiled contract request, this option is used to + * modify contract table, which address scheduled by 'to', by a + * certain precompiled contract + */ + bool internalCall = false; + + // delegateCall + bool delegateCall = false; + bytes delegateCallCode; + std::string delegateCallSender; + + std::string toString() + { + std::stringstream ss; + ss << "[" << contextID << "|" << seq << "|"; + switch (type) + { + case MESSAGE: + ss << "MESSAGE"; + break; + case KEY_LOCK: + ss << "KEY_LOCK"; + break; + case FINISHED: + ss << "FINISHED"; + break; + case REVERT: + ss << "REVERT"; + break; + }; + ss << "]"; + return ss.str(); + } + + // this method only for trace log + std::string toFullString() + { + std::stringstream ss; + // clang-format off + ss << toString() + << "senderAddress:" << senderAddress << "|" + << "codeAddress:" << codeAddress << "|" + << "receiveAddress:" << receiveAddress << "|" + << "origin:" << origin << "|" + << "gas:" << gas << "|" + << "dataSize:" << data.size() << "|" + << "abiSize:" << abi.size() << "|" + << "acquireKeyLock:" << acquireKeyLock << "|" + << "message:" << message << "|" + << "newEVMContractAddress:" << newEVMContractAddress << "|" + << "staticCall:" << staticCall << "|" + << "create :" << create << "|" + << "delegateCall:" << delegateCall << "|" + << "delegateCallSender" << delegateCallSender ; + // clang-format on + ss << "|logEntries: "; + for (const auto& logEntry : logEntries) + { + ss << "[" << logEntry.address() << "|" + << toHexStringWithPrefix( + h256((byte*)logEntry.topics().data(), logEntry.topics().size())) + << "|" << toHexStringWithPrefix(logEntry.data()) << "]"; + } + return ss.str(); + } +}; +} // namespace bcos::executor \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/Common.cpp" "b/BFPL\345\243\271/bcos-executor/src/Common.cpp" new file mode 100644 index 00000000..cafbaff0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/Common.cpp" @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2021 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. + * + * @brief vm common + * @file Common.cpp + * @author: xingqiangbai + * @date: 2021-05-24 + */ + +#include "Common.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include + +using namespace bcos::protocol; + +namespace bcos +{ +bool hasWasmPreamble(const std::string_view& _input) +{ + return hasWasmPreamble( + bytesConstRef(reinterpret_cast(_input.data()), _input.size())); +} +bool hasWasmPreamble(const bytesConstRef& _input) +{ + return _input.size() >= 8 && _input[0] == 0 && _input[1] == 'a' && _input[2] == 's' && + _input[3] == 'm'; +} + +bool hasWasmPreamble(const bytes& _input) +{ + return hasWasmPreamble(bytesConstRef(_input.data(), _input.size())); +} + +bool hasPrecompiledPrefix(const std::string_view& _code) +{ + return _code.size() > precompiled::PRECOMPILED_CODE_FIELD_SIZE && + _code.substr(0, precompiled::PRECOMPILED_CODE_FIELD_SIZE) == + precompiled::PRECOMPILED_CODE_FIELD; +} + +namespace executor +{ +TransactionStatus toTransactionStatus(Exception const& _e) +{ + if (!!dynamic_cast(&_e)) + return TransactionStatus::OutOfGasLimit; + if (!!dynamic_cast(&_e)) + return TransactionStatus::NotEnoughCash; + if (!!dynamic_cast(&_e)) + return TransactionStatus::BadInstruction; + if (!!dynamic_cast(&_e)) + return TransactionStatus::BadJumpDestination; + if (!!dynamic_cast(&_e)) + return TransactionStatus::OutOfGas; + if (!!dynamic_cast(&_e)) + return TransactionStatus::OutOfStack; + if (!!dynamic_cast(&_e)) + return TransactionStatus::StackUnderflow; + if (!!dynamic_cast(&_e)) + return TransactionStatus::ContractAddressAlreadyUsed; + if (!!dynamic_cast(&_e)) + return TransactionStatus::PrecompiledError; + if (!!dynamic_cast(&_e)) + return TransactionStatus::RevertInstruction; + if (!!dynamic_cast(&_e)) + return TransactionStatus::PermissionDenied; + if (!!dynamic_cast(&_e)) + return TransactionStatus::CallAddressError; + if (!!dynamic_cast(&_e)) + return TransactionStatus::GasOverflow; + if (!!dynamic_cast(&_e)) + return TransactionStatus::ContractFrozen; + if (!!dynamic_cast(&_e)) + return TransactionStatus::AccountFrozen; + return TransactionStatus::Unknown; +} +} // namespace executor + +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/Common.h" "b/BFPL\345\243\271/bcos-executor/src/Common.h" new file mode 100644 index 00000000..742f8a4b --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/Common.h" @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2021 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. + * + * @brief vm common + * @file Common.h + * @author: xingqiangbai + * @date: 2021-05-24 + * @brief vm common + * @file Common.h + * @author: ancelmo + * @date: 2021-10-08 + */ + +#pragma once + +#include "CallParameters.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/protocol/BlockHeader.h" +#include "bcos-protocol/TransactionStatus.h" +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +DERIVE_BCOS_EXCEPTION(PermissionDenied); +DERIVE_BCOS_EXCEPTION(InternalVMError); +DERIVE_BCOS_EXCEPTION(InvalidInputSize); +DERIVE_BCOS_EXCEPTION(InvalidEncoding); + +namespace executor +{ +#define EXECUTOR_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("EXECUTOR") +#define EXECUTOR_BLK_LOG(LEVEL, number) EXECUTOR_LOG(LEVEL) << BLOCK_NUMBER(number) +#define EXECUTOR_NAME_LOG(LEVEL) \ + BCOS_LOG(LEVEL) << LOG_BADGE("EXECUTOR:" + std::to_string(m_schedulerTermId)) +#define COROUTINE_TRACE_LOG(LEVEL, contextID, seq) \ + BCOS_LOG(LEVEL) << LOG_BADGE("EXECUTOR") << "[" << (contextID) << "," << (seq) << "]" +#define PARA_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("PARA") << LOG_BADGE(utcTime()) + + +static constexpr std::string_view USER_TABLE_PREFIX = "/tables/"; +static constexpr std::string_view USER_APPS_PREFIX = "/apps/"; +static constexpr std::string_view USER_SYS_PREFIX = "/sys/"; +static constexpr std::string_view USER_USR_PREFIX = "/usr/"; + +static const char* const STORAGE_VALUE = "value"; +static const char* const ACCOUNT_CODE_HASH = "codeHash"; +static const char* const ACCOUNT_CODE = "code"; +static const char* const ACCOUNT_BALANCE = "balance"; +static const char* const ACCOUNT_ABI = "abi"; +static const char* const ACCOUNT_NONCE = "nonce"; +static const char* const ACCOUNT_ALIVE = "alive"; +static const char* const ACCOUNT_FROZEN = "frozen"; + +/// auth +static const char* const CONTRACT_SUFFIX = "_accessAuth"; +static const char* const ADMIN_FIELD = "admin"; +static const char* const STATUS_FIELD = "status"; +static const char* const METHOD_AUTH_TYPE = "method_auth_type"; +static const char* const METHOD_AUTH_WHITE = "method_auth_white"; +static const char* const METHOD_AUTH_BLACK = "method_auth_black"; + +/// account +static constexpr const std::string_view ACCOUNT_STATUS = "status"; +static constexpr const std::string_view ACCOUNT_LAST_UPDATE = "last_update"; +static constexpr const std::string_view ACCOUNT_LAST_STATUS = "last_status"; +enum AccountStatus : uint8_t +{ + normal = 0, + freeze = 1, + abolish = 2, +}; + +/// contract status +static const char* const CONTRACT_FROZEN = "frozen"; +static const char* const CONTRACT_NORMAL = "normal"; + +/// FileSystem table keys +static const char* const FS_KEY_NAME = "name"; +static const char* const FS_KEY_TYPE = "type"; +static const char* const FS_KEY_SUB = "sub"; +static const char* const FS_ACL_TYPE = "acl_type"; +static const char* const FS_ACL_WHITE = "acl_white"; +static const char* const FS_ACL_BLACK = "acl_black"; +static const char* const FS_KEY_EXTRA = "extra"; +static const char* const FS_LINK_ADDRESS = "link_address"; +static const char* const FS_LINK_ABI = "link_abi"; + +/// FileSystem file type +static const char* const FS_TYPE_DIR = "directory"; +static const char* const FS_TYPE_CONTRACT = "contract"; +static const char* const FS_TYPE_LINK = "link"; + +#define EXECUTIVE_LOG(LEVEL) BCOS_LOG(LEVEL) << "[EXECUTOR]" + +struct GlobalHashImpl +{ + static crypto::Hash::Ptr g_hashImpl; +}; + +struct SubState +{ + std::set suicides; ///< Any accounts that have suicided. + protocol::LogEntriesPtr logs = std::make_shared(); ///< Any logs. + u256 refunds; ///< Refund counter of SSTORE nonzero->zero. + + SubState& operator+=(SubState const& _s) + { + suicides += _s.suicides; + refunds += _s.refunds; + *logs += *_s.logs; + return *this; + } + + void clear() + { + suicides.clear(); + logs->clear(); + refunds = 0; + } +}; + +struct VMSchedule +{ + VMSchedule() : tierStepGas(std::array{{0, 2, 3, 5, 8, 10, 20, 0}}) {} + VMSchedule(bool _efcd, bool _hdc, unsigned const& _txCreateGas) + : tierStepGas(std::array{{0, 2, 3, 5, 8, 10, 20, 0}}), + exceptionalFailedCodeDeposit(_efcd), + haveDelegateCall(_hdc), + txCreateGas(_txCreateGas) + {} + + std::array tierStepGas; + bool exceptionalFailedCodeDeposit = true; + bool haveDelegateCall = false; + bool eip150Mode = true; + bool eip158Mode = true; + bool haveBitwiseShifting = false; + bool haveRevert = true; + bool haveReturnData = true; + bool haveStaticCall = true; + bool haveCreate2 = true; + bool haveExtcodehash = false; + bool enableIstanbul = false; + bool enableLondon = false; + /// gas cost for specified calculation + /// exp gas cost + unsigned expGas = 10; + unsigned expByteGas = 10; + /// sha3 gas cost + unsigned sha3Gas = 30; + unsigned sha3WordGas = 6; + /// load/store gas cost + unsigned sloadGas = 50; + unsigned sstoreSetGas = 20000; + unsigned sstoreResetGas = 5000; + unsigned sstoreRefundGas = 15000; + /// jump gas cost + unsigned jumpdestGas = 1; + /// log gas cost + unsigned logGas = 375; + unsigned logDataGas = 8; + unsigned logTopicGas = 375; + /// creat contract gas cost + unsigned createGas = 32000; + /// call function of contract gas cost + unsigned callGas = 40; + unsigned callStipend = 2300; + unsigned callValueTransferGas = 9000; + unsigned callNewAccountGas = 25000; + + unsigned suicideRefundGas = 24000; + unsigned memoryGas = 3; + unsigned quadCoeffDiv = 512; + unsigned createDataGas = 20; + /// transaction related gas + unsigned txGas = 21000; + unsigned txCreateGas = 53000; + unsigned txDataZeroGas = 4; + unsigned txDataNonZeroGas = 68; + unsigned copyGas = 3; + /// extra code related gas + unsigned extcodesizeGas = 20; + unsigned extcodecopyGas = 20; + unsigned extcodehashGas = 400; + unsigned balanceGas = 20; + unsigned suicideGas = 0; + unsigned blockhashGas = 20; + unsigned maxCodeSize = unsigned(-1); + + boost::optional blockRewardOverwrite; + + bool staticCallDepthLimit() const { return !eip150Mode; } + bool suicideChargesNewAccountGas() const { return eip150Mode; } + bool emptinessIsNonexistence() const { return eip158Mode; } + bool zeroValueTransferChargesNewAccountGas() const { return !eip158Mode; } +}; + +/// exceptionalFailedCodeDeposit: false +/// haveDelegateCall: false +/// tierStepGas: {0, 2, 3, 5, 8, 10, 20, 0} +/// txCreateGas: 21000 +static const VMSchedule FrontierSchedule = VMSchedule(false, false, 21000); +/// value of params are equal to HomesteadSchedule +static const VMSchedule HomesteadSchedule = VMSchedule(true, true, 53000); +/// EIP150(refer to: +/// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md) +static const VMSchedule EIP150Schedule = [] { + VMSchedule schedule = HomesteadSchedule; + schedule.eip150Mode = true; + schedule.extcodesizeGas = 700; + schedule.extcodecopyGas = 700; + schedule.balanceGas = 400; + schedule.sloadGas = 200; + schedule.callGas = 700; + schedule.suicideGas = 5000; + return schedule; +}(); +/// EIP158 +static const VMSchedule EIP158Schedule = [] { + VMSchedule schedule = EIP150Schedule; + schedule.expByteGas = 50; + schedule.eip158Mode = true; + schedule.maxCodeSize = 0x6000; + return schedule; +}(); + +static const VMSchedule ByzantiumSchedule = [] { + VMSchedule schedule = EIP158Schedule; + schedule.haveRevert = true; + schedule.haveReturnData = true; + schedule.haveStaticCall = true; + // schedule.blockRewardOverwrite = {3 * ether}; + return schedule; +}(); + +static const VMSchedule ConstantinopleSchedule = [] { + VMSchedule schedule = ByzantiumSchedule; + schedule.blockhashGas = 800; + schedule.haveCreate2 = true; + schedule.haveBitwiseShifting = true; + schedule.haveExtcodehash = true; + return schedule; +}(); + +static const VMSchedule FiscoBcosSchedule = [] { + VMSchedule schedule = ConstantinopleSchedule; + return schedule; +}(); + +static const VMSchedule FiscoBcosScheduleV2 = [] { + VMSchedule schedule = ConstantinopleSchedule; + schedule.maxCodeSize = 0x40000; + return schedule; +}(); + +static const VMSchedule FiscoBcosScheduleV3 = [] { + VMSchedule schedule = FiscoBcosScheduleV2; + schedule.enableIstanbul = true; + return schedule; +}(); + +static const VMSchedule FiscoBcosScheduleV4 = [] { + VMSchedule schedule = FiscoBcosScheduleV3; + schedule.enableLondon = true; + return schedule; +}(); + +static const VMSchedule BCOSWASMSchedule = [] { + VMSchedule schedule = FiscoBcosScheduleV4; + schedule.maxCodeSize = 0xF00000; // 15MB + // Ensure that zero bytes are not subsidised and are charged the same as + // non-zero bytes. + schedule.txDataZeroGas = schedule.txDataNonZeroGas; + return schedule; +}(); + +static const VMSchedule DefaultSchedule = FiscoBcosScheduleV4; + +struct ImportRequirements +{ + using value = unsigned; + enum + { + ValidSeal = 1, ///< Validate seal + TransactionBasic = 8, ///< Check the basic structure of the transactions. + TransactionSignatures = 32, ///< Check the basic structure of the transactions. + Parent = 64, ///< Check parent block header + PostGenesis = 256, ///< Require block to be non-genesis. + CheckTransactions = TransactionBasic | TransactionSignatures, ///< Check transaction + ///< signatures. + OutOfOrderChecks = ValidSeal | CheckTransactions, ///< Do all checks that can be done + ///< independently of prior blocks having + ///< been imported. + InOrderChecks = Parent, ///< Do all checks that cannot be done independently + ///< of prior blocks having been imported. + Everything = ValidSeal | CheckTransactions | Parent, + None = 0 + }; +}; + +protocol::TransactionStatus toTransactionStatus(Exception const& _e); + +} // namespace executor + +bool hasWasmPreamble(const std::string_view& _input); +bool hasWasmPreamble(const bytesConstRef& _input); +bool hasWasmPreamble(const bytes& _input); +bool hasPrecompiledPrefix(const std::string_view& _code); +/** + * @brief : trans string addess to evm address + * @param _addr : the string address + * @return evmc_address : the transformed evm address + */ +inline evmc_address toEvmC(const std::string_view& _addr) +{ // TODO: add another interfaces for wasm + evmc_address ret; + memcpy(ret.bytes, _addr.data(), 20); + return ret; +} + +/** + * @brief : trans ethereum hash to evm hash + * @param _h : hash value + * @return evmc_bytes32 : transformed hash + */ +inline evmc_bytes32 toEvmC(h256 const& _h) +{ + return reinterpret_cast(_h); +} +/** + * @brief : trans uint256 number of evm-represented to u256 + * @param _n : the uint256 number that can parsed by evm + * @return u256 : transformed u256 number + */ +inline u256 fromEvmC(evmc_bytes32 const& _n) +{ + return fromBigEndian(_n.bytes); +} + +/** + * @brief : trans evm address to ethereum address + * @param _addr : the address that can parsed by evm + * @return string_view : the transformed ethereum address + */ +inline std::string_view fromEvmC(evmc_address const& _addr) +{ + return {(char*)_addr.bytes, 20}; +} + +inline std::string fromBytes(const bytes& _addr) +{ + return {(char*)_addr.data(), _addr.size()}; +} + +inline std::string fromBytes(const bytesConstRef& _addr) +{ + return _addr.toString(); +} + +inline bytes toBytes(const std::string_view& _addr) +{ + return {(char*)_addr.data(), (char*)(_addr.data() + _addr.size())}; +} + +inline std::string getContractTableName(const std::string_view& _address) +{ + std::string formatAddress(_address); + + std::string address = (_address[0] == '/') ? formatAddress.substr(1) : formatAddress; + + return std::string("/apps/").append(address); +} + +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/dag/Abi.cpp" "b/BFPL\345\243\271/bcos-executor/src/dag/Abi.cpp" new file mode 100644 index 00000000..62649b0d --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/dag/Abi.cpp" @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2021 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. + * + * @brief ABI data structure used in transaction construction + * @file Abi.cpp + * @author: catli + * @date: 2021-09-11 + */ + +#include "Abi.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace bcos::executor; + +ParameterAbi parseParameter(const Json::Value& input) +{ + auto paramType = input["type"].asString(); + auto components = vector(); + if (boost::starts_with(paramType, "tuple")) + { + auto& paramComponents = input["components"]; + assert(!paramComponents.isNull()); + + components.reserve(paramComponents.size()); + for (auto& component : paramComponents) + { + components.emplace_back(parseParameter(component)); + } + } + auto parameterAbi = ParameterAbi(paramType, components); + return parameterAbi; +} + +vector flattenStaticParameter(const ParameterAbi& param) +{ // TODO: return vector>>, pair is type and access path + const auto TUPLE_STR = "tuple"; + auto flatTypes = vector(); + if (boost::starts_with(param.type, TUPLE_STR)) + { + for (auto i = (size_t)0; i < param.components.size(); i++) + { + auto types = flattenStaticParameter(param.components[i]); + flatTypes.insert(flatTypes.end(), types.begin(), types.end()); + } + } + else if (boost::algorithm::contains(param.type, "[") && + !boost::algorithm::contains(param.type, "[]")) + { + auto type = param.type.substr(0, param.type.find("[")); + size_t len = std::stoi(param.type.substr(param.type.find("["), param.type.find("]"))); + flatTypes.insert(flatTypes.end(), len, type); + } + else + { + flatTypes.push_back(param.type); + } + return flatTypes; +} + +unique_ptr FunctionAbi::deserialize( + string_view abiStr, const bytes& expected, bool isSMCrypto) +{ + assert(expected.size() == 4); + + Json::Reader reader; + Json::Value root; + if (!reader.parse(abiStr.begin(), abiStr.end(), root)) + { + BCOS_LOG(DEBUG) << LOG_BADGE("EXECUTOR") << LOG_DESC("unable to parse contract ABI") + << LOG_KV("abiStr", abiStr); + return nullptr; + } + + if (!root.isArray()) + { + BCOS_LOG(DEBUG) << LOG_BADGE("EXECUTOR") << LOG_DESC("contract ABI is not an array") + << LOG_KV("abiStr", abiStr); + return nullptr; + } + + for (auto& function : root) + { + auto& type = function["type"]; + if (type.isNull() || type.asString() != "function") + { + continue; + } + + if (!function["constant"].isNull()) + { // liquid + if (function["constant"].asBool()) + { + continue; + } + } + else if (!function["stateMutability"].isNull()) + { // solidity + if (function["stateMutability"].asString() == "view" || + function["stateMutability"].asString() == "pure") + { + continue; + } + } + else + { + continue; + } + auto& functionName = function["name"]; + assert(!functionName.isNull()); + uint32_t selector = 0; + if (!function["selector"].isNull() && function["selector"].isArray()) + { + if (isSMCrypto) + { + selector = (uint32_t)function["selector"][1].asUInt(); + } + else + { + selector = (uint32_t)function["selector"][0].asUInt(); + } + } + + auto expectedSelector = *((uint32_t*)expected.data()); + expectedSelector = ((expectedSelector & 0xff) << 24) | ((expectedSelector & 0xff00) << 8) | + ((expectedSelector & 0xff0000) >> 8) | + ((expectedSelector & 0xff000000) >> 24); + + if (expectedSelector != selector) + { + BCOS_LOG(DEBUG) << LOG_BADGE("EXECUTOR") << LOG_DESC("selector mismatch") + << LOG_KV("name", functionName) + << LOG_KV("expected selector", expectedSelector) + << LOG_KV("selector", selector); + continue; + } + + auto& functionConflictFields = function["conflictFields"]; + auto conflictFields = vector(); + conflictFields.reserve(functionConflictFields.size()); + if (!functionConflictFields.isNull()) + { + for (auto& conflictField : functionConflictFields) + { + auto value = vector(); + if (!conflictField["value"].isNull()) + { + value.reserve(conflictField["value"].size()); + for (auto& pathItem : conflictField["value"]) + { + value.emplace_back(static_cast(pathItem.asUInt())); + } + } + std::optional slot = std::nullopt; + if (!conflictField["slot"].isNull()) + { + slot = std::optional(conflictField["slot"].asInt()); + } + conflictFields.emplace_back(ConflictField{ + static_cast(conflictField["kind"].asUInt()), value, slot}); + } + } + + auto& functionInputs = function["inputs"]; + assert(!functionInputs.isNull()); + auto inputs = vector(); + inputs.reserve(functionInputs.size()); + auto flatInputs = vector(); + for (auto i = (Json::ArrayIndex)0; i < functionInputs.size(); ++i) + { + auto param = parseParameter(functionInputs[i]); + auto flatTypes = flattenStaticParameter(param); + flatInputs.insert(flatInputs.end(), flatTypes.begin(), flatTypes.end()); + inputs.emplace_back(std::move(param)); + } + + return unique_ptr( + new FunctionAbi{functionName.asString(), inputs, selector, conflictFields, flatInputs}); + } + + return nullptr; +} diff --git "a/BFPL\345\243\271/bcos-executor/src/dag/Abi.h" "b/BFPL\345\243\271/bcos-executor/src/dag/Abi.h" new file mode 100644 index 00000000..156d92b0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/dag/Abi.h" @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2021 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. + * + * @brief ABI data structure used in transaction construction + * @file Abi.h + * @author: catli + * @date: 2021-09-11 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace executor +{ +struct ConflictField +{ + std::uint8_t kind; + std::vector value; + std::optional slot; +}; + +struct ParameterAbi +{ + std::string type; + std::vector components; + + ParameterAbi() = default; + + ParameterAbi(const std::string& type) { this->type = type; } + + ParameterAbi(const std::string& type, const std::vector& components) + { + this->type = type; + this->components = components; + } + + friend std::ostream& operator<<(std::ostream& output, const ParameterAbi& param) + { + output << "{\"type\": " << param.type << ", \"components\": ["; + for (auto& component : param.components) + { + output << component; + } + output << "]}"; + return output; + } +}; + +struct FunctionAbi +{ + std::string name; + std::vector inputs; + std::uint32_t selector; + std::vector conflictFields; + std::vector flatInputs; + + static std::unique_ptr deserialize( + std::string_view abiStr, const bcos::bytes& expected, bool isSMCrypto); +}; +} // namespace executor +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/dag/ClockCache.cpp" "b/BFPL\345\243\271/bcos-executor/src/dag/ClockCache.cpp" new file mode 100644 index 00000000..f257cc0c --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/dag/ClockCache.cpp" @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2021 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. + * + * @brief implementation of clock cache, which used to cache deserialization result of ABI + * info + * @file ClockCache.cpp + * @author: catli + * @date: 2021-09-11 + */ + +#include "ClockCache.h" +#include +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::executor; + +CacheItem* CacheShard::insert(size_t hash, void* value, bool holdReference) +{ + auto guard = lock_guard(m_mutex); + auto success = evictFromCache(); + if (!success) + { + return nullptr; + } + + // Grab available slot from recycle bin. If recycle bin is empty, create + // and append new slot to end of circular list. + CacheItem* item = nullptr; + if (!m_recycle.empty()) + { + item = m_recycle.back(); + m_recycle.pop_back(); + } + else + { + m_list.emplace_back(); + item = &m_list.back(); + } + + item->hash = hash; + item->value = value; + auto flags = holdReference ? s_inCacheBit + s_oneRef : s_inCacheBit; + + item->flags.store(flags, std::memory_order_relaxed); + HashTable::accessor accessor; + if (m_table.find(accessor, hash)) + { + auto existingHandle = accessor->second; + m_table.erase(accessor); + unsetInCache(existingHandle); + } + m_table.insert(HashTable::value_type(hash, item)); + m_usage.fetch_add(1, std::memory_order_relaxed); + return item; +} + +CacheItem* CacheShard::lookup(size_t hash) +{ + HashTable::const_accessor accessor; + if (!m_table.find(accessor, hash)) + { + return nullptr; + } + + CacheItem* item = accessor->second; + accessor.release(); + // ref() could fail if another thread sneak in and evict/erase the cache + // entry before we are able to hold reference. + if (!ref(item)) + { + return nullptr; + } + else + + // Double check the key since the item may now representing another key + // if other threads sneak in, evict/erase the entry and re-used the item + // for another cache entry. + if (hash != item->hash) + { + unref(item, false); + return nullptr; + } + return item; +} + +bool CacheShard::ref(CacheItem* item) +{ + // CAS loop to increase reference count. + uint32_t flags = item->flags.load(std::memory_order_relaxed); + while (inCache(flags)) + { + // Use acquire semantics on success, as further operations on the cache + // entry has to be order after reference count is increased. + if (item->flags.compare_exchange_weak( + flags, flags + s_oneRef, std::memory_order_acquire, std::memory_order_relaxed)) + { + return true; + } + } + return false; +} + +bool CacheShard::unref(CacheItem* item, bool setUsage) +{ + if (setUsage) + { + item->flags.fetch_or(s_usageBit, std::memory_order_relaxed); + } + // Use acquire-release semantics as previous operations on the cache entry + // has to be order before reference count is decreased, and potential cleanup + // of the entry has to be order after. + uint32_t flags = item->flags.fetch_sub(s_oneRef, std::memory_order_acq_rel); + assert(refCounts(flags) > 0); + if (refCounts(flags) == 1) + { + if (!inCache(flags)) + { + auto guard = lock_guard(m_mutex); + recycleItem(item); + return true; + } + } + return false; +} + +void CacheShard::unsetInCache(CacheItem* item) +{ + assert(!m_mutex.try_lock()); + + // Use acquire-release semantics as previous operations on the cache entry + // has to be order before reference count is decreased, and potential cleanup + // of the entry has to be order after. + uint32_t flags = item->flags.fetch_and(~s_inCacheBit, std::memory_order_acq_rel); + // Cleanup if it is the last reference. + if (inCache(flags) && refCounts(flags) == 0) + { + recycleItem(item); + } +} + +bool CacheShard::evictFromCache() +{ + assert(!m_mutex.try_lock()); + auto usage = m_usage.load(std::memory_order_relaxed); + auto capacity = m_capacity.load(memory_order_relaxed); + if (usage == 0) + { + return true; + } + + auto newHead = m_head; + bool is2ndIteration = false; + while (usage >= capacity) + { + assert(newHead < m_list.size()); + auto evicted = tryEvict(&m_list[newHead]); + newHead = (newHead + 1 >= m_list.size()) ? 0 : newHead + 1; + if (evicted) + { + usage = m_usage.load(memory_order_relaxed); + } + else + { + if (newHead == m_head) + { + if (is2ndIteration) + { + return false; + } + else + { + is2ndIteration = true; + } + } + } + } + m_head = newHead; + return true; +} + +bool CacheShard::tryEvict(CacheItem* item) +{ + assert(!m_mutex.try_lock()); + auto flags = s_inCacheBit; + if (item->flags.compare_exchange_strong( + flags, 0, std::memory_order_acquire, std::memory_order_relaxed)) + { + auto erased = m_table.erase(item->hash); + assert(erased); + boost::ignore_unused(erased); + boost::ignore_unused(flags); + + recycleItem(item); + return true; + } + item->flags.fetch_and(~s_usageBit, memory_order_relaxed); + return false; +} + +void CacheShard::recycleItem(CacheItem* item) +{ + assert(!m_mutex.try_lock()); + auto& flags = item->flags; + assert(!inCache(flags) && refCounts(flags) == 0); + boost::ignore_unused(flags); + + m_deleter(item->value); + m_recycle.push_back(item); + m_usage.fetch_sub(1, std::memory_order_relaxed); +} + +void CacheShard::setCapacity(size_t capacity) +{ + assert(capacity > 0); + auto guard = lock_guard(m_mutex); + m_capacity.store(capacity, std::memory_order_relaxed); + evictFromCache(); +} + +CacheShard::~CacheShard() +{ + for (auto& item : m_list) + { + uint32_t flags = item.flags.load(std::memory_order_relaxed); + if (inCache(flags) || refCounts(flags) > 0) + { + m_deleter(item.value); + } + } +} diff --git "a/BFPL\345\243\271/bcos-executor/src/dag/ClockCache.h" "b/BFPL\345\243\271/bcos-executor/src/dag/ClockCache.h" new file mode 100644 index 00000000..7896c8f8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/dag/ClockCache.h" @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2021 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. + * + * @brief implementation of clock cache, which used to cache deserialization result of ABI + * info + * @file ClockCache.h + * @author: catli + * @date: 2021-09-11 + */ + +#pragma once + +#include "Abi.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace executor +{ +// Cache entry meta data. +struct CacheItem +{ + CacheItem() = default; + + CacheItem(const CacheItem& a) { *this = a; } + + CacheItem& operator=(const CacheItem& a) + { + value = a.value; + return *this; + } + + size_t hash; + + void* value; + + // Flags and counters associated with the cache item: + // lowest bit: in-cache bit + // second lowest bit: usage bit + // the rest bits: reference count + // The item is unused when flags equals to 0. The thread decreases the count + // to 0 is responsible to put the item back to recycle and cleanup memory. + std::atomic flags; +}; + +// A cache shard which maintains its own clock cache. +class CacheShard +{ +public: + using HashTable = tbb::concurrent_hash_map; + using Deleter = void (*)(void* value); + + CacheShard() : m_head(0), m_usage(0) {} + + // Insert a mapping from key->value into the cache. + CacheItem* insert(size_t hash, void* value, bool holdReference); + + // If the cache has no mapping for "key", returns nullptr, otherwise return a + // item that corresponds to the mapping. The caller must call this->unref(item) + // when the returned mapping is no longer needed. + CacheItem* lookup(size_t hash); + + // Increments the reference count for the item if it refers to an entry in + // the cache. Returns true if refcount was incremented; otherwise, returns + // false. + // + // REQUIRES: item must have been returned by a method on *this. + bool ref(CacheItem* item); + + // Decrease reference count of the entry. If this decreases the count to 0, + // recycle the entry. If set_usage is true, also set the usage bit. Returns true + // if a value is erased. + // + // Not necessary to hold mutex_ before being called. + bool unref(CacheItem* item, bool setUsage); + + void setCapacity(size_t capacity); + + void setDeleter(Deleter deleter) { m_deleter = deleter; } + + ~CacheShard(); + +private: + static const std::uint32_t s_inCacheBit = 1; + static const std::uint32_t s_usageBit = 2; + static const std::uint32_t s_refCountOffset = 2; + static const std::uint32_t s_oneRef = 1 << s_refCountOffset; + + static bool inCache(uint32_t flags) { return flags & s_inCacheBit; } + static bool hasUsage(uint32_t flags) { return flags & s_usageBit; } + static uint32_t refCounts(uint32_t flags) { return flags >> s_refCountOffset; } + + // Unset in-cache bit of the entry. Recycle the item if necessary, returns + // true if a value is erased. + // + // Has to hold mutex_ before being called. + void unsetInCache(CacheItem* item); + + // Scan through the circular list, evict entries until we get enough space + // for a new cache entry. Return true if success, false otherwise. + // + // Has to hold mutex_ before being called. + bool evictFromCache(); + + // Examine the item for eviction. If the item is in cache, usage bit is + // not set, and referece count is 0, evict it from cache. Otherwise unset + // the usage bit. + // + // Has to hold mutex_ before being called. + bool tryEvict(CacheItem* item); + + // Put the item back to recycle list, and put the value associated with + // it into to-be-deleted list. + // + // Has to hold mutex_ before being called. + void recycleItem(CacheItem* item); + + // The circular list of cache handles. Initially the list is empty. Once a + // item is needed by insertion, and no more handles are available in + // recycle bin, one more item is appended to the end. + // + // We use std::deque for the circular list because we want to make sure + // pointers to handles are valid through out the life-cycle of the cache + // (in contrast to std::vector), and be able to grow the list (in contrast + // to statically allocated arrays). + std::deque m_list; + + // Pointer to the next item in the circular list to be examine for + // eviction. + size_t m_head; + + // Recycle bin of cache handles. + std::vector m_recycle; + + // Maximum cache size. + std::atomic m_capacity; + + // Current total size of the cache. + std::atomic m_usage; + + // Guards m_list, m_head, and m_recycle. In addition, updating m_table also has + // to hold the mutex, to avoid the cache being in inconsistent state. + std::mutex m_mutex; + + // Hash table (tbb::concurrent_hash_map) for lookup. + HashTable m_table; + + Deleter m_deleter; +}; + +template +class CacheHandle +{ +public: + CacheHandle() = default; + + CacheHandle(CacheItem* item, CacheShard* ownedShard) + { + m_item = item; + m_ownedShard = ownedShard; + } + + CacheHandle(const CacheHandle&) = delete; + + CacheHandle& operator=(const CacheHandle&) = delete; + + CacheHandle(CacheHandle&& a) + { + if (isValid()) + { + m_ownedShard->unref(m_item, true); + } + m_item = a.m_item; + m_ownedShard = a.m_ownedShard; + a.m_item = nullptr; + a.m_ownedShard = nullptr; + } + + CacheHandle& operator=(CacheHandle&& a) + { + if (this != &a) + { + if (isValid()) + { + m_ownedShard->unref(m_item, true); + } + m_item = a.m_item; + m_ownedShard = a.m_ownedShard; + a.m_item = nullptr; + a.m_ownedShard = nullptr; + } + return *this; + } + + ~CacheHandle() + { + if (isValid()) + { + m_ownedShard->unref(m_item, true); + } + } + + T& value() const { return *static_cast(m_item->value); } + + bool release() + { + if (isValid()) + { + auto result = m_ownedShard->unref(m_item, true); + m_item = nullptr; + m_ownedShard = nullptr; + return result; + } + return false; + } + + bool isValid() const { return m_item != nullptr && m_ownedShard != nullptr; } + +private: + CacheItem* m_item; + CacheShard* m_ownedShard; +}; + +template +class ClockCache +{ +public: + ClockCache(size_t capacity, int numShardBits = -1) + { + assert(capacity > 0); + if (numShardBits == -1) + { + numShardBits = 4; + } + m_numShards = 1 << numShardBits; + m_shardMask = (size_t{1} << numShardBits) - 1; + m_shards = new CacheShard[m_numShards]; + + for (auto i = 0u; i < m_numShards; ++i) + { + m_shards[i].setCapacity(capacity); + m_shards[i].setDeleter([](void* value) { delete static_cast(value); }); + } + } + + CacheHandle lookup(const K& key) + { + auto hasher = boost::hash(); + auto hash = hasher(key); + auto& ownedShard = getShard(hash); + auto item = ownedShard.lookup(hash); + return CacheHandle(item, &ownedShard); + } + + bool insert(const K& key, V* value, CacheHandle* outHandle = nullptr) + { + auto hasher = boost::hash(); + auto hash = hasher(key); + auto& shard = getShard(hash); + auto item = shard.insert(hash, value, outHandle != nullptr); + if (outHandle != nullptr) + { + if (item != nullptr) + { + (*outHandle) = CacheHandle(item, &shard); + } + } + return item != nullptr; + } + + ~ClockCache() { delete[] m_shards; } + +private: + CacheShard& getShard(size_t hash) + { + auto shardId = hash & m_shardMask; + return m_shards[shardId]; + } + + size_t m_numShards; + size_t m_shardMask; + CacheShard* m_shards; +}; +} // namespace executor +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/dag/CriticalFields.h" "b/BFPL\345\243\271/bcos-executor/src/dag/CriticalFields.h" new file mode 100644 index 00000000..28962da0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/dag/CriticalFields.h" @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2021 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. + */ +/** + * @brief : Critical fields analysing + * @author: jimmyshi + * @date: 2022-1-14 + */ + + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace executor +{ +namespace critical +{ +using ID = uint32_t; +static const ID INVALID_ID = (ID(0) - 1); + +using OnConflictHandler = std::function; // conflict from -> to +using OnFirstConflictHandler = std::function; // conflict +using OnEmptyConflictHandler = std::function; // conflict +using OnAllConflictHandler = std::function; // conflict + +class CriticalFieldsInterface +{ +public: + using Ptr = std::shared_ptr; + + virtual size_t size() = 0; + + virtual bool contains(size_t id) = 0; + + virtual void traverseDag(OnConflictHandler const& _onConflict, + OnFirstConflictHandler const& _onFirstConflict, + OnEmptyConflictHandler const& _onEmptyConflict, + OnAllConflictHandler const& _onAllConflict) = 0; +}; + +class CriticalFields : public virtual CriticalFieldsInterface +{ +public: + using Ptr = std::shared_ptr; + using CriticalField = std::vector>; + using CriticalFieldPtr = std::shared_ptr; + + CriticalFields(size_t _size) : m_criticals(std::vector(_size)) {} + virtual ~CriticalFields() {} + + size_t size() override { return m_criticals.size(); } + bool contains(size_t id) override { return id < size() && get(id) != nullptr; }; + void put(size_t _id, CriticalFieldPtr _criticalField) { m_criticals[_id] = _criticalField; } + CriticalFieldPtr get(size_t _id) { return m_criticals[_id]; } + + void traverseDag(OnConflictHandler const& _onConflict, + OnFirstConflictHandler const& _onFirstConflict, + OnEmptyConflictHandler const& _onEmptyConflict, + OnAllConflictHandler const& _onAllConflict) override + { + auto dependencies = std::unordered_map, std::vector, boost::hash>>(); + + for (ID id = 0; id < m_criticals.size(); ++id) + { + auto criticals = m_criticals[id]; + + if (criticals == nullptr) + { + _onAllConflict(id); + } + else if (criticals->empty()) + { + _onEmptyConflict(id); + } + else if (!criticals->empty()) + { + // Get conflict parent's id set + std::set pIds; + for (auto const& c : *criticals) + { + auto& ids = dependencies[c]; + for (auto pId : ids) + { + pIds.insert(pId); + } + ids.push_back(id); + } + + if (pIds.empty()) + { + _onFirstConflict(id); + } + else + { + for (ID pId : pIds) + { + _onConflict(pId, id); + } + } + } + else + { + continue; + } + } + }; + +private: + std::vector m_criticals; +}; +} // namespace critical +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/dag/DAG.cpp" "b/BFPL\345\243\271/bcos-executor/src/dag/DAG.cpp" new file mode 100644 index 00000000..391e5f43 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/dag/DAG.cpp" @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2021 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. + */ +/** + * @brief : DAG(Directed Acyclic Graph) basic implement + * @author: jimmyshi + * @date: 2019-1-8 + */ + +#include "DAG.h" +using namespace std; +using namespace bcos; +using namespace bcos::executor; + +DAG::~DAG() +{ + clear(); +} + +void DAG::init(ID _maxSize) +{ + clear(); + for (ID i = 0; i < _maxSize; ++i) + m_vtxs.emplace_back(make_shared()); + m_totalVtxs = _maxSize; + m_totalConsume = 0; +} + +void DAG::addEdge(ID _f, ID _t) +{ + if (_f >= m_vtxs.size() && _t >= m_vtxs.size()) + return; + m_vtxs[_f]->outEdge.emplace_back(_t); + m_vtxs[_t]->inDegree += 1; + // PARA_LOG(TRACE) << LOG_BADGE("DAG") << LOG_DESC("Add edge") << LOG_KV("from", _f) + // << LOG_KV("to", _t); +} + +void DAG::generate() +{ + for (ID id = 0; id < m_vtxs.size(); ++id) + { + if (m_vtxs[id]->inDegree == 0) + m_topLevel.push(id); + } + + // PARA_LOG(TRACE) << LOG_BADGE("DAG") << LOG_DESC("generate") + // << LOG_KV("queueSize", m_topLevel.size()); + // for (ID id = 0; id < m_vtxs.size(); id++) + // printVtx(id); +} + +ID DAG::waitPop(bool _needWait) +{ + // Note: concurrent_queue of TBB can't be used with boost::conditional_variable + // the try_pop will already be false + std::unique_lock ul(x_topLevel); + ID top = INVALID_ID; + cv_topLevel.wait_for(ul, std::chrono::milliseconds(10), [&]() { + auto has = m_topLevel.try_pop(top); + if (has) + { + return true; + } + else if (m_totalConsume >= m_totalVtxs) + { + return true; + } + else if (!_needWait) + { + return true; + } + // process-exit related: + // if the m_stop is true (may be the storage has exceptioned) + // return true, will pop INVALID_ID + else if (m_stop.load()) + { + return true; + } + + return false; + }); + return top; +} + +ID DAG::consume(ID _id) +{ + ID producedNum = 0; + ID nextId = INVALID_ID; + ID lastDegree = INVALID_ID; + for (ID id : m_vtxs[_id]->outEdge) + { + auto vtx = m_vtxs[id]; + { + lastDegree = vtx->inDegree.fetch_sub(1); + } + if (lastDegree == 1) + { + ++producedNum; + if (producedNum == 1) + { + nextId = id; + } + else + { + m_topLevel.push(id); + cv_topLevel.notify_one(); + } + } + } + + if (m_totalConsume.fetch_add(1) + 1 == m_totalVtxs) + { + cv_topLevel.notify_all(); + } + // PARA_LOG(TRACE) << LOG_BADGE("TbbCqDAG") << LOG_DESC("consumed") + // << LOG_KV("queueSize", m_topLevel.size()); + // for (ID id = 0; id < m_vtxs.size(); id++) + // printVtx(id); + return nextId; +} + +void DAG::clear() +{ + m_vtxs = std::vector>(); + // XXXX m_topLevel.clear(); +} + +void DAG::printVtx(ID _id) +{ + for (ID id : m_vtxs[_id]->outEdge) + { + PARA_LOG(TRACE) << LOG_BADGE("DAG") << LOG_DESC("VertexEdge") << LOG_KV("ID", _id) + << LOG_KV("inDegree", m_vtxs[_id]->inDegree) << LOG_KV("edge", id); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/dag/DAG.h" "b/BFPL\345\243\271/bcos-executor/src/dag/DAG.h" new file mode 100644 index 00000000..5229306b --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/dag/DAG.h" @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2021 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. + */ +/** + * @brief : DAG(Directed Acyclic Graph) basic implement + * @author: jimmyshi + * @date: 2019-1-8 + */ + + +#pragma once +#include "../Common.h" +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace executor +{ +using ID = uint32_t; +using IDs = std::vector; +static const ID INVALID_ID = (ID(0) - 1); + +struct Vertex +{ + std::atomic inDegree; + std::vector outEdge; +}; + +class DAG +{ + // Just algorithm, not thread safe +public: + DAG(){}; + ~DAG(); + + // Init DAG basic memory, should call before other function + // _maxSize is max ID + 1 + void init(ID _maxSize); + + // Add edge between vertex + void addEdge(ID _f, ID _t); + + // Generate DAG + void generate(); + + // Wait until topLevel is not empty, return INVALID_ID if DAG reach the end + ID waitPop(bool _needWait = true); + + // Consume the top and add new top in top queue (thread safe) + ID consume(ID _id); + + // Clear all data of this class (thread safe) + void clear(); + +private: + std::vector> m_vtxs; + tbb::concurrent_queue m_topLevel; + + ID m_totalVtxs = 0; + std::atomic m_totalConsume; + +private: + void printVtx(ID _id); + mutable std::mutex x_topLevel; + std::condition_variable cv_topLevel; + std::atomic_bool m_stop = {false}; +}; + +} // namespace executor +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/dag/ScaleUtils.cpp" "b/BFPL\345\243\271/bcos-executor/src/dag/ScaleUtils.cpp" new file mode 100644 index 00000000..18f94eba --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/dag/ScaleUtils.cpp" @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2021 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. + * + * @brief tools for scale codec + * info + * @file ScaleUtils.cpp + * @author: catli + * @date: 2021-09-11 + */ + +#include "ScaleUtils.h" +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::executor; + +optional bcos::executor::decodeCompactInteger(const bytes& encodedBytes, size_t startPos) +{ + if (encodedBytes.size() - startPos < 1) + { + return nullopt; + } + + auto _1stByte = encodedBytes[startPos]; + auto flag = (_1stByte)&0b00000011u; + auto number = 0u; + + switch (flag) + { + case 0b00u: + { + // single-byte mode: + // upper six bits are the LE encoding of the value (valid only for values of 0-63). + number = static_cast(_1stByte >> 2u); + break; + } + case 0b01u: + { + // two-byte mode: + // upper six bits and the following byte is the LE encoding of the value (valid only + // for values 64-(2**14-1)). + if (encodedBytes.size() - startPos < 2) + { + BCOS_LOG(TRACE) << LOG_BADGE("decodeCompactInteger") + << LOG_DESC("not enough data to decode compact integer"); + return nullopt; + } + auto _2ndByte = encodedBytes[startPos + 1]; + number = + (static_cast((_1stByte)&0b11111100u) + static_cast(_2ndByte) * 256u) >> + 2u; + break; + } + case 0b10u: + { + // four-byte mode: + // upper six bits and the following three bytes are the LE encoding of the value + // (valid only for values (2**14)-(2**30-1)) + number = _1stByte; + size_t multiplier = 256u; + if (encodedBytes.size() - startPos < 4) + { + BCOS_LOG(TRACE) << LOG_BADGE("decodeCompactInteger") + << LOG_DESC("not enough data to decode compact integer"); + return nullopt; + } + + for (auto i = 1u; i < 4u; ++i) + { + number += encodedBytes[startPos + i] * multiplier; + multiplier = multiplier << 8u; + } + number = number >> 2u; + break; + } + case 0b11: + { + // big-integer mode: + // upper six bits are the number of bytes following, less four. The value is + // contained, LE encoded, in the bytes following. The final (most significant) byte + // must be non-zero. Valid only for values (2**30)-(2**536-1) + auto bytesCount = ((_1stByte) >> 2u) + 4u; + if (encodedBytes.size() - startPos < bytesCount + 1) + { + BCOS_LOG(TRACE) << LOG_BADGE("decodeCompactInteger") + << LOG_DESC("not enough data to decode compact integer"); + return nullopt; + } + + auto multiplier = 1u; + for (auto i = 1u; i < bytesCount + 1; ++i) + { + number += (encodedBytes[startPos + i]) * multiplier; + multiplier *= 256u; + } + break; + } + default: + return nullopt; + } + return number; +} + +optional bcos::executor::scaleEncodingLength( + const ParameterAbi& param, const bytes& encodedBytes, size_t startPos) +{ + auto& type = param.type; + if (boost::ends_with(type, "]")) + { + auto leftBracketPos = type.rfind("["); + if (leftBracketPos == type.npos) + { + BCOS_LOG(TRACE) << LOG_BADGE("scaleEncodingLength") + << LOG_DESC("unable to parse array type") << LOG_KV("type", type); + return nullopt; + } + + auto size = 0u; + auto length = 0u; + if (leftBracketPos == type.length() - 2) + { + auto compactLength = decodeCompactInteger(encodedBytes, startPos); + if (compactLength) + { + size = compactLength.value(); + length += codec::scale::compactLen(size); + startPos += length; + } + else + { + BCOS_LOG(TRACE) << LOG_BADGE("scaleEncodingLength") + << LOG_DESC("unable to parse length of dynamic array"); + return nullopt; + } + } + else + { + auto dimension = type.substr(leftBracketPos + 1, type.length() - leftBracketPos - 1); + try + { + size = stoul(dimension); + } + catch (...) + { + BCOS_LOG(TRACE) << LOG_BADGE("scaleEncodingLength") + << LOG_DESC("unable to parse dimension") + << LOG_KV("dimension", dimension); + return nullopt; + } + } + + auto subParam = ParameterAbi{type.substr(0, leftBracketPos), param.components}; + for (auto i = 0u; i < size; ++i) + { + auto subTypeLength = scaleEncodingLength(subParam, encodedBytes, startPos); + if (subTypeLength) + { + auto value = subTypeLength.value(); + length += value; + startPos += value; + } + else + { + BCOS_LOG(TRACE) << LOG_BADGE("scaleEncodingLength") + << LOG_DESC("unable to calculate length of element"); + return nullopt; + } + } + return {length}; + } + + if (boost::starts_with(type, "uint") || boost::starts_with(type, "int")) + { + auto digitStartPos = type.rfind("t"); + auto digitsNum = 0u; + try + { + digitsNum = stoul(type.substr(digitStartPos + 1)); + } + catch (...) + { + BCOS_LOG(TRACE) << LOG_BADGE("scaleEncodingLength") << LOG_DESC("unable to parse type") + << LOG_KV("type", type); + return nullopt; + } + return digitsNum >> 3; + } + + if (type == "string" || type == "bytes") + { + auto compactLength = decodeCompactInteger(encodedBytes, startPos); + if (compactLength) + { + auto value = compactLength.value(); + return {value + codec::scale::compactLen(value)}; + } + else + { + BCOS_LOG(TRACE) << LOG_BADGE("scaleEncodingLength") + << LOG_DESC("unable to parse string or bytes"); + return nullopt; + } + } + + if (type == "bool" || type == "byte") + { + return 1; + } + + if (boost::starts_with(type, "bytes")) + { + auto digitStartPos = type.rfind("s"); + auto digitsNum = 0u; + try + { + digitsNum = stoul(type.substr(digitStartPos + 1)); + } + catch (...) + { + BCOS_LOG(TRACE) << LOG_BADGE("scaleEncodingLength") << LOG_DESC("unable to parse type") + << LOG_KV("type", type); + return nullopt; + } + return digitsNum; + } + + if (type == "tuple") + { + auto length = 0u; + for (auto& component : param.components) + { + auto componentLength = scaleEncodingLength(component, encodedBytes, startPos); + if (componentLength) + { + auto value = componentLength.value(); + length += value; + startPos += value; + } + else + { + BCOS_LOG(TRACE) << LOG_BADGE("scaleEncodingLength") + << LOG_DESC("unable to parse component") + << LOG_KV("type", component); + return nullopt; + } + } + return {length}; + } + + BCOS_LOG(TRACE) << LOG_BADGE("scaleEncodingLength") << LOG_DESC("unable to parse type") + << LOG_KV("type", type); + return nullopt; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/dag/ScaleUtils.h" "b/BFPL\345\243\271/bcos-executor/src/dag/ScaleUtils.h" new file mode 100644 index 00000000..cf27479b --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/dag/ScaleUtils.h" @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 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. + * + * @brief tools for scale codec + * info + * @file ScaleUtils.h + * @author: catli + * @date: 2021-09-11 + */ + +#pragma once + +#include "../Common.h" +#include "Abi.h" +#include + +namespace bcos +{ +namespace executor +{ +std::optional decodeCompactInteger(const bcos::bytes& encodedBytes, size_t startPos); + +std::optional scaleEncodingLength( + const ParameterAbi& param, const bytes& encodedBytes, size_t startPos); +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/dag/TxDAG2.cpp" "b/BFPL\345\243\271/bcos-executor/src/dag/TxDAG2.cpp" new file mode 100644 index 00000000..b37829f6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/dag/TxDAG2.cpp" @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2021 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. + */ +/** + * @brief : Transaction DAG flowGraph implementation + * @author: jimmyshi + * @date: 2022-1-4 + */ + + +#include "TxDAG2.h" +#include "CriticalFields.h" + +using namespace std; +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::executor::critical; +using namespace tbb::flow; + +#define DAG_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("DAG") + +// Generate DAG according with given transactions +void TxDAG2::init(critical::CriticalFieldsInterface::Ptr _txsCriticals, ExecuteTxFunc const& _f) +{ + auto txsSize = _txsCriticals->size(); + DAG_LOG(INFO) << LOG_DESC("Begin init transaction DAG") << LOG_KV("transactionNum", txsSize); + + f_executeTx = _f; + m_totalParaTxs = _txsCriticals->size(); + + // Generate tasks in m_tasks + // Note: we must generate every task in m_tasks before make_edge(). Otherwise, may lead to core. + using Msg = const continue_msg&; + std::vector id2TaskId(txsSize); + for (ID id = 0; id < _txsCriticals->size(); ++id) + { + if (_txsCriticals->contains(id)) + { + // generate tasks + auto task = [this, id](Msg) { f_executeTx(id); }; + auto t = Task(m_dag, std::move(task)); + auto taskId = m_tasks.size(); + m_tasks.push_back(t); + + id2TaskId[id] = taskId; + } + } + + // define conflict handler + auto onConflictHandler = [&](ID pId, ID id) { + auto pTaskId = id2TaskId[pId]; + auto taskId = id2TaskId[id]; + make_edge(m_tasks[pTaskId], m_tasks[taskId]); + }; + auto onFirstConflictHandler = [&](ID id) { + auto taskId = id2TaskId[id]; + make_edge(m_startTask, m_tasks[taskId]); + }; + auto onEmptyConflictHandler = [&](ID id) { + auto taskId = id2TaskId[id]; + make_edge(m_startTask, m_tasks[taskId]); + }; + auto onAllConflictHandler = [&](ID id) { + // do nothing + // ignore normal tx, only handle DAG tx, normal tx has been sent back to be executed by DMC + (void)id; + }; + + // parse criticals + _txsCriticals->traverseDag( + onConflictHandler, onFirstConflictHandler, onEmptyConflictHandler, onAllConflictHandler); + + DAG_LOG(TRACE) << LOG_DESC("End init transaction DAG"); +} + +void TxDAG2::run(unsigned int threadNum) +{ + // TODO: add timeout logic + (void)threadNum; + m_startTask.try_put(continue_msg()); + m_dag.wait_for_all(); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/dag/TxDAG2.h" "b/BFPL\345\243\271/bcos-executor/src/dag/TxDAG2.h" new file mode 100644 index 00000000..0092ae5a --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/dag/TxDAG2.h" @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2021 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. + */ +/** + * @brief : Transaction DAG flowGraph implementation + * @author: jimmyshi + * @date: 2022-1-4 + */ + + +#pragma once +#include "./TxDAGInterface.h" +#include "tbb/flow_graph.h" +#include + + +namespace bcos +{ +namespace executor +{ + +class TxDAG2 : public virtual TxDAGInterface +{ + using Task = tbb::flow::continue_node; + +public: + TxDAG2() : m_startTask(m_dag) {} + + virtual ~TxDAG2() {} + + void init( + critical::CriticalFieldsInterface::Ptr _txsCriticals, ExecuteTxFunc const& _f) override; + + void run(unsigned int threadNum) override; + +private: + ExecuteTxFunc f_executeTx; + tbb::flow::graph m_dag; + tbb::flow::broadcast_node m_startTask; + std::vector m_tasks = std::vector(); + size_t m_totalParaTxs; +}; + +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/dag/TxDAGInterface.h" "b/BFPL\345\243\271/bcos-executor/src/dag/TxDAGInterface.h" new file mode 100644 index 00000000..492b0a32 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/dag/TxDAGInterface.h" @@ -0,0 +1,69 @@ +/* +* Copyright (C) 2021 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. +*/ +/** +* @brief : Transaction DAG interface +* @author: jimmyshi +* @date: 2022-1-4 +*/ + + +#pragma once +#include "../CallParameters.h" +#include "../Common.h" +#include "../executive/BlockContext.h" +#include "../executive/TransactionExecutive.h" +#include "../executor/TransactionExecutor.h" +#include "CriticalFields.h" +#include +#include +#include +#include + +namespace bcos +{ +namespace executor +{ +class TransactionExecutive; +using ExecuteTxFunc = std::function; + +enum ConflictFieldKind : std::uint8_t +{ + All = 0, + Len, + Env, + Params, + Const, + None, +}; + +enum EnvKind : std::uint8_t +{ + Caller = 0, + Origin, + Now, + BlockNumber, + Addr, +}; + +class TxDAGInterface +{ +public: + virtual void init(critical::CriticalFieldsInterface::Ptr _txsCriticals, ExecuteTxFunc const& _f) = 0; + + virtual void run(unsigned int threadNum) = 0; +}; +}} + diff --git "a/BFPL\345\243\271/bcos-executor/src/executive/BlockContext.cpp" "b/BFPL\345\243\271/bcos-executor/src/executive/BlockContext.cpp" new file mode 100644 index 00000000..57cd40ef --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executive/BlockContext.cpp" @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2021 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. + * + * @brief block level context + * @file BlockContext.h + * @author: xingqiangbai + * @date: 2021-05-27 + */ + +#include "BlockContext.h" +#include "../vm/Precompiled.h" +#include "ExecutiveStackFlow.h" +#include "TransactionExecutive.h" +#include "bcos-codec/abi/ContractABICodec.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include "bcos-framework/protocol/Exceptions.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-framework/storage/Table.h" +#include +#include +#include +#include +#include + +using namespace bcos::executor; +using namespace bcos::protocol; +using namespace bcos::precompiled; +using namespace std; + +BlockContext::BlockContext(std::shared_ptr storage, + LedgerCache::Ptr ledgerCache, crypto::Hash::Ptr _hashImpl, + bcos::protocol::BlockNumber blockNumber, h256 blockHash, uint64_t timestamp, + uint32_t blockVersion, const VMSchedule& _schedule, bool _isWasm, bool _isAuthCheck) + : m_blockNumber(blockNumber), + m_blockHash(blockHash), + m_timeStamp(timestamp), + m_blockVersion(blockVersion), + m_schedule(_schedule), + m_isWasm(_isWasm), + m_isAuthCheck(_isAuthCheck), + m_storage(std::move(storage)), + m_hashImpl(_hashImpl), + m_ledgerCache(ledgerCache) +{} + +BlockContext::BlockContext(std::shared_ptr storage, + LedgerCache::Ptr ledgerCache, crypto::Hash::Ptr _hashImpl, + protocol::BlockHeader::ConstPtr _current, const VMSchedule& _schedule, bool _isWasm, + bool _isAuthCheck, std::shared_ptr>> _keyPageIgnoreTables) + : BlockContext(storage, ledgerCache, _hashImpl, _current->number(), _current->hash(), + _current->timestamp(), _current->version(), _schedule, _isWasm, _isAuthCheck) +{ + m_keyPageIgnoreTables = std::move(_keyPageIgnoreTables); +} + + +ExecutiveFlowInterface::Ptr BlockContext::getExecutiveFlow(std::string codeAddress) +{ + bcos::ReadGuard l(x_executiveFlows); + auto it = m_executiveFlows.find(codeAddress); + if (it == m_executiveFlows.end()) + { + /* + bool success; + std::tie(it, success) = + m_executiveFlows.emplace(codeAddress, std::make_shared()); + + */ + return nullptr; + } + return it->second; +} + +void BlockContext::setExecutiveFlow( + std::string codeAddress, ExecutiveFlowInterface::Ptr executiveFlow) +{ + bcos::ReadGuard l(x_executiveFlows); + m_executiveFlows.emplace(codeAddress, executiveFlow); +} + +void BlockContext::suicide(std::string_view contract2Suicide) +{ + { + bcos::WriteGuard l(x_suicides); + m_suicides.emplace(contract2Suicide); + } + + EXECUTOR_LOG(TRACE) << LOG_BADGE("SUICIDE") + << "Add suicide: " << LOG_KV("table2Suicide", contract2Suicide) + << LOG_KV("suicides.size", m_suicides.size()) + << LOG_KV("blockNumber", m_blockNumber); +} + +void BlockContext::killSuicides() +{ + bcos::ReadGuard l(x_suicides); + if (m_suicides.empty()) + { + return; + } + + auto emptyCodeHash = m_hashImpl->hash(""); + for (std::string_view table2Suicide : m_suicides) + { + auto contractTable = storage()->openTable(table2Suicide); + + if (contractTable) + { + // set codeHash + bcos::storage::Entry emptyCodeHashEntry; + emptyCodeHashEntry.importFields({emptyCodeHash.asBytes()}); + contractTable->setRow(ACCOUNT_CODE_HASH, std::move(emptyCodeHashEntry)); + + // delete binary + bcos::storage::Entry emptyCodeEntry; + emptyCodeEntry.importFields({std::move("")}); + contractTable->setRow(ACCOUNT_CODE, std::move(emptyCodeEntry)); + } + + EXECUTOR_LOG(TRACE) << LOG_BADGE("SUICIDE") + << "Kill contract: " << LOG_KV("contract2Suicide", table2Suicide) + << LOG_KV("blockNumber", m_blockNumber); + } +} diff --git "a/BFPL\345\243\271/bcos-executor/src/executive/BlockContext.h" "b/BFPL\345\243\271/bcos-executor/src/executive/BlockContext.h" new file mode 100644 index 00000000..5ccc553f --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executive/BlockContext.h" @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2021 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. + * + * @brief block level context + * @file BlockContext.h + * @author: xingqiangbai + * @date: 2021-05-26 + */ + +#pragma once + +#include "../Common.h" +#include "ExecutiveFactory.h" +#include "ExecutiveFlowInterface.h" +#include "LedgerCache.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/protocol/Block.h" +#include "bcos-framework/protocol/ProtocolTypeDef.h" +#include "bcos-framework/protocol/Transaction.h" +#include "bcos-framework/storage/Table.h" +#include "bcos-table/src/StateStorage.h" +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace executor +{ +class TransactionExecutive; +class PrecompiledContract; + +class BlockContext : public std::enable_shared_from_this +{ +public: + typedef std::shared_ptr Ptr; + + BlockContext(std::shared_ptr storage, + LedgerCache::Ptr ledgerCache, crypto::Hash::Ptr _hashImpl, + bcos::protocol::BlockNumber blockNumber, h256 blockHash, uint64_t timestamp, + uint32_t blockVersion, const VMSchedule& _schedule, bool _isWasm, bool _isAuthCheck); + + BlockContext(std::shared_ptr storage, + LedgerCache::Ptr ledgerCache, crypto::Hash::Ptr _hashImpl, + protocol::BlockHeader::ConstPtr _current, const VMSchedule& _schedule, bool _isWasm, + bool _isAuthCheck, std::shared_ptr>> = nullptr); + + using getTxCriticalsHandler = std::function>( + const protocol::Transaction::ConstPtr& _tx)>; + virtual ~BlockContext(){}; + + std::shared_ptr storage() { return m_storage; } + + uint64_t txGasLimit() const { return m_ledgerCache->fetchTxGasLimit(); } + + auto getTxCriticals(const protocol::Transaction::ConstPtr& _tx) + -> std::shared_ptr>; + + crypto::Hash::Ptr hashHandler() const { return m_hashImpl; } + bool isWasm() const { return m_isWasm; } + bool isAuthCheck() const { return m_isAuthCheck; } + int64_t number() const { return m_blockNumber; } + h256 hash() const { return m_blockHash; } + h256 blockHash(int64_t _number) const { return m_ledgerCache->fetchBlockHash(_number); } + uint64_t timestamp() const { return m_timeStamp; } + uint32_t blockVersion() const { return m_blockVersion; } + void suicide(std::string_view address); + void killSuicides(); + + VMSchedule const& vmSchedule() const { return m_schedule; } + + ExecutiveFlowInterface::Ptr getExecutiveFlow(std::string codeAddress); + void setExecutiveFlow(std::string codeAddress, ExecutiveFlowInterface::Ptr executiveFlow); + + void stop() + { + std::vector executiveFlow2Stop; + { + bcos::ReadGuard l(x_executiveFlows); + for (auto it : m_executiveFlows) + { + EXECUTOR_LOG(INFO) << "Try to stop flow: " << it.first; + executiveFlow2Stop.push_back(it.second); + } + } + + if (executiveFlow2Stop.empty()) + { + return; + } + + for (auto executiveFlow : executiveFlow2Stop) + { + executiveFlow->stop(); + } + } + void clear() + { + bcos::WriteGuard l(x_executiveFlows); + m_executiveFlows.clear(); + } + + void registerNeedSwitchEvent(std::function event) { f_onNeedSwitchEvent = event; } + + void triggerSwitch() + { + if (f_onNeedSwitchEvent) + { + f_onNeedSwitchEvent(); + } + } + + auto keyPageIgnoreTables() const { return m_keyPageIgnoreTables; } + +private: + mutable bcos::SharedMutex x_executiveFlows; + tbb::concurrent_unordered_map m_executiveFlows; + + bcos::protocol::BlockNumber m_blockNumber; + h256 m_blockHash; + uint64_t m_timeStamp; + uint32_t m_blockVersion; + + VMSchedule m_schedule; + bool m_isWasm = false; + bool m_isAuthCheck = false; + std::shared_ptr m_storage; + crypto::Hash::Ptr m_hashImpl; + std::function f_onNeedSwitchEvent; + std::shared_ptr>> m_keyPageIgnoreTables; + LedgerCache::Ptr m_ledgerCache; + std::set m_suicides; // contract address need to selfdestruct + mutable bcos::SharedMutex x_suicides; +}; + +} // namespace executor + +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/executive/CoroutineTransactionExecutive.cpp" "b/BFPL\345\243\271/bcos-executor/src/executive/CoroutineTransactionExecutive.cpp" new file mode 100644 index 00000000..2300cec5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executive/CoroutineTransactionExecutive.cpp" @@ -0,0 +1,138 @@ +#include "CoroutineTransactionExecutive.h" +#include + +using namespace bcos::executor; +CallParameters::UniquePtr CoroutineTransactionExecutive::start(CallParameters::UniquePtr input) +{ + m_pullMessage.emplace([this, inputPtr = input.release()](Coroutine::push_type& push) { + COROUTINE_TRACE_LOG(TRACE, m_contextID, m_seq) << "Create new coroutine"; + + // Take ownership from input + m_pushMessage.emplace(std::move(push)); + + auto callParameters = std::unique_ptr(inputPtr); + auto blockContext = m_blockContext.lock(); + if (!blockContext) + { + BOOST_THROW_EXCEPTION(BCOS_ERROR(-1, "blockContext is null")); + } + + m_syncStorageWrapper = std::make_unique(blockContext->storage(), + std::bind(&CoroutineTransactionExecutive::externalAcquireKeyLocks, this, + std::placeholders::_1), + m_recoder); + + m_storageWrapper = m_syncStorageWrapper; // must set to base class + + + if (!callParameters->keyLocks.empty()) + { + m_syncStorageWrapper->importExistsKeyLocks(callParameters->keyLocks); + } + + m_exchangeMessage = execute(std::move(callParameters)); + // Execute is finished, erase the key locks + m_exchangeMessage->keyLocks.clear(); + + // Return the ownership to input + push = std::move(*m_pushMessage); + + COROUTINE_TRACE_LOG(TRACE, m_contextID, m_seq) << "Finish coroutine executing"; + }); + + return dispatcher(); +} + +CallParameters::UniquePtr CoroutineTransactionExecutive::dispatcher() +{ + try + { + for (auto it = RANGES::begin(*m_pullMessage); it != RANGES::end(*m_pullMessage); ++it) + { + if (*it) + { + COROUTINE_TRACE_LOG(TRACE, m_contextID, m_seq) + << "Context switch to main coroutine to call func"; + (*it)(ResumeHandler(*this)); + } + + if (m_exchangeMessage) + { + COROUTINE_TRACE_LOG(TRACE, m_contextID, m_seq) + << "Context switch to main coroutine to return output"; + return std::move(m_exchangeMessage); + } + } + } + catch (std::exception& e) + { + COROUTINE_TRACE_LOG(TRACE, m_contextID, m_seq) + << "Error while dispatch, " << boost::diagnostic_information(e); + BOOST_THROW_EXCEPTION(BCOS_ERROR_WITH_PREV(-1, "Error while dispatch", e)); + } + + COROUTINE_TRACE_LOG(TRACE, m_contextID, m_seq) << "Context switch to main coroutine, Finished!"; + return std::move(m_exchangeMessage); +} + +CallParameters::UniquePtr CoroutineTransactionExecutive::externalCall( + CallParameters::UniquePtr input) +{ + input->keyLocks = m_syncStorageWrapper->exportKeyLocks(); + + spawnAndCall([this, inputPtr = input.release()]( + ResumeHandler) { m_exchangeMessage = CallParameters::UniquePtr(inputPtr); }); + + // When resume, exchangeMessage set to output + auto output = std::move(m_exchangeMessage); + + if (output->delegateCall && output->type != CallParameters::FINISHED) + { + EXECUTIVE_LOG(DEBUG) << "Could not getCode during DMC externalCall" + << LOG_KV("codeAddress", output->codeAddress); + output->data = bytes(); + output->status = (int32_t)bcos::protocol::TransactionStatus::RevertInstruction; + output->evmStatus = EVMC_REVERT; + } + + // After coroutine switch, set the recoder + m_syncStorageWrapper->setRecoder(m_recoder); + + // Set the keyLocks + m_syncStorageWrapper->importExistsKeyLocks(output->keyLocks); + + return output; +} + +void CoroutineTransactionExecutive::externalAcquireKeyLocks(std::string acquireKeyLock) +{ + EXECUTOR_LOG(TRACE) << "Executor acquire key lock: " << acquireKeyLock; + + auto callParameters = std::make_unique(CallParameters::KEY_LOCK); + callParameters->senderAddress = m_contractAddress; + callParameters->keyLocks = m_syncStorageWrapper->exportKeyLocks(); + callParameters->acquireKeyLock = std::move(acquireKeyLock); + + spawnAndCall([this, inputPtr = callParameters.release()]( + ResumeHandler) { m_exchangeMessage = CallParameters::UniquePtr(inputPtr); }); + + // After coroutine switch, set the recoder, before the exception throw + m_syncStorageWrapper->setRecoder(m_recoder); + + auto output = std::move(m_exchangeMessage); + if (output->type == CallParameters::REVERT) + { + // Deadlock, revert + BOOST_THROW_EXCEPTION(BCOS_ERROR( + ExecuteError::DEAD_LOCK, "Dead lock detected, revert transaction: " + + boost::lexical_cast(output->type))); + } + + // Set the keyLocks + m_syncStorageWrapper->importExistsKeyLocks(output->keyLocks); +} + +void CoroutineTransactionExecutive::spawnAndCall(std::function function) +{ + (*m_pushMessage)(std::move(function)); +} diff --git "a/BFPL\345\243\271/bcos-executor/src/executive/CoroutineTransactionExecutive.h" "b/BFPL\345\243\271/bcos-executor/src/executive/CoroutineTransactionExecutive.h" new file mode 100644 index 00000000..f881a509 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executive/CoroutineTransactionExecutive.h" @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2021 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. + * + * @brief The serial transaction execute context without coroutine + * @file CoroutineTransactionExecutive.h + * @author: jimmyshi + * @date: 2022-07-19 + */ + +#pragma once + +#include "SyncStorageWrapper.h" +#include "TransactionExecutive.h" +#include + +namespace bcos +{ +namespace executor +{ + + +class CoroutineTransactionExecutive : public TransactionExecutive +{ +public: + using Ptr = std::shared_ptr; + + + class ResumeHandler; + + using CoroutineMessage = std::function; + using Coroutine = boost::coroutines2::coroutine; + + class ResumeHandler + { + public: + ResumeHandler(CoroutineTransactionExecutive& executive) : m_executive(executive) {} + + void operator()() + { + COROUTINE_TRACE_LOG(TRACE, m_executive.contextID(), m_executive.seq()) + << "Context switch to executive coroutine, from ResumeHandler"; + (*m_executive.m_pullMessage)(); + } + + private: + CoroutineTransactionExecutive& m_executive; + }; + + + CoroutineTransactionExecutive(std::weak_ptr blockContext, + std::string contractAddress, int64_t contextID, int64_t seq, + std::shared_ptr& gasInjector) + : TransactionExecutive( + std::move(blockContext), std::move(contractAddress), contextID, seq, gasInjector) + {} + + CallParameters::UniquePtr start(CallParameters::UniquePtr input) override; // start a new + // coroutine to + // execute + + // External call request + CallParameters::UniquePtr externalCall(CallParameters::UniquePtr input) override; // call by + // hostContext + + // External request key locks, throw exception if dead lock detected + void externalAcquireKeyLocks(std::string acquireKeyLock); + + virtual void setExchangeMessage(CallParameters::UniquePtr callParameters) + { + m_exchangeMessage = std::move(callParameters); + } + + std::string getExchangeMessageStr() + { + if (m_exchangeMessage) + { + return m_exchangeMessage->toString(); + } + else + { + return "[empty exchange message]"; + } + } + + + virtual void appendResumeKeyLocks(std::vector keyLocks) + { + std::copy( + keyLocks.begin(), keyLocks.end(), std::back_inserter(m_exchangeMessage->keyLocks)); + } + + virtual CallParameters::UniquePtr resume() + { + EXECUTOR_LOG(TRACE) << "Context switch to executive coroutine, from resume"; + (*m_pullMessage)(); + + return dispatcher(); + } + +private: + CallParameters::UniquePtr dispatcher(); + void spawnAndCall(std::function function); + + std::shared_ptr m_syncStorageWrapper; + CallParameters::UniquePtr m_exchangeMessage = nullptr; + + std::optional m_pullMessage; + std::optional m_pushMessage; +}; +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveFactory.cpp" "b/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveFactory.cpp" new file mode 100644 index 00000000..3e06037f --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveFactory.cpp" @@ -0,0 +1,76 @@ +/* + * 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. + * + * @brief factory of executive + * @file ExecutiveFactory.cpp + * @author: jimmyshi + * @date: 2022-03-22 + */ + +#include "ExecutiveFactory.h" +#include "CoroutineTransactionExecutive.h" +#include "TransactionExecutive.h" +#include "bcos-executor/src/precompiled/extension/AccountManagerPrecompiled.h" +#include "bcos-executor/src/precompiled/extension/AccountPrecompiled.h" +#include "bcos-framework/executor/PrecompiledTypeDef.h" +#include "bcos-framework/protocol/Protocol.h" + +using namespace bcos::executor; +using namespace bcos::precompiled; + + +std::shared_ptr ExecutiveFactory::build( + const std::string& _contractAddress, int64_t contextID, int64_t seq, bool useCoroutine) +{ + std::shared_ptr executive; + if (useCoroutine) + { + executive = std::make_shared( + m_blockContext, _contractAddress, contextID, seq, m_gasInjector); + } + else + { + executive = std::make_shared( + m_blockContext, _contractAddress, contextID, seq, m_gasInjector); + } + executive->setConstantPrecompiled(m_constantPrecompiled); + executive->setEVMPrecompiled(m_precompiledContract); + executive->setBuiltInPrecompiled(m_builtInPrecompiled); + + registerExtPrecompiled(executive); + return executive; +} +void ExecutiveFactory::registerExtPrecompiled(std::shared_ptr& executive) +{ + auto blockContext = m_blockContext.lock(); + if (blockContext->blockVersion() >= (uint32_t)protocol::BlockVersion::V3_1_VERSION) + { + if (!executive->isPrecompiled(ACCOUNT_MGR_ADDRESS) && + !executive->isPrecompiled(ACCOUNT_MANAGER_NAME)) + { + executive->setConstantPrecompiled( + blockContext->isWasm() ? ACCOUNT_MANAGER_NAME : ACCOUNT_MGR_ADDRESS, + std::make_shared()); + } + + if (!executive->isPrecompiled(ACCOUNT_ADDRESS)) + { + executive->setConstantPrecompiled( + ACCOUNT_ADDRESS, std::make_shared()); + } + } + // TODO: register User developed Precompiled contract + // registerUserPrecompiled(context); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveFactory.h" "b/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveFactory.h" new file mode 100644 index 00000000..bcc3180a --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveFactory.h" @@ -0,0 +1,71 @@ +/* + * 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. + * + * @brief factory of executive + * @file ExecutiveFactory.h + * @author: jimmyshi + * @date: 2022-03-22 + */ + +#pragma once + +#include "../executor/TransactionExecutor.h" +#include +#include +#include + +namespace bcos +{ +namespace executor +{ +class BlockContext; +class TransactionExecutive; + +class ExecutiveFactory +{ +public: + using Ptr = std::shared_ptr; + + ExecutiveFactory(std::weak_ptr blockContext, + std::shared_ptr>> + precompiledContract, + std::shared_ptr>> + constantPrecompiled, + std::shared_ptr> builtInPrecompiled, + std::shared_ptr gasInjector) + : m_precompiledContract(precompiledContract), + m_constantPrecompiled(constantPrecompiled), + m_builtInPrecompiled(builtInPrecompiled), + m_blockContext(blockContext), + m_gasInjector(gasInjector) + {} + virtual ~ExecutiveFactory() = default; + virtual std::shared_ptr build(const std::string& _contractAddress, + int64_t contextID, int64_t seq, bool useCoroutine = true); + +private: + void registerExtPrecompiled(std::shared_ptr& executive); + + std::shared_ptr>> + m_precompiledContract; + std::shared_ptr>> + m_constantPrecompiled; + std::shared_ptr> m_builtInPrecompiled; + std::weak_ptr m_blockContext; + std::shared_ptr m_gasInjector; +}; + +} // namespace executor +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveFlowInterface.h" "b/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveFlowInterface.h" new file mode 100644 index 00000000..1bb7c20a --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveFlowInterface.h" @@ -0,0 +1,102 @@ +/* + * 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. + * + * @brief interface definition of TransactionFlow + * @file ExecutiveFlowInterface.h + * @author: jimmyshi + * @date: 2022-03-22 + */ + +#pragma once + +#include "../CallParameters.h" +#include + +namespace bcos +{ +namespace executor +{ +class ExecutiveFlowInterface +{ +public: + using Ptr = std::shared_ptr; + + virtual void submit(CallParameters::UniquePtr txInput) = 0; + virtual void submit(std::shared_ptr> txInputs) = 0; + + virtual void asyncRun( + // onTxReturn(output) + std::function onTxReturn, + + // onFinished(success, errorMessage) + std::function onFinished) = 0; + + virtual void stop() + { + try + { + auto pool = getPoolInstance(); + if (pool) + { + pool->stop(); + } + } + catch (std::exception const& e) + { + EXECUTOR_LOG(DEBUG) << "ExecutiveFlowInterface stop: " << e.what(); + } + } + + void setThreadPool(bcos::ThreadPool::Ptr pool) + { + bcos::RecursiveGuard lock(x_pool); + m_pool = pool; + } + +protected: + template + void asyncTo(F f) + { + // f(); + getPoolInstance()->enqueue([f = std::move(f)]() { f(); }); + } + +private: + bcos::ThreadPool::Ptr getPoolInstance() + { + if (!m_pool) + { + bcos::RecursiveGuard lock(x_pool); + if (!m_pool) + { + m_pool = std::make_shared( + "ExecutiveFlow", std::thread::hardware_concurrency()); + } + } + + if (m_pool->hasStopped()) + { + throw std::runtime_error("Executive flow has stopped"); + } + + return m_pool; + } + + bcos::ThreadPool::Ptr m_pool; + bcos::RecursiveMutex x_pool; +}; + +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveSerialFlow.cpp" "b/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveSerialFlow.cpp" new file mode 100644 index 00000000..a8742f08 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveSerialFlow.cpp" @@ -0,0 +1,125 @@ + +#include "ExecutiveSerialFlow.h" +#include "TransactionExecutive.h" +#include + +using namespace bcos; +using namespace bcos::executor; + +void ExecutiveSerialFlow::submit(CallParameters::UniquePtr txInput) +{ + WriteGuard lock(x_lock); + + auto contextID = txInput->contextID; + + if (m_txInputs == nullptr) + { + m_txInputs = std::make_shared(); + } + + (*m_txInputs)[contextID] = std::move(txInput); +} + +void ExecutiveSerialFlow::submit(std::shared_ptr> txInputs) +{ + WriteGuard lock(x_lock); + if (m_txInputs == nullptr) + { + m_txInputs = std::make_shared(); + } + + for (auto& txInput : *txInputs) + { + auto contextID = txInput->contextID; + (*m_txInputs)[contextID] = std::move(txInput); + } +} + +void ExecutiveSerialFlow::asyncRun(std::function onTxReturn, + std::function onFinished) +{ + try + { + auto self = std::weak_ptr(shared_from_this()); + asyncTo([self, onTxReturn = std::move(onTxReturn), onFinished = std::move(onFinished)]() { + try + { + auto flow = self.lock(); + if (flow) + { + flow->run(onTxReturn, onFinished); + } + } + catch (std::exception& e) + { + onFinished(BCOS_ERROR_UNIQUE_PTR(ExecuteError::EXECUTE_ERROR, + "ExecutiveSerialFlow asyncRun exception:" + std::string(e.what()))); + } + }); + } + catch (std::exception const& e) + { + onFinished(BCOS_ERROR_UNIQUE_PTR(ExecuteError::EXECUTE_ERROR, + "ExecutiveSerialFlow asyncTo exception:" + std::string(e.what()))); + } +} + +void ExecutiveSerialFlow::run(std::function onTxReturn, + std::function onFinished) +{ + try + { + std::shared_ptr blockTxs = nullptr; + + { + bcos::WriteGuard lock(x_lock); + blockTxs = std::move(m_txInputs); + } + + for (auto it = blockTxs->begin(); it != blockTxs->end(); it++) + { + if (!m_isRunning) + { + EXECUTOR_LOG(DEBUG) << "ExecutiveSerialFlow has stopped during running"; + onFinished(BCOS_ERROR_UNIQUE_PTR( + ExecuteError::STOPPED, "ExecutiveSerialFlow has stopped during running")); + return; + } + + auto contextID = it->first; + auto& txInput = it->second; + if (!txInput) + { + EXECUTIVE_LOG(WARNING) << "Ignore tx[" << contextID << "] with empty message"; + continue; + } + + EXECUTOR_LOG(DEBUG) << "Serial execute tx start" << txInput->toString(); + + auto seq = txInput->seq; + // build executive + auto executive = m_executiveFactory->build( + txInput->codeAddress, txInput->contextID, txInput->seq, false); + + + // run evm + CallParameters::UniquePtr output = executive->start(std::move(txInput)); + + // set result + output->contextID = contextID; + output->seq = seq; + + // call back + EXECUTOR_LOG(DEBUG) << "Serial execute tx finish" << output->toString(); + onTxReturn(std::move(output)); + } + + onFinished(nullptr); + } + catch (std::exception& e) + { + EXECUTIVE_LOG(ERROR) << "ExecutiveSerialFlow run error: " + << boost::diagnostic_information(e); + onFinished(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(-1, "ExecutiveSerialFlow run error", e)); + } +} diff --git "a/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveSerialFlow.h" "b/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveSerialFlow.h" new file mode 100644 index 00000000..aefc6341 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveSerialFlow.h" @@ -0,0 +1,95 @@ +/* + * 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. + * + * @brief Executive flow for serial execution + * @file ExecutiveSerialFlow.h + * @author: jimmyshi + * @date: 2022-07-21 + */ + +#pragma once + +#include "ExecutiveFactory.h" +#include "ExecutiveFlowInterface.h" +#include "ExecutiveState.h" +#include +#include +#include + +namespace bcos +{ +namespace executor +{ +class ExecutiveSerialFlow : public virtual ExecutiveFlowInterface, + public std::enable_shared_from_this +{ +public: + ExecutiveSerialFlow(ExecutiveFactory::Ptr executiveFactory) + : m_executiveFactory(executiveFactory) + {} + + virtual ~ExecutiveSerialFlow() {} + + void submit(CallParameters::UniquePtr txInput) override; + void submit(std::shared_ptr> txInputs) override; + + void asyncRun( + // onTxReturn(output) + std::function onTxReturn, + + // onFinished(success, errorMessage) + std::function onFinished) override; + + void stop() override + { + EXECUTOR_LOG(DEBUG) << "Try to stop ExecutiveSerialFlow"; + if (!m_isRunning) + { + EXECUTOR_LOG(DEBUG) << "Executor has tried to stop"; + return; + } + + m_isRunning = false; + ExecutiveFlowInterface::stop(); + }; + +private: + using SerialMap = std::map>; + using SerialMapPtr = std::shared_ptr; + + void run(std::function onTxReturn, + std::function onFinished); + + template + void asyncTo(F f) + { + // call super function + ExecutiveFlowInterface::asyncTo(std::move(f)); + } + + + // -> Executive + SerialMapPtr m_txInputs; + + ExecutiveFactory::Ptr m_executiveFactory; + + mutable SharedMutex x_lock; + + bool m_isRunning = true; +}; + + +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveStackFlow.cpp" "b/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveStackFlow.cpp" new file mode 100644 index 00000000..2938716f --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveStackFlow.cpp" @@ -0,0 +1,223 @@ +/* + * 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. + * + * @brief interface definition of ExecutiveFlow + * @file ExecutiveStackFlow.cpp + * @author: jimmyshi + * @date: 2022-03-22 + */ + +#include "ExecutiveStackFlow.h" +#include "../Common.h" +#include + +using namespace bcos; +using namespace bcos::executor; + +void ExecutiveStackFlow::submit(CallParameters::UniquePtr txInput) +{ + auto contextID = txInput->contextID; + auto seq = txInput->seq; + auto type = txInput->type; + auto executiveState = m_executives[{contextID, seq}]; + if (executiveState == nullptr) + { + // add to top if not exists + executiveState = std::make_shared(m_executiveFactory, std::move(txInput)); + m_executives[{contextID, seq}] = executiveState; + } + else + { + // update resume params + executiveState->setResumeParam(std::move(txInput)); + } + + if (seq == 0 && type == CallParameters::MESSAGE) + { + // the tx has not been executed ever (created by a user) + m_originFlow.push(executiveState); + } + else + { + // the tx is not first run: + // 1. created by sending from a contract + // 2. is a revert message, seq = 0 but type = REVERT + m_pausedPool.erase({contextID, seq}); + m_waitingFlow.insert({contextID, seq}); + }; +} + +void ExecutiveStackFlow::submit(std::shared_ptr> txInputs) +{ + WriteGuard lock(x_lock); + + // from back to front, push in stack, so stack's tx can be executed from top + for (std::size_t i = 0; i < txInputs->size(); i++) + { + submit(std::move((*txInputs)[i])); + } +} + +void ExecutiveStackFlow::asyncRun(std::function onTxReturn, + std::function onFinished) +{ + try + { + auto self = std::weak_ptr(shared_from_this()); + asyncTo([self, onTxReturn = std::move(onTxReturn), onFinished = std::move(onFinished)]() { + try + { + auto flow = self.lock(); + if (flow) + { + flow->run(onTxReturn, onFinished); + } + } + catch (std::exception& e) + { + onFinished(BCOS_ERROR_UNIQUE_PTR(ExecuteError::EXECUTE_ERROR, + "ExecutiveStackFlow asyncRun exception:" + std::string(e.what()))); + } + }); + } + catch (std::exception const& e) + { + onFinished(BCOS_ERROR_UNIQUE_PTR(ExecuteError::EXECUTE_ERROR, + "ExecutiveStackFlow asyncTo exception:" + std::string(e.what()))); + } +} + +void ExecutiveStackFlow::run(std::function onTxReturn, + std::function onFinished) +{ + // origin flow: all messages received in first DMC iteration. if paused, move to paused pool + // paused poll: all paused messages during DMC iteration. if resumed, move to waiting pool + // waiting flow: include all messages received during DMC iteration. + + // These three pool above run as a stack manner. + // We must run waiting flow before origin flow and push message in waiting flow during DMC + // iteration. + + try + { + bcos::WriteGuard lock(x_lock); + + // must run all messages in waiting pool before origin pool + if (!m_waitingFlow.empty()) + { + runWaitingFlow(onTxReturn); + } + + if (m_pausedPool.empty()) + { + // origin flow can only be run if there is no paused message + runOriginFlow(onTxReturn); + } + + onFinished(nullptr); + } + catch (std::exception& e) + { + EXECUTIVE_LOG(ERROR) << "ExecutiveStackFlow run error: " + << boost::diagnostic_information(e); + onFinished(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(-1, "ExecutiveStackFlow run error", e)); + } +} + + +void ExecutiveStackFlow::runWaitingFlow(std::function onTxReturn) +{ + std::vector lastKeyLocks; + auto callback = [&lastKeyLocks, onTxReturn = std::move(onTxReturn)]( + CallParameters::UniquePtr output) { + if (output->type == CallParameters::MESSAGE || output->type == CallParameters::KEY_LOCK) + { + std::copy( + output->keyLocks.begin(), output->keyLocks.end(), std::back_inserter(lastKeyLocks)); + } + + + onTxReturn(std::move(output)); + }; + + for (auto contextIDAndSeq : m_waitingFlow) + { + if (!m_isRunning) + { + EXECUTOR_LOG(DEBUG) << "ExecutiveStackFlow has stopped during running waiting flow"; + return; + } + + auto executiveState = m_executives[contextIDAndSeq]; + executiveState->appendKeyLocks(lastKeyLocks); + + runOne(executiveState, callback); + } + + m_waitingFlow.clear(); +} + +void ExecutiveStackFlow::runOriginFlow(std::function onTxReturn) +{ + while (!m_originFlow.empty()) + { + if (!m_isRunning) + { + EXECUTOR_LOG(DEBUG) << "ExecutiveStackFlow has stopped during running origin flow"; + return; + } + + auto executiveState = m_originFlow.front(); + m_originFlow.pop(); + runOne(executiveState, onTxReturn); + + if (executiveState->getStatus() == ExecutiveState::PAUSED) + { + break; // break at once paused + } + } +} + +void ExecutiveStackFlow::runOne( + ExecutiveState::Ptr executiveState, std::function onTxReturn) +{ + CallParameters::UniquePtr output; + + output = executiveState->go(); + + switch (executiveState->getStatus()) + { + case ExecutiveState::NEED_RUN: + case ExecutiveState::NEED_RESUME: + { + // assume never goes here + + assert(false); + EXECUTIVE_LOG(FATAL) << "Invalid executiveState type"; + break; + } + case ExecutiveState::PAUSED: + { + m_pausedPool.insert({executiveState->getContextID(), executiveState->getSeq()}); + onTxReturn(std::move(output)); + break; + } + case ExecutiveState::FINISHED: + { + onTxReturn(std::move(output)); + break; + } + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveStackFlow.h" "b/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveStackFlow.h" new file mode 100644 index 00000000..bf2c24bf --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveStackFlow.h" @@ -0,0 +1,117 @@ +/* + * 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. + * + * @brief Executive flow for DMC execution + * @file ExecutiveStackFlow.h + * @author: jimmyshi + * @date: 2022-03-22 + */ + +#pragma once + +#include "ExecutiveFactory.h" +#include "ExecutiveFlowInterface.h" +#include "ExecutiveState.h" +#include +#include +#include + +namespace bcos +{ +namespace executor +{ +class ExecutiveStackFlow : public virtual ExecutiveFlowInterface, + public std::enable_shared_from_this +{ +public: + ExecutiveStackFlow(ExecutiveFactory::Ptr executiveFactory) + : m_executiveFactory(executiveFactory) + {} + + virtual ~ExecutiveStackFlow() {} + + void submit(CallParameters::UniquePtr txInput) override; + void submit(std::shared_ptr> txInputs) override; + + void asyncRun( + // onTxReturn(output) + std::function onTxReturn, + + // onFinished(success, errorMessage) + std::function onFinished) override; + + struct ContextIDSeqCmp + { + bool operator()( + const std::tuple& a, const std::tuple& b) const + { + // order: ContextID increasing and Seq decreasing + return std::get<0>(a) == std::get<0>(b) ? std::get<1>(a) < std::get<1>(b) : + std::get<0>(a) > std::get<0>(b); + } + }; + + void stop() override + { + EXECUTOR_LOG(DEBUG) << "Try to stop ExecutiveStackFlow"; + if (!m_isRunning) + { + EXECUTOR_LOG(DEBUG) << "Executor has tried to stop"; + return; + } + + m_isRunning = false; + ExecutiveFlowInterface::stop(); + }; + +private: + void run(std::function onTxReturn, + std::function onFinished); + + void runWaitingFlow(std::function onTxReturn); + + void runOriginFlow(std::function onTxReturn); + + void runOne(ExecutiveState::Ptr executiveState, + std::function onTxReturn); + + template + void asyncTo(F f) + { + // call super function + ExecutiveFlowInterface::asyncTo(std::move(f)); + } + + std::queue m_originFlow; + + // -> Executive + std::set> m_pausedPool; + + // ContextID -> Executive + std::set, ContextIDSeqCmp> m_waitingFlow; + + // -> Executive + std::map, ExecutiveState::Ptr> m_executives; + + ExecutiveFactory::Ptr m_executiveFactory; + + mutable SharedMutex x_lock; + + bool m_isRunning = true; +}; + + +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveState.cpp" "b/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveState.cpp" new file mode 100644 index 00000000..e30f2a4f --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveState.cpp" @@ -0,0 +1,102 @@ +/* + * 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. + * + * @brief the state of executive for TransactionFlow run + * @file ExecutiveState.cpp + * @author: jimmyshi + * @date: 2022-03-23 + */ + +#include "ExecutiveState.h" + +using namespace bcos; +using namespace bcos::executor; + +CallParameters::UniquePtr ExecutiveState::go() +{ + // init + if (!m_executive) + { + m_executive = + std::dynamic_pointer_cast(m_executiveFactory->build( + m_input->codeAddress, m_input->contextID, m_input->seq, true)); + } + + // run + CallParameters::UniquePtr output; + switch (m_status) + { + case NEED_RUN: + EXECUTOR_LOG(DEBUG) << "DMC Execute tx start" << m_input->toString(); + output = m_executive->start(std::move(m_input)); + break; + case PAUSED: + // just ignore, need to set resume params + EXECUTOR_LOG(FATAL) << "Invalid type"; + assert(false); + break; + case NEED_RESUME: + EXECUTOR_LOG(DEBUG) << "DMC Execute tx resume" << m_executive->getExchangeMessageStr(); + output = m_executive->resume(); + break; + case FINISHED: + // do nothing + break; + } + + // update status + EXECUTOR_LOG(DEBUG) << "DMC Execute tx done" << output->toString(); + switch (output->type) + { + case CallParameters::MESSAGE: + case CallParameters::KEY_LOCK: + m_status = PAUSED; + break; + case CallParameters::FINISHED: + case CallParameters::REVERT: + m_status = FINISHED; + break; + } + + // TODO: debug in executive + // Bug: Must force set contextID here to fix bug. + // But why output->context & output->seq here always be 0 ????? + output->contextID = m_contextID; + output->seq = m_seq; + return output; +} + +void ExecutiveState::setResumeParam(CallParameters::UniquePtr pullParam) +{ + m_status = NEED_RESUME; + m_executive->setExchangeMessage(std::move(pullParam)); +} + + +void ExecutiveState::appendKeyLocks(std::vector keyLocks) +{ + switch (getStatus()) + { + case NEED_RUN: + std::copy(keyLocks.begin(), keyLocks.end(), std::back_inserter(m_input->keyLocks)); + break; + case PAUSED: + case NEED_RESUME: + m_executive->appendResumeKeyLocks(std::move(keyLocks)); + break; + case FINISHED: + break; + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveState.h" "b/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveState.h" new file mode 100644 index 00000000..97424d33 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executive/ExecutiveState.h" @@ -0,0 +1,71 @@ +/* + * 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. + * + * @brief the state of executive for TransactionFlow run + * @file ExecutiveState.h + * @author: jimmyshi + * @date: 2022-03-23 + */ + +#pragma once + +#include "../CallParameters.h" +#include "CoroutineTransactionExecutive.h" +#include "ExecutiveFactory.h" + +namespace bcos +{ +namespace executor +{ +class ExecutiveState +{ +public: + using Ptr = std::shared_ptr; + + ExecutiveState(ExecutiveFactory::Ptr executiveFactory, CallParameters::UniquePtr input) + : m_isStaticCall(input->staticCall), + m_contextID(input->contextID), + m_seq(input->seq), + m_input(std::move(input)), + m_executiveFactory(executiveFactory){}; + + enum Status + { + NEED_RUN = 0, + PAUSED = 1, + NEED_RESUME = 2, + FINISHED = 3, + }; + + Status getStatus() { return m_status; } + CallParameters::UniquePtr go(); + void setResumeParam(CallParameters::UniquePtr pullParam); + int64_t getContextID() { return m_contextID; } + int64_t getSeq() { return m_seq; } + + void appendKeyLocks(std::vector keyLocks); + +private: + bool m_isStaticCall; + int64_t m_contextID; + int64_t m_seq; + CallParameters::UniquePtr m_input; + std::shared_ptr m_executive; + Status m_status = NEED_RUN; + ExecutiveFactory::Ptr m_executiveFactory; +}; + +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/executive/LedgerCache.h" "b/BFPL\345\243\271/bcos-executor/src/executive/LedgerCache.h" new file mode 100644 index 00000000..9e3a67bd --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executive/LedgerCache.h" @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2021 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. + * + * @brief Cache uncommitted but executed block info + * @file LedgerCache.h + * @author: jimmyshi + * @date: 2022-11-07 + */ + +#pragma once + +#include "../Common.h" +#include "bcos-framework/ledger/LedgerInterface.h" +#include +#include + +namespace bcos::executor +{ +class LedgerCache : public bcos::tool::LedgerConfigFetcher +{ +public: + using Ptr = std::shared_ptr; + LedgerCache(bcos::ledger::LedgerInterface::Ptr ledger) : bcos::tool::LedgerConfigFetcher(ledger) + {} + virtual ~LedgerCache() = default; + + void setBlockNumber2Hash(int64_t blockNumber, h256 blockHash) + { + bcos::WriteGuard l(x_blockNumber2Hash); + m_blockNumber2Hash[blockNumber] = blockHash; + } + + void clearCacheByNumber(int64_t blockNumber) + { + // remaining cache >= blockNumber + bcos::WriteGuard l(x_blockNumber2Hash); + std::erase_if(m_blockNumber2Hash, + [blockNumber](const auto& item) { return item.first < blockNumber; }); + } + + bcos::crypto::HashType fetchBlockHash(bcos::protocol::BlockNumber _blockNumber) override + { + EXECUTOR_LOG(TRACE) << LOG_BADGE("LedgerCache") << "fetchBlockHash start" + << LOG_KV("blockNumber", _blockNumber); + { + bcos::ReadGuard l(x_blockNumber2Hash); + if (m_blockNumber2Hash.contains(_blockNumber)) + { + auto& hash = m_blockNumber2Hash.at(_blockNumber); + EXECUTOR_LOG(TRACE) + << LOG_BADGE("LedgerCache") << "fetchBlockHash return cache" + << LOG_KV("blockNumber", _blockNumber) << LOG_KV("hash", hash.abridged()); + return hash; + } + } + + auto hash = bcos::tool::LedgerConfigFetcher::fetchBlockHash(_blockNumber); + EXECUTOR_LOG(TRACE) << LOG_BADGE("LedgerCache") + << "fetchBlockHash return by fetching from ledger" + << LOG_KV("blockNumber", _blockNumber) + << LOG_KV("hash", hash.abridged()); + return hash; + } + + uint64_t fetchTxGasLimit() + { + EXECUTOR_LOG(TRACE) << LOG_BADGE("LedgerCache") << "fetchTxGasLimit start"; + std::promise txGasLimitFuture; + + m_ledger->asyncGetSystemConfigByKey(ledger::SYSTEM_KEY_TX_GAS_LIMIT, + [&txGasLimitFuture]( + Error::Ptr error, std::string config, protocol::BlockNumber _number) mutable { + if (error) + { + EXECUTOR_LOG(ERROR) + << LOG_BADGE("LedgerCache") + << "fetchTxGasLimit error: " << LOG_KV("code", error->errorCode()) + << LOG_KV("message", error->errorMessage()); + txGasLimitFuture.set_value(0); + } + else + { + txGasLimitFuture.set_value(boost::lexical_cast(config)); + EXECUTOR_LOG(TRACE) + << LOG_BADGE("LedgerCache") << "fetchTxGasLimit finish" + << LOG_KV("txGasLimit", config) << LOG_KV("blockNumber", _number); + } + }); + auto txGasLimit = txGasLimitFuture.get_future().get(); + return txGasLimit; + } + +private: + std::map> m_blockNumber2Hash; + mutable bcos::SharedMutex x_blockNumber2Hash; +}; +} // namespace bcos::executor diff --git "a/BFPL\345\243\271/bcos-executor/src/executive/SyncStorageWrapper.h" "b/BFPL\345\243\271/bcos-executor/src/executive/SyncStorageWrapper.h" new file mode 100644 index 00000000..33e62995 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executive/SyncStorageWrapper.h" @@ -0,0 +1,120 @@ +#pragma once + +#include "../Common.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-framework/storage/Table.h" +#include "bcos-table/src/StateStorage.h" +#include "bcos-table/src/StorageWrapper.h" +#include +#include +#include +#include +#include + +namespace bcos::executor +{ +using KeyLockResponse = std::tuple; +using AcquireKeyLockResponse = std::tuple>; + +class SyncStorageWrapper : public storage::StorageWrapper +{ +public: + using Ptr = std::shared_ptr; + + SyncStorageWrapper(storage::StateStorageInterface::Ptr storage, + std::function externalAcquireKeyLocks, + bcos::storage::Recoder::Ptr recoder) + : StorageWrapper(storage, recoder), + m_externalAcquireKeyLocks(std::move(externalAcquireKeyLocks)) + {} + + SyncStorageWrapper(const SyncStorageWrapper&) = delete; + SyncStorageWrapper(SyncStorageWrapper&&) = delete; + SyncStorageWrapper& operator=(const SyncStorageWrapper&) = delete; + SyncStorageWrapper& operator=(SyncStorageWrapper&&) = delete; + + + std::optional getRow( + const std::string_view& table, const std::string_view& _key) override + { + acquireKeyLock(_key); + + return StorageWrapper::getRow(table, _key); + } + + std::vector> getRows( + const std::string_view& table, const std::variant, + const gsl::span>& _keys) override + { + std::visit( + [this](auto&& keys) { + for (auto& it : keys) + { + acquireKeyLock(it); + } + }, + _keys); + + return StorageWrapper::getRows(table, _keys); + } + + void setRow( + const std::string_view& table, const std::string_view& key, storage::Entry entry) override + { + acquireKeyLock(key); + + StorageWrapper::setRow(table, key, std::move(entry)); + } + + void importExistsKeyLocks(gsl::span keyLocks) + { + m_existsKeyLocks.clear(); + + for (auto& it : keyLocks) + { + m_existsKeyLocks.emplace(std::move(it)); + } + } + + std::vector exportKeyLocks() + { + std::vector keyLocks; + keyLocks.reserve(m_myKeyLocks.size()); + for (auto& it : m_myKeyLocks) + { + keyLocks.emplace_back(std::move(it)); + } + + m_myKeyLocks.clear(); + + return keyLocks; + } + +private: + void acquireKeyLock(const std::string_view& key) + { + /* + if (!key.compare(ACCOUNT_CODE)) + { + // ignore static system key + return; + } +*/ + if (m_existsKeyLocks.find(key) != m_existsKeyLocks.end()) + { + m_externalAcquireKeyLocks(std::string(key)); + } + + auto it = m_myKeyLocks.lower_bound(key); + if (it == m_myKeyLocks.end() || *it != key) + { + m_myKeyLocks.emplace_hint(it, key); + } + } + + std::function m_externalAcquireKeyLocks; + + std::set> m_existsKeyLocks; + std::set> m_myKeyLocks; +}; +} // namespace bcos::executor \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/executive/TransactionExecutive.cpp" "b/BFPL\345\243\271/bcos-executor/src/executive/TransactionExecutive.cpp" new file mode 100644 index 00000000..9e936092 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executive/TransactionExecutive.cpp" @@ -0,0 +1,1462 @@ +/* + * Copyright (C) 2021 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. + * + * @brief executive of vm + * @file TransactionExecutive.cpp + * @author: xingqiangbai + * @date: 2021-05-24 + */ + +#include "TransactionExecutive.h" +#include "../precompiled/BFSPrecompiled.h" +#include "../precompiled/extension/AccountPrecompiled.h" +#include "../precompiled/extension/AuthManagerPrecompiled.h" +#include "../precompiled/extension/ContractAuthMgrPrecompiled.h" +#include "../vm/DelegateHostContext.h" +#include "../vm/EVMHostInterface.h" +#include "../vm/HostContext.h" +#include "../vm/Precompiled.h" +#include "../vm/VMFactory.h" +#include "../vm/VMInstance.h" +#include "../vm/gas_meter/GasInjector.h" +#include "BlockContext.h" +#include "ExecutiveFactory.h" +#include "bcos-codec/abi/ContractABICodec.h" +#include "bcos-crypto/bcos-crypto/ChecksumAddress.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/protocol/Exceptions.h" +#include "bcos-framework/protocol/Protocol.h" +#include "bcos-protocol/TransactionStatus.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace std; +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::protocol; +using namespace bcos::codec; +using namespace bcos::precompiled; + +/// Error info for VMInstance status code. +using errinfo_evmcStatusCode = boost::error_info; + +CallParameters::UniquePtr TransactionExecutive::start(CallParameters::UniquePtr input) +{ + EXECUTIVE_LOG(TRACE) << "Execute start\t" << input->toFullString(); + + + auto& callParameters = input; + auto blockContext = m_blockContext.lock(); + if (!blockContext) + { + BOOST_THROW_EXCEPTION(BCOS_ERROR(-1, "blockContext is null")); + } + + m_storageWrapper = std::make_shared(blockContext->storage(), m_recoder); + + auto message = execute(std::move(callParameters)); + + EXECUTIVE_LOG(TRACE) << "Execute finish\t" << message->toFullString(); + + return message; +} + +CallParameters::UniquePtr TransactionExecutive::externalCall(CallParameters::UniquePtr input) +{ + if (c_fileLogLevel == LogLevel::TRACE) [[unlikely]] + { + EXECUTIVE_LOG(TRACE) << "externalCall start\t" << input->toFullString(); + } + auto newSeq = seq() + 1; + bool isCreate = input->create; + input->seq = newSeq; + input->contextID = m_contextID; + + std::string newAddress; + // if internalCreate, sometimes it will use given address, if receiveAddress is empty then give + // a new address + if (isCreate && !m_blockContext.lock()->isWasm() && std::empty(input->receiveAddress)) + { + if (input->createSalt) + { + // TODO: Add sender in this process(consider compat with ethereum) + newAddress = bcos::newEVMAddress(m_hashImpl, input->senderAddress, + bytesConstRef(input->data.data(), input->data.size()), *(input->createSalt)); + } + else + { + // TODO: Add sender in this process(consider compat with ethereum) + newAddress = bcos::newEVMAddress( + m_hashImpl, m_blockContext.lock()->number(), m_contextID, newSeq); + } + + input->receiveAddress = newAddress; + input->codeAddress = newAddress; + } + + if (input->delegateCall) + { + assert(!m_blockContext.lock()->isWasm()); + auto tableName = getContractTableName(input->codeAddress, false); + + // get codeHash in contract table + auto codeHashEntry = storage().getRow(tableName, ACCOUNT_CODE_HASH); + if (!codeHashEntry || codeHashEntry->get().empty()) + { + auto& output = input; + EXECUTIVE_LOG(DEBUG) << "Could not getCodeHash during externalCall" + << LOG_KV("codeAddress", input->codeAddress); + output->data = bytes(); + output->status = (int32_t)TransactionStatus::RevertInstruction; + output->evmStatus = EVMC_REVERT; + return std::move(output); + } + + auto codeHash = codeHashEntry->getField(0); + + // get code in code binary table + auto entry = storage().getRow(bcos::ledger::SYS_CODE_BINARY, codeHash); + if (!entry || entry->get().empty()) + { + auto& output = input; + EXECUTIVE_LOG(DEBUG) << "Could not getCode during externalCall" + << LOG_KV("codeAddress", input->codeAddress); + output->data = bytes(); + output->status = (int32_t)TransactionStatus::RevertInstruction; + output->evmStatus = EVMC_REVERT; + return std::move(output); + } + input->delegateCallCode = toBytes(entry->get()); + } + + if (input->data == bcos::protocol::GET_CODE_INPUT_BYTES) + { + EXECUTIVE_LOG(DEBUG) << "Get external code request" + << LOG_KV("codeAddress", input->codeAddress); + + auto tableName = getContractTableName(input->codeAddress, false); + + auto& output = input; + // get codeHash in contract table + auto codeHashEntry = storage().getRow(tableName, ACCOUNT_CODE_HASH); + if (!codeHashEntry || codeHashEntry->get().empty()) + { + EXECUTIVE_LOG(DEBUG) << "Could not get external code hash from local storage" + << LOG_KV("codeAddress", input->codeAddress); + output->data = bytes(); + return std::move(output); + } + + auto codeHash = codeHashEntry->getField(0); + + // get code in code binary table + auto entry = storage().getRow(bcos::ledger::SYS_CODE_BINARY, codeHash); + if (!entry || entry->get().empty()) + { + EXECUTIVE_LOG(DEBUG) << "Could not get external code from local storage" + << LOG_KV("codeAddress", input->codeAddress); + output->data = bytes(); + return std::move(output); + } + output->data = toBytes(entry->get()); + return std::move(output); + } + + auto executiveFactory = std::make_shared(m_blockContext, m_evmPrecompiled, + m_constantPrecompiled, m_builtInPrecompiled, m_gasInjector); + auto executive = executiveFactory->build(input->codeAddress, m_contextID, newSeq, false); + + auto output = executive->start(std::move(input)); + + // update seq + m_seq = executive->seq(); + + if (c_fileLogLevel == LogLevel::TRACE) [[unlikely]] + { + EXECUTIVE_LOG(TRACE) << "externalCall finish\t" << output->toFullString(); + } + return output; +} + + +CallParameters::UniquePtr TransactionExecutive::execute(CallParameters::UniquePtr callParameters) +{ + if (c_fileLogLevel <= LogLevel::TRACE) + { + EXECUTIVE_LOG(TRACE) << LOG_BADGE("Execute") << LOG_DESC("Execute begin") + << LOG_KV("callParameters", callParameters->toFullString()) + << LOG_KV("blockNumber", blockContext().lock()->number()); + } + m_storageWrapper->setRecoder(m_recoder); + + std::unique_ptr hostContext; + CallParameters::UniquePtr callResults; + if (callParameters->create) + { + std::tie(hostContext, callResults) = create(std::move(callParameters)); + } + else + { + std::tie(hostContext, callResults) = call(std::move(callParameters)); + } + + if (hostContext) + { + callResults = go(*hostContext, std::move(callResults)); + + // TODO: check if needed + hostContext->sub().refunds += + hostContext->vmSchedule().suicideRefundGas * hostContext->sub().suicides.size(); + } + if (c_fileLogLevel <= LogLevel::TRACE) + { + EXECUTIVE_LOG(TRACE) << LOG_BADGE("Execute") << LOG_DESC("Execute finished") + << LOG_KV("callResults", callResults->toFullString()) + << LOG_KV("blockNumber", blockContext().lock()->number()); + } + return callResults; +} + +std::tuple, CallParameters::UniquePtr> TransactionExecutive::call( + CallParameters::UniquePtr callParameters) +{ + auto blockContext = m_blockContext.lock(); + if (!blockContext) + { + BOOST_THROW_EXCEPTION(BCOS_ERROR(-1, "blockContext is null")); + } + + EXECUTIVE_LOG(DEBUG) << BLOCK_NUMBER(blockContext->number()) << LOG_DESC("executive call") + << LOG_KV("contract", callParameters->receiveAddress) + << LOG_KV("sender", callParameters->senderAddress) + << LOG_KV("internalCall", callParameters->internalCall) + << LOG_KV("delegateCall", callParameters->delegateCall) + << LOG_KV("codeAddress", callParameters->codeAddress); + + auto tableName = getContractTableName(callParameters->receiveAddress, blockContext->isWasm()); + // delegateCall is just about to replace code, no need to check permission beforehand + if (callParameters->delegateCall) + { + auto hostContext = make_unique( + std::move(callParameters), shared_from_this(), std::move(tableName)); + return {std::move(hostContext), nullptr}; + } + // check permission first + if (blockContext->isAuthCheck() && !checkAuth(callParameters)) + { + revert(); + return {nullptr, std::move(callParameters)}; + } + if (isPrecompiled(callParameters->receiveAddress) || callParameters->internalCall) + { + return {nullptr, callPrecompiled(std::move(callParameters))}; + } + + auto hostContext = make_unique( + std::move(callParameters), shared_from_this(), std::move(tableName)); + return {std::move(hostContext), nullptr}; +} + +CallParameters::UniquePtr TransactionExecutive::callPrecompiled( + CallParameters::UniquePtr callParameters) +{ + auto precompiledCallParams = std::make_shared(callParameters); + bytes data{}; + if (callParameters->internalCall) + { + std::string contract; + auto blockContext = m_blockContext.lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(ref(callParameters->data), contract, data); + precompiledCallParams->m_precompiledAddress = contract; + precompiledCallParams->m_input = ref(data); + } + try + { + execPrecompiled(precompiledCallParams); + + if (precompiledCallParams->m_gasLeft < 0) + { + revert(); + EXECUTIVE_LOG(INFO) << "Revert transaction: call precompiled out of gas."; + callParameters->type = CallParameters::REVERT; + callParameters->status = (int32_t)TransactionStatus::OutOfGas; + if (versionCompareTo( + blockContext().lock()->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput("Call precompiled out of gas.", *callParameters); + } + return callParameters; + } + precompiledCallParams->takeDataToCallParameter(callParameters); + } + catch (protocol::PrecompiledError const& e) + { + EXECUTIVE_LOG(INFO) << "Revert transaction: " + << "PrecompiledError" + << LOG_KV("address", precompiledCallParams->m_precompiledAddress) + << LOG_KV("error", e.what()); + // Note: considering the scenario where the contract calls the contract, the error message + // still needs to be written to the output + writeErrInfoToOutput(e.what(), *callParameters); + revert(); + callParameters->type = CallParameters::REVERT; + callParameters->status = (int32_t)TransactionStatus::PrecompiledError; + callParameters->message = e.what(); + } + catch (Exception const& e) + { + EXECUTIVE_LOG(WARNING) << "Exception" + << LOG_KV("address", precompiledCallParams->m_precompiledAddress) + << LOG_KV("error", e.what()); + writeErrInfoToOutput(e.what(), *callParameters); + revert(); + callParameters->type = CallParameters::REVERT; + callParameters->status = (int32_t)executor::toTransactionStatus(e); + callParameters->message = e.what(); + } + catch (std::exception const& e) + { + // Note: Since the information of std::exception may be affected by the version of the c++ + // library, in order to ensure compatibility, the information is not written to output + writeErrInfoToOutput("InternalPrecompiledError", *callParameters); + EXECUTIVE_LOG(WARNING) << LOG_DESC("callPrecompiled") + << LOG_KV("error", boost::diagnostic_information(e)); + revert(); + callParameters->type = CallParameters::REVERT; + callParameters->status = (int32_t)TransactionStatus::Unknown; + callParameters->message = e.what(); + } + return callParameters; +} + +std::tuple, CallParameters::UniquePtr> TransactionExecutive::create( + CallParameters::UniquePtr callParameters) +{ + auto blockContext = m_blockContext.lock(); + if (!blockContext) + { + BOOST_THROW_EXCEPTION(BCOS_ERROR(-1, "blockContext is null")); + } + auto newAddress = string(callParameters->codeAddress); + auto tableName = getContractTableName(newAddress, blockContext->isWasm()); + auto extraData = std::make_unique(CallParameters::MESSAGE); + extraData->abi = std::move(callParameters->abi); + + EXECUTIVE_LOG(DEBUG) << BLOCK_NUMBER(blockContext->number()) + << LOG_DESC("executive deploy contract") << LOG_KV("tableName", tableName) + << LOG_KV("abi len", extraData->abi.size()) + << LOG_KV("sender", callParameters->senderAddress) + << LOG_KV("internalCreate", callParameters->internalCreate); + + // check permission first + if (blockContext->isAuthCheck() && !checkAuth(callParameters)) + { + revert(); + return {nullptr, std::move(callParameters)}; + } + if (callParameters->internalCreate) + { + callParameters->abi = std::move(extraData->abi); + auto sender = callParameters->senderAddress; + auto response = internalCreate(std::move(callParameters)); + if (blockContext->isAuthCheck() && + blockContext->blockVersion() >= static_cast(BlockVersion::V3_1_VERSION)) + { + // Create auth table + creatAuthTable(tableName, response->origin, std::move(sender)); + } + return {nullptr, std::move(response)}; + } + // Create table + try + { + m_storageWrapper->createTable(tableName, STORAGE_VALUE); + EXECUTIVE_LOG(INFO) << "create contract table " << LOG_KV("table", tableName) + << LOG_KV("sender", callParameters->senderAddress); + if (blockContext->isAuthCheck()) + { + // Create auth table + creatAuthTable(tableName, callParameters->origin, callParameters->senderAddress); + } + } + catch (exception const& e) + { + // this exception will be frequent to happened in liquid + revert(); + callParameters->status = (int32_t)TransactionStatus::ContractAddressAlreadyUsed; + callParameters->type = CallParameters::REVERT; + callParameters->message = e.what(); + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput("Contract address already used.", *callParameters); + } + EXECUTIVE_LOG(INFO) << "Revert transaction: " << LOG_DESC("createTable failed") + << callParameters->message << LOG_KV("tableName", tableName); + return {nullptr, std::move(callParameters)}; + } + + if (blockContext->isWasm()) + { + // Liquid + std::tuple input; + auto codec = CodecWrapper(blockContext->hashHandler(), true); + codec.decode(ref(callParameters->data), input); + auto& [code, params] = input; + + if (!hasWasmPreamble(code)) + { + revert(); + + auto callResults = std::move(callParameters); + callResults->type = CallParameters::REVERT; + callResults->status = (int32_t)TransactionStatus::WASMValidationFailure; + callResults->message = "the code is not wasm bytecode"; + EXECUTIVE_LOG(INFO) << "Revert transaction: " << callResults->message; + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput( + "WASM bytecode invalid or use unsupported opcode.", *callResults); + } + return {nullptr, std::move(callResults)}; + } + + auto result = m_gasInjector->InjectMeter(code); + if (result.status == wasm::GasInjector::Status::Success) + { + result.byteCode->swap(code); + } + else + { + revert(); + + auto callResults = std::move(callParameters); + callResults->type = CallParameters::REVERT; + callResults->status = (int32_t)TransactionStatus::WASMValidationFailure; + callResults->message = "wasm bytecode invalid or use unsupported opcode"; + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput( + "WASM bytecode invalid or use unsupported opcode.", *callResults); + } + // use wrong wasm code + EXECUTIVE_LOG(WARNING) << "Revert transaction: " << callResults->message; + return {nullptr, std::move(callResults)}; + } + + callParameters->data.swap(code); + + extraData->data = std::move(params); + } + + auto hostContext = + std::make_unique(std::move(callParameters), shared_from_this(), tableName); + return {std::move(hostContext), std::move(extraData)}; +} + +CallParameters::UniquePtr TransactionExecutive::internalCreate( + CallParameters::UniquePtr callParameters) +{ + auto blockContext = m_blockContext.lock(); + if (!blockContext) + { + BOOST_THROW_EXCEPTION(BCOS_ERROR(-1, "blockContext is null")); + } + auto newAddress = string(callParameters->codeAddress); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + std::string tableName; + std::string codeString; + codec.decode(ref(callParameters->data), tableName, codeString); + EXECUTIVE_LOG(DEBUG) << LOG_DESC("internalCreate") << LOG_KV("newAddress", newAddress) + << LOG_KV("codeString", codeString); + + if (blockContext->isWasm()) + { + /// BFS create contract table and write metadata in parent table + if (!buildBfsPath(newAddress, callParameters->origin, newAddress, FS_TYPE_CONTRACT, + callParameters->gas)) + { + revert(); + auto buildCallResults = move(callParameters); + buildCallResults->type = CallParameters::REVERT; + buildCallResults->status = (int32_t)TransactionStatus::RevertInstruction; + buildCallResults->message = "Error occurs in build BFS dir"; + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput("Error occurs in building BFS dir.", *buildCallResults); + } + EXECUTIVE_LOG(INFO) << "Revert transaction: " << buildCallResults->message + << LOG_KV("newAddress", newAddress); + return buildCallResults; + } + /// create contract table + m_storageWrapper->createTable(newAddress, STORAGE_VALUE); + /// set code field + Entry entry = {}; + entry.importFields({codeString}); + m_storageWrapper->setRow(newAddress, ACCOUNT_CODE, std::move(entry)); + } + else + { + /// BFS create link table and write metadata in parent table + if (!buildBfsPath( + tableName, callParameters->origin, newAddress, FS_TYPE_LINK, callParameters->gas)) + { + revert(); + auto buildCallResults = move(callParameters); + buildCallResults->type = CallParameters::REVERT; + buildCallResults->status = (int32_t)TransactionStatus::RevertInstruction; + buildCallResults->message = "Error occurs in build BFS dir"; + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput("Error occurs in building BFS dir.", *buildCallResults); + } + EXECUTIVE_LOG(INFO) << "Revert transaction: " << buildCallResults->message + << LOG_KV("newAddress", newAddress); + return buildCallResults; + } + + /// create link table + auto linkTable = m_storageWrapper->createTable(tableName, STORAGE_VALUE); + + /// create code index contract + auto codeTable = getContractTableName(newAddress, false); + m_storageWrapper->createTable(codeTable, STORAGE_VALUE); + + /// set code field + Entry entry = {}; + entry.importFields({codeString}); + m_storageWrapper->setRow(codeTable, ACCOUNT_CODE, std::move(entry)); + if (!callParameters->abi.empty()) + { + Entry abiEntry = {}; + abiEntry.importFields({std::move(callParameters->abi)}); + m_storageWrapper->setRow(codeTable, ACCOUNT_ABI, std::move(abiEntry)); + } + + /// set link data + tool::BfsFileFactory::buildLink(linkTable.value(), newAddress, ""); + } + callParameters->type = CallParameters::FINISHED; + callParameters->status = (int32_t)TransactionStatus::None; + callParameters->internalCreate = false; + callParameters->create = false; + callParameters->data.clear(); + return callParameters; +} + +CallParameters::UniquePtr TransactionExecutive::go( + HostContext& hostContext, CallParameters::UniquePtr extraData) +{ + try + { + auto getEVMCMessage = [&extraData](const BlockContext& blockContext, + const HostContext& hostContext) -> evmc_message { + // the block number will be larger than 0, + // can be controlled by the programmers + if (!blockContext.isAuthCheck()) + { + assert(blockContext.number() > 0); + } + + evmc_call_kind kind = hostContext.isCreate() ? EVMC_CREATE : EVMC_CALL; + uint32_t flags = hostContext.staticCall() ? EVMC_STATIC : 0; + // this is ensured by solidity compiler + assert(flags != EVMC_STATIC || kind == EVMC_CALL); // STATIC implies a CALL. + auto leftGas = hostContext.gas(); + + evmc_message evmcMessage; + evmcMessage.kind = kind; + evmcMessage.flags = flags; + evmcMessage.depth = 0; // depth own by scheduler + evmcMessage.gas = leftGas; + evmcMessage.value = toEvmC(h256(0)); + evmcMessage.create2_salt = toEvmC(0x0_cppui256); + + if (blockContext.isWasm()) + { + evmcMessage.destination_ptr = (uint8_t*)hostContext.myAddress().data(); + evmcMessage.destination_len = hostContext.codeAddress().size(); + + evmcMessage.sender_ptr = (uint8_t*)hostContext.caller().data(); + evmcMessage.sender_len = hostContext.caller().size(); + + if (hostContext.isCreate()) + { + assert(extraData != nullptr); + evmcMessage.input_data = extraData->data.data(); + evmcMessage.input_size = extraData->data.size(); + } + else + { + evmcMessage.input_data = hostContext.data().data(); + evmcMessage.input_size = hostContext.data().size(); + } + } + else + { + evmcMessage.input_data = hostContext.data().data(); + evmcMessage.input_size = hostContext.data().size(); + + auto myAddressBytes = boost::algorithm::unhex(std::string(hostContext.myAddress())); + auto callerBytes = boost::algorithm::unhex(std::string(hostContext.caller())); + + evmcMessage.destination = toEvmC(myAddressBytes); + evmcMessage.sender = toEvmC(callerBytes); + } + + return evmcMessage; + }; + + auto blockContext = m_blockContext.lock(); + if (!blockContext) + { + BOOST_THROW_EXCEPTION(BCOS_ERROR(-1, "blockContext is null!")); + } + + if (hostContext.isCreate()) + { + auto mode = toRevision(hostContext.vmSchedule()); + auto evmcMessage = getEVMCMessage(*blockContext, hostContext); + + auto code = hostContext.data(); + auto vmKind = VMKind::evmone; + + if (blockContext->isWasm()) + { + vmKind = VMKind::BcosWasm; + } + + auto vm = VMFactory::create(vmKind); + + auto ret = vm.exec(hostContext, mode, &evmcMessage, code.data(), code.size()); + + auto callResults = hostContext.takeCallParameters(); + // clear unnecessary logs + if (blockContext->blockVersion() >= static_cast(BlockVersion::V3_1_VERSION)) + { + EXECUTIVE_LOG(TRACE) + << "logEntries" << LOG_KV("LogSize", callResults->logEntries.size()); + } + else + { + if (callResults->origin != callResults->senderAddress) + { + EXECUTIVE_LOG(TRACE) + << "clear logEntries" + << LOG_KV("beforeClearLogSize", callResults->logEntries.size()); + callResults->logEntries.clear(); + } + } + callResults = parseEVMCResult(std::move(callResults), ret); + + if (callResults->status != (int32_t)TransactionStatus::None) + { + EXECUTIVE_LOG(INFO) + << "Revert transaction: " << LOG_DESC("deploy failed due to status error") + << LOG_KV("status", callResults->status) + << LOG_KV("sender", callResults->senderAddress) + << LOG_KV("address", callResults->codeAddress); + revert(); + callResults->type = CallParameters::REVERT; + // Clear the creation flag + callResults->create = false; + return callResults; + } + + auto outputRef = ret.output(); + if (outputRef.size() > hostContext.vmSchedule().maxCodeSize) + { + revert(); + callResults->type = CallParameters::REVERT; + callResults->status = (int32_t)TransactionStatus::OutOfGas; + callResults->message = + "Code is too large: " + boost::lexical_cast(outputRef.size()) + + " limit: " + + boost::lexical_cast(hostContext.vmSchedule().maxCodeSize); + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput("Deploy code is too large.", *callResults); + } + EXECUTIVE_LOG(DEBUG) + << "Revert transaction: " << LOG_DESC("deploy failed code too large") + << LOG_KV("message", callResults->message); + return callResults; + } + + if ((int64_t)(outputRef.size() * hostContext.vmSchedule().createDataGas) > + callResults->gas) + { + if (hostContext.vmSchedule().exceptionalFailedCodeDeposit) + { + revert(); + callResults->type = CallParameters::REVERT; + callResults->status = (int32_t)TransactionStatus::OutOfGas; + callResults->message = "exceptionalFailedCodeDeposit"; + if (versionCompareTo( + blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput("Exceptional Failed Code Deposit", *callResults); + } + EXECUTIVE_LOG(INFO) + << "Revert transaction: " << LOG_DESC("deploy failed OutOfGas") + << LOG_KV("message", callResults->message); + return callResults; + } + } + + if (blockContext->isWasm()) + { + // BFS create contract table and write metadata in parent table + auto tableName = getContractTableName(hostContext.myAddress(), true); + if (!buildBfsPath(tableName, callResults->origin, hostContext.myAddress(), + FS_TYPE_CONTRACT, callResults->gas)) + { + revert(); + auto buildCallResults = move(callResults); + buildCallResults->type = CallParameters::REVERT; + buildCallResults->status = (int32_t)TransactionStatus::RevertInstruction; + buildCallResults->message = "Error occurs in build BFS dir"; + if (versionCompareTo( + blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput( + "Error occurs in building BFS dir.", *buildCallResults); + } + EXECUTIVE_LOG(DEBUG) << "Revert transaction: " << buildCallResults->message + << LOG_KV("tableName", tableName); + return buildCallResults; + } + } + + assert(extraData != nullptr); + hostContext.setCodeAndAbi(outputRef.toBytes(), extraData->abi); + if (!blockContext->isWasm()) + { + if (outputRef.empty()) + { + callResults->type = CallParameters::REVERT; + callResults->status = (int32_t)TransactionStatus::Unknown; + callResults->message = "Create contract with empty code, wrong code input."; + EXECUTIVE_LOG(WARNING) + << "Revert transaction: " << LOG_DESC("deploy failed code empty") + << LOG_KV("message", callResults->message); + // Clear the creation flag + callResults->create = false; + // Clear the data + if (versionCompareTo( + blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput( + "Create contract with empty code, invalid code input.", *callResults); + } + else if (versionCompareTo( + blockContext->blockVersion(), BlockVersion::V3_0_VERSION) <= 0) + { + callResults->data.clear(); + } + revert(); + return callResults; + } + hostContext.setCode(outputRef.toBytes()); + } + + callResults->gas -= outputRef.size() * hostContext.vmSchedule().createDataGas; + callResults->newEVMContractAddress = callResults->codeAddress; + + // Clear the creation flag + callResults->create = false; + + // Clear the data + callResults->data.clear(); + + return callResults; + } + else + { + auto codeEntry = hostContext.code(); + if (!codeEntry.has_value()) + { + revert(); + auto callResult = hostContext.takeCallParameters(); + callResult->type = CallParameters::REVERT; + callResult->status = (int32_t)TransactionStatus::CallAddressError; + callResult->message = "Error contract address."; + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput("Call address error.", *callResult); + } + EXECUTIVE_LOG(INFO) << "Revert transaction: " + << LOG_DESC("call address error, maybe address not exist") + << LOG_KV("address", callResult->codeAddress) + << LOG_KV("sender", callResult->senderAddress); + return callResult; + } + auto code = codeEntry->get(); + if (hasPrecompiledPrefix(code)) + { + return callDynamicPrecompiled(hostContext.takeCallParameters(), std::string(code)); + } + + auto vmKind = VMKind::evmone; + if (hasWasmPreamble(code)) + { + vmKind = VMKind::BcosWasm; + } + auto vm = VMFactory::create(vmKind); + + auto mode = toRevision(hostContext.vmSchedule()); + auto evmcMessage = getEVMCMessage(*blockContext, hostContext); + auto ret = vm.exec(hostContext, mode, &evmcMessage, + reinterpret_cast(code.data()), code.size()); + + auto callResults = hostContext.takeCallParameters(); + callResults = parseEVMCResult(std::move(callResults), ret); + + if (blockContext->blockVersion() >= static_cast(BlockVersion::V3_1_VERSION)) + { + EXECUTIVE_LOG(TRACE) + << "logEntries" << LOG_KV("LogSize", callResults->logEntries.size()); + return callResults; + } + if (callResults->origin != callResults->senderAddress) + { + EXECUTIVE_LOG(TRACE) + << "clear logEntries" + << LOG_KV("beforeClearLogSize", callResults->logEntries.size()); + callResults->logEntries.clear(); + } + return callResults; + } + } + catch (bcos::Error& e) + { + auto callResults = hostContext.takeCallParameters(); + if (!callResults) + { + callResults = std::make_unique(CallParameters::REVERT); + } + callResults->type = CallParameters::REVERT; + callResults->status = (int32_t)TransactionStatus::RevertInstruction; + callResults->message = e.errorMessage(); + if (versionCompareTo(blockContext().lock()->blockVersion(), BlockVersion::V3_1_VERSION) >= + 0) + { + writeErrInfoToOutput(e.errorMessage(), *callResults); + } + + revert(); + + + if (e.errorCode() == DEAD_LOCK) + { + // DEAD LOCK revert need provide sender and receiver + EXECUTOR_LOG(DEBUG) << "Revert by dead lock, sender: " << callResults->senderAddress + << " receiver: " << callResults->receiveAddress; + } + /*else if (StorageError::UnknownError <= e.errorCode() && + StorageError::TimestampMismatch <= e.errorCode()) + { + // is storage error + EXECUTOR_LOG(DEBUG) + << "Storage exception during tx execute. trigger switch(if this tx is not call). e:" + << diagnostic_information(e); + auto blockContext = m_blockContext.lock(); + blockContext->triggerSwitch(); + } + */ + else + { + EXECUTIVE_LOG(ERROR) << "BCOS Error: " << diagnostic_information(e); + } + + return callResults; + } + catch (InternalVMError const& _e) + { + EXECUTIVE_LOG(ERROR) << "Internal VM Error (" + << *boost::get_error_info(_e) << ")\n" + << diagnostic_information(_e); + exit(1); + } + catch (Exception const& _e) + { + // TODO: AUDIT: check that this can never reasonably happen. Consider what + // to do if it does. + EXECUTIVE_LOG(ERROR) << "Unexpected exception in VM. There may be a bug " + "in this implementation. " + << diagnostic_information(_e); + exit(1); + // Another solution would be to reject this transaction, but that also + // has drawbacks. Essentially, the amount of ram has to be increased here. + } + catch (std::exception& _e) + { + // TODO: AUDIT: check that this can never reasonably happen. Consider what + // to do if it does. + EXECUTIVE_LOG(ERROR) << "Unexpected std::exception in VM. Not enough RAM? " + << LOG_KV("what", _e.what()) + << LOG_KV("diagnostic", boost::diagnostic_information(_e)); + exit(1); + // Another solution would be to reject this transaction, but that also + // has drawbacks. Essentially, the amount of ram has to be increased here. + } +} + +CallParameters::UniquePtr TransactionExecutive::callDynamicPrecompiled( + CallParameters::UniquePtr callParameters, const std::string& code) +{ + auto blockContext = m_blockContext.lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + std::vector codeParameters{}; + boost::split(codeParameters, code, boost::is_any_of(",")); + if (codeParameters.size() < 3) + { + BOOST_THROW_EXCEPTION(BCOS_ERROR(-1, "CallDynamicPrecompiled error code field.")); + } + callParameters->codeAddress = callParameters->receiveAddress; + callParameters->receiveAddress = codeParameters[1]; + // for scalability, erase [PRECOMPILED_PREFIX,codeAddress], left actual parameters + codeParameters.erase(codeParameters.begin(), codeParameters.begin() + 2); + // enc([call precompiled parameters],[user call parameters]) + auto newParams = codec.encode(codeParameters, callParameters->data); + + callParameters->data = std::move(newParams); + EXECUTIVE_LOG(TRACE) << LOG_DESC("callDynamicPrecompiled") + << LOG_KV("codeAddr", callParameters->codeAddress) + << LOG_KV("recvAddr", callParameters->receiveAddress) + << LOG_KV("code", code); + auto callResult = callPrecompiled(std::move(callParameters)); + + callResult->receiveAddress = callResult->codeAddress; + return callResult; +} + +std::shared_ptr TransactionExecutive::execPrecompiled( + precompiled::PrecompiledExecResult::Ptr const& _precompiledParams) +{ + auto precompiled = getPrecompiled(_precompiledParams->m_precompiledAddress); + + if (precompiled) + { + auto execResult = precompiled->call(shared_from_this(), _precompiledParams); + return execResult; + } + [[unlikely]] EXECUTIVE_LOG(ERROR) + << LOG_DESC("[call]Can't find precompiled address") + << LOG_KV("address", _precompiledParams->m_precompiledAddress); + BOOST_THROW_EXCEPTION(PrecompiledError("can't find precompiled address.")); +} + +bool TransactionExecutive::isPrecompiled(const std::string& address) const +{ + return m_constantPrecompiled->count(address) > 0; +} + +std::shared_ptr TransactionExecutive::getPrecompiled(const std::string& address) const +{ + auto constantPrecompiled = m_constantPrecompiled->find(address); + + if (constantPrecompiled != m_constantPrecompiled->end()) + { + return constantPrecompiled->second; + } + return {}; +} + +std::pair TransactionExecutive::executeOriginPrecompiled( + const string& _a, bytesConstRef _in) const +{ + return m_evmPrecompiled->at(_a)->execute(_in); +} + +int64_t TransactionExecutive::costOfPrecompiled(const string& _a, bytesConstRef _in) const +{ + return m_evmPrecompiled->at(_a)->cost(_in).convert_to(); +} + +void TransactionExecutive::setEVMPrecompiled( + std::shared_ptr> precompiledContract) +{ + m_evmPrecompiled = precompiledContract; +} +void TransactionExecutive::setConstantPrecompiled( + const string& address, std::shared_ptr precompiled) +{ + m_constantPrecompiled->insert({address, precompiled}); +} +void TransactionExecutive::setConstantPrecompiled( + std::shared_ptr>> + _constantPrecompiled) +{ + m_constantPrecompiled = _constantPrecompiled; +} + +void TransactionExecutive::revert() +{ + auto blockContext = m_blockContext.lock(); + if (!blockContext) + { + BOOST_THROW_EXCEPTION(BCOS_ERROR(-1, "blockContext is null!")); + } + EXECUTOR_BLK_LOG(INFO, blockContext->number()) << "Revert transaction"; + + blockContext->storage()->rollback(*m_recoder); + m_recoder->clear(); +} + +CallParameters::UniquePtr TransactionExecutive::parseEVMCResult( + CallParameters::UniquePtr callResults, const Result& _result) +{ + auto blockContext = m_blockContext.lock(); + callResults->type = CallParameters::REVERT; + // FIXME: if EVMC_REJECTED, then use default vm to run. maybe wasm call evm + // need this + callResults->evmStatus = _result.status(); + auto outputRef = _result.output(); + switch (_result.status()) + { + case EVMC_SUCCESS: + { + callResults->type = CallParameters::FINISHED; + callResults->status = _result.status(); + callResults->gas = _result.gasLeft(); + if (!callResults->create) + { + callResults->data.assign(outputRef.begin(), outputRef.end()); // TODO: avoid the data + // copy + } + break; + } + case EVMC_REVERT: + { + EXECUTIVE_LOG(INFO) << LOG_DESC("EVMC_REVERT") << LOG_KV("to", callResults->receiveAddress) + << LOG_KV("gasLeft", callResults->gas); + // FIXME: Copy the output for now, but copyless version possible. + callResults->gas = _result.gasLeft(); + revert(); + // Note: both the precompiled or the application-developer may calls writeErrorInfo to the + // data when revert + callResults->data.assign(outputRef.begin(), outputRef.end()); + // m_output = owning_bytes_ref( + // bytes(outputRef.data(), outputRef.data() + outputRef.size()), 0, outputRef.size()); + callResults->status = (int32_t)TransactionStatus::RevertInstruction; + // m_excepted = TransactionStatus::RevertInstruction; + break; + } + case EVMC_OUT_OF_GAS: + { + revert(); + EXECUTIVE_LOG(INFO) << "Revert transaction: " << LOG_DESC("OutOfGas") + << LOG_KV("to", callResults->receiveAddress) + << LOG_KV("gas", _result.gasLeft()); + callResults->status = (int32_t)TransactionStatus::OutOfGas; + callResults->gas = _result.gasLeft(); + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput("Execution out of gas.", *callResults); + } + break; + } + case EVMC_FAILURE: + { + revert(); + EXECUTIVE_LOG(INFO) << "Revert transaction: " << LOG_DESC("WASMTrap") + << LOG_KV("to", callResults->receiveAddress); + callResults->status = (int32_t)TransactionStatus::WASMTrap; + callResults->gas = _result.gasLeft(); + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput("Execution failure.", *callResults); + } + break; + } + case EVMC_INVALID_INSTRUCTION: // NOTE: this could have its own exception + case EVMC_UNDEFINED_INSTRUCTION: + { + EXECUTIVE_LOG(INFO) << LOG_DESC("EVMC_INVALID_INSTRUCTION/EVMC_INVALID_INSTRUCTION") + << LOG_KV("to", callResults->receiveAddress); + callResults->status = (int32_t)TransactionStatus::BadInstruction; + revert(); + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput("Execution invalid/undefined opcode.", *callResults); + } + break; + } + case EVMC_BAD_JUMP_DESTINATION: + { + EXECUTIVE_LOG(INFO) << LOG_DESC("EVMC_BAD_JUMP_DESTINATION") + << LOG_KV("to", callResults->receiveAddress); + // m_remainGas = 0; + callResults->status = (int32_t)TransactionStatus::BadJumpDestination; + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput( + "Execution has violated the jump destination restrictions.", *callResults); + } + revert(); + break; + } + case EVMC_STACK_OVERFLOW: + { + EXECUTIVE_LOG(INFO) << LOG_DESC("EVMC_STACK_OVERFLOW") + << LOG_KV("to", callResults->receiveAddress); + // m_remainGas = 0; + callResults->status = (int32_t)TransactionStatus::OutOfStack; + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput("Execution stack overflow.", *callResults); + } + revert(); + break; + } + case EVMC_STACK_UNDERFLOW: + { + EXECUTIVE_LOG(INFO) << LOG_DESC("EVMC_STACK_UNDERFLOW") + << LOG_KV("to", callResults->receiveAddress); + callResults->status = (int32_t)TransactionStatus::StackUnderflow; + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput("Execution needs more items on EVM stack.", *callResults); + } + revert(); + break; + } + case EVMC_INVALID_MEMORY_ACCESS: + { + // m_remainGas = 0; + EXECUTIVE_LOG(INFO) << LOG_DESC("VM error, BufferOverrun") + << LOG_KV("to", callResults->receiveAddress); + callResults->status = (int32_t)TransactionStatus::StackUnderflow; + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput("Execution tried to read outside memory bounds.", *callResults); + } + revert(); + break; + } + case EVMC_STATIC_MODE_VIOLATION: + { + // m_remainGas = 0; + EXECUTIVE_LOG(INFO) << LOG_DESC("VM error, DisallowedStateChange") + << LOG_KV("to", callResults->receiveAddress); + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput( + "Execution tried to execute an operation which is restricted in static mode.", + *callResults); + } + callResults->status = (int32_t)TransactionStatus::Unknown; + revert(); + break; + } + case EVMC_CONTRACT_VALIDATION_FAILURE: + { + EXECUTIVE_LOG(INFO) + << LOG_DESC("WASM validation failed, contract hash algorithm dose not match host.") + << LOG_KV("to", callResults->receiveAddress); + callResults->status = (int32_t)TransactionStatus::WASMValidationFailure; + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput("Contract validation has failed.", *callResults); + } + revert(); + break; + } + case EVMC_ARGUMENT_OUT_OF_RANGE: + { + EXECUTIVE_LOG(INFO) << LOG_DESC("WASM Argument Out Of Range") + << LOG_KV("to", callResults->receiveAddress); + callResults->status = (int32_t)TransactionStatus::WASMArgumentOutOfRange; + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput( + "An argument to a state accessing method has a value outside of the accepted range " + "of values.", + *callResults); + } + revert(); + break; + } + case EVMC_WASM_TRAP: + case EVMC_WASM_UNREACHABLE_INSTRUCTION: + { + EXECUTIVE_LOG(INFO) << LOG_DESC("WASM Unreachable/Trap Instruction") + << LOG_KV("to", callResults->receiveAddress) + << LOG_KV("status", _result.status()); + callResults->status = (int32_t)TransactionStatus::WASMUnreachableInstruction; + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput("A WebAssembly trap has been hit during execution.", *callResults); + } + revert(); + break; + } + case EVMC_INTERNAL_ERROR: + default: + { + EXECUTIVE_LOG(WARNING) << LOG_DESC("EVMC_INTERNAL_ERROR/default revert") + << LOG_KV("to", callResults->receiveAddress) + << LOG_KV("status", _result.status()); + revert(); + if (_result.status() <= EVMC_INTERNAL_ERROR) + { + BOOST_THROW_EXCEPTION(InternalVMError{} << errinfo_evmcStatusCode(_result.status())); + } + else + { // These cases aren't really internal errors, just more specific + // error codes returned by the VM. Map all of them to OOG.m_externalCallCallback + callResults->type = CallParameters::REVERT; + callResults->status = (int32_t)TransactionStatus::OutOfGas; + } + } + } + + return callResults; +} + +void TransactionExecutive::creatAuthTable( + std::string_view _tableName, std::string_view _origin, std::string_view _sender) +{ + // Create the access table + // /sys/ not create + if (_tableName.starts_with(USER_SYS_PREFIX) || + getContractTableName(_sender, false).starts_with(USER_SYS_PREFIX)) + { + return; + } + auto authTableName = std::string(_tableName).append(CONTRACT_SUFFIX); + std::string admin; + if (_sender != _origin) + { + // if contract external create contract, then inheritance admin, always be origin + admin = std::string(_origin); + } + else + { + admin = std::string(_sender); + } + EXECUTIVE_LOG(DEBUG) << "creatAuthTable in deploy" << LOG_KV("tableName", _tableName) + << LOG_KV("origin", _origin) << LOG_KV("sender", _sender) + << LOG_KV("admin", admin); + auto table = m_storageWrapper->createTable(authTableName, STORAGE_VALUE); + + if (table) + { + tool::BfsFileFactory::buildAuth(table.value(), admin); + } +} + +bool TransactionExecutive::buildBfsPath(std::string_view _absoluteDir, std::string_view _origin, + std::string_view _sender, std::string_view _type, int64_t gasLeft) +{ + /// this method only write bfs metadata, not create final table + /// you should create locally, after external call successfully + EXECUTIVE_LOG(TRACE) << LOG_DESC("build BFS metadata") << LOG_KV("absoluteDir", _absoluteDir) + << LOG_KV("type", _type); + auto response = + externalTouchNewFile(shared_from_this(), _origin, _sender, _absoluteDir, _type, gasLeft); + return response == (int)precompiled::CODE_SUCCESS; +} + +bool TransactionExecutive::checkAuth(const CallParameters::UniquePtr& callParameters) +{ + auto blockContext = m_blockContext.lock(); + // check account first + if (blockContext->blockVersion() >= (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION) + { + uint8_t accountStatus = checkAccountAvailable(callParameters); + if (accountStatus == AccountStatus::freeze) + { + writeErrInfoToOutput("Account is frozen.", *callParameters); + callParameters->status = (int32_t)TransactionStatus::AccountFrozen; + callParameters->type = CallParameters::REVERT; + callParameters->message = "Account's status is abnormal"; + callParameters->create = false; + EXECUTIVE_LOG(INFO) << "Revert transaction: " << callParameters->message + << LOG_KV("origin", callParameters->origin); + return false; + } + else if (accountStatus == AccountStatus::abolish) + { + writeErrInfoToOutput("Account is abolished.", *callParameters); + callParameters->status = (int32_t)TransactionStatus::AccountAbolished; + callParameters->type = CallParameters::REVERT; + callParameters->message = "Account's status is abnormal"; + callParameters->create = false; + EXECUTIVE_LOG(INFO) << "Revert transaction: " << callParameters->message + << LOG_KV("origin", callParameters->origin); + return false; + } + } + if (callParameters->create) + { + // if create contract, then + // check exec auth + if (!checkExecAuth(callParameters)) + { + auto newAddress = string(callParameters->codeAddress); + callParameters->status = (int32_t)TransactionStatus::PermissionDenied; + callParameters->type = CallParameters::REVERT; + callParameters->message = "Create permission denied"; + callParameters->create = false; + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput("Create permission denied.", *callParameters); + } + EXECUTIVE_LOG(INFO) << "Revert transaction: " << callParameters->message + << LOG_KV("newAddress", newAddress) + << LOG_KV("origin", callParameters->origin); + return false; + } + } + else + { + // if internal call, then not check auth + if (callParameters->internalCall) + { + return true; + } + auto tableName = + getContractTableName(callParameters->receiveAddress, blockContext->isWasm()); + // if call contract, then + // check contract available + // check exec auth + if (!checkContractAvailable(callParameters)) + { + callParameters->status = (int32_t)TransactionStatus::ContractFrozen; + callParameters->type = CallParameters::REVERT; + callParameters->message = "Contract is frozen"; + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput("Contract is frozen.", *callParameters); + } + else if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_0_VERSION) <= + 0) + { + callParameters->data.clear(); + } + EXECUTIVE_LOG(INFO) << "Revert transaction: " << callParameters->message + << LOG_KV("tableName", tableName) + << LOG_KV("origin", callParameters->origin); + return false; + } + if (!checkExecAuth(callParameters)) + { + callParameters->status = (int32_t)TransactionStatus::PermissionDenied; + callParameters->type = CallParameters::REVERT; + callParameters->message = "Call permission denied"; + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + writeErrInfoToOutput("Call permission denied.", *callParameters); + } + else if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_0_VERSION) <= + 0) + { + callParameters->data.clear(); + } + EXECUTIVE_LOG(INFO) << "Revert transaction: " << callParameters->message + << LOG_KV("tableName", tableName) + << LOG_KV("origin", callParameters->origin); + return false; + } + } + return true; +} + +bool TransactionExecutive::checkExecAuth(const CallParameters::UniquePtr& callParameters) +{ + // static call does not have 'origin', so return true for now + // precompiled return true by default + if (callParameters->staticCall || isPrecompiled(callParameters->receiveAddress)) + { + return true; + } + auto blockContext = m_blockContext.lock(); + const auto* authMgrAddress = + blockContext->isWasm() ? precompiled::AUTH_MANAGER_NAME : precompiled::AUTH_MANAGER_ADDRESS; + auto contractAuthPrecompiled = dynamic_pointer_cast( + m_constantPrecompiled->at(AUTH_CONTRACT_MGR_ADDRESS)); + std::string address = callParameters->origin; + auto path = callParameters->receiveAddress; + EXECUTIVE_LOG(TRACE) << "check auth" << LOG_KV("codeAddress", path) + << LOG_KV("isCreate", callParameters->create) + << LOG_KV("originAddress", address); + bool result = true; + if (callParameters->create) + { + /// external call authMgrAddress to check deploy auth + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + auto input = blockContext->isWasm() ? + codec.encodeWithSig("hasDeployAuth(string)", address) : + codec.encodeWithSig("hasDeployAuth(address)", Address(address)); + auto response = externalRequest(shared_from_this(), ref(input), callParameters->origin, + callParameters->receiveAddress, authMgrAddress, false, false, callParameters->gas); + codec.decode(ref(response->data), result); + } + else + { + bytesRef func = ref(callParameters->data).getCroppedData(0, 4); + result = contractAuthPrecompiled->checkMethodAuth(shared_from_this(), path, func, address); + } + EXECUTIVE_LOG(TRACE) << "check auth finished" << LOG_KV("codeAddress", path) + << LOG_KV("result", result); + return result; +} + +bool TransactionExecutive::checkContractAvailable(const CallParameters::UniquePtr& callParameters) +{ + // precompiled always available + if (isPrecompiled(callParameters->receiveAddress) || + c_systemTxsAddress.contains(callParameters->receiveAddress)) + { + return true; + } + auto blockContext = m_blockContext.lock(); + auto contractAuthPrecompiled = dynamic_pointer_cast( + m_constantPrecompiled->at(AUTH_CONTRACT_MGR_ADDRESS)); + auto path = callParameters->receiveAddress; + + return contractAuthPrecompiled->getContractStatus(shared_from_this(), std::move(path)) != 0; +} + +uint8_t TransactionExecutive::checkAccountAvailable(const CallParameters::UniquePtr& callParameters) +{ + if (callParameters->staticCall || callParameters->origin != callParameters->senderAddress || + callParameters->internalCall) + { + // static call sender and origin will be empty + // contract calls, pass through + return 0; + } + auto blockContext = m_blockContext.lock(); + AccountPrecompiled::Ptr accountPrecompiled = + dynamic_pointer_cast( + m_constantPrecompiled->at(ACCOUNT_ADDRESS)); + + return accountPrecompiled->getAccountStatus(callParameters->origin, shared_from_this()); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/executive/TransactionExecutive.h" "b/BFPL\345\243\271/bcos-executor/src/executive/TransactionExecutive.h" new file mode 100644 index 00000000..f5cc8424 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executive/TransactionExecutive.h" @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2021 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. + * + * @brief executive of vm + * @file TransactionExecutive.h + * @author: xingqiangbai + * @date: 2021-05-24 + */ + +#pragma once + +#include "../Common.h" +#include "../executor/TransactionExecutor.h" +#include "BlockContext.h" +#include "SyncStorageWrapper.h" +#include "bcos-executor/src/precompiled/common/PrecompiledResult.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/executor/PrecompiledTypeDef.h" +#include "bcos-framework/protocol/BlockHeader.h" +#include "bcos-framework/protocol/Transaction.h" +#include "bcos-protocol/TransactionStatus.h" +#include +#include +#include +#include + +namespace bcos +{ +namespace executor +{ +class Result; + +} // namespace executor +namespace precompiled +{ +struct PrecompiledExecResult; +} + +namespace executor +{ +class HostContext; + +class TransactionExecutive : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + TransactionExecutive(std::weak_ptr blockContext, std::string contractAddress, + int64_t contextID, int64_t seq, std::shared_ptr& gasInjector) + : m_blockContext(std::move(blockContext)), + m_contractAddress(std::move(contractAddress)), + m_contextID(contextID), + m_seq(seq), + m_gasInjector(gasInjector) + { + m_recoder = std::make_shared(); + m_hashImpl = m_blockContext.lock()->hashHandler(); + } + + TransactionExecutive(TransactionExecutive const&) = delete; + TransactionExecutive& operator=(TransactionExecutive) = delete; + TransactionExecutive(TransactionExecutive&&) = delete; + TransactionExecutive& operator=(TransactionExecutive&&) = delete; + + virtual ~TransactionExecutive() = default; + + virtual CallParameters::UniquePtr start(CallParameters::UniquePtr input); + + // External call request + virtual CallParameters::UniquePtr externalCall(CallParameters::UniquePtr input); + + auto& storage() + { + assert(m_storageWrapper); + return *m_storageWrapper; + } + + std::weak_ptr blockContext() { return m_blockContext; } + + int64_t contextID() const { return m_contextID; } + int64_t seq() const { return m_seq; } + + std::string_view contractAddress() { return m_contractAddress; } + + CallParameters::UniquePtr execute( + CallParameters::UniquePtr callParameters); // execute parameters in + // current corouitine + + bool isPrecompiled(const std::string& _address) const; + + std::shared_ptr getPrecompiled(const std::string& _address) const; + + void setConstantPrecompiled( + const std::string& _address, std::shared_ptr precompiled); + + void setBuiltInPrecompiled(std::shared_ptr> _builtInPrecompiled) + { + m_builtInPrecompiled = _builtInPrecompiled; + } + + inline bool isBuiltInPrecompiled(const std::string& _a) const + { + std::stringstream prefix; + prefix << std::setfill('0') << std::setw(36) << "0"; + return _a.starts_with(prefix.str()) && m_builtInPrecompiled->contains(_a); + } + + inline bool isEthereumPrecompiled(const std::string& _a) const + { + std::stringstream prefix; + prefix << std::setfill('0') << std::setw(39) << "0"; + return m_evmPrecompiled != nullptr && _a.starts_with(prefix.str()) && + m_evmPrecompiled->contains(_a); + } + + std::pair executeOriginPrecompiled(const std::string& _a, bytesConstRef _in) const; + + int64_t costOfPrecompiled(const std::string& _a, bytesConstRef _in) const; + + void setEVMPrecompiled( + std::shared_ptr>> + precompiledContract); + + void setConstantPrecompiled( + std::shared_ptr>> + _constantPrecompiled); + + std::shared_ptr execPrecompiled( + precompiled::PrecompiledExecResult::Ptr const& _precompiledParams); + + + VMSchedule const& vmSchedule() const { return m_blockContext.lock()->vmSchedule(); } + + bool isWasm() { return m_blockContext.lock()->isWasm(); } + +protected: + std::tuple, CallParameters::UniquePtr> call( + CallParameters::UniquePtr callParameters); + CallParameters::UniquePtr callPrecompiled(CallParameters::UniquePtr callParameters); + std::tuple, CallParameters::UniquePtr> create( + CallParameters::UniquePtr callParameters); + CallParameters::UniquePtr internalCreate(CallParameters::UniquePtr callParameters); + CallParameters::UniquePtr go( + HostContext& hostContext, CallParameters::UniquePtr extraData = nullptr); + CallParameters::UniquePtr callDynamicPrecompiled( + CallParameters::UniquePtr callParameters, const std::string& code); + + void revert(); + + CallParameters::UniquePtr parseEVMCResult( + CallParameters::UniquePtr callResults, const Result& _result); + + void writeErrInfoToOutput(std::string const& errInfo, CallParameters& _callParameters) + { + bcos::codec::abi::ContractABICodec abi(m_hashImpl); + auto codecOutput = abi.abiIn("Error(string)", errInfo); + _callParameters.data = std::move(codecOutput); + } + + inline std::string getContractTableName(const std::string_view& _address, bool isWasm = false) + { + auto blockContext = m_blockContext.lock(); + + if (blockContext->isAuthCheck()) + { + if (_address.starts_with(precompiled::SYS_ADDRESS_PREFIX)) + { + return std::string(USER_SYS_PREFIX).append(_address); + } + } + + + std::string formatAddress(_address); + if (isWasm) + { + if (_address.find(USER_TABLE_PREFIX) == 0) + { + return formatAddress; + } + formatAddress = (_address[0] == '/') ? formatAddress.substr(1) : formatAddress; + } + + return std::string(USER_APPS_PREFIX).append(formatAddress); + } + + bool checkAuth(const CallParameters::UniquePtr& callParameters); + bool checkExecAuth(const CallParameters::UniquePtr& callParameters); + bool checkContractAvailable(const CallParameters::UniquePtr& callParameters); + uint8_t checkAccountAvailable(const CallParameters::UniquePtr& callParameters); + + void creatAuthTable( + std::string_view _tableName, std::string_view _origin, std::string_view _sender); + + bool buildBfsPath(std::string_view _absoluteDir, std::string_view _origin, + std::string_view _sender, std::string_view _type, int64_t gasLeft); + + std::weak_ptr m_blockContext; ///< Information on the runtime environment. + std::shared_ptr>> + m_constantPrecompiled; + std::shared_ptr>> + m_evmPrecompiled; + std::shared_ptr> m_builtInPrecompiled; + + std::string m_contractAddress; + int64_t m_contextID; + int64_t m_seq; + crypto::Hash::Ptr m_hashImpl; + + std::shared_ptr m_gasInjector = nullptr; + + bcos::storage::Recoder::Ptr m_recoder; + std::shared_ptr m_storageWrapper; +}; + +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/executor/ExecuteOutputs.h" "b/BFPL\345\243\271/bcos-executor/src/executor/ExecuteOutputs.h" new file mode 100644 index 00000000..0552f988 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executor/ExecuteOutputs.h" @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2021 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. + * + * @brief block level context + * @file ExecuteOutputs.h + * @author: jimmyshi + * @date: 2022-04-02 + */ + +#pragma once + +#include "bcos-executor/src/Common.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include + + +namespace bcos +{ +namespace executor +{ +class ExecuteOutputs +{ +public: + using Ptr = std::shared_ptr; + + void add(protocol::ExecutionMessage::UniquePtr result) + { + auto contextID = result->contextID(); + m_contextID2Result.emplace(contextID, std::move(result)); + } + std::vector dumpAndClear() + { + auto results = std::vector(); + + std::set> contextIDs; + + for (auto it = m_contextID2Result.begin(); it != m_contextID2Result.end(); it++) + { + // we assume that context id is in sequence increasing + contextIDs.insert(it->first); + } + + for (auto contextID : contextIDs) + { + auto& result = m_contextID2Result.at(contextID); + // std::cout << " dump " << result->contextID() << " | " << result->seq() << " | " + // << protocol::ExecutionMessage::getTypeName(result->type()) << std::endl; + results.push_back(std::move(result)); + } + + m_contextID2Result.clear(); + return results; + } + + void clear() { m_contextID2Result.clear(); } + +private: + tbb::concurrent_unordered_map + m_contextID2Result; +}; +} // namespace executor +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/executor/SwitchExecutorManager.h" "b/BFPL\345\243\271/bcos-executor/src/executor/SwitchExecutorManager.h" new file mode 100644 index 00000000..515c6436 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executor/SwitchExecutorManager.h" @@ -0,0 +1,667 @@ +#pragma once + +#include "TransactionExecutorFactory.h" +#include "bcos-executor/src/executor/TransactionExecutor.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include +#include +#include +#include + +namespace bcos::executor +{ +class SwitchExecutorManager : public executor::ParallelTransactionExecutorInterface +{ +public: + using Ptr = std::shared_ptr; + + const int64_t INIT_SCHEDULER_TERM_ID = 0; + const int64_t STOPPED_TERM_ID = -1; + + SwitchExecutorManager(bcos::executor::TransactionExecutorFactory::Ptr factory) + : m_pool("exec", std::thread::hardware_concurrency()), m_factory(factory) + { + factory->registerNeedSwitchEvent([this]() { selfAsyncRefreshExecutor(); }); + + // refreshExecutor(INIT_SCHEDULER_TERM_ID + 1); + } + + ~SwitchExecutorManager() noexcept override {} + + void refreshExecutor(int64_t schedulerTermId) + { + // refresh when receive larger schedulerTermId + if (schedulerTermId > m_schedulerTermId) + { + bcos::executor::TransactionExecutor::Ptr oldExecutor; + { + WriteGuard l(m_mutex); + if (m_schedulerTermId != schedulerTermId) + { + oldExecutor = m_executor; + + m_executor = m_factory->build(); // may throw exception + + m_schedulerTermId = schedulerTermId; + + EXECUTOR_LOG(DEBUG) << LOG_BADGE("Switch") + << "ExecutorSwitch: Build new executor instance with " + << LOG_KV("schedulerTermId", schedulerTermId); + } + } + if (oldExecutor) + { + oldExecutor->stop(); + } + } + } + + void selfAsyncRefreshExecutor() + { + auto toTermId = m_schedulerTermId + 1; + auto toSeq = m_seq + 1; + m_pool.enqueue([toTermId, toSeq, this]() { + if (toTermId == m_schedulerTermId) + { + // already switch + return; + } + + try + { + refreshExecutor(toTermId); + } + catch (Exception const& _e) + { + EXECUTOR_LOG(ERROR) + << LOG_DESC("selfAsyncRefreshExecutor exception. Re-push to task pool") + << LOG_KV("toTermId", toTermId) << LOG_KV("currentTermId", m_schedulerTermId) + << diagnostic_information(_e); + + selfAsyncRefreshExecutor(); + return; + } + + + if (toTermId == m_schedulerTermId) + { + // if switch success, set seq to trigger scheduler switch + m_seq = toSeq; + EXECUTOR_LOG(DEBUG) + << LOG_BADGE("Switch") << "ExecutorSwitch: selfAsyncRefreshExecutor success" + << LOG_KV("schedulerTermId", m_schedulerTermId) << LOG_KV("seq", m_seq); + } + }); + } + + void triggerSwitch() { selfAsyncRefreshExecutor(); } + + bool hasStopped() { return m_schedulerTermId == STOPPED_TERM_ID; } + + bool hasNextBlockHeaderDone() { return m_schedulerTermId != INIT_SCHEDULER_TERM_ID; } + + void nextBlockHeader(int64_t schedulerTermId, + const bcos::protocol::BlockHeader::ConstPtr& blockHeader, + std::function callback) override + { + if (hasStopped()) + { + std::string message = "nextBlockHeader: executor has been stopped"; + EXECUTOR_LOG(DEBUG) << message; + callback(BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::STOPPED, message)); + return; + } + + if (schedulerTermId < m_schedulerTermId) + { + // Call from an outdated scheduler instance + // Just return without callback, because run callback may trigger a new switch, thus + // some message will be outdated and trigger switch again and again. + EXECUTOR_LOG(INFO) + << LOG_DESC("nextBlockHeader: not refreshExecutor for invalid schedulerTermId") + << LOG_KV("termId", schedulerTermId) << LOG_KV("currentTermId", m_schedulerTermId); + callback(BCOS_ERROR_UNIQUE_PTR( + bcos::executor::ExecuteError::STOPPED, "old executor has been stopped")); + return; + } + + try + { + refreshExecutor(schedulerTermId); + } + catch (Exception const& _e) + { + EXECUTOR_LOG(ERROR) << LOG_DESC("nextBlockHeader: not refreshExecutor for exception") + << LOG_KV("toTermId", schedulerTermId) + << LOG_KV("currentTermId", m_schedulerTermId) + << diagnostic_information(_e); + callback(BCOS_ERROR_UNIQUE_PTR( + bcos::executor::ExecuteError::INTERNAL_ERROR, "refreshExecutor exception")); + return; + } + + m_pool.enqueue([executor = m_executor, schedulerTermId, + blockHeader = std::move(blockHeader), callback = std::move(callback)]() { + // create a holder + auto _holdExecutorCallback = [executorHolder = executor, callback = + std::move(callback)]( + bcos::Error::UniquePtr error) { + EXECUTOR_LOG(TRACE) + << "Release executor holder" << LOG_KV("ptr count", executorHolder.use_count()); + callback(std::move(error)); + }; + + // execute the function + executor->nextBlockHeader( + schedulerTermId, blockHeader, std::move(_holdExecutorCallback)); + }); + } + + void executeTransaction(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + { + if (hasStopped()) + { + std::string message = "executeTransaction: executor has been stopped"; + EXECUTOR_LOG(DEBUG) << message; + callback( + BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::STOPPED, message), nullptr); + return; + } + + if (!hasNextBlockHeaderDone()) + { + callback(BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR, + "Executor has just inited, need to switch"), + nullptr); + return; + } + + m_pool.enqueue( + [executor = m_executor, inputRaw = input.release(), callback = std::move(callback)] { + // create a holder + auto _holdExecutorCallback = + [executorHolder = executor, callback = std::move(callback)]( + bcos::Error::UniquePtr error, + bcos::protocol::ExecutionMessage::UniquePtr output) { + EXECUTOR_LOG(TRACE) << "Release executor holder" + << LOG_KV("ptr count", executorHolder.use_count()); + callback(std::move(error), std::move(output)); + }; + + // execute the function + executor->executeTransaction(bcos::protocol::ExecutionMessage::UniquePtr(inputRaw), + std::move(_holdExecutorCallback)); + }); + } + + void call(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + { + if (hasStopped()) + { + std::string message = "call: executor has been stopped"; + EXECUTOR_LOG(DEBUG) << message; + callback( + BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::STOPPED, message), nullptr); + return; + } + + auto currentExecutor = getAndNewExecutorIfNotExists(); + + // create a holder + auto _holdExecutorCallback = + [executorHolder = currentExecutor, callback = std::move(callback)]( + bcos::Error::UniquePtr error, bcos::protocol::ExecutionMessage::UniquePtr output) { + EXECUTOR_LOG(TRACE) + << "Release executor holder" << LOG_KV("ptr count", executorHolder.use_count()); + callback(std::move(error), std::move(output)); + }; + + // execute the function + currentExecutor->call(bcos::protocol::ExecutionMessage::UniquePtr(input.release()), + std::move(_holdExecutorCallback)); + } + + void executeTransactions(std::string contractAddress, + gsl::span inputs, + std::function)> + callback) override + { + if (hasStopped()) + { + std::string message = "executeTransactions: executor has been stopped"; + EXECUTOR_LOG(DEBUG) << message; + callback(BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::STOPPED, message), {}); + return; + } + + // Note: copy the inputs here in case of inputs has been released + if (!hasNextBlockHeaderDone()) + { + callback(BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR, + "Executor has just inited, need to switch"), + {}); + return; + } + + auto inputsVec = + std::make_shared>(); + for (auto i = 0u; i < inputs.size(); i++) + { + inputsVec->emplace_back(std::move(inputs[i])); + } + m_pool.enqueue([executor = m_executor, contractAddress = std::move(contractAddress), + inputsVec, callback = std::move(callback)] { + // create a holder + auto _holdExecutorCallback = + [executorHolder = executor, callback = std::move(callback)]( + bcos::Error::UniquePtr error, + std::vector outputs) { + EXECUTOR_LOG(TRACE) << "Release executor holder" + << LOG_KV("ptr count", executorHolder.use_count()); + callback(std::move(error), std::move(outputs)); + }; + + // execute the function + executor->executeTransactions( + contractAddress, *inputsVec, std::move(_holdExecutorCallback)); + }); + } + + void dmcExecuteTransactions(std::string contractAddress, + gsl::span inputs, + std::function)> + callback) override + { + if (hasStopped()) + { + std::string message = "dmcExecuteTransactions: executor has been stopped"; + EXECUTOR_LOG(DEBUG) << message; + callback(BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::STOPPED, message), {}); + return; + } + + if (!hasNextBlockHeaderDone()) + { + callback(BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR, + "Executor has just inited, need to switch"), + {}); + return; + } + + // Note: copy the inputs here in case of inputs has been released + auto inputsVec = + std::make_shared>(); + for (auto i = 0u; i < inputs.size(); i++) + { + inputsVec->emplace_back(std::move(inputs[i])); + } + m_pool.enqueue([executor = m_executor, contractAddress = std::move(contractAddress), + inputsVec, callback = std::move(callback)] { + // create a holder + auto _holdExecutorCallback = + [executorHolder = executor, callback = std::move(callback)]( + bcos::Error::UniquePtr error, + std::vector outputs) { + EXECUTOR_LOG(TRACE) << "Release executor holder" + << LOG_KV("ptr count", executorHolder.use_count()); + callback(std::move(error), std::move(outputs)); + }; + + // execute the function + executor->dmcExecuteTransactions( + contractAddress, *inputsVec, std::move(_holdExecutorCallback)); + }); + } + + void dagExecuteTransactions(gsl::span inputs, + std::function)> + callback) override + { + if (hasStopped()) + { + std::string message = "dagExecuteTransactions: executor has been stopped"; + EXECUTOR_LOG(DEBUG) << message; + callback(BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::STOPPED, message), {}); + return; + } + + if (!hasNextBlockHeaderDone()) + { + callback(BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR, + "Executor has just inited, need to switch"), + {}); + return; + } + + auto inputsVec = + std::make_shared>(); + for (auto i = 0u; i < inputs.size(); i++) + { + inputsVec->emplace_back(std::move(inputs[i])); + } + m_pool.enqueue([executor = m_executor, inputsVec, callback = std::move(callback)] { + // create a holder + auto _holdExecutorCallback = + [executorHolder = executor, callback = std::move(callback)]( + bcos::Error::UniquePtr error, + std::vector outputs) { + EXECUTOR_LOG(TRACE) << "Release executor holder" + << LOG_KV("ptr count", executorHolder.use_count()); + callback(std::move(error), std::move(outputs)); + }; + + // execute the function + executor->dagExecuteTransactions(*inputsVec, std::move(_holdExecutorCallback)); + }); + } + + void dmcCall(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + { + if (hasStopped()) + { + std::string message = "dmcCall: executor has been stopped"; + EXECUTOR_LOG(DEBUG) << message; + callback( + BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::STOPPED, message), nullptr); + return; + } + + auto currentExecutor = getAndNewExecutorIfNotExists(); + + // create a holder + auto _holdExecutorCallback = + [executorHolder = currentExecutor, callback = std::move(callback)]( + bcos::Error::UniquePtr error, bcos::protocol::ExecutionMessage::UniquePtr output) { + EXECUTOR_LOG(TRACE) + << "Release executor holder" << LOG_KV("ptr count", executorHolder.use_count()); + callback(std::move(error), std::move(output)); + }; + + // execute the function + currentExecutor->dmcCall(bcos::protocol::ExecutionMessage::UniquePtr(input.release()), + std::move(_holdExecutorCallback)); + } + + void getHash(bcos::protocol::BlockNumber number, + std::function callback) override + { + if (hasStopped()) + { + std::string message = "getHash: executor has been stopped"; + EXECUTOR_LOG(DEBUG) << message; + callback(BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::STOPPED, message), {}); + return; + } + + auto currentExecutor = getAndNewExecutorIfNotExists(); + + m_pool.enqueue([executor = currentExecutor, number, callback = std::move(callback)] { + // create a holder + auto _holdExecutorCallback = + [executorHolder = executor, callback = std::move(callback)]( + bcos::Error::UniquePtr error, crypto::HashType hashType) { + EXECUTOR_LOG(TRACE) << "Release executor holder" + << LOG_KV("ptr count", executorHolder.use_count()); + callback(std::move(error), std::move(hashType)); + }; + + // execute the function + executor->getHash(number, std::move(_holdExecutorCallback)); + }); + } + + /* ----- XA Transaction interface Start ----- */ + + // Write data to storage uncommitted + void prepare(const bcos::protocol::TwoPCParams& params, + std::function callback) override + { + if (hasStopped()) + { + std::string message = "prepare: executor has been stopped"; + EXECUTOR_LOG(DEBUG) << message; + callback(BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::STOPPED, message)); + return; + } + + if (!hasNextBlockHeaderDone()) + { + callback(BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR, + "Executor has just inited, need to switch")); + return; + } + + m_pool.enqueue([executor = m_executor, params = bcos::protocol::TwoPCParams(params), + callback = std::move(callback)] { + // create a holder + auto _holdExecutorCallback = [executorHolder = executor, callback = + std::move(callback)]( + bcos::Error::Ptr error) { + EXECUTOR_LOG(TRACE) + << "Release executor holder" << LOG_KV("ptr count", executorHolder.use_count()); + callback(std::move(error)); + }; + + // execute the function + executor->prepare(params, std::move(_holdExecutorCallback)); + }); + } + + // Commit uncommitted data + void commit(const bcos::protocol::TwoPCParams& params, + std::function callback) override + { + if (hasStopped()) + { + std::string message = "commit: executor has been stopped"; + EXECUTOR_LOG(DEBUG) << message; + callback(BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::STOPPED, message)); + return; + } + + if (!hasNextBlockHeaderDone()) + { + callback(BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR, + "Executor has just inited, need to switch")); + return; + } + + m_pool.enqueue([executor = m_executor, params = bcos::protocol::TwoPCParams(params), + callback = std::move(callback)] { + // create a holder + auto _holdExecutorCallback = [executorHolder = executor, callback = + std::move(callback)]( + bcos::Error::Ptr error) { + EXECUTOR_LOG(TRACE) + << "Release executor holder" << LOG_KV("ptr count", executorHolder.use_count()); + callback(std::move(error)); + }; + + // execute the function + executor->commit(params, std::move(_holdExecutorCallback)); + }); + } + + // Rollback the changes + void rollback(const bcos::protocol::TwoPCParams& params, + std::function callback) override + { + if (hasStopped()) + { + std::string message = "rollback: executor has been stopped"; + EXECUTOR_LOG(DEBUG) << message; + callback(BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::STOPPED, message)); + return; + } + + if (!hasNextBlockHeaderDone()) + { + callback(BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR, + "Executor has just inited, need to switch")); + return; + } + + m_pool.enqueue([executor = m_executor, params = bcos::protocol::TwoPCParams(params), + callback = std::move(callback)] { + // create a holder + auto _holdExecutorCallback = [executorHolder = executor, callback = + std::move(callback)]( + bcos::Error::Ptr error) { + EXECUTOR_LOG(TRACE) + << "Release executor holder" << LOG_KV("ptr count", executorHolder.use_count()); + callback(std::move(error)); + }; + + // execute the function + executor->rollback(params, std::move(_holdExecutorCallback)); + }); + } + + /* ----- XA Transaction interface End ----- */ + + // drop all status + void reset(std::function callback) override + { + if (hasStopped()) + { + std::string message = "reset: executor has been stopped"; + EXECUTOR_LOG(DEBUG) << message; + callback(BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::STOPPED, message)); + return; + } + + auto currentExecutor = getAndNewExecutorIfNotExists(); + + m_pool.enqueue([executor = currentExecutor, callback = std::move(callback)] { + // create a holder + auto _holdExecutorCallback = [executorHolder = executor, callback = + std::move(callback)]( + bcos::Error::Ptr error) { + EXECUTOR_LOG(TRACE) + << "Release executor holder" << LOG_KV("ptr count", executorHolder.use_count()); + callback(std::move(error)); + }; + + // execute the function + executor->reset(std::move(_holdExecutorCallback)); + }); + } + void getCode(std::string_view contract, + std::function callback) override + { + if (hasStopped()) + { + std::string message = "getCode: executor has been stopped"; + EXECUTOR_LOG(DEBUG) << message; + callback(BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::STOPPED, message), {}); + return; + } + + auto currentExecutor = getAndNewExecutorIfNotExists(); + + m_pool.enqueue([executor = currentExecutor, contract = std::string(contract), + callback = std::move(callback)] { + // create a holder + auto _holdExecutorCallback = [executorHolder = executor, callback = + std::move(callback)]( + bcos::Error::Ptr error, bcos::bytes bytes) { + EXECUTOR_LOG(TRACE) + << "Release executor holder" << LOG_KV("ptr count", executorHolder.use_count()); + callback(std::move(error), std::move(bytes)); + }; + + // execute the function + executor->getCode(contract, std::move(_holdExecutorCallback)); + }); + } + + void getABI(std::string_view contract, + std::function callback) override + { + if (hasStopped()) + { + std::string message = "getABI: executor has been stopped"; + EXECUTOR_LOG(DEBUG) << message; + callback(BCOS_ERROR_UNIQUE_PTR(bcos::executor::ExecuteError::STOPPED, message), {}); + return; + } + + auto currentExecutor = getAndNewExecutorIfNotExists(); + + m_pool.enqueue([executor = currentExecutor, contract = std::string(contract), + callback = std::move(callback)] { + // create a holder + auto _holdExecutorCallback = [executorHolder = executor, callback = + std::move(callback)]( + bcos::Error::Ptr error, std::string str) { + EXECUTOR_LOG(TRACE) + << "Release executor holder" << LOG_KV("ptr count", executorHolder.use_count()); + callback(std::move(error), std::move(str)); + }; + + // execute the function + executor->getABI(contract, std::move(_holdExecutorCallback)); + }); + } + + void stop() override + { + EXECUTOR_LOG(INFO) << "Try to stop SwitchExecutorManager"; + m_schedulerTermId = STOPPED_TERM_ID; + + auto executorUseCount = 0; + bcos::executor::TransactionExecutor::Ptr executor = getCurrentExecutor(); + m_executor = nullptr; + + if (executor) + { + executor->stop(); + } + executorUseCount = executor.use_count(); + + // waiting for stopped + while (executorUseCount > 1) + { + if (executor != nullptr) + { + executorUseCount = executor.use_count(); + } + EXECUTOR_LOG(DEBUG) << "Executor is stopping.. " + << LOG_KV("unfinishedTaskNum", executorUseCount - 1); + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + } + EXECUTOR_LOG(INFO) << "Executor has stopped."; + } + + bcos::executor::TransactionExecutor::Ptr getCurrentExecutor() { return m_executor; } + + bcos::executor::TransactionExecutor::Ptr getAndNewExecutorIfNotExists() + { + if (!m_executor) + { + refreshExecutor(INIT_SCHEDULER_TERM_ID + 1); + } + + auto executor = m_executor; + return executor; + } + +private: + bcos::ThreadPool m_pool; + bcos::executor::TransactionExecutor::Ptr m_executor; + int64_t m_schedulerTermId = INIT_SCHEDULER_TERM_ID; + + mutable bcos::SharedMutex m_mutex; + + bcos::executor::TransactionExecutorFactory::Ptr m_factory; +}; +} // namespace bcos::executor \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/executor/TransactionExecutor.cpp" "b/BFPL\345\243\271/bcos-executor/src/executor/TransactionExecutor.cpp" new file mode 100644 index 00000000..5e0b0422 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executor/TransactionExecutor.cpp" @@ -0,0 +1,2726 @@ +/* + * Copyright (C) 2021 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. + * + * @brief TransactionExecutor + * @file TransactionExecutor.cpp + * @author: xingqiangbai + * @date: 2021-09-01 + */ + +#include "TransactionExecutor.h" +#include "../Common.h" +#include "../dag/Abi.h" +#include "../dag/ClockCache.h" +#include "../dag/CriticalFields.h" +#include "../dag/ScaleUtils.h" +#include "../dag/TxDAG2.h" +#include "../executive/BlockContext.h" +#include "../executive/ExecutiveFactory.h" +#include "../executive/ExecutiveSerialFlow.h" +#include "../executive/ExecutiveStackFlow.h" +#include "../executive/TransactionExecutive.h" +#include "../precompiled/BFSPrecompiled.h" +#include "../precompiled/ConsensusPrecompiled.h" +#include "../precompiled/CryptoPrecompiled.h" +#include "../precompiled/KVTablePrecompiled.h" +#include "../precompiled/SystemConfigPrecompiled.h" +#include "../precompiled/TableManagerPrecompiled.h" +#include "../precompiled/TablePrecompiled.h" +#include "../precompiled/extension/AccountManagerPrecompiled.h" +#include "../precompiled/extension/AccountPrecompiled.h" +#include "../precompiled/extension/AuthManagerPrecompiled.h" +#include "../precompiled/extension/ContractAuthMgrPrecompiled.h" +#include "../precompiled/extension/DagTransferPrecompiled.h" +#include "../precompiled/extension/GroupSigPrecompiled.h" +#include "../precompiled/extension/RingSigPrecompiled.h" +#include "../precompiled/extension/UserPrecompiled.h" +#include "../precompiled/extension/ZkpPrecompiled.h" +#include "../vm/Precompiled.h" +#include "../vm/gas_meter/GasInjector.h" +#include "ExecuteOutputs.h" +#include "bcos-codec/abi/ContractABIType.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include "bcos-executor/src/precompiled/common/PrecompiledResult.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include "bcos-framework/dispatcher/SchedulerInterface.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/executor/PrecompiledTypeDef.h" +#include "bcos-framework/ledger/LedgerTypeDef.h" +#include "bcos-framework/protocol/ProtocolTypeDef.h" +#include "bcos-framework/protocol/TransactionReceipt.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-framework/storage/Table.h" +#include "bcos-table/src/KeyPageStorage.h" +#include "bcos-table/src/StateStorage.h" +#include "bcos-tool/BfsFileFactory.h" +#include "tbb/flow_graph.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace bcos; +using namespace std; +using namespace bcos::executor; +using namespace bcos::executor::critical; +using namespace bcos::wasm; +using namespace bcos::protocol; +using namespace bcos::storage; +using namespace bcos::precompiled; +using namespace tbb::flow; + +crypto::Hash::Ptr GlobalHashImpl::g_hashImpl; + + +TransactionExecutor::TransactionExecutor(bcos::ledger::LedgerInterface::Ptr ledger, + txpool::TxPoolInterface::Ptr txpool, storage::MergeableStorageInterface::Ptr cachedStorage, + storage::TransactionalStorageInterface::Ptr backendStorage, + protocol::ExecutionMessageFactory::Ptr executionMessageFactory, + bcos::crypto::Hash::Ptr hashImpl, bool isWasm, bool isAuthCheck, size_t keyPageSize = 0, + std::shared_ptr>> keyPageIgnoreTables = nullptr, + std::string name = "default-executor-name") + : m_name(std::move(name)), + m_ledger(ledger), + m_txpool(std::move(txpool)), + m_cachedStorage(std::move(cachedStorage)), + m_backendStorage(std::move(backendStorage)), + m_executionMessageFactory(std::move(executionMessageFactory)), + m_hashImpl(std::move(hashImpl)), + m_isAuthCheck(isAuthCheck), + m_isWasm(isWasm), + m_keyPageSize(keyPageSize), + m_keyPageIgnoreTables(std::move(keyPageIgnoreTables)), + m_ledgerCache(std::make_shared(ledger)) +{ + assert(m_backendStorage); + m_ledgerCache->fetchCompatibilityVersion(); + m_blockVersion = m_ledgerCache->ledgerConfig()->compatibilityVersion(); + GlobalHashImpl::g_hashImpl = m_hashImpl; + m_abiCache = make_shared>(32); + m_gasInjector = std::make_shared(wasm::GetInstructionTable()); + + m_threadPool = std::make_shared(name, std::thread::hardware_concurrency()); + if (m_isWasm) + { + initWasmEnvironment(); + } + else + { + initEvmEnvironment(); + } + assert(!m_constantPrecompiled->empty()); + assert(m_builtInPrecompiled); + start(); +} + +void TransactionExecutor::initEvmEnvironment() +{ + m_schedule = FiscoBcosScheduleV4; + + auto fillZero = [](int _num) -> std::string { + std::stringstream stream; + stream << std::setfill('0') << std::setw(40) << std::hex << _num; + return stream.str(); + }; + m_precompiledContract = + std::make_shared>>(); + m_builtInPrecompiled = std::make_shared>(); + m_constantPrecompiled = + std::make_shared>>(); + + m_precompiledContract->insert(std::make_pair(fillZero(1), + make_shared(3000, 0, PrecompiledRegistrar::executor("ecrecover")))); + m_precompiledContract->insert(std::make_pair(fillZero(2), + make_shared(60, 12, PrecompiledRegistrar::executor("sha256")))); + m_precompiledContract->insert(std::make_pair(fillZero(3), + make_shared(600, 120, PrecompiledRegistrar::executor("ripemd160")))); + m_precompiledContract->insert(std::make_pair(fillZero(4), + make_shared(15, 3, PrecompiledRegistrar::executor("identity")))); + m_precompiledContract->insert( + {fillZero(5), make_shared(PrecompiledRegistrar::pricer("modexp"), + PrecompiledRegistrar::executor("modexp"))}); + m_precompiledContract->insert( + {fillZero(6), make_shared( + 150, 0, PrecompiledRegistrar::executor("alt_bn128_G1_add"))}); + m_precompiledContract->insert( + {fillZero(7), make_shared( + 6000, 0, PrecompiledRegistrar::executor("alt_bn128_G1_mul"))}); + m_precompiledContract->insert({fillZero(8), + make_shared(PrecompiledRegistrar::pricer("alt_bn128_pairing_product"), + PrecompiledRegistrar::executor("alt_bn128_pairing_product"))}); + m_precompiledContract->insert({fillZero(9), + make_shared(PrecompiledRegistrar::pricer("blake2_compression"), + PrecompiledRegistrar::executor("blake2_compression"))}); + assert(m_precompiledContract); + + auto sysConfig = std::make_shared(); + auto consensusPrecompiled = std::make_shared(m_hashImpl); + auto tableManagerPrecompiled = + std::make_shared(m_hashImpl); + auto kvTablePrecompiled = std::make_shared(m_hashImpl); + auto tablePrecompiled = std::make_shared(m_hashImpl); + + // in EVM + m_constantPrecompiled->insert({SYS_CONFIG_ADDRESS, sysConfig}); + m_constantPrecompiled->insert({CONSENSUS_ADDRESS, consensusPrecompiled}); + m_constantPrecompiled->insert({TABLE_MANAGER_ADDRESS, tableManagerPrecompiled}); + m_constantPrecompiled->insert({KV_TABLE_ADDRESS, kvTablePrecompiled}); + m_constantPrecompiled->insert({TABLE_ADDRESS, tablePrecompiled}); + m_constantPrecompiled->insert( + {DAG_TRANSFER_ADDRESS, std::make_shared(m_hashImpl)}); + m_constantPrecompiled->insert( + {CRYPTO_ADDRESS, std::make_shared(m_hashImpl)}); + m_constantPrecompiled->insert( + {BFS_ADDRESS, std::make_shared(m_hashImpl)}); + /// auth precompiled + if (m_isAuthCheck) + { + m_constantPrecompiled->insert({AUTH_MANAGER_ADDRESS, + std::make_shared(m_hashImpl)}); + m_constantPrecompiled->insert({AUTH_CONTRACT_MGR_ADDRESS, + std::make_shared(m_hashImpl)}); + } + m_constantPrecompiled->insert( + {GROUP_SIG_ADDRESS, std::make_shared(m_hashImpl)}); + m_constantPrecompiled->insert( + {RING_SIG_ADDRESS, std::make_shared(m_hashImpl)}); + + set builtIn = {CRYPTO_ADDRESS, GROUP_SIG_ADDRESS, RING_SIG_ADDRESS}; + m_builtInPrecompiled = make_shared>(builtIn); + + // create the zkp-precompiled + m_constantPrecompiled->insert( + {DISCRETE_ZKP_ADDRESS, std::make_shared(m_hashImpl)}); +} + +void TransactionExecutor::initWasmEnvironment() +{ + m_schedule = BCOSWASMSchedule; + + m_builtInPrecompiled = std::make_shared>(); + m_constantPrecompiled = + std::make_shared>>(); + + auto sysConfig = std::make_shared(); + auto consensusPrecompiled = std::make_shared(m_hashImpl); + auto tableManagerPrecompiled = + std::make_shared(m_hashImpl); + auto kvTablePrecompiled = std::make_shared(m_hashImpl); + auto tablePrecompiled = std::make_shared(m_hashImpl); + + // in WASM + m_constantPrecompiled->insert({SYS_CONFIG_NAME, sysConfig}); + m_constantPrecompiled->insert({CONSENSUS_NAME, consensusPrecompiled}); + m_constantPrecompiled->insert({TABLE_MANAGER_NAME, tableManagerPrecompiled}); + m_constantPrecompiled->insert({KV_TABLE_NAME, kvTablePrecompiled}); + m_constantPrecompiled->insert({TABLE_NAME, tablePrecompiled}); + m_constantPrecompiled->insert( + {DAG_TRANSFER_NAME, std::make_shared(m_hashImpl)}); + m_constantPrecompiled->insert({CRYPTO_NAME, std::make_shared(m_hashImpl)}); + m_constantPrecompiled->insert( + {BFS_NAME, std::make_shared(m_hashImpl)}); + + m_constantPrecompiled->insert( + {GROUP_SIG_NAME, std::make_shared(m_hashImpl)}); + m_constantPrecompiled->insert( + {RING_SIG_NAME, std::make_shared(m_hashImpl)}); + if (m_isAuthCheck) + { + m_constantPrecompiled->insert( + {AUTH_MANAGER_NAME, std::make_shared(m_hashImpl)}); + m_constantPrecompiled->insert({AUTH_CONTRACT_MGR_ADDRESS, + std::make_shared(m_hashImpl)}); + } + + set builtIn = {CRYPTO_NAME, GROUP_SIG_NAME, RING_SIG_NAME}; + m_builtInPrecompiled = make_shared>(builtIn); + // create the zkp-precompiled + m_constantPrecompiled->insert( + {DISCRETE_ZKP_NAME, std::make_shared(m_hashImpl)}); +} + +void TransactionExecutor::initTestPrecompiled(storage::StorageInterface::Ptr storage) +{ + CpuHeavyPrecompiled::registerPrecompiled(m_constantPrecompiled, m_hashImpl); + SmallBankPrecompiled::registerPrecompiled(storage, m_constantPrecompiled, m_hashImpl); + DagTransferPrecompiled::createDagTable(storage); +} + +BlockContext::Ptr TransactionExecutor::createBlockContext( + const protocol::BlockHeader::ConstPtr& currentHeader, + storage::StateStorageInterface::Ptr storage) +{ + BlockContext::Ptr context = make_shared(storage, m_ledgerCache, m_hashImpl, + currentHeader, m_schedule, m_isWasm, m_isAuthCheck, m_keyPageIgnoreTables); + + if (f_onNeedSwitchEvent) + { + context->registerNeedSwitchEvent(f_onNeedSwitchEvent); + } + + return context; +} + +std::shared_ptr TransactionExecutor::createBlockContextForCall( + bcos::protocol::BlockNumber blockNumber, h256 blockHash, uint64_t timestamp, + int32_t blockVersion, storage::StateStorageInterface::Ptr storage) +{ + BlockContext::Ptr context = make_shared(storage, m_ledgerCache, m_hashImpl, + blockNumber, blockHash, timestamp, blockVersion, m_schedule, m_isWasm, m_isAuthCheck); + + return context; +} + + +void TransactionExecutor::nextBlockHeader(int64_t schedulerTermId, + const bcos::protocol::BlockHeader::ConstPtr& blockHeader, + std::function callback) +{ + m_schedulerTermId = schedulerTermId; + + if (!m_isRunning) + { + EXECUTOR_NAME_LOG(ERROR) << "TransactionExecutor is not running"; + callback( + BCOS_ERROR_UNIQUE_PTR(ExecuteError::STOPPED, "TransactionExecutor is not running")); + return; + } + + try + { + EXECUTOR_NAME_LOG(INFO) << BLOCK_NUMBER(blockHeader->number()) + << "NextBlockHeader request: " + << LOG_KV("blockVersion", blockHeader->version()) + << LOG_KV("schedulerTermId", schedulerTermId) + << LOG_KV("parentHash", + blockHeader->number() > 0 ? + blockHeader->parentInfo()[0].blockHash.abridged() : + "null"); + m_blockVersion = blockHeader->version(); + { + std::unique_lock lock(m_stateStoragesMutex); + bcos::storage::StateStorageInterface::Ptr stateStorage; + if (m_stateStorages.empty()) + { + if (m_cachedStorage) + { + stateStorage = createStateStorage(m_cachedStorage); + } + else + { + stateStorage = createStateStorage(m_backendStorage); + } + + // check storage block Number + auto storageBlockNumber = getBlockNumberInStorage(); + EXECUTOR_NAME_LOG(DEBUG) << "NextBlockHeader, executor load from backend storage, " + "check storage blockNumber" + << LOG_KV("storageBlockNumber", storageBlockNumber) + << LOG_KV("requestBlockNumber", blockHeader->number()); + // Note: skip check for sys contract deploy + if (blockHeader->number() - storageBlockNumber != 1 && + !isSysContractDeploy(blockHeader->number())) + { + auto fmt = boost::format( + "[%s] Block number mismatch in storage! request: %d, current in " + "storage: %d, trigger switch") % + m_name % blockHeader->number() % storageBlockNumber; + EXECUTOR_NAME_LOG(ERROR) << fmt; + // to trigger switch operation + callback( + BCOS_ERROR_UNIQUE_PTR(ExecuteError::SCHEDULER_TERM_ID_ERROR, fmt.str())); + return; + } + } + else + { + auto& prev = m_stateStorages.back(); + + // check number continuity + if (blockHeader->number() - prev.number != 1) + { + // m_stateStorages.pop_back(); + auto fmt = boost::format( + "[%s] Block number mismatch! request: %d, current: %d. trigger " + "switch.") % + m_name % blockHeader->number() % prev.number; + EXECUTOR_NAME_LOG(ERROR) << fmt; + callback( + BCOS_ERROR_UNIQUE_PTR(ExecuteError::SCHEDULER_TERM_ID_ERROR, fmt.str())); + return; + } + + prev.storage->setReadOnly(true); + stateStorage = createStateStorage(prev.storage); + } + + if (m_blockContext) + { + m_blockContext->clear(); + } + + // set last commit state storage to blockContext, to auth read last block state + m_blockContext = createBlockContext(blockHeader, stateStorage); + m_stateStorages.emplace_back(blockHeader->number(), stateStorage); + + if (blockHeader->number() == 0) + { + initTestPrecompiled(stateStorage); + } + } + + // cache parentHash + if (blockHeader->number() > 0) + { + m_ledgerCache->setBlockNumber2Hash( + blockHeader->number() - 1, blockHeader->parentInfo()[0].blockHash); + } + + EXECUTOR_NAME_LOG(INFO) << BLOCK_NUMBER(blockHeader->number()) << "NextBlockHeader success" + << LOG_KV("number", blockHeader->number()) + << LOG_KV("parentHash", + blockHeader->number() > 0 ? + blockHeader->parentInfo()[0].blockHash.abridged() : + "null"); + callback(nullptr); + } + catch (std::exception& e) + { + EXECUTOR_NAME_LOG(ERROR) << "NextBlockHeader error: " << boost::diagnostic_information(e); + + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(-1, "nextBlockHeader unknown error", e)); + } +} + +void TransactionExecutor::dmcExecuteTransaction(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) +{ + EXECUTOR_NAME_LOG(TRACE) << "ExecuteTransaction request" + << LOG_KV("ContextID", input->contextID()) + << LOG_KV("seq", input->seq()) << LOG_KV("message type", input->type()) + << LOG_KV("to", input->to()) << LOG_KV("create", input->create()); + + if (!m_isRunning) + { + EXECUTOR_NAME_LOG(ERROR) << "TransactionExecutor is not running"; + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::STOPPED, "TransactionExecutor is not running"), + nullptr); + return; + } + + if (!m_blockContext) + { + callback(BCOS_ERROR_UNIQUE_PTR( + ExecuteError::EXECUTE_ERROR, "Execute failed with empty blockContext!"), + nullptr); + return; + } + + asyncExecute(m_blockContext, std::move(input), true, + [this, callback = std::move(callback)]( + Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + if (error) + { + std::string errorMessage = "ExecuteTransaction failed: " + error->errorMessage(); + EXECUTOR_NAME_LOG(ERROR) << errorMessage; + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(-1, errorMessage, *error), nullptr); + return; + } + + callback(std::move(error), std::move(result)); + }); +} + +void TransactionExecutor::dmcCall(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) +{ + EXECUTOR_NAME_LOG(TRACE) << "dmcCall request" << LOG_KV("ContextID", input->contextID()) + << LOG_KV("seq", input->seq()) << LOG_KV("Message type", input->type()) + << LOG_KV("To", input->to()) << LOG_KV("Create", input->create()); + + if (!m_isRunning) + { + EXECUTOR_NAME_LOG(ERROR) << "TransactionExecutor is not running"; + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::STOPPED, "TransactionExecutor is not running"), + nullptr); + return; + } + + BlockContext::Ptr blockContext; + switch (input->type()) + { + case protocol::ExecutionMessage::MESSAGE: + { + auto blockHeader = m_lastCommittedBlockHeader; + if (!blockHeader) + { + auto message = "dmcCall could not get current block header, contextID: " + + boost::lexical_cast(input->contextID()) + + " seq: " + boost::lexical_cast(input->seq()); + EXECUTOR_NAME_LOG(ERROR) << message; + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::CALL_ERROR, message), nullptr); + return; + } + + storage::StorageInterface::Ptr prev; + + if (m_cachedStorage) + { + prev = m_cachedStorage; + } + else + { + prev = m_backendStorage; + } + + // Create a temp storage + auto storage = createStateStorage(std::move(prev), true); + + // Create a temp block context + blockContext = createBlockContextForCall( + blockHeader->number() + 1, h256(), utcTime(), m_blockVersion, std::move(storage)); + + auto inserted = m_calledContext->emplace( + std::tuple{input->contextID(), input->seq()}, CallState{blockContext}); + + if (!inserted) + { + auto message = "dmcCall error, contextID: " + + boost::lexical_cast(input->contextID()) + + " seq: " + boost::lexical_cast(input->seq()) + " exists"; + EXECUTOR_NAME_LOG(ERROR) << message; + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::CALL_ERROR, message), nullptr); + return; + } + + break; + } + case protocol::ExecutionMessage::FINISHED: + case protocol::ExecutionMessage::REVERT: + { + tbb::concurrent_hash_map, CallState, HashCombine>::accessor it; + m_calledContext->find(it, std::tuple{input->contextID(), input->seq()}); + + if (it.empty()) + { + auto message = "dmcCall error, contextID: " + + boost::lexical_cast(input->contextID()) + + " seq: " + boost::lexical_cast(input->seq()) + + " does not exists"; + EXECUTOR_NAME_LOG(ERROR) << message; + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::CALL_ERROR, message), nullptr); + return; + } + + blockContext = it->second.blockContext; + + break; + } + default: + { + auto message = + "dmcCall error, Unknown call type: " + boost::lexical_cast(input->type()); + EXECUTOR_NAME_LOG(ERROR) << message; + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::CALL_ERROR, message), nullptr); + return; + + break; + } + } + + asyncExecute(std::move(blockContext), std::move(input), true, + [this, callback = std::move(callback)]( + Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + if (error) + { + std::string errorMessage = "Call failed: " + error->errorMessage(); + EXECUTOR_NAME_LOG(WARNING) + << LOG_DESC("Call error") << LOG_KV("code", error->errorCode()) + << LOG_KV("msg", error->errorMessage()); + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(-1, errorMessage, *error), nullptr); + return; + } + + if (result->type() == protocol::ExecutionMessage::FINISHED || + result->type() == protocol::ExecutionMessage::REVERT) + { + auto erased = + m_calledContext->erase(std::tuple{result->contextID(), result->seq()}); + + if (!erased) + { + auto message = "dmcCall error, erase contextID: " + + boost::lexical_cast(result->contextID()) + + " seq: " + boost::lexical_cast(result->seq()) + + " does not exists"; + EXECUTOR_NAME_LOG(ERROR) << message; + + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::CALL_ERROR, message), nullptr); + return; + } + } + + EXECUTOR_NAME_LOG(TRACE) + << "dmcCall success" << LOG_KV("staticCall", result->staticCall()) + << LOG_KV("from", result->from()) << LOG_KV("to", result->to()) + << LOG_KV("context", result->contextID()); + callback(std::move(error), std::move(result)); + }); +} + +void TransactionExecutor::executeTransaction(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) +{ + EXECUTOR_NAME_LOG(TRACE) << "ExecuteTransaction request" + << LOG_KV("ContextID", input->contextID()) + << LOG_KV("seq", input->seq()) << LOG_KV("message type", input->type()) + << LOG_KV("to", input->to()) << LOG_KV("create", input->create()); + + if (!m_isRunning) + { + EXECUTOR_NAME_LOG(ERROR) << "TransactionExecutor is not running"; + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::STOPPED, "TransactionExecutor is not running"), + nullptr); + return; + } + + if (!m_blockContext) + { + callback(BCOS_ERROR_UNIQUE_PTR( + ExecuteError::EXECUTE_ERROR, "Execute failed with empty blockContext!"), + nullptr); + return; + } + + asyncExecute(m_blockContext, std::move(input), false, + [this, callback = std::move(callback)]( + Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + if (error) + { + std::string errorMessage = "ExecuteTransaction failed: " + error->errorMessage(); + EXECUTOR_NAME_LOG(ERROR) << errorMessage; + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(-1, errorMessage, *error), nullptr); + return; + } + + callback(std::move(error), std::move(result)); + }); +} + +void TransactionExecutor::call(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) +{ + EXECUTOR_NAME_LOG(TRACE) << "Call request" << LOG_KV("ContextID", input->contextID()) + << LOG_KV("seq", input->seq()) << LOG_KV("Message type", input->type()) + << LOG_KV("To", input->to()) << LOG_KV("Create", input->create()); + + if (!m_isRunning) + { + EXECUTOR_NAME_LOG(ERROR) << "TransactionExecutor is not running"; + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::STOPPED, "TransactionExecutor is not running"), + nullptr); + return; + } + + BlockContext::Ptr blockContext; + switch (input->type()) + { + case protocol::ExecutionMessage::MESSAGE: + { + auto blockHeader = m_lastCommittedBlockHeader; + if (!blockHeader) + { + auto message = "call could not get current block header, contextID: " + + boost::lexical_cast(input->contextID()) + + " seq: " + boost::lexical_cast(input->seq()); + EXECUTOR_NAME_LOG(ERROR) << message; + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::CALL_ERROR, message), nullptr); + return; + } + + storage::StorageInterface::Ptr prev; + + if (m_cachedStorage) + { + prev = m_cachedStorage; + } + else + { + prev = m_backendStorage; + } + + // Create a temp storage + auto storage = createStateStorage(std::move(prev), true); + + // Create a temp block context + blockContext = createBlockContextForCall( + blockHeader->number() + 1, h256(), utcTime(), m_blockVersion, std::move(storage)); + + auto inserted = m_calledContext->emplace( + std::tuple{input->contextID(), input->seq()}, CallState{blockContext}); + + if (!inserted) + { + auto message = + "Call error, contextID: " + boost::lexical_cast(input->contextID()) + + " seq: " + boost::lexical_cast(input->seq()) + " exists"; + EXECUTOR_NAME_LOG(ERROR) << message; + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::CALL_ERROR, message), nullptr); + return; + } + + break; + } + case protocol::ExecutionMessage::FINISHED: + case protocol::ExecutionMessage::REVERT: + { + tbb::concurrent_hash_map, CallState, HashCombine>::accessor it; + m_calledContext->find(it, std::tuple{input->contextID(), input->seq()}); + + if (it.empty()) + { + auto message = + "Call error, contextID: " + boost::lexical_cast(input->contextID()) + + " seq: " + boost::lexical_cast(input->seq()) + " does not exists"; + EXECUTOR_NAME_LOG(ERROR) << message; + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::CALL_ERROR, message), nullptr); + return; + } + + blockContext = it->second.blockContext; + + break; + } + default: + { + auto message = + "Call error, Unknown call type: " + boost::lexical_cast(input->type()); + EXECUTOR_NAME_LOG(ERROR) << message; + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::CALL_ERROR, message), nullptr); + return; + + break; + } + } + + asyncExecute(std::move(blockContext), std::move(input), false, + [this, callback = std::move(callback)]( + Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + if (error) + { + std::string errorMessage = "Call failed: " + error->errorMessage(); + EXECUTOR_NAME_LOG(WARNING) + << LOG_DESC("Call error") << LOG_KV("code", error->errorCode()) + << LOG_KV("msg", error->errorMessage()); + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(-1, errorMessage, *error), nullptr); + return; + } + + if (result->type() == protocol::ExecutionMessage::FINISHED || + result->type() == protocol::ExecutionMessage::REVERT) + { + auto erased = + m_calledContext->erase(std::tuple{result->contextID(), result->seq()}); + + if (!erased) + { + auto message = "Call error, erase contextID: " + + boost::lexical_cast(result->contextID()) + + " seq: " + boost::lexical_cast(result->seq()) + + " does not exists"; + EXECUTOR_NAME_LOG(ERROR) << message; + + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::CALL_ERROR, message), nullptr); + return; + } + } + + EXECUTOR_NAME_LOG(TRACE) << "Call success" << LOG_KV("staticCall", result->staticCall()) + << LOG_KV("from", result->from()) << LOG_KV("to", result->to()) + << LOG_KV("context", result->contextID()); + callback(std::move(error), std::move(result)); + }); +} + +void TransactionExecutor::executeTransactionsInternal(std::string contractAddress, + gsl::span inputs, bool useCoroutine, + std::function)> + _callback) +{ + if (!m_blockContext) + { + _callback(BCOS_ERROR_UNIQUE_PTR( + ExecuteError::EXECUTE_ERROR, "Execute failed with empty blockContext!"), + {}); + return; + } + + auto requestTimestamp = utcTime(); + auto txNum = inputs.size(); + auto blockNumber = m_blockContext->number(); + EXECUTOR_NAME_LOG(INFO) << BLOCK_NUMBER(blockNumber) << "executeTransactionsInternal request" + << LOG_KV("useCoroutine", useCoroutine) << LOG_KV("txNum", txNum) + << LOG_KV("contractAddress", contractAddress) + << LOG_KV("requestTimestamp", requestTimestamp); + + auto callback = [this, useCoroutine, _callback = _callback, requestTimestamp, blockNumber, + txNum, contractAddress](bcos::Error::UniquePtr error, + std::vector outputs) { + EXECUTOR_NAME_LOG(DEBUG) << BLOCK_NUMBER(blockNumber) + << "executeTransactionsInternal response" + << LOG_KV("useCoroutine", useCoroutine) << LOG_KV("txNum", txNum) + << LOG_KV("outputNum", outputs.size()) + << LOG_KV("contractAddress", contractAddress) + << LOG_KV("requestTimestamp", requestTimestamp) + << LOG_KV("msg", error ? error->errorMessage() : "ok") + << LOG_KV("timeCost", utcTime() - requestTimestamp); + _callback(std::move(error), std::move(outputs)); + }; + + if (!m_isRunning) + { + EXECUTOR_NAME_LOG(ERROR) << "TransactionExecutor is not running"; + callback( + BCOS_ERROR_UNIQUE_PTR(ExecuteError::STOPPED, "TransactionExecutor is not running"), {}); + return; + } + + auto recoredT = utcTime(); + auto startT = utcTime(); + // for fill block + auto txHashes = make_shared(); + std::vector indexes; + auto fillInputs = std::make_shared>(); + + // final result + auto callParametersList = + std::make_shared>(inputs.size()); + + bool isStaticCall = true; + + std::mutex writeMutex; + tbb::parallel_for(tbb::blocked_range(0U, inputs.size()), [&, this](auto const& range) { + for (auto i = range.begin(); i < range.end(); ++i) + { + auto& params = inputs[i]; + + if (!params->staticCall()) + { + isStaticCall = false; + } + + switch (params->type()) + { + case ExecutionMessage::TXHASH: + { + std::unique_lock lock(writeMutex); + txHashes->emplace_back(params->transactionHash()); + indexes.emplace_back(i); + fillInputs->emplace_back(std::move(params)); + + break; + } + case ExecutionMessage::MESSAGE: + case bcos::protocol::ExecutionMessage::REVERT: + case bcos::protocol::ExecutionMessage::FINISHED: + case bcos::protocol::ExecutionMessage::KEY_LOCK: + { + callParametersList->at(i) = createCallParameters(*params, params->staticCall()); + break; + } + default: + { + auto message = + (boost::format("Unsupported message type: %d") % params->type()).str(); + EXECUTOR_NAME_LOG(ERROR) + << BLOCK_NUMBER(blockNumber) << "DAG Execute error, " << message; + // callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::DAG_ERROR, message), {}); + break; + } + } + } + }); + + if (isStaticCall) + { + EXECUTOR_NAME_LOG(FATAL) + << "executeTransactionsInternal() only handle non static transactions but " + "receive static call"; + assert(false); + } + + auto prepareT = utcTime() - startT; + startT = utcTime(); + + if (!txHashes->empty()) + { + m_txpool->asyncFillBlock(txHashes, + [this, startT, useCoroutine, contractAddress, indexes = std::move(indexes), + fillInputs = std::move(fillInputs), + callParametersList = std::move(callParametersList), callback = std::move(callback), + txHashes, + blockNumber](Error::Ptr error, protocol::TransactionsPtr transactions) mutable { + auto fillTxsT = (utcTime() - startT); + + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR( + ExecuteError::STOPPED, "TransactionExecutor is not running"), + {}); + return; + } + + + if (error) + { + auto errorMessage = "[" + m_name + "] asyncFillBlock failed"; + EXECUTOR_NAME_LOG(ERROR) + << BLOCK_NUMBER(blockNumber) << errorMessage << error->errorMessage(); + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR( + ExecuteError::DAG_ERROR, errorMessage, *error), + {}); + return; + } + auto recordT = utcTime(); + tbb::parallel_for(tbb::blocked_range(0U, transactions->size()), + [this, &transactions, &callParametersList, &indexes, &fillInputs]( + auto const& range) { + for (auto i = range.begin(); i < range.end(); ++i) + { + assert(transactions->at(i)); + callParametersList->at(indexes[i]) = + createCallParameters(*fillInputs->at(i), *transactions->at(i)); + } + }); + + auto prepareT = utcTime() - recordT; + recordT = utcTime(); + + auto executiveFlow = + getExecutiveFlow(m_blockContext, contractAddress, useCoroutine); + executiveFlow->submit(callParametersList); + + asyncExecuteExecutiveFlow(executiveFlow, + [callback = std::move(callback)](bcos::Error::UniquePtr&& error, + std::vector&& messages) { + callback(std::move(error), std::move(messages)); + }); + + EXECUTOR_NAME_LOG(INFO) + << BLOCK_NUMBER(blockNumber) + << LOG_DESC("executeTransactionsInternal after fillblock") + << LOG_KV("useCoroutine", useCoroutine) << LOG_KV("fillTxsT", fillTxsT) + << LOG_KV("prepareT", prepareT) << LOG_KV("dmcT", (utcTime() - recordT)); + }); + } + else + { + auto executiveFlow = getExecutiveFlow(m_blockContext, contractAddress, useCoroutine); + executiveFlow->submit(callParametersList); + + asyncExecuteExecutiveFlow(executiveFlow, + [callback = std::move(callback)](bcos::Error::UniquePtr&& error, + std::vector&& messages) { + callback(std::move(error), std::move(messages)); + }); + } + + EXECUTOR_NAME_LOG(TRACE) << LOG_DESC("executeTransactionsInternal") + << LOG_KV("useCoroutine", useCoroutine) << LOG_KV("prepareT", prepareT) + << LOG_KV("total", (utcTime() - recoredT)); +} + +void TransactionExecutor::executeTransactions(std::string contractAddress, + gsl::span inputs, + std::function)> + _callback) +{ + executeTransactionsInternal( + std::move(contractAddress), std::move(inputs), false, std::move(_callback)); +} + +void TransactionExecutor::dmcExecuteTransactions(std::string contractAddress, + gsl::span inputs, + std::function)> + _callback) +{ + executeTransactionsInternal( + std::move(contractAddress), std::move(inputs), true, std::move(_callback)); +} + +void TransactionExecutor::getHash(bcos::protocol::BlockNumber number, + std::function callback) +{ + EXECUTOR_NAME_LOG(INFO) << BLOCK_NUMBER(number) << "GetTableHashes"; + + if (!m_isRunning) + { + EXECUTOR_NAME_LOG(ERROR) << "TransactionExecutor is not running"; + callback( + BCOS_ERROR_UNIQUE_PTR(ExecuteError::STOPPED, "TransactionExecutor is not running"), {}); + return; + } + + if (m_stateStorages.empty()) + { + EXECUTOR_NAME_LOG(ERROR) << "GetTableHashes error: No uncommitted state"; + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::GETHASH_ERROR, "No uncommitted state"), + crypto::HashType()); + return; + } + + auto& last = m_stateStorages.back(); + if (last.number != number) + { + auto errorMessage = + "GetTableHashes error: Request blockNumber: " + + boost::lexical_cast(number) + + " not equal to last blockNumber: " + boost::lexical_cast(last.number); + + EXECUTOR_NAME_LOG(ERROR) << errorMessage; + callback( + BCOS_ERROR_UNIQUE_PTR(ExecuteError::GETHASH_ERROR, errorMessage), crypto::HashType()); + + return; + } + + // remove suicides beforehand + m_blockContext->killSuicides(); + + auto hash = last.storage->hash(m_hashImpl); + EXECUTOR_NAME_LOG(INFO) << BLOCK_NUMBER(number) << "GetTableHashes success" + << LOG_KV("hash", hash.hex()); + + callback(nullptr, std::move(hash)); +} + +void TransactionExecutor::dagExecuteTransactions( + gsl::span inputs, + std::function)> + _callback) +{ + if (!m_blockContext) + { + _callback(BCOS_ERROR_UNIQUE_PTR( + ExecuteError::EXECUTE_ERROR, "Execute failed with empty blockContext!"), + {}); + return; + } + + auto requestTimestamp = utcTime(); + auto txNum = inputs.size(); + auto blockNumber = m_blockContext->number(); + EXECUTOR_NAME_LOG(INFO) << "dagExecuteTransactions request" + << LOG_KV("blockNumber", blockNumber) << LOG_KV("txNum", txNum) + << LOG_KV("requestTimestamp", requestTimestamp); + + auto callback = [this, _callback = _callback, requestTimestamp, blockNumber, txNum]( + bcos::Error::UniquePtr error, + std::vector outputs) { + EXECUTOR_NAME_LOG(DEBUG) << "dagExecuteTransactions response" + << LOG_KV("blockNumber", blockNumber) << LOG_KV("txNum", txNum) + << LOG_KV("outputNum", outputs.size()) + << LOG_KV("requestTimestamp", requestTimestamp) + << LOG_KV("msg", error ? error->errorMessage() : "ok") + << LOG_KV("timeCost", utcTime() - requestTimestamp); + _callback(std::move(error), std::move(outputs)); + }; + + + if (!m_isRunning) + { + EXECUTOR_NAME_LOG(ERROR) << "TransactionExecutor is not running"; + callback( + BCOS_ERROR_UNIQUE_PTR(ExecuteError::STOPPED, "TransactionExecutor is not running"), {}); + return; + } + + auto recoredT = utcTime(); + auto startT = utcTime(); + // for fill block + auto txHashes = make_shared(); + std::vector indexes; + auto fillInputs = std::make_shared>(); + + // final result + auto callParametersList = + std::make_shared>(inputs.size()); + +#pragma omp parallel for + for (auto i = 0u; i < inputs.size(); ++i) + { + auto& params = inputs[i]; + switch (params->type()) + { + case ExecutionMessage::TXHASH: + { +#pragma omp critical + { + txHashes->emplace_back(params->transactionHash()); + indexes.emplace_back(i); + fillInputs->emplace_back(std::move(params)); + } + + break; + } + case ExecutionMessage::MESSAGE: + { + callParametersList->at(i) = createCallParameters(*params, false); + break; + } + default: + { + auto message = (boost::format("Unsupported message type: %d") % params->type()).str(); + EXECUTOR_NAME_LOG(ERROR) << "DAG Execute error, " << message; + // callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::DAG_ERROR, message), {}); + break; + } + } + } + auto prepareT = utcTime() - startT; + startT = utcTime(); + if (!txHashes->empty()) + { + m_txpool->asyncFillBlock(txHashes, + [this, startT, indexes = std::move(indexes), fillInputs = std::move(fillInputs), + callParametersList = std::move(callParametersList), callback = std::move(callback), + txHashes, + blockNumber](Error::Ptr error, protocol::TransactionsPtr transactions) mutable { + auto fillTxsT = utcTime() - startT; + + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR( + ExecuteError::STOPPED, "TransactionExecutor is not running"), + {}); + return; + } + + if (error) + { + auto errorMessage = "[" + m_name + "] asyncFillBlock failed"; + EXECUTOR_NAME_LOG(ERROR) + << BLOCK_NUMBER(blockNumber) << errorMessage << error->errorMessage(); + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR( + ExecuteError::DAG_ERROR, errorMessage, *error), + {}); + return; + } + auto recordT = utcTime(); +#pragma omp parallel for + for (size_t i = 0; i < transactions->size(); ++i) + { + assert(transactions->at(i)); + callParametersList->at(indexes[i]) = + createCallParameters(*fillInputs->at(i), *transactions->at(i)); + } + auto prepareT = utcTime() - recordT; + recordT = utcTime(); + dagExecuteTransactionsInternal(*callParametersList, std::move(callback)); + + EXECUTOR_NAME_LOG(INFO) + << LOG_DESC("dagExecuteTransactionsInternal after fillblock") + << LOG_KV("fillTxsT", fillTxsT) << LOG_KV("prepareT", prepareT) + << LOG_KV("dagT", (utcTime() - recordT)); + }); + } + else + { + dagExecuteTransactionsInternal(*callParametersList, std::move(callback)); + } + + EXECUTOR_NAME_LOG(INFO) << LOG_DESC("dagExecuteTransactions") << LOG_KV("prepareT", prepareT) + << LOG_KV("total", (utcTime() - recoredT)) + << LOG_KV("inputSize", inputs.size()); +} + +bytes getComponentBytes(size_t index, const std::string& typeName, const bytesConstRef& data) +{ + size_t indexOffset = index * 32; + auto header = bytes(data.begin() + indexOffset, data.begin() + indexOffset + 32); + if (typeName == "string" || typeName == "bytes") + { + u256 u = fromBigEndian(header); + auto offset = static_cast(u); + auto rawData = data.getCroppedData(offset); + auto len = static_cast( + fromBigEndian(bytes(rawData.begin(), rawData.begin() + 32))); + return bytes(rawData.begin() + 32, rawData.begin() + 32 + static_cast(len)); + } + return header; +} + +std::shared_ptr> TransactionExecutor::extractConflictFields( + const FunctionAbi& functionAbi, const CallParameters& params, + std::shared_ptr _blockContext) +{ + if (functionAbi.conflictFields.empty()) + { + EXECUTOR_NAME_LOG(TRACE) << LOG_BADGE("extractConflictFields") + << LOG_DESC("conflictFields is empty") + << LOG_KV("address", params.senderAddress) + << LOG_KV("functionName", functionAbi.name); + return nullptr; + } + + const auto& to = params.receiveAddress; + auto hasher = boost::hash(); + auto toHash = hasher(to); + + auto conflictFields = make_shared>(); + + for (auto& conflictField : functionAbi.conflictFields) + { + auto criticalKey = bytes(); + + size_t slot = toHash; + if (conflictField.slot.has_value()) + { + slot += static_cast(conflictField.slot.value()); + } + criticalKey.insert(criticalKey.end(), (uint8_t*)&slot, (uint8_t*)&slot + sizeof(slot)); + EXECUTOR_NAME_LOG(TRACE) << LOG_BADGE("extractConflictFields") << LOG_KV("to", to) + << LOG_KV("functionName", functionAbi.name) + << LOG_KV("addressHash", toHash) << LOG_KV("slot", slot); + + switch (conflictField.kind) + { + case All: + { + EXECUTOR_NAME_LOG(TRACE) << LOG_BADGE("extractConflictFields") << LOG_DESC("use `All`"); + return nullptr; + } + case Len: + { + EXECUTOR_NAME_LOG(TRACE) << LOG_BADGE("extractConflictFields") << LOG_DESC("use `Len`"); + break; + } + case Env: + { + assert(conflictField.value.size() == 1); + + auto envKind = conflictField.value[0]; + switch (envKind) + { + case EnvKind::Caller: + { + const auto& sender = params.senderAddress; + criticalKey.insert(criticalKey.end(), sender.begin(), sender.end()); + + EXECUTOR_NAME_LOG(TRACE) << LOG_BADGE("extractConflictFields") + << LOG_DESC("use `Caller`") << LOG_KV("caller", sender); + break; + } + case EnvKind::Origin: + { + const auto& sender = params.origin; + criticalKey.insert(criticalKey.end(), sender.begin(), sender.end()); + + EXECUTOR_NAME_LOG(TRACE) << LOG_BADGE("extractConflictFields") + << LOG_DESC("use `Origin`") << LOG_KV("origin", sender); + break; + } + case EnvKind::Now: + { + auto now = _blockContext->timestamp(); + auto bytes = static_cast(static_cast(&now)); + criticalKey.insert(criticalKey.end(), bytes, bytes + sizeof(now)); + + EXECUTOR_NAME_LOG(TRACE) << LOG_BADGE("extractConflictFields") + << LOG_DESC("use `Now`") << LOG_KV("now", now); + break; + } + case EnvKind::BlockNumber: + { + auto blockNumber = _blockContext->number(); + auto bytes = static_cast(static_cast(&blockNumber)); + criticalKey.insert(criticalKey.end(), bytes, bytes + sizeof(blockNumber)); + + EXECUTOR_NAME_LOG(DEBUG) + << LOG_BADGE("extractConflictFields") << LOG_DESC("use `BlockNumber`") + << LOG_KV("functionName", functionAbi.name) + << LOG_KV("blockNumber", blockNumber); + break; + } + case EnvKind::Addr: + { + criticalKey.insert(criticalKey.end(), to.begin(), to.end()); + + EXECUTOR_NAME_LOG(DEBUG) << LOG_BADGE("extractConflictFields") + << LOG_DESC("use `Addr`") << LOG_KV("addr", to); + break; + } + default: + { + EXECUTOR_NAME_LOG(ERROR) << LOG_BADGE("unknown env kind in conflict field") + << LOG_KV("envKind", envKind); + return nullptr; + } + } + break; + } + case Params: + { + assert(!conflictField.value.empty()); + const ParameterAbi* paramAbi = nullptr; + auto components = &functionAbi.inputs; + auto inputData = ref(params.data).getCroppedData(4).toBytes(); + if (_blockContext->isWasm()) + { + auto startPos = 0u; + for (auto segment : conflictField.value) + { + if (segment >= components->size()) + { + return nullptr; + } + + for (auto i = 0u; i < segment; ++i) + { + auto length = scaleEncodingLength(components->at(i), inputData, startPos); + if (!length.has_value()) + { + return nullptr; + } + startPos += length.value(); + } + paramAbi = &components->at(segment); + components = ¶mAbi->components; + } + auto length = scaleEncodingLength(*paramAbi, inputData, startPos); + if (!length.has_value()) + { + return nullptr; + } + assert(startPos + length.value() <= inputData.size()); + bytes var( + inputData.begin() + startPos, inputData.begin() + startPos + length.value()); + criticalKey.insert(criticalKey.end(), var.begin(), var.end()); + } + else + { // evm + auto index = conflictField.value[0]; + auto typeName = functionAbi.flatInputs[index]; + if (typeName.empty()) + { + return nullptr; + } + auto out = getComponentBytes(index, typeName, ref(params.data).getCroppedData(4)); + criticalKey.insert(criticalKey.end(), out.begin(), out.end()); + } + + EXECUTOR_NAME_LOG(DEBUG) + << LOG_BADGE("extractConflictFields") << LOG_DESC("use `Params`") + << LOG_KV("functionName", functionAbi.name) + << LOG_KV("criticalKey", toHexStringWithPrefix(criticalKey)); + break; + } + case Const: + { + criticalKey.insert( + criticalKey.end(), conflictField.value.begin(), conflictField.value.end()); + EXECUTOR_NAME_LOG(DEBUG) + << LOG_BADGE("extractConflictFields") << LOG_DESC("use `Const`") + << LOG_KV("functionName", functionAbi.name) + << LOG_KV("criticalKey", toHexStringWithPrefix(criticalKey)); + break; + } + case None: + { + EXECUTOR_NAME_LOG(DEBUG) << LOG_BADGE("extractConflictFields") << LOG_DESC("use `None`") + << LOG_KV("functionName", functionAbi.name) + << LOG_KV("criticalKey", toHexStringWithPrefix(criticalKey)); + break; + } + default: + { + EXECUTOR_NAME_LOG(ERROR) << LOG_BADGE("unknown conflict field kind") + << LOG_KV("conflictFieldKind", conflictField.kind); + return nullptr; + } + } + + conflictFields->emplace_back(std::move(criticalKey)); + } + return conflictFields; +} + +void TransactionExecutor::dagExecuteTransactionsInternal( + gsl::span> inputs, + std::function)> + callback) +{ + auto recordT = utcTime(); + auto startT = utcTime(); + + auto transactionsNum = inputs.size(); + auto executionResults = vector(transactionsNum); + + CriticalFields::Ptr txsCriticals = make_shared(transactionsNum); + + mutex tableMutex; + + // parallel to extract critical fields + tbb::parallel_for(tbb::blocked_range(0, transactionsNum), + [&](const tbb::blocked_range& range) { + try + { + for (auto i = range.begin(); i != range.end(); ++i) + { + auto defaultExecutionResult = + m_executionMessageFactory->createExecutionMessage(); + executionResults[i].swap(defaultExecutionResult); + + const auto& params = inputs[i]; + + auto to = params->receiveAddress; + const auto& input = params->data; + + if (params->create) + { + executionResults[i] = toExecutionResult(std::move(inputs[i])); + executionResults[i]->setType(ExecutionMessage::SEND_BACK); + continue; + } + CriticalFields::CriticalFieldPtr conflictFields = nullptr; + auto selector = ref(input).getCroppedData(0, 4); + auto abiKey = bytes(to.cbegin(), to.cend()); + abiKey.insert(abiKey.end(), selector.begin(), selector.end()); + // if precompiled + auto executiveFactory = + std::make_shared(m_blockContext, m_precompiledContract, + m_constantPrecompiled, m_builtInPrecompiled, m_gasInjector); + auto executive = executiveFactory->build( + params->codeAddress, params->contextID, params->seq, false); + auto p = executive->getPrecompiled(params->receiveAddress); + if (p) + { + // Precompile transaction + if (p->isParallelPrecompiled()) + { + auto criticals = + vector(p->getParallelTag(ref(params->data), m_isWasm)); + conflictFields = make_shared>(); + for (string& critical : criticals) + { + critical += params->receiveAddress; + conflictFields->push_back(bytes((uint8_t*)critical.data(), + (uint8_t*)critical.data() + critical.size())); + } + } + else + { + // Note: must be sure that the log accessed data should be valid + // always + EXECUTOR_NAME_LOG(DEBUG) + << LOG_BADGE("dagExecuteTransactionsInternal") + << LOG_DESC("the precompiled can't be parallel") + << LOG_KV("address", to); + executionResults[i] = toExecutionResult(std::move(inputs[i])); + executionResults[i]->setType(ExecutionMessage::SEND_BACK); + continue; + } + } + else + { + auto cacheHandle = m_abiCache->lookup(abiKey); + // find FunctionAbi in cache first + if (!cacheHandle.isValid()) + { + EXECUTOR_NAME_LOG(TRACE) + << LOG_BADGE("dagExecuteTransactionsInternal") + << LOG_DESC("No ABI found in cache, try to load") + << LOG_KV("abiKey", toHexStringWithPrefix(abiKey)); + + std::lock_guard guard(tableMutex); + + cacheHandle = m_abiCache->lookup(abiKey); + if (cacheHandle.isValid()) + { + EXECUTOR_NAME_LOG(TRACE) + << LOG_BADGE("dagExecuteTransactionsInternal") + << LOG_DESC("ABI had been loaded by other workers") + << LOG_KV("abiKey", toHexStringWithPrefix(abiKey)); + auto& functionAbi = cacheHandle.value(); + conflictFields = + extractConflictFields(functionAbi, *params, m_blockContext); + } + else + { + auto storage = m_blockContext->storage(); + + auto tableName = "/apps/" + string(to); + + auto table = storage->openTable(tableName); + if (!table.has_value()) + { + executionResults[i] = toExecutionResult(std::move(inputs[i])); + executionResults[i]->setType(ExecutionMessage::REVERT); + EXECUTOR_NAME_LOG(INFO) + << LOG_BADGE("dagExecuteTransactionsInternal") + << LOG_DESC("No ABI found, please deploy first") + << LOG_KV("tableName", tableName); + continue; + } + // get abi json + // new logic + std::string_view abiStr; + if (m_blockContext->blockVersion() >= + uint32_t(bcos::protocol::BlockVersion::V3_1_VERSION)) + { + // get codehash + auto entry = table->getRow(ACCOUNT_CODE_HASH); + if (!entry || entry->get().empty()) + { + executionResults[i] = + toExecutionResult(std::move(inputs[i])); + executionResults[i]->setType(ExecutionMessage::SEND_BACK); + EXECUTOR_NAME_LOG(INFO) + << "No codeHash found, please deploy first " + << LOG_KV("tableName", tableName); + continue; + } + + auto codeHash = entry->getField(0); + + // get abi according to codeHash + auto abiTable = + storage->openTable(bcos::ledger::SYS_CONTRACT_ABI); + auto abiEntry = abiTable->getRow(codeHash); + if (!abiEntry || abiEntry->get().empty()) + { + abiEntry = table->getRow(ACCOUNT_ABI); + if (!abiEntry || abiEntry->get().empty()) + { + executionResults[i] = + toExecutionResult(std::move(inputs[i])); + executionResults[i]->setType( + ExecutionMessage::SEND_BACK); + EXECUTOR_NAME_LOG(INFO) + << "No ABI found, please deploy first " + << LOG_KV("tableName", tableName); + continue; + } + } + abiStr = abiEntry->getField(0); + } + else + { + // old logic + auto entry = table->getRow(ACCOUNT_ABI); + abiStr = entry->getField(0); + } + bool isSmCrypto = + m_hashImpl->getHashImplType() == crypto::HashImplType::Sm3Hash; + + EXECUTOR_NAME_LOG(TRACE) + << LOG_BADGE("dagExecuteTransactionsInternal") + << LOG_DESC("ABI loaded") << LOG_KV("address", to) + << LOG_KV("selector", toHexString(selector)) + << LOG_KV("ABI", abiStr); + auto functionAbi = FunctionAbi::deserialize( + abiStr, selector.toBytes(), isSmCrypto); + if (!functionAbi) + { + EXECUTOR_NAME_LOG(DEBUG) + << LOG_BADGE("dagExecuteTransactionsInternal") + << LOG_DESC("ABI deserialize failed") + << LOG_KV("address", to) << LOG_KV("ABI", abiStr); + executionResults[i] = toExecutionResult(std::move(inputs[i])); + executionResults[i]->setType(ExecutionMessage::SEND_BACK); + // If abi is not valid, we don't impact the cache. In such a + // situation, if the caller invokes this method over and + // over again, executor will read the contract table + // repeatedly, which may cause performance loss. But we + // think occurrence of invalid abi is impossible in actual + // situations. + continue; + } + + auto abiPtr = functionAbi.get(); + if (m_abiCache->insert(abiKey, abiPtr, &cacheHandle)) + { + // If abi object had been inserted into the cache + // successfully, the cache will take charge of life time + // management of the object. After this object being + // eliminated, the cache will delete its memory storage. + std::ignore = functionAbi.release(); + } + conflictFields = + extractConflictFields(*abiPtr, *params, m_blockContext); + } + } + else + { + EXECUTOR_NAME_LOG(DEBUG) + << LOG_BADGE("dagExecuteTransactionsInternal") + << LOG_DESC("Found ABI in cache") << LOG_KV("address", to) + << LOG_KV("abiKey", toHexStringWithPrefix(abiKey)); + auto& functionAbi = cacheHandle.value(); + conflictFields = + extractConflictFields(functionAbi, *params, m_blockContext); + } + } + if (conflictFields == nullptr) + { + EXECUTOR_NAME_LOG(DEBUG) + << LOG_BADGE("dagExecuteTransactionsInternal") + << LOG_DESC("The transaction can't be executed concurrently") + << LOG_KV("address", to) + << LOG_KV("abiKey", toHexStringWithPrefix(abiKey)); + executionResults[i] = toExecutionResult(std::move(inputs[i])); + executionResults[i]->setType(ExecutionMessage::SEND_BACK); + continue; + } + txsCriticals->put(i, std::move(conflictFields)); + } + } + catch (exception& e) + { + EXECUTOR_NAME_LOG(ERROR) << LOG_BADGE("dagExecuteTransactionsInternal") + << LOG_DESC("Error during parallel extractConflictFields") + << LOG_KV("EINFO", boost::diagnostic_information(e)); + BOOST_THROW_EXCEPTION( + BCOS_ERROR_WITH_PREV(-1, "Error while extractConflictFields", e)); + } + }); + auto dagInitT = utcTime() - startT; + startT = utcTime(); + // DAG run + try + { + // DAG run + EXECUTOR_NAME_LOG(INFO) << BLOCK_NUMBER(m_blockContext->number()) + << LOG_DESC("begin executeTransactionsWithCriticals") + << LOG_KV("txsCriticalsSize", txsCriticals->size()); + executeTransactionsWithCriticals(txsCriticals, inputs, executionResults); + } + catch (exception& e) + { + EXECUTOR_NAME_LOG(ERROR) << LOG_BADGE("executeBlock") + << LOG_DESC("Error during dag execution") + << LOG_KV("EINFO", boost::diagnostic_information(e)); + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::CALL_ERROR, boost::diagnostic_information(e)), + vector{}); + return; + } + EXECUTOR_NAME_LOG(INFO) << LOG_DESC("dagExecuteTransactions") << LOG_KV("dagInitT", dagInitT) + << LOG_KV("dagRunT", (utcTime() - startT)) + << LOG_KV("totalCost", (utcTime() - recordT)); + callback(nullptr, std::move(executionResults)); +} + +void TransactionExecutor::prepare( + const TwoPCParams& params, std::function callback) +{ + EXECUTOR_NAME_LOG(INFO) << BLOCK_NUMBER(params.number) << "Prepare request"; + + if (!m_isRunning) + { + EXECUTOR_NAME_LOG(ERROR) << "TransactionExecutor is not running"; + callback( + BCOS_ERROR_UNIQUE_PTR(ExecuteError::STOPPED, "TransactionExecutor is not running")); + return; + } + + auto first = m_stateStorages.begin(); + if (first == m_stateStorages.end()) + { + const auto* errorMessage = "Prepare error: empty stateStorages"; + EXECUTOR_NAME_LOG(ERROR) << errorMessage; + callback(BCOS_ERROR_PTR(-1, errorMessage)); + + return; + } + + if (first->number != params.number) + { + auto errorMessage = + "Prepare error: Request blockNumber: " + + boost::lexical_cast(params.number) + + " not equal to last blockNumber: " + boost::lexical_cast(first->number) + + " trigger switch"; + + EXECUTOR_NAME_LOG(ERROR) << errorMessage; + callback(BCOS_ERROR_PTR(ExecuteError::SCHEDULER_TERM_ID_ERROR, errorMessage)); + + return; + } + + bcos::protocol::TwoPCParams storageParams{params.number, params.primaryKey, params.timestamp}; + + m_backendStorage->asyncPrepare(storageParams, *(first->storage), + [this, callback = std::move(callback), blockNumber = params.number]( + auto&& error, uint64_t, const std::string&) { + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR( + ExecuteError::STOPPED, "TransactionExecutor is not running")); + return; + } + + if (error) + { + auto errorMessage = "Prepare error: " + error->errorMessage(); + + EXECUTOR_NAME_LOG(ERROR) << BLOCK_NUMBER(blockNumber) << errorMessage; + callback( + BCOS_ERROR_WITH_PREV_PTR(ExecuteError::PREPARE_ERROR, errorMessage, *error)); + return; + } + + EXECUTOR_NAME_LOG(INFO) << BLOCK_NUMBER(blockNumber) << "Prepare success"; + callback(nullptr); + }); +} + +void TransactionExecutor::commit( + const TwoPCParams& params, std::function callback) +{ + EXECUTOR_NAME_LOG(TRACE) << BLOCK_NUMBER(params.number) << "Commit request"; + + if (!m_isRunning) + { + EXECUTOR_NAME_LOG(ERROR) << "TransactionExecutor is not running"; + callback( + BCOS_ERROR_UNIQUE_PTR(ExecuteError::STOPPED, "TransactionExecutor is not running")); + return; + } + + auto first = m_stateStorages.begin(); + if (first == m_stateStorages.end()) + { + auto errorMessage = "Commit error: empty stateStorages"; + EXECUTOR_NAME_LOG(ERROR) << errorMessage; + callback(BCOS_ERROR_PTR(INVALID_BLOCKNUMBER, errorMessage)); + + return; + } + + if (first->number != params.number) + { + auto errorMessage = + "Commit error: Request blockNumber: " + + boost::lexical_cast(params.number) + + " not equal to last blockNumber: " + boost::lexical_cast(first->number); + + EXECUTOR_NAME_LOG(ERROR) << errorMessage; + callback(BCOS_ERROR_PTR(INVALID_BLOCKNUMBER, errorMessage)); + + return; + } + + bcos::protocol::TwoPCParams storageParams{params.number, params.primaryKey, params.timestamp}; + m_backendStorage->asyncCommit(storageParams, [this, callback = std::move(callback), + blockNumber = params.number]( + Error::Ptr&& error, uint64_t) { + if (!m_isRunning) + { + callback( + BCOS_ERROR_UNIQUE_PTR(ExecuteError::STOPPED, "TransactionExecutor is not running")); + return; + } + + if (error) + { + auto errorMessage = "Commit error: " + error->errorMessage(); + + EXECUTOR_NAME_LOG(ERROR) << BLOCK_NUMBER(blockNumber) << errorMessage; + callback(BCOS_ERROR_WITH_PREV_PTR(ExecuteError::COMMIT_ERROR, errorMessage, *error)); + return; + } + + EXECUTOR_NAME_LOG(DEBUG) << BLOCK_NUMBER(blockNumber) << "Commit success"; + + m_lastCommittedBlockHeader = getBlockHeaderInStorage(blockNumber); + m_ledgerCache->fetchCompatibilityVersion(); + m_blockVersion = m_ledgerCache->ledgerConfig()->compatibilityVersion(); + + removeCommittedState(); + + callback(nullptr); + }); +} + +void TransactionExecutor::rollback( + const TwoPCParams& params, std::function callback) +{ + EXECUTOR_NAME_LOG(INFO) << BLOCK_NUMBER(params.number) << "Rollback request: "; + + if (!m_isRunning) + { + EXECUTOR_NAME_LOG(ERROR) << "TransactionExecutor is not running"; + callback( + BCOS_ERROR_UNIQUE_PTR(ExecuteError::STOPPED, "TransactionExecutor is not running")); + return; + } + + auto first = m_stateStorages.begin(); + if (first == m_stateStorages.end()) + { + auto errorMessage = "Rollback error: empty stateStorages"; + EXECUTOR_NAME_LOG(ERROR) << errorMessage; + callback(BCOS_ERROR_PTR(-1, errorMessage)); + + return; + } + + if (first->number != params.number) + { + auto errorMessage = + "Rollback error: Request blockNumber: " + + boost::lexical_cast(params.number) + + " not equal to last blockNumber: " + boost::lexical_cast(first->number) + + " trigger switch"; + + EXECUTOR_NAME_LOG(ERROR) << errorMessage; + callback(BCOS_ERROR_PTR(ExecuteError::SCHEDULER_TERM_ID_ERROR, errorMessage)); + + return; + } + + bcos::protocol::TwoPCParams storageParams{params.number, params.primaryKey, params.timestamp}; + m_backendStorage->asyncRollback(storageParams, + [this, callback = std::move(callback), blockNumber = params.number](auto&& error) { + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR( + ExecuteError::STOPPED, "TransactionExecutor is not running")); + return; + } + + if (error) + { + auto errorMessage = "Rollback error: " + error->errorMessage(); + + EXECUTOR_NAME_LOG(ERROR) << BLOCK_NUMBER(blockNumber) << errorMessage; + callback(BCOS_ERROR_WITH_PREV_PTR(-1, errorMessage, *error)); + return; + } + + EXECUTOR_NAME_LOG(INFO) << BLOCK_NUMBER(blockNumber) << "Rollback success"; + callback(nullptr); + }); +} + +void TransactionExecutor::reset(std::function callback) +{ + m_stateStorages.clear(); + + callback(nullptr); +} + +void TransactionExecutor::getCode( + std::string_view contract, std::function callback) +{ + EXECUTOR_NAME_LOG(INFO) << "Get code request" << LOG_KV("Contract", contract); + + if (!m_isRunning) + { + EXECUTOR_NAME_LOG(ERROR) << "TransactionExecutor is not running"; + callback( + BCOS_ERROR_UNIQUE_PTR(ExecuteError::STOPPED, "TransactionExecutor is not running"), {}); + return; + } + + storage::StateStorageInterface::Ptr stateStorage; + + { + std::unique_lock lock(m_stateStoragesMutex); + if (!m_stateStorages.empty()) + { + stateStorage = createStateStorage(m_stateStorages.back().storage, true); + } + } + // create temp state storage + if (!stateStorage) + { + if (m_cachedStorage) + { + stateStorage = createStateStorage(m_cachedStorage, true); + } + else + { + stateStorage = createStateStorage(m_backendStorage, true); + } + } + + std::string contractTableName = getContractTableName(contract); + + auto getCodeFromContractTable = [stateStorage, this](std::string_view contractTableName, + decltype(callback) _callback) { + stateStorage->asyncGetRow(contractTableName, ACCOUNT_CODE, + [this, callback = std::move(_callback)]( + Error::UniquePtr error, std::optional entry) { + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR( + ExecuteError::STOPPED, "TransactionExecutor is not running"), + {}); + return; + } + + if (error) + { + EXECUTOR_NAME_LOG(INFO) << "Get code error: " << error->errorMessage(); + + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(-1, "Get code error", *error), {}); + return; + } + + if (!entry) + { + EXECUTOR_NAME_LOG(DEBUG) << "Get code success, empty code"; + + callback(nullptr, bcos::bytes()); + return; + } + + auto code = entry->getField(0); + EXECUTOR_NAME_LOG(INFO) << "Get code success" << LOG_KV("code size", code.size()); + + auto codeBytes = bcos::bytes(code.begin(), code.end()); + callback(nullptr, std::move(codeBytes)); + }); + }; + if (m_blockVersion >= uint32_t(bcos::protocol::BlockVersion::V3_1_VERSION)) + { + auto codeHash = getCodeHash(contractTableName, stateStorage); + // asyncGetRow key should not be empty + auto codeKey = codeHash.empty() ? ACCOUNT_CODE : codeHash; + // try to get abi from SYS_CODE_BINARY first + stateStorage->asyncGetRow(bcos::ledger::SYS_CODE_BINARY, codeKey, + [this, contractTableName, callback = std::move(callback), + getCodeFromContractTable = std::move(getCodeFromContractTable)]( + Error::UniquePtr error, std::optional entry) { + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR( + ExecuteError::STOPPED, "TransactionExecutor is not running"), + {}); + return; + } + + if (error) + { + EXECUTOR_NAME_LOG(INFO) << "Get code error: " << error->errorMessage(); + + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(-1, "Get code error", *error), {}); + return; + } + + if (!entry) + { + EXECUTOR_NAME_LOG(DEBUG) + << "Get code success, empty code, try to search in the contract table"; + getCodeFromContractTable(contractTableName, std::move(callback)); + return; + } + + auto code = entry->getField(0); + EXECUTOR_NAME_LOG(INFO) << "Get code success" << LOG_KV("code size", code.size()); + + auto codeBytes = bcos::bytes(code.begin(), code.end()); + callback(nullptr, std::move(codeBytes)); + }); + return; + } + getCodeFromContractTable(contractTableName, std::move(callback)); +} + +void TransactionExecutor::getABI( + std::string_view contract, std::function callback) +{ + EXECUTOR_NAME_LOG(INFO) << "Get ABI request" << LOG_KV("Contract", contract); + + if (!m_isRunning) + { + EXECUTOR_NAME_LOG(ERROR) << "TransactionExecutor is not running"; + callback( + BCOS_ERROR_UNIQUE_PTR(ExecuteError::STOPPED, "TransactionExecutor is not running"), {}); + return; + } + + storage::StateStorageInterface::Ptr stateStorage; + + { + std::unique_lock lock(m_stateStoragesMutex); + if (!m_stateStorages.empty()) + { + stateStorage = createStateStorage(m_stateStorages.back().storage, true); + } + } + // create temp state storage + if (!stateStorage) + { + if (m_cachedStorage) + { + stateStorage = createStateStorage(m_cachedStorage, true); + } + else + { + stateStorage = createStateStorage(m_backendStorage, true); + } + } + + + std::string contractTableName = getContractTableName(contract); + auto getAbiFromContractTable = [stateStorage, this](std::string_view contractTableName, + decltype(callback) _callback) { + stateStorage->asyncGetRow(contractTableName, ACCOUNT_ABI, + [this, callback = std::move(_callback)]( + Error::UniquePtr error, std::optional entry) { + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR( + ExecuteError::STOPPED, "TransactionExecutor is not running"), + {}); + return; + } + + if (error) + { + EXECUTOR_NAME_LOG(INFO) << "Get ABI error: " << error->errorMessage(); + + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(-1, "Get ABI error", *error), {}); + return; + } + + if (!entry) + { + EXECUTOR_NAME_LOG(DEBUG) << "Get ABI success, empty ABI"; + + callback(nullptr, std::string()); + return; + } + auto abi = entry->getField(0); + EXECUTOR_NAME_LOG(INFO) << "Get ABI success" << LOG_KV("ABI size", abi.size()); + callback(nullptr, std::string(abi)); + }); + }; + if (m_blockVersion >= uint32_t(bcos::protocol::BlockVersion::V3_1_VERSION)) + { + auto codeHash = getCodeHash(contractTableName, stateStorage); + // asyncGetRow key should not be empty + std::string abiKey = codeHash.empty() ? ACCOUNT_ABI : codeHash; + // try to get abi from SYS_CONTRACT_ABI first + EXECUTOR_LOG(TRACE) << LOG_DESC("get abi") << LOG_KV("abiKey", abiKey); + + stateStorage->asyncGetRow(bcos::ledger::SYS_CONTRACT_ABI, abiKey, + [this, contractTableName, callback = std::move(callback), + getAbiFromContractTable = std::move(getAbiFromContractTable)]( + Error::UniquePtr error, std::optional entry) { + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR( + ExecuteError::STOPPED, "TransactionExecutor is not running"), + {}); + return; + } + + if (error) + { + EXECUTOR_NAME_LOG(INFO) << "Get ABI error: " << error->errorMessage(); + + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(-1, "Get ABI error", *error), {}); + return; + } + + // when get abi from SYS_CONTRACT_ABI failed, try to get abi from contract table + if (!entry) + { + EXECUTOR_NAME_LOG(DEBUG) + << "Get ABI failed, empty entry, try to search in the contract table"; + getAbiFromContractTable(contractTableName, callback); + return; + } + + auto abi = entry->getField(0); + EXECUTOR_NAME_LOG(INFO) << "Get ABI success" << LOG_KV("ABI size", abi.size()); + callback(nullptr, std::string(abi)); + }); + return; + } + getAbiFromContractTable(contractTableName, std::move(callback)); +} + +ExecutiveFlowInterface::Ptr TransactionExecutor::getExecutiveFlow( + std::shared_ptr blockContext, std::string codeAddress, bool useCoroutine) +{ + EXECUTOR_NAME_LOG(DEBUG) << "getExecutiveFlow" << LOG_KV("codeAddress", codeAddress); + bcos::RecursiveGuard lock(x_executiveFlowLock); + ExecutiveFlowInterface::Ptr executiveFlow = blockContext->getExecutiveFlow(codeAddress); + if (executiveFlow == nullptr) + { + auto executiveFactory = std::make_shared(blockContext, + m_precompiledContract, m_constantPrecompiled, m_builtInPrecompiled, m_gasInjector); + if (!useCoroutine) + { + executiveFlow = std::make_shared(executiveFactory); + executiveFlow->setThreadPool(m_threadPool); + blockContext->setExecutiveFlow(codeAddress, executiveFlow); + } + else + { + executiveFlow = std::make_shared(executiveFactory); + executiveFlow->setThreadPool(m_threadPool); + blockContext->setExecutiveFlow(codeAddress, executiveFlow); + } + } + return executiveFlow; +} + + +void TransactionExecutor::asyncExecuteExecutiveFlow(ExecutiveFlowInterface::Ptr executiveFlow, + std::function&&)> + callback) +{ + ExecuteOutputs::Ptr allOutputs = std::make_shared(); + EXECUTOR_NAME_LOG(DEBUG) << "asyncExecuteExecutiveFlow start"; + executiveFlow->asyncRun( + // onTxReturn + [this, allOutputs, callback](CallParameters::UniquePtr output) { + auto message = toExecutionResult(std::move(output)); + allOutputs->add(std::move(message)); + }, + // onFinished + [this, allOutputs, callback](bcos::Error::UniquePtr error) { + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR( + ExecuteError::STOPPED, "TransactionExecutor is not running"), + {}); + return; + } + + // do nothing + if (error != nullptr) + { + EXECUTOR_NAME_LOG(ERROR) + << "ExecutiveFlow asyncRun error: " << LOG_KV("errorCode", error->errorCode()) + << LOG_KV("errorMessage", error->errorMessage()); + m_blockContext->clear(); + callback(std::move(error), std::vector()); + } + else + { + auto messages = allOutputs->dumpAndClear(); + callback(nullptr, std::move(messages)); + } + }); +} + +void TransactionExecutor::asyncExecute(std::shared_ptr blockContext, + bcos::protocol::ExecutionMessage::UniquePtr input, bool useCoroutine, + std::function + callback) +{ + EXECUTOR_NAME_LOG(DEBUG) << BLOCK_NUMBER(blockContext->number()) << LOG_DESC("asyncExecute") + << LOG_KV("contextID", input->contextID()) + << LOG_KV("seq", input->seq()) + << LOG_KV("MessageType", std::to_string(input->type())) + << LOG_KV("To", input->to()) + << LOG_KV("staticCall", input->staticCall()) + << LOG_KV("Create", input->create()); + switch (input->type()) + { + case bcos::protocol::ExecutionMessage::TXHASH: + { + // Get transaction first + auto txHashes = std::make_shared(1); + (*txHashes)[0] = (input->transactionHash()); + + m_txpool->asyncFillBlock(std::move(txHashes), + [this, useCoroutine, inputPtr = input.release(), blockContext = std::move(blockContext), + callback](Error::Ptr error, bcos::protocol::TransactionsPtr transactions) mutable { + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR( + ExecuteError::STOPPED, "TransactionExecutor is not running"), + nullptr); + return; + } + + auto input = std::unique_ptr(inputPtr); + + if (error) + { + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(ExecuteError::EXECUTE_ERROR, + "Transaction does not exists: " + input->transactionHash().hex(), + *error), + nullptr); + return; + } + + if (!transactions || transactions->empty()) + { + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::EXECUTE_ERROR, + "Transaction does not exists: " + input->transactionHash().hex()), + nullptr); + return; + } + + auto tx = (*transactions)[0]; + if (!tx) + { + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::EXECUTE_ERROR, + "Transaction is null: " + input->transactionHash().hex()), + nullptr); + return; + } + + auto callParameters = createCallParameters(*input, *tx); + + ExecutiveFlowInterface::Ptr executiveFlow = + getExecutiveFlow(blockContext, callParameters->receiveAddress, useCoroutine); + executiveFlow->submit(std::move(callParameters)); + + asyncExecuteExecutiveFlow(executiveFlow, + [this, callback = std::move(callback)](bcos::Error::UniquePtr&& error, + std::vector&& messages) { + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::STOPPED, + "TransactionExecutor is not running"), + nullptr); + return; + } + + if (error) + { + EXECUTOR_LOG(ERROR) << "asyncExecuteExecutiveFlow error: " + << LOG_KV("msg", error->errorMessage()) + << LOG_KV("code", error->errorCode()); + callback(std::move(error), nullptr); + } + else + { + EXECUTOR_NAME_LOG(TRACE) << "asyncExecuteExecutiveFlow complete: " + << messages[0]->toString(); + callback(std::move(error), std::move(messages[0])); + } + }); + }); + break; + } + case bcos::protocol::ExecutionMessage::MESSAGE: + case bcos::protocol::ExecutionMessage::REVERT: + case bcos::protocol::ExecutionMessage::FINISHED: + case bcos::protocol::ExecutionMessage::KEY_LOCK: + { + auto callParameters = createCallParameters(*input, input->staticCall()); + ExecutiveFlowInterface::Ptr executiveFlow = + getExecutiveFlow(blockContext, callParameters->receiveAddress, useCoroutine); + executiveFlow->submit(std::move(callParameters)); + asyncExecuteExecutiveFlow(executiveFlow, + [this, callback = std::move(callback)](bcos::Error::UniquePtr&& error, + std::vector&& messages) { + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR( + ExecuteError::STOPPED, "TransactionExecutor is not running"), + {}); + return; + } + + if (error) + { + EXECUTOR_LOG(ERROR) << "asyncExecuteExecutiveFlow error: " + << LOG_KV("msg", error->errorMessage()) + << LOG_KV("code", error->errorCode()); + callback(std::move(error), nullptr); + } + else + { + EXECUTOR_NAME_LOG(TRACE) + << "asyncExecuteExecutiveFlow complete: " << messages[0]->toString(); + callback(std::move(error), std::move(messages[0])); + } + }); + + break; + } + default: + { + EXECUTOR_NAME_LOG(ERROR) << "Unknown message type: " << input->type(); + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::EXECUTE_ERROR, + "Unknown type" + boost::lexical_cast(input->type())), + nullptr); + return; + } + } +} + +std::function input)> +TransactionExecutor::createExternalFunctionCall( + std::function& + callback) +{ + return [this, &callback]( + const TransactionExecutive& executive, CallParameters::UniquePtr input) { + if (!m_isRunning) + { + callback( + BCOS_ERROR_UNIQUE_PTR(ExecuteError::STOPPED, "TransactionExecutor is not running"), + nullptr); + return; + } + auto message = toExecutionResult(executive, std::move(input)); + callback(nullptr, std::move(message)); + }; +} + +std::unique_ptr TransactionExecutor::toExecutionResult( + const TransactionExecutive& executive, std::unique_ptr params) +{ + auto message = toExecutionResult(std::move(params)); + + message->setContextID(executive.contextID()); + message->setSeq(executive.seq()); + + return message; +} + +std::unique_ptr TransactionExecutor::toExecutionResult( + std::unique_ptr params) +{ + auto message = m_executionMessageFactory->createExecutionMessage(); + switch (params->type) + { + case CallParameters::MESSAGE: + message->setFrom(std::move(params->senderAddress)); + message->setTo(std::move(params->receiveAddress)); + message->setType(ExecutionMessage::MESSAGE); + message->setKeyLocks(std::move(params->keyLocks)); + break; + case CallParameters::KEY_LOCK: + message->setFrom(params->senderAddress); + message->setTo(std::move(params->senderAddress)); + message->setType(ExecutionMessage::KEY_LOCK); + message->setKeyLockAcquired(std::move(params->acquireKeyLock)); + message->setKeyLocks(std::move(params->keyLocks)); + + break; + case CallParameters::FINISHED: + // Response message, Swap the from and to + message->setFrom(std::move(params->receiveAddress)); + message->setTo(std::move(params->senderAddress)); + message->setType(ExecutionMessage::FINISHED); + break; + case CallParameters::REVERT: + // Response message, Swap the from and to + message->setFrom(std::move(params->receiveAddress)); + message->setTo(std::move(params->senderAddress)); + message->setType(ExecutionMessage::REVERT); + break; + } + + message->setContextID(params->contextID); + message->setSeq(params->seq); + message->setOrigin(std::move(params->origin)); + message->setGasAvailable(params->gas); + message->setData(std::move(params->data)); + message->setStaticCall(params->staticCall); + message->setCreate(params->create); + message->setInternalCreate(params->internalCreate); + message->setInternalCall(params->internalCall); + if (params->createSalt) + { + message->setCreateSalt(*params->createSalt); + } + + message->setEvmStatus(params->evmStatus); + message->setStatus(params->status); + message->setMessage(std::move(params->message)); + message->setLogEntries(std::move(params->logEntries)); + message->setNewEVMContractAddress(std::move(params->newEVMContractAddress)); + message->setDelegateCall(params->delegateCall); + message->setDelegateCallAddress(std::move(params->codeAddress)); + message->setDelegateCallCode(std::move(params->delegateCallCode)); + message->setDelegateCallSender(std::move(params->delegateCallSender)); + + return message; +} + +void TransactionExecutor::removeCommittedState() +{ + if (m_stateStorages.empty()) + { + EXECUTOR_NAME_LOG(ERROR) << "Remove committed state failed, empty states"; + return; + } + + bcos::protocol::BlockNumber number; + bcos::storage::StateStorageInterface::Ptr storage; + + { + std::unique_lock lock(m_stateStoragesMutex); + auto it = m_stateStorages.begin(); + number = it->number; + storage = it->storage; + } + + if (m_cachedStorage) + { + auto keyPageStorage = std::dynamic_pointer_cast(storage); + if (keyPageStorage) + { + EXECUTOR_NAME_LOG(INFO) + << LOG_DESC("merge keyPage to cachedStorage") << LOG_KV("number", number); + keyPageStorage->setReadOnly(true); + } + else + { + EXECUTOR_NAME_LOG(INFO) << "Merge state number: " << number << " to cachedStorage"; + } + + m_cachedStorage->merge(true, *storage); + + std::unique_lock lock(m_stateStoragesMutex); + auto it = m_stateStorages.begin(); + it = m_stateStorages.erase(it); + if (it != m_stateStorages.end()) + { + EXECUTOR_NAME_LOG(INFO) + << "Set state number, " << it->number << " prev to cachedStorage"; + it->storage->setPrev(m_cachedStorage); + } + } + else if (m_backendStorage) + { + std::unique_lock lock(m_stateStoragesMutex); + auto it = m_stateStorages.begin(); + EXECUTOR_NAME_LOG(DEBUG) << LOG_DESC("removeCommittedState") + << LOG_KV("commitNumber", number) + << LOG_KV("erasedStorage", it->number) + << LOG_KV("stateStorageSize", m_stateStorages.size()); + it = m_stateStorages.erase(it); + if (it != m_stateStorages.end()) + { + it->storage->setPrev(m_backendStorage); + } + } + + m_ledgerCache->clearCacheByNumber(number); +} + +std::unique_ptr TransactionExecutor::createCallParameters( + bcos::protocol::ExecutionMessage& input, bool staticCall) +{ + auto callParameters = std::make_unique(CallParameters::MESSAGE); + callParameters->status = input.status(); + + switch (input.type()) + { + case ExecutionMessage::MESSAGE: + { + break; + } + case ExecutionMessage::REVERT: + { + callParameters->type = CallParameters::REVERT; + break; + } + case ExecutionMessage::FINISHED: + { + callParameters->type = CallParameters::FINISHED; + break; + } + case ExecutionMessage::KEY_LOCK: + { + break; + } + case ExecutionMessage::SEND_BACK: + case ExecutionMessage::TXHASH: + { + BOOST_THROW_EXCEPTION(BCOS_ERROR( + ExecuteError::EXECUTE_ERROR, "Unexpected execution message type: " + + boost::lexical_cast(input.type()))); + } + } + + callParameters->contextID = input.contextID(); + callParameters->seq = input.seq(); + constexpr static auto addressSize = Address::SIZE * 2; + if (staticCall) + { + // padding zero + callParameters->origin = std::string(addressSize - input.origin().size(), '0'); + callParameters->senderAddress = std::string(addressSize - input.from().size(), '0'); + } + callParameters->origin += input.origin(); + callParameters->senderAddress += input.from(); + callParameters->receiveAddress = input.to(); + callParameters->codeAddress = input.to(); + callParameters->create = input.create(); + callParameters->internalCreate = input.internalCreate(); + callParameters->internalCall = input.internalCall(); + callParameters->evmStatus = input.evmStatus(); + callParameters->message = input.message(); + callParameters->data = input.takeData(); + callParameters->gas = input.gasAvailable(); + callParameters->staticCall = staticCall; + callParameters->newEVMContractAddress = input.newEVMContractAddress(); + callParameters->keyLocks = input.takeKeyLocks(); + callParameters->logEntries = input.takeLogEntries(); + if (input.create()) + { + callParameters->abi = input.abi(); + } + callParameters->delegateCall = input.delegateCall(); + callParameters->delegateCallCode = input.takeDelegateCallCode(); + callParameters->delegateCallSender = input.delegateCallSender(); + if (input.delegateCall()) + { + callParameters->codeAddress = input.delegateCallAddress(); + } + + if (!m_isWasm) + { + if (callParameters->codeAddress.size() < addressSize) [[unlikely]] + { + callParameters->codeAddress.insert( + 0, callParameters->codeAddress.size() - addressSize, '0'); + } + if (callParameters->receiveAddress.size() < addressSize) [[unlikely]] + { + callParameters->receiveAddress.insert( + 0, callParameters->receiveAddress.size() - addressSize, '0'); + } + } + + return callParameters; +} + +std::unique_ptr TransactionExecutor::createCallParameters( + bcos::protocol::ExecutionMessage& input, const bcos::protocol::Transaction& tx) +{ + auto callParameters = std::make_unique(CallParameters::MESSAGE); + + callParameters->contextID = input.contextID(); + callParameters->seq = input.seq(); + callParameters->origin = toHex(tx.sender()); + callParameters->senderAddress = callParameters->origin; + callParameters->receiveAddress = input.to(); + callParameters->codeAddress = input.to(); + callParameters->gas = input.gasAvailable(); + callParameters->staticCall = input.staticCall(); + callParameters->evmStatus = input.evmStatus(); + callParameters->create = input.create(); + callParameters->internalCreate = input.internalCreate(); + callParameters->internalCall = input.internalCall(); + callParameters->message = input.message(); + callParameters->data = tx.input().toBytes(); + callParameters->keyLocks = input.takeKeyLocks(); + callParameters->logEntries = input.takeLogEntries(); + callParameters->abi = tx.abi(); + callParameters->delegateCall = false; + callParameters->delegateCallCode = bytes(); + callParameters->delegateCallSender = ""; + + if (!m_isWasm) + { + constexpr static auto addressSize = Address::SIZE * 2; + if (callParameters->codeAddress.size() < addressSize) [[unlikely]] + { + callParameters->codeAddress.insert( + 0, callParameters->codeAddress.size() - addressSize, '0'); + } + if (callParameters->receiveAddress.size() < addressSize) [[unlikely]] + { + callParameters->receiveAddress.insert( + 0, callParameters->receiveAddress.size() - addressSize, '0'); + } + } + return callParameters; +} + +void TransactionExecutor::executeTransactionsWithCriticals( + critical::CriticalFieldsInterface::Ptr criticals, + gsl::span> inputs, + vector& executionResults) +{ + // DAG run + shared_ptr txDag = make_shared(); + txDag->init(criticals, [this, &inputs, &executionResults](ID id) { + if (!m_isRunning) + { + return; + } + + auto& input = inputs[id]; + auto executiveFactory = std::make_shared(m_blockContext, + m_precompiledContract, m_constantPrecompiled, m_builtInPrecompiled, m_gasInjector); + auto executive = + executiveFactory->build(input->codeAddress, input->contextID, input->seq, false); + + EXECUTOR_NAME_LOG(TRACE) << LOG_BADGE("executeTransactionsWithCriticals") + << LOG_DESC("Start transaction") + << LOG_KV("to", input->receiveAddress) + << LOG_KV("data", toHexStringWithPrefix(input->data)); + try + { + auto output = executive->start(std::move(input)); + assert(output); + if (output->type == CallParameters::MESSAGE) + { + EXECUTOR_NAME_LOG(DEBUG) << LOG_BADGE("call/deploy in dag") + << LOG_KV("senderAddress", output->senderAddress) + << LOG_KV("codeAddress", output->codeAddress); + } + executionResults[id] = toExecutionResult(*executive, std::move(output)); + } + catch (std::exception& e) + { + EXECUTOR_NAME_LOG(ERROR) + << "executeTransactionsWithCriticals error: " << boost::diagnostic_information(e); + } + }); + + txDag->run(m_DAGThreadNum); +} + +bcos::storage::StateStorageInterface::Ptr TransactionExecutor::createStateStorage( + bcos::storage::StorageInterface::Ptr storage, bool ignoreNotExist) +{ + if (m_keyPageSize > 0) + { + if (m_blockVersion >= static_cast(BlockVersion::V3_1_VERSION)) + { + if (m_keyPageIgnoreTables->contains(tool::FS_ROOT)) + { + for (const auto& _sub : tool::FS_ROOT_SUBS) + { + std::string sub(_sub); + m_keyPageIgnoreTables->erase(sub); + } + } + m_keyPageIgnoreTables->insert( + {std::string(ledger::SYS_CODE_BINARY), std::string(ledger::SYS_CONTRACT_ABI)}); + } + return std::make_shared( + storage, m_keyPageSize, m_blockVersion, m_keyPageIgnoreTables, ignoreNotExist); + } + return std::make_shared(storage); +} + +protocol::BlockNumber TransactionExecutor::getBlockNumberInStorage() +{ + std::promise blockNumberFuture; + m_ledger->asyncGetBlockNumber( + [this, &blockNumberFuture](Error::Ptr error, protocol::BlockNumber number) { + if (error) + { + EXECUTOR_NAME_LOG(INFO) << "Get blockNumber from storage failed"; + blockNumberFuture.set_value(-1); + } + else + { + blockNumberFuture.set_value(number); + } + }); + + return blockNumberFuture.get_future().get(); +} + +protocol::BlockHeader::Ptr TransactionExecutor::getBlockHeaderInStorage( + protocol::BlockNumber number) +{ + std::promise blockHeaderFuture; + + m_ledger->asyncGetBlockDataByNumber(number, bcos::ledger::HEADER, + [this, &blockHeaderFuture](Error::Ptr error, Block::Ptr block) { + if (error) + { + EXECUTOR_NAME_LOG(INFO) << "Get getBlockHeader from storage failed"; + blockHeaderFuture.set_value(nullptr); + } + else + { + blockHeaderFuture.set_value(block->blockHeader()); + } + }); + + + return blockHeaderFuture.get_future().get(); +} + +std::string TransactionExecutor::getCodeHash( + std::string_view tableName, storage::StateStorageInterface::Ptr const& stateStorage) +{ + std::promise codeHashPromise; + stateStorage->asyncGetRow( + tableName, ACCOUNT_CODE_HASH, [&codeHashPromise, this](auto&& error, auto&& entry) mutable { + if (error || !entry) + { + EXECUTOR_NAME_LOG(DEBUG) << "Get codeHashes success, empty codeHash"; + codeHashPromise.set_value(std::string()); + return; + } + auto codeHash = std::string(entry->getField(0)); + codeHashPromise.set_value(std::move(codeHash)); + }); + return codeHashPromise.get_future().get(); +} + + +void TransactionExecutor::stop() +{ + EXECUTOR_NAME_LOG(INFO) << "Try to stop executor"; + if (!m_isRunning) + { + EXECUTOR_NAME_LOG(INFO) << "Executor has just tried to stop"; + return; + } + m_isRunning = false; + if (m_blockContext) + { + m_blockContext->stop(); + } +} diff --git "a/BFPL\345\243\271/bcos-executor/src/executor/TransactionExecutor.h" "b/BFPL\345\243\271/bcos-executor/src/executor/TransactionExecutor.h" new file mode 100644 index 00000000..d271b68f --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executor/TransactionExecutor.h" @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2021 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. + * + * @brief TransactionExecutor + * @file TransactionExecutor.h + * @author: xingqiangbai + * @date: 2021-05-27 + * @brief TransactionExecutor + * @file TransactionExecutor.h + * @author: ancelmo + * @date: 2021-10-16 + */ +#pragma once + +#include "../Common.h" +#include "../dag/CriticalFields.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/executor/ParallelTransactionExecutorInterface.h" +#include "bcos-framework/ledger/LedgerInterface.h" +#include "bcos-framework/protocol/Block.h" +#include "bcos-framework/protocol/BlockFactory.h" +#include "bcos-framework/protocol/ProtocolTypeDef.h" +#include "bcos-framework/protocol/Transaction.h" +#include "bcos-framework/protocol/TransactionReceipt.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-framework/txpool/TxPoolInterface.h" +#include "bcos-table/src/StateStorage.h" +#include "tbb/concurrent_unordered_map.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace bcos +{ +namespace precompiled +{ +class Precompiled; +struct PrecompiledExecResult; +} // namespace precompiled +namespace wasm +{ +class GasInjector; +} +namespace executor +{ +enum ExecutorVersion : int32_t +{ + Version_3_0_0 = 1, +}; + +class TransactionExecutive; +class ExecutiveFlowInterface; +class BlockContext; +class PrecompiledContract; +template +class ClockCache; +struct FunctionAbi; +struct CallParameters; + +using executionCallback = std::function&)>; + +class TransactionExecutor : public ParallelTransactionExecutorInterface, + public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + TransactionExecutor(bcos::ledger::LedgerInterface::Ptr ledger, + txpool::TxPoolInterface::Ptr txpool, storage::MergeableStorageInterface::Ptr cachedStorage, + storage::TransactionalStorageInterface::Ptr backendStorage, + protocol::ExecutionMessageFactory::Ptr executionMessageFactory, + bcos::crypto::Hash::Ptr hashImpl, bool isWasm, bool isAuthCheck, size_t keyPageSize, + std::shared_ptr>> keyPageIgnoreTables, std::string name); + + ~TransactionExecutor() override = default; + + void nextBlockHeader(int64_t schedulerTermId, + const bcos::protocol::BlockHeader::ConstPtr& blockHeader, + std::function callback) override; + + void executeTransaction(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override; + + void call(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override; + + void executeTransactions(std::string contractAddress, + gsl::span inputs, + std::function)> + callback) override; + + void dmcExecuteTransactions(std::string contractAddress, + gsl::span inputs, + std::function)> + callback) override; + + void dmcExecuteTransaction(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override; + + void dmcCall(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override; + + void getHash(bcos::protocol::BlockNumber number, + std::function callback) override; + + void dagExecuteTransactions(gsl::span inputs, + std::function)> + callback) override; + + /* ----- XA Transaction interface Start ----- */ + + // Write data to storage uncommitted + void prepare(const bcos::protocol::TwoPCParams& params, + std::function callback) override; + + // Commit uncommitted data + void commit(const bcos::protocol::TwoPCParams& params, + std::function callback) override; + + // Rollback the changes + void rollback(const bcos::protocol::TwoPCParams& params, + std::function callback) override; + + /* ----- XA Transaction interface End ----- */ + + // drop all status + void reset(std::function callback) override; + void getCode(std::string_view contract, + std::function callback) override; + void getABI(std::string_view contract, + std::function callback) override; + + void start() override { m_isRunning = true; } + void stop() override; + + void registerNeedSwitchEvent(std::function event) { f_onNeedSwitchEvent = event; } + +protected: + void executeTransactionsInternal(std::string contractAddress, + gsl::span inputs, bool useCoroutine, + std::function)> + callback); + + virtual void dagExecuteTransactionsInternal(gsl::span> inputs, + std::function)> + callback); + virtual std::shared_ptr> extractConflictFields( + const FunctionAbi& functionAbi, const CallParameters& params, + std::shared_ptr _blockContext); + + virtual std::shared_ptr createBlockContext( + const protocol::BlockHeader::ConstPtr& currentHeader, + storage::StateStorageInterface::Ptr tableFactory); + + virtual std::shared_ptr createBlockContextForCall( + bcos::protocol::BlockNumber blockNumber, h256 blockHash, uint64_t timestamp, + int32_t blockVersion, storage::StateStorageInterface::Ptr tableFactory); + + void asyncExecute(std::shared_ptr blockContext, + bcos::protocol::ExecutionMessage::UniquePtr input, bool useCoroutine, + std::function + callback); + + std::unique_ptr toExecutionResult( + const TransactionExecutive& executive, std::unique_ptr params); + + std::unique_ptr toExecutionResult( + std::unique_ptr params); + + std::unique_ptr createCallParameters( + bcos::protocol::ExecutionMessage& inputs, bool staticCall); + + std::unique_ptr createCallParameters( + bcos::protocol::ExecutionMessage& input, const bcos::protocol::Transaction& tx); + + std::function input)> + createExternalFunctionCall(std::function& callback); + + + void removeCommittedState(); + + // execute transactions with criticals and return in executionResults + void executeTransactionsWithCriticals(critical::CriticalFieldsInterface::Ptr criticals, + gsl::span> inputs, + std::vector& executionResults); + + std::shared_ptr getExecutiveFlow( + std::shared_ptr blockContext, std::string codeAddress, bool useCoroutine); + + + void asyncExecuteExecutiveFlow(std::shared_ptr executiveFlow, + std::function&&)> + callback); + + + bcos::storage::StateStorageInterface::Ptr createStateStorage( + bcos::storage::StorageInterface::Ptr storage, bool ignoreNotExist = false); + + protocol::BlockNumber getBlockNumberInStorage(); + protocol::BlockHeader::Ptr getBlockHeaderInStorage(protocol::BlockNumber number); + std::string getCodeHash( + std::string_view tableName, storage::StateStorageInterface::Ptr const& stateStorage); + + std::string m_name; + bcos::ledger::LedgerInterface::Ptr m_ledger; + txpool::TxPoolInterface::Ptr m_txpool; + storage::MergeableStorageInterface::Ptr m_cachedStorage; + std::shared_ptr m_backendStorage; + protocol::ExecutionMessageFactory::Ptr m_executionMessageFactory; + std::shared_ptr m_blockContext; + crypto::Hash::Ptr m_hashImpl; + bool m_isAuthCheck = false; + std::shared_ptr> m_abiCache; + + struct State + { + State( + bcos::protocol::BlockNumber _number, bcos::storage::StateStorageInterface::Ptr _storage) + : number(_number), storage(std::move(_storage)) + {} + State(const State&) = delete; + State& operator=(const State&) = delete; + + bcos::protocol::BlockNumber number; + bcos::storage::StateStorageInterface::Ptr storage; + }; + std::list m_stateStorages; + + bcos::protocol::BlockHeader::Ptr m_lastCommittedBlockHeader = + getBlockHeaderInStorage(getBlockNumberInStorage()); + + struct HashCombine + { + size_t hash(const std::tuple& val) const + { + size_t seed = hashInt64(std::get<0>(val)); + boost::hash_combine(seed, hashInt64(std::get<1>(val))); + + return seed; + } + + bool equal( + const std::tuple& lhs, const std::tuple& rhs) const + { + return std::get<0>(lhs) == std::get<0>(rhs) && std::get<1>(lhs) == std::get<1>(rhs); + } + + std::hash hashInt64; + }; + + struct CallState + { + std::shared_ptr blockContext; + }; + std::shared_ptr, CallState, HashCombine>> + m_calledContext = std::make_shared< + tbb::concurrent_hash_map, CallState, HashCombine>>(); + std::shared_mutex m_stateStoragesMutex; + + std::shared_ptr>> + m_precompiledContract; + std::shared_ptr>> + m_constantPrecompiled = + std::make_shared>>(); + mutable bcos::SharedMutex x_constantPrecompiled; + + std::shared_ptr> m_builtInPrecompiled; + unsigned int m_DAGThreadNum = std::max(std::thread::hardware_concurrency(), (unsigned int)1); + std::shared_ptr m_gasInjector = nullptr; + mutable bcos::RecursiveMutex x_executiveFlowLock; + bool m_isWasm = false; + uint32_t m_blockVersion = 0; + size_t m_keyPageSize = 0; + VMSchedule m_schedule = FiscoBcosScheduleV4; + std::shared_ptr>> m_keyPageIgnoreTables; + bool m_isRunning = false; + int64_t m_schedulerTermId = -1; + + bcos::ThreadPool::Ptr m_threadPool; + void initEvmEnvironment(); + void initWasmEnvironment(); + void initTestPrecompiled(storage::StorageInterface::Ptr storage); + + std::function f_onNeedSwitchEvent; + + LedgerCache::Ptr m_ledgerCache; +}; + +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/executor/TransactionExecutorFactory.h" "b/BFPL\345\243\271/bcos-executor/src/executor/TransactionExecutorFactory.h" new file mode 100644 index 00000000..b920e648 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/executor/TransactionExecutorFactory.h" @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2021 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. + * + * @brief TransactionExecutorFactory + * @file TransactionExecutorFactory.h + * @author: jimmyshi + * @date: 2022-01-19 + */ +#pragma once + +#include "TransactionExecutor.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-ledger/src/libledger/utilities/Common.h" +#include + + +namespace bcos +{ +namespace executor +{ + +class TransactionExecutorFactory +{ +public: + using Ptr = std::shared_ptr; + + static TransactionExecutor::Ptr build(bcos::ledger::LedgerInterface::Ptr ledger, + txpool::TxPoolInterface::Ptr txpool, storage::MergeableStorageInterface::Ptr cachedStorage, + storage::TransactionalStorageInterface::Ptr backendStorage, + protocol::ExecutionMessageFactory::Ptr executionMessageFactory, + bcos::crypto::Hash::Ptr hashImpl, bool isWasm, bool isAuthCheck, size_t keyPageSize, + std::string name = "executor-" + std::to_string(utcTime())) + { // only for test + auto keyPageIgnoreTables = std::make_shared>>( + std::initializer_list>::value_type>{ + std::string(ledger::SYS_CONFIG), + std::string(ledger::SYS_CONSENSUS), + ledger::FS_ROOT, + ledger::FS_APPS, + ledger::FS_USER, + ledger::FS_SYS_BIN, + ledger::FS_USER_TABLE, + storage::StorageInterface::SYS_TABLES, + }); + return std::make_shared(ledger, txpool, cachedStorage, backendStorage, + executionMessageFactory, hashImpl, isWasm, isAuthCheck, keyPageSize, + keyPageIgnoreTables, name); + } + + TransactionExecutorFactory(bcos::ledger::LedgerInterface::Ptr ledger, + txpool::TxPoolInterface::Ptr txpool, storage::CacheStorageFactory::Ptr cacheFactory, + storage::TransactionalStorageInterface::Ptr storage, + protocol::ExecutionMessageFactory::Ptr executionMessageFactory, + bcos::crypto::Hash::Ptr hashImpl, bool isWasm, bool isAuthCheck, size_t keyPageSize, + std::string name) + : m_name(name), + m_keyPageSize(keyPageSize), + m_ledger(ledger), + m_txpool(txpool), + m_cacheFactory(cacheFactory), + m_storage(storage), + m_executionMessageFactory(executionMessageFactory), + m_hashImpl(hashImpl), + m_isWasm(isWasm), + m_isAuthCheck(isAuthCheck) + { + m_keyPageIgnoreTables = std::make_shared>>( + std::initializer_list>::value_type>{ + std::string(ledger::SYS_CONFIG), + std::string(ledger::SYS_CONSENSUS), + ledger::FS_ROOT, + ledger::FS_APPS, + ledger::FS_USER, + ledger::FS_SYS_BIN, + ledger::FS_USER_TABLE, + storage::StorageInterface::SYS_TABLES, + }); + } + + TransactionExecutor::Ptr build() + { + auto executor = std::make_shared(m_ledger, m_txpool, + m_cacheFactory ? m_cacheFactory->build() : nullptr, m_storage, + m_executionMessageFactory, m_hashImpl, m_isWasm, m_isAuthCheck, m_keyPageSize, + m_keyPageIgnoreTables, m_name + "-" + std::to_string(utcTime())); + if (f_onNeedSwitchEvent) + { + executor->registerNeedSwitchEvent(f_onNeedSwitchEvent); + } + return executor; + } + + void registerNeedSwitchEvent(std::function event) { f_onNeedSwitchEvent = event; } + +private: + std::string m_name; + size_t m_keyPageSize; + std::shared_ptr>> m_keyPageIgnoreTables; + bcos::ledger::LedgerInterface::Ptr m_ledger; + txpool::TxPoolInterface::Ptr m_txpool; + storage::CacheStorageFactory::Ptr m_cacheFactory; + storage::TransactionalStorageInterface::Ptr m_storage; + protocol::ExecutionMessageFactory::Ptr m_executionMessageFactory; + bcos::crypto::Hash::Ptr m_hashImpl; + bool m_isWasm; + bool m_isAuthCheck; + std::function f_onNeedSwitchEvent; +}; + +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/BFSPrecompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/BFSPrecompiled.cpp" new file mode 100644 index 00000000..42dfed97 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/BFSPrecompiled.cpp" @@ -0,0 +1,1048 @@ +/** + * Copyright (C) 2021 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 BFSPrecompiled.cpp + * @author: kyonRay + * @date 2021-06-10 + */ + +#include "BFSPrecompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include "bcos-executor/src/precompiled/common/PrecompiledResult.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::precompiled; +using namespace bcos::protocol; + +constexpr const char* const FILE_SYSTEM_METHOD_LIST = "list(string)"; +constexpr const char* const FILE_SYSTEM_METHOD_LIST_PAGE = "list(string,uint256,uint256)"; +constexpr const char* const FILE_SYSTEM_METHOD_MKDIR = "mkdir(string)"; +constexpr const char* const FILE_SYSTEM_METHOD_LINK_CNS = "link(string,string,string,string)"; +constexpr const char* const FILE_SYSTEM_METHOD_LINK = "link(string,string,string)"; +constexpr const char* const FILE_SYSTEM_METHOD_RLINK = "readlink(string)"; +constexpr const char* const FILE_SYSTEM_METHOD_TOUCH = "touch(string,string)"; +constexpr const char* const FILE_SYSTEM_METHOD_INIT = "initBfs()"; +constexpr const char* const FILE_SYSTEM_METHOD_REBUILD = "rebuildBfs(uint256,uint256)"; + +BFSPrecompiled::BFSPrecompiled(crypto::Hash::Ptr _hashImpl) : Precompiled(_hashImpl) +{ + name2Selector[FILE_SYSTEM_METHOD_LIST] = getFuncSelector(FILE_SYSTEM_METHOD_LIST, _hashImpl); + name2Selector[FILE_SYSTEM_METHOD_LIST_PAGE] = + getFuncSelector(FILE_SYSTEM_METHOD_LIST_PAGE, _hashImpl); + name2Selector[FILE_SYSTEM_METHOD_MKDIR] = getFuncSelector(FILE_SYSTEM_METHOD_MKDIR, _hashImpl); + name2Selector[FILE_SYSTEM_METHOD_LINK] = getFuncSelector(FILE_SYSTEM_METHOD_LINK, _hashImpl); + name2Selector[FILE_SYSTEM_METHOD_LINK_CNS] = + getFuncSelector(FILE_SYSTEM_METHOD_LINK_CNS, _hashImpl); + name2Selector[FILE_SYSTEM_METHOD_TOUCH] = getFuncSelector(FILE_SYSTEM_METHOD_TOUCH, _hashImpl); + name2Selector[FILE_SYSTEM_METHOD_RLINK] = getFuncSelector(FILE_SYSTEM_METHOD_RLINK, _hashImpl); + name2Selector[FILE_SYSTEM_METHOD_INIT] = getFuncSelector(FILE_SYSTEM_METHOD_INIT, _hashImpl); + name2Selector[FILE_SYSTEM_METHOD_REBUILD] = + getFuncSelector(FILE_SYSTEM_METHOD_REBUILD, _hashImpl); + BfsTypeSet = {FS_TYPE_DIR, FS_TYPE_CONTRACT, FS_TYPE_LINK}; +} + +std::shared_ptr BFSPrecompiled::call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) +{ + uint32_t func = getParamFunc(_callParameters->input()); + uint32_t version = _executive->blockContext().lock()->blockVersion(); + + if (func == name2Selector[FILE_SYSTEM_METHOD_LIST]) + { + // list(string) => (int32,fileList) + listDir(_executive, _callParameters); + } + else if (version >= static_cast(BlockVersion::V3_1_VERSION) && + func == name2Selector[FILE_SYSTEM_METHOD_LIST_PAGE]) + { + // list(string,uint,uint) => (int32,fileList) + listDirPage(_executive, _callParameters); + } + else if (func == name2Selector[FILE_SYSTEM_METHOD_MKDIR]) + { + // mkdir(string) => int32 + makeDir(_executive, _callParameters); + } + else if (func == name2Selector[FILE_SYSTEM_METHOD_LINK_CNS]) + { + // link(string name, string version, address, abi) => int32 + linkAdaptCNS(_executive, _callParameters); + } + else if (version >= static_cast(BlockVersion::V3_1_VERSION) && + func == name2Selector[FILE_SYSTEM_METHOD_LINK]) + { + // link(absolutePath, address, abi) => int32 + link(_executive, _callParameters); + } + else if (func == name2Selector[FILE_SYSTEM_METHOD_RLINK]) + { + readLink(_executive, _callParameters); + } + else if (func == name2Selector[FILE_SYSTEM_METHOD_TOUCH]) + { + // touch(string absolute,string type) => int32 + touch(_executive, _callParameters); + } + else if (version >= static_cast(BlockVersion::V3_1_VERSION) && + func == name2Selector[FILE_SYSTEM_METHOD_INIT]) + { + // initBfs for the first time + initBfs(_executive, _callParameters); + } + else if (func == name2Selector[FILE_SYSTEM_METHOD_REBUILD]) + { + // initBfs for the first time + rebuildBfs(_executive, _callParameters); + } + else + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("BFSPrecompiled") + << LOG_DESC("call undefined function!"); + BOOST_THROW_EXCEPTION(PrecompiledError("BFSPrecompiled call undefined function!")); + } + + return _callParameters; +} + +int BFSPrecompiled::checkLinkParam(TransactionExecutive::Ptr _executive, + std::string const& _contractAddress, std::string& _contractName, std::string& _contractVersion, + std::string const& _contractAbi) +{ + boost::trim(_contractName); + boost::trim(_contractVersion); + // check the status of the contract(only print the error message to the log) + std::string tableName = getContractTableName(_contractAddress); + ContractStatus contractStatus = getContractStatus(_executive, tableName); + + if (contractStatus != ContractStatus::Available) + { + std::stringstream errorMessage; + errorMessage << "Link operation failed for "; + switch (contractStatus) + { + case ContractStatus::Frozen: + errorMessage << "\"" << _contractName + << "\" has been frozen, contractAddress = " << _contractAddress; + break; + case ContractStatus::AddressNonExistent: + errorMessage << "the contract \"" << _contractName << "\" with address " + << _contractAddress << " does not exist"; + break; + case ContractStatus::NotContractAddress: + errorMessage << "invalid address " << _contractAddress + << ", please make sure it's a contract address"; + break; + default: + errorMessage << "invalid contract \"" << _contractName << "\" with address " + << _contractAddress << ", error code:" << std::to_string(contractStatus); + break; + } + PRECOMPILED_LOG(INFO) << LOG_BADGE("BFSPrecompiled") << LOG_DESC(errorMessage.str()) + << LOG_KV("contractAddress", _contractAddress) + << LOG_KV("contractName", _contractName); + return CODE_ADDRESS_OR_VERSION_ERROR; + } + if (_contractVersion.find('/') != std::string::npos || + _contractName.find('/') != std::string::npos) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("BFSPrecompiled") + << LOG_DESC("version or name contains \"/\"") + << LOG_KV("contractName", _contractName) + << LOG_KV("version", _contractVersion); + return CODE_ADDRESS_OR_VERSION_ERROR; + } + // check the length of the field value + checkLengthValidate( + _contractAbi, USER_TABLE_FIELD_VALUE_MAX_LENGTH, CODE_TABLE_FIELD_VALUE_LENGTH_OVERFLOW); + return CODE_SUCCESS; +} + +void BFSPrecompiled::makeDir(const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) + +{ + // mkdir(string) + std::string absolutePath; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_callParameters->params(), absolutePath); + PRECOMPILED_LOG(INFO) << BLOCK_NUMBER(blockContext->number()) << LOG_BADGE("BFSPrecompiled") + << LOG_KV("mkdir", absolutePath); + auto table = _executive->storage().openTable(absolutePath); + if (table) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("BFSPrecompiled") + << LOG_DESC("BFS file name already exists, please check") + << LOG_KV("absolutePath", absolutePath); + _callParameters->setExecResult(codec.encode(int32_t(CODE_FILE_ALREADY_EXIST))); + return; + } + + const auto* bfsAddress = blockContext->isWasm() ? BFS_NAME : BFS_ADDRESS; + + auto response = externalTouchNewFile(_executive, _callParameters->m_origin, bfsAddress, + absolutePath, FS_TYPE_DIR, _callParameters->m_gasLeft); + _callParameters->setExecResult(codec.encode(response)); +} + +void BFSPrecompiled::listDir(const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) +{ + // list(string) + std::string absolutePath; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_callParameters->params(), absolutePath); + std::vector files = {}; + PRECOMPILED_LOG(TRACE) << LOG_BADGE("BFSPrecompiled") << LOG_DESC("ls path") + << LOG_KV("path", absolutePath); + if (!checkPathValid(absolutePath)) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("BFSPrecompiled") << LOG_DESC("invalid path name") + << LOG_KV("path", absolutePath); + _callParameters->setExecResult(codec.encode(int32_t(CODE_FILE_INVALID_PATH), files)); + return; + } + auto table = _executive->storage().openTable(absolutePath); + + if (table) + { + // exist + auto [parentDir, baseName] = getParentDirAndBaseName(absolutePath); + if (blockContext->blockVersion() >= (uint32_t)BlockVersion::V3_1_VERSION) + { + // check parent dir to get type + auto baseNameEntry = _executive->storage().getRow(parentDir, baseName); + if (baseName == tool::FS_ROOT) + { + // root special logic + Entry entry; + tool::BfsFileFactory::buildDirEntry(entry, FS_TYPE_DIR); + baseNameEntry = std::make_optional(entry); + } + if (!baseNameEntry) [[unlikely]] + { + // maybe hidden table + PRECOMPILED_LOG(DEBUG) + << LOG_BADGE("BFSPrecompiled") << LOG_DESC("list not exist file") + << LOG_KV("absolutePath", absolutePath); + _callParameters->setExecResult(codec.encode(int32_t(CODE_FILE_NOT_EXIST), files)); + return; + } + auto baseFields = baseNameEntry->getObject>(); + if (baseFields[0] == tool::FS_TYPE_DIR) + { + // if type is dir, then return sub resource + auto keyCondition = std::make_optional(); + // max return is 500 + keyCondition->limit(0, USER_TABLE_MAX_LIMIT_COUNT); + auto keys = _executive->storage().getPrimaryKeys(absolutePath, keyCondition); + for (const auto& key : keys | RANGES::views::all) + { + auto entry = _executive->storage().getRow(absolutePath, key); + auto fields = entry->getObject>(); + files.emplace_back( + key, fields[0], std::vector{fields.begin() + 1, fields.end()}); + } + } + else if (baseFields[0] == tool::FS_TYPE_LINK) + { + // if type is link, then return link address + auto addressEntry = _executive->storage().getRow(absolutePath, FS_LINK_ADDRESS); + auto abiEntry = _executive->storage().getRow(absolutePath, FS_LINK_ABI); + std::vector ext = {std::string(addressEntry->getField(0)), + abiEntry.has_value() ? std::string(abiEntry->getField(0)) : ""}; + BfsTuple link = std::make_tuple(baseName, FS_TYPE_LINK, std::move(ext)); + files.emplace_back(std::move(link)); + } + else if (baseFields[0] == tool::FS_TYPE_CONTRACT) + { + // if type is contract, then return contract name + files.emplace_back(baseName, tool::FS_TYPE_CONTRACT, + std::vector{baseFields.begin() + 1, baseFields.end()}); + } + _callParameters->setExecResult(codec.encode(int32_t(CODE_SUCCESS), files)); + return; + } + + // file exists, try to get type + auto typeEntry = _executive->storage().getRow(absolutePath, FS_KEY_TYPE); + if (typeEntry) + { + // get type success, this is dir or link + // if dir + if (typeEntry->getField(0) == FS_TYPE_DIR) + { + auto subEntry = _executive->storage().getRow(absolutePath, FS_KEY_SUB); + std::map bfsInfo; + auto&& out = asBytes(std::string(subEntry->getField(0))); + codec::scale::decode(bfsInfo, gsl::make_span(out)); + files.reserve(bfsInfo.size()); + for (const auto& bfs : bfsInfo) + { + BfsTuple file = + std::make_tuple(bfs.first, bfs.second, std::vector({})); + files.emplace_back(std::move(file)); + } + } + else if (typeEntry->getField(0) == FS_TYPE_LINK) + { + // if link + auto addressEntry = _executive->storage().getRow(absolutePath, FS_LINK_ADDRESS); + auto abiEntry = _executive->storage().getRow(absolutePath, FS_LINK_ABI); + std::vector ext = {std::string(addressEntry->getField(0)), + abiEntry.has_value() ? std::string(abiEntry->getField(0)) : ""}; + BfsTuple link = std::make_tuple(baseName, FS_TYPE_LINK, std::move(ext)); + files.emplace_back(std::move(link)); + } + } + else + { + // fail to get type, this is contract + BfsTuple file = + std::make_tuple(baseName, FS_TYPE_CONTRACT, std::vector({})); + files.emplace_back(std::move(file)); + } + + _callParameters->setExecResult(codec.encode(int32_t(CODE_SUCCESS), files)); + } + else + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("BFSPrecompiled") << LOG_DESC("list not exist file") + << LOG_KV("absolutePath", absolutePath); + _callParameters->setExecResult(codec.encode(int32_t(CODE_FILE_NOT_EXIST), files)); + } +} + +void BFSPrecompiled::listDirPage(const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) + +{ + // list(string,uint,uint) + std::string absolutePath; + u256 offset = 0; + u256 count = 0; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_callParameters->params(), absolutePath, offset, count); + std::vector files = {}; + PRECOMPILED_LOG(TRACE) << LOG_BADGE("BFSPrecompiled") << LOG_DESC("ls path") + << LOG_KV("path", absolutePath) << LOG_KV("offset", offset) + << LOG_KV("count", count); + if (!checkPathValid(absolutePath)) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("BFSPrecompiled") << LOG_DESC("invalid path name") + << LOG_KV("path", absolutePath); + _callParameters->setExecResult(codec.encode(s256((int)CODE_FILE_INVALID_PATH), files)); + return; + } + auto table = _executive->storage().openTable(absolutePath); + + if (!table) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("BFSPrecompiled") << LOG_DESC("list not exist file") + << LOG_KV("absolutePath", absolutePath); + _callParameters->setExecResult(codec.encode(s256((int)CODE_FILE_NOT_EXIST), files)); + return; + } + + // exist + auto [parentDir, baseName] = getParentDirAndBaseName(absolutePath); + + // check parent dir to get type + auto baseNameEntry = _executive->storage().getRow(parentDir, baseName); + if (baseName == tool::FS_ROOT) + { + baseNameEntry = std::make_optional(); + tool::BfsFileFactory::buildDirEntry(baseNameEntry.value(), tool::FileType::DIRECTOR); + } + if (!baseNameEntry) [[unlikely]] + { + // maybe hidden table + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("BFSPrecompiled") << LOG_DESC("list not exist file") + << LOG_KV("parentDir", parentDir) << LOG_KV("baseName", baseName); + _callParameters->setExecResult(codec.encode(s256((int)CODE_FILE_NOT_EXIST), files)); + return; + } + auto baseFields = baseNameEntry->getObject>(); + if (baseFields[0] == tool::FS_TYPE_DIR) + { + // if type is dir, then return sub resource + auto keyCondition = std::make_optional(); + keyCondition->limit((size_t)offset, (size_t)count); + auto keys = _executive->storage().getPrimaryKeys(absolutePath, keyCondition); + + for (const auto& key : keys | RANGES::views::all) + { + auto entry = _executive->storage().getRow(absolutePath, key); + auto fields = entry->getObject>(); + files.emplace_back( + key, fields[0], std::vector{fields.begin() + 1, fields.end()}); + } + if (count == keys.size()) + { + // count is full, maybe still left elements + auto [total, error] = _executive->storage().count(absolutePath); + if (error) + { + PRECOMPILED_LOG(DEBUG) + << LOG_BADGE("BFSPrecompiled") << LOG_DESC("list not exist file") + << LOG_KV("absolutePath", absolutePath); + _callParameters->setExecResult( + codec.encode(s256((int)CODE_FILE_COUNT_ERROR), std::vector{})); + return; + } + auto result = codec.encode(s256(total - offset - count), files); + if (c_fileLogLevel == LogLevel::TRACE) + { + PRECOMPILED_LOG(TRACE) + << LOG_BADGE("BFSPrecompiled") << LOG_DESC("list trace") + << LOG_KV("filesSize", files.size()) << LOG_KV("resultDataSize", result.size()); + } + _callParameters->setExecResult(std::move(result)); + return; + } + } + else if (baseFields[0] == tool::FS_TYPE_LINK) + { + // if type is link, then return link address + auto addressEntry = _executive->storage().getRow(absolutePath, FS_LINK_ADDRESS); + auto abiEntry = _executive->storage().getRow(absolutePath, FS_LINK_ABI); + std::vector ext = {std::string(addressEntry->getField(0)), + abiEntry.has_value() ? std::string(abiEntry->getField(0)) : ""}; + BfsTuple link = std::make_tuple(baseName, FS_TYPE_LINK, std::move(ext)); + files.emplace_back(std::move(link)); + } + else if (baseFields[0] == tool::FS_TYPE_CONTRACT) + { + // if type is contract, then return contract name + files.emplace_back(baseName, tool::FS_TYPE_CONTRACT, + std::vector{baseFields.begin() + 1, baseFields.end()}); + } + auto result = codec.encode(s256((int)CODE_SUCCESS), files); + if (c_fileLogLevel <= LogLevel::TRACE) + { + PRECOMPILED_LOG(TRACE) << LOG_BADGE("BFSPrecompiled") << LOG_DESC("list trace") + << LOG_KV("filesSize", files.size()) + << LOG_KV("resultDataSize", result.size()); + } + _callParameters->setExecResult(std::move(result)); +} + +void BFSPrecompiled::link(const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) +{ + std::string absolutePath, contractAddress, contractAbi; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_callParameters->params(), absolutePath, contractAddress, contractAbi); + if (!blockContext->isWasm()) + { + contractAddress = trimHexPrefix(contractAddress); + } + + PRECOMPILED_LOG(INFO) << BLOCK_NUMBER(blockContext->number()) << LOG_BADGE("BFSPrecompiled") + << LOG_DESC("link") << LOG_KV("absolutePath", absolutePath) + << LOG_KV("contractAddress", contractAddress) + << LOG_KV("contractAbiSize", contractAbi.size()); + auto linkTableName = getContractTableName(absolutePath); + + if (!checkPathValid(linkTableName)) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("BFSPrecompiled") + << LOG_DESC("check link params failed, invalid path name") + << LOG_KV("absolutePath", absolutePath) + << LOG_KV("contractAddress", contractAddress); + _callParameters->setExecResult(codec.encode(s256((int)CODE_FILE_INVALID_PATH))); + return; + } + + auto linkTable = _executive->storage().openTable(linkTableName); + if (linkTable) + { + // table exist, check this resource is a link + auto typeEntry = _executive->storage().getRow(linkTableName, FS_KEY_TYPE); + if (typeEntry && typeEntry->getField(0) == FS_TYPE_LINK) + { + // contract name and version exist, overwrite address and abi + tool::BfsFileFactory::buildLink(linkTable.value(), contractAddress, contractAbi); + _callParameters->setExecResult(codec.encode(s256((int)CODE_SUCCESS))); + return; + } + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("BFSPrecompiled") << LOG_DESC("File already exists.") + << LOG_KV("absolutePath", absolutePath); + _callParameters->setExecResult(codec.encode(s256((int)CODE_FILE_ALREADY_EXIST))); + return; + } + // table not exist, mkdir -p /apps/contractName first + std::string bfsAddress = blockContext->isWasm() ? BFS_NAME : BFS_ADDRESS; + + auto response = externalTouchNewFile(_executive, _callParameters->m_origin, bfsAddress, + linkTableName, FS_TYPE_LINK, _callParameters->m_gasLeft); + if (response != 0) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("BFSPrecompiled") + << LOG_DESC("external build link file metadata failed") + << LOG_KV("absolutePath", absolutePath); + _callParameters->setExecResult(codec.encode(s256((int)CODE_FILE_BUILD_DIR_FAILED))); + return; + } + auto newLinkTable = _executive->storage().createTable(linkTableName, STORAGE_VALUE); + // set link info to link table + tool::BfsFileFactory::buildLink(newLinkTable.value(), contractAddress, contractAbi); + _callParameters->setExecResult(codec.encode(s256((int)CODE_SUCCESS))); +} + +void BFSPrecompiled::linkAdaptCNS(const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) +{ + std::string contractName, contractVersion, contractAddress, contractAbi; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode( + _callParameters->params(), contractName, contractVersion, contractAddress, contractAbi); + if (!blockContext->isWasm()) + { + contractAddress = trimHexPrefix(contractAddress); + } + + PRECOMPILED_LOG(INFO) << BLOCK_NUMBER(blockContext->number()) << LOG_BADGE("BFSPrecompiled") + << LOG_DESC("link") << LOG_KV("contractName", contractName) + << LOG_KV("contractVersion", contractVersion) + << LOG_KV("contractAddress", contractAddress) + << LOG_KV("contractAbiSize", contractAbi.size()); + int validCode = + checkLinkParam(_executive, contractAddress, contractName, contractVersion, contractAbi); + auto linkTableName = std::string(USER_APPS_PREFIX) + contractName + '/' + contractVersion; + + if (validCode < 0 || !checkPathValid(linkTableName)) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("BFSPrecompiled") + << LOG_DESC("check link params failed, invalid path name") + << LOG_KV("contractName", contractName) + << LOG_KV("contractVersion", contractVersion) + << LOG_KV("contractAddress", contractAddress); + _callParameters->setExecResult( + codec.encode(int32_t(validCode < 0 ? validCode : CODE_FILE_INVALID_PATH))); + return; + } + auto linkTable = _executive->storage().openTable(linkTableName); + if (linkTable) + { + // table exist, check this resource is a link + auto typeEntry = _executive->storage().getRow(linkTableName, FS_KEY_TYPE); + if (typeEntry && typeEntry->getField(0) == FS_TYPE_LINK) + { + // contract name and version exist, overwrite address and abi + auto addressEntry = linkTable->newEntry(); + addressEntry.importFields({contractAddress}); + auto abiEntry = linkTable->newEntry(); + abiEntry.importFields({contractAbi}); + _executive->storage().setRow(linkTableName, FS_LINK_ADDRESS, std::move(addressEntry)); + _executive->storage().setRow(linkTableName, FS_LINK_ABI, std::move(abiEntry)); + _callParameters->setExecResult(codec.encode(int32_t(CODE_SUCCESS))); + return; + } + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("BFSPrecompiled") << LOG_DESC("File already exists.") + << LOG_KV("contractName", contractName) + << LOG_KV("version", contractVersion); + _callParameters->setExecResult(codec.encode((int32_t)CODE_FILE_ALREADY_EXIST)); + return; + } + // table not exist, mkdir -p /apps/contractName first + std::string bfsAddress = blockContext->isWasm() ? BFS_NAME : BFS_ADDRESS; + + auto response = externalTouchNewFile(_executive, _callParameters->m_origin, bfsAddress, + linkTableName, FS_TYPE_LINK, _callParameters->m_gasLeft); + if (response != 0) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("BFSPrecompiled") + << LOG_DESC("external build link file metadata failed") + << LOG_KV("contractName", contractName) + << LOG_KV("contractVersion", contractVersion); + _callParameters->setExecResult(codec.encode((int32_t)CODE_FILE_BUILD_DIR_FAILED)); + return; + } + auto newLinkTable = _executive->storage().createTable(linkTableName, STORAGE_VALUE); + // set link info to link table + tool::BfsFileFactory::buildLink( + newLinkTable.value(), contractAddress, contractAbi, contractVersion); + _callParameters->setExecResult(codec.encode((int32_t)CODE_SUCCESS)); +} + +void BFSPrecompiled::readLink(const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) +{ + // readlink(string) + std::string absolutePath; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_callParameters->params(), absolutePath); + PRECOMPILED_LOG(TRACE) << LOG_BADGE("BFSPrecompiled") << LOG_DESC("readLink path") + << LOG_KV("path", absolutePath); + bytes emptyResult = + blockContext->isWasm() ? codec.encode(std::string("")) : codec.encode(Address()); + if (!checkPathValid(absolutePath)) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("BFSPrecompiled") + << LOG_DESC("invalid file path name, return empty address") + << LOG_KV("path", absolutePath); + _callParameters->setExecResult(emptyResult); + return; + } + auto table = _executive->storage().openTable(absolutePath); + + if (table) + { + // file exists, try to get type + auto typeEntry = _executive->storage().getRow(absolutePath, FS_KEY_TYPE); + if (typeEntry && typeEntry->getField(0) == FS_TYPE_LINK) + { + // if link + auto addressEntry = _executive->storage().getRow(absolutePath, FS_LINK_ADDRESS); + auto contractAddress = std::string(addressEntry->getField(0)); + auto codecAddress = blockContext->isWasm() ? codec.encode(contractAddress) : + codec.encode(Address(contractAddress)); + _callParameters->setExecResult(codecAddress); + return; + } + + _callParameters->setExecResult(emptyResult); + return; + } + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("BFSPrecompiled") + << LOG_DESC("link file not exist, return empty address") + << LOG_KV("path", absolutePath); + _callParameters->setExecResult(emptyResult); +} + +void BFSPrecompiled::touch(const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) +{ + // touch(string absolute, string type) + std::string absolutePath; + std::string type; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_callParameters->params(), absolutePath, type); + PRECOMPILED_LOG(INFO) << BLOCK_NUMBER(blockContext->number()) << LOG_BADGE("BFSPrecompiled") + << LOG_DESC("touch new file") << LOG_KV("absolutePath", absolutePath) + << LOG_KV("type", type); + if (!checkPathValid(absolutePath)) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("BFSPrecompiled") << LOG_DESC("file name is invalid"); + + _callParameters->setExecResult(codec.encode(int32_t(CODE_FILE_INVALID_PATH))); + return; + } + if (BfsTypeSet.find(type) == BfsTypeSet.end()) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("BFSPrecompiled") + << LOG_DESC("touch file in error type") + << LOG_KV("absolutePath", absolutePath) << LOG_KV("type", type); + _callParameters->setExecResult(codec.encode(int32_t(CODE_FILE_INVALID_TYPE))); + return; + } + if (!absolutePath.starts_with(USER_APPS_PREFIX) && !absolutePath.starts_with(USER_TABLE_PREFIX)) + { + if (blockContext->blockVersion() >= + (uint32_t)(bcos::protocol::BlockVersion::V3_1_VERSION) && + absolutePath.starts_with(USER_USR_PREFIX)) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("BFSPrecompiled") << LOG_DESC("touch /usr/ file") + << LOG_KV("absolutePath", absolutePath) << LOG_KV("type", type); + } + else + { + PRECOMPILED_LOG(DEBUG) + << LOG_BADGE("BFSPrecompiled") + << LOG_DESC("only support touch file under the system dir /apps/, /tables/") + << LOG_KV("absolutePath", absolutePath) << LOG_KV("type", type); + _callParameters->setExecResult(codec.encode(int32_t(CODE_FILE_INVALID_PATH))); + return; + } + } + + std::string parentDir; + std::string baseName; + if (type == FS_TYPE_DIR) + { + parentDir = absolutePath; + } + else + { + std::tie(parentDir, baseName) = getParentDirAndBaseName(absolutePath); + } + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("BFSPrecompiled") + << LOG_DESC("directory not exists, build dir first") + << LOG_KV("parentDir", parentDir) << LOG_KV("baseName", baseName) + << LOG_KV("type", type); + auto buildResult = recursiveBuildDir(_executive, parentDir); + if (!buildResult) + { + BOOST_THROW_EXCEPTION(PrecompiledError("Recursive build bfs dir error.")); + } + if (type == FS_TYPE_DIR) + { + _callParameters->setExecResult(codec.encode(int32_t(CODE_SUCCESS))); + return; + } + + // set meta data in parent table + if (blockContext->blockVersion() >= (uint32_t)BlockVersion::V3_1_VERSION) + { + Entry subEntry; + tool::BfsFileFactory::buildDirEntry(subEntry, type); + _executive->storage().setRow(parentDir, baseName, std::move(subEntry)); + + _callParameters->setExecResult(codec.encode(int32_t(CODE_SUCCESS))); + } + else + { + std::map bfsInfo; + auto subEntry = _executive->storage().getRow(parentDir, FS_KEY_SUB); + auto&& out = asBytes(std::string(subEntry->getField(0))); + codec::scale::decode(bfsInfo, gsl::make_span(out)); + bfsInfo.insert(std::make_pair(baseName, type)); + subEntry->setField(0, asString(codec::scale::encode(bfsInfo))); + _executive->storage().setRow(parentDir, FS_KEY_SUB, std::move(subEntry.value())); + + _callParameters->setExecResult(codec.encode(int32_t(CODE_SUCCESS))); + } +} + +void BFSPrecompiled::initBfs(const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) +{ + PRECOMPILED_LOG(INFO) << LOG_BADGE("BFSPrecompiled") << LOG_DESC("initBfs"); + auto table = _executive->storage().openTable(tool::FS_ROOT); + if (table.has_value()) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("BFSPrecompiled") + << LOG_DESC("initBfs, root table already exist, return by default"); + return; + } + // create / dir + _executive->storage().createTable(std::string(tool::FS_ROOT), std::string(tool::FS_DIR_FIELDS)); + // build root subs metadata + for (const auto& subName : tool::FS_ROOT_SUBS | RANGES::views::drop(1)) + { + Entry entry; + // type, status, acl_type, acl_white, acl_black, extra + tool::BfsFileFactory::buildDirEntry(entry, tool::FileType::DIRECTOR); + _executive->storage().setRow(tool::FS_ROOT, + subName == tool::FS_ROOT ? subName : subName.substr(1), std::move(entry)); + } + // build apps, usr, tables metadata + _executive->storage().createTable(std::string(tool::FS_USER), std::string(tool::FS_DIR_FIELDS)); + _executive->storage().createTable(std::string(tool::FS_APPS), std::string(tool::FS_DIR_FIELDS)); + _executive->storage().createTable( + std::string(tool::FS_USER_TABLE), std::string(tool::FS_DIR_FIELDS)); + _executive->storage().createTable( + std::string(tool::FS_SYS_BIN), std::string(tool::FS_DIR_FIELDS)); + + // build /sys/ + buildSysSubs(_executive); +} + +void BFSPrecompiled::rebuildBfs(const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) +{ + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + if (_callParameters->m_sender != precompiled::SYS_CONFIG_ADDRESS && + _callParameters->m_sender != precompiled::SYS_CONFIG_NAME) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("BFSPrecompiled") + << LOG_DESC("rebuildBfs not called by sys config") + << LOG_KV("sender", _callParameters->m_sender); + _callParameters->setExecResult(codec.encode(int32_t(CODE_NO_AUTHORIZED))); + return; + } + uint32_t fromVersion = 0; + uint32_t toVersion = 0; + codec.decode(_callParameters->params(), fromVersion, toVersion); + PRECOMPILED_LOG(INFO) << LOG_BADGE("BFSPrecompiled") << LOG_DESC("rebuildBfs") + << LOG_KV("fromVersion", fromVersion) << LOG_KV("toVersion", toVersion); + // TODO: add from and to version check + if (fromVersion <= static_cast(BlockVersion::V3_0_VERSION) && + toVersion >= static_cast(BlockVersion::V3_1_VERSION)) + { + rebuildBfs310(_executive); + } + _callParameters->setExecResult(codec.encode(int32_t(CODE_SUCCESS))); +} + +void BFSPrecompiled::rebuildBfs310( + const std::shared_ptr& _executive) +{ + auto blockContext = _executive->blockContext().lock(); + auto keyPageIgnoreTables = blockContext->keyPageIgnoreTables(); + // child, parent, all absolute path + std::queue> rebuildQ; + rebuildQ.push({std::string(tool::FS_ROOT), ""}); + bool rebuildSys = true; + while (!rebuildQ.empty()) + { + keyPageIgnoreTables->insert(tool::FS_ROOT_SUBS.begin(), tool::FS_ROOT_SUBS.end()); + auto [rebuildPath, parentPath] = rebuildQ.front(); + rebuildQ.pop(); + auto subEntry = _executive->storage().getRow(rebuildPath, tool::FS_KEY_SUB); + auto table = _executive->storage().openTable(rebuildPath); + if (!subEntry.has_value() || table->tableInfo()->fields().size() > 1) + { + // not old data structure + // root is new data structure + rebuildSys = (rebuildPath != tool::FS_ROOT); + continue; + } + + // rewrite type, acl_type, acl_white, acl_black, extra to parent + auto typeEntry = _executive->storage().getRow(rebuildPath, tool::FS_KEY_TYPE); + auto aclTypeEntry = _executive->storage().getRow(rebuildPath, tool::FS_ACL_TYPE); + auto aclWhiteEntry = _executive->storage().getRow(rebuildPath, tool::FS_ACL_WHITE); + auto aclBlackEntry = _executive->storage().getRow(rebuildPath, tool::FS_ACL_BLACK); + auto extraEntry = _executive->storage().getRow(rebuildPath, tool::FS_KEY_EXTRA); + // root has no parent + Entry newFormEntry; + newFormEntry.setObject(std::vector{ + std::string(typeEntry->get()), + std::string("0"), + std::string(aclTypeEntry->get()), + std::string(aclWhiteEntry->get()), + std::string(aclBlackEntry->get()), + std::string(extraEntry->get()), + }); + typeEntry->setStatus(Entry::Status::DELETED); + aclTypeEntry->setStatus(Entry::Status::DELETED); + aclWhiteEntry->setStatus(Entry::Status::DELETED); + aclBlackEntry->setStatus(Entry::Status::DELETED); + extraEntry->setStatus(Entry::Status::DELETED); + _executive->storage().setRow(rebuildPath, tool::FS_KEY_TYPE, std::move(typeEntry.value())); + _executive->storage().setRow( + rebuildPath, tool::FS_ACL_TYPE, std::move(aclTypeEntry.value())); + _executive->storage().setRow( + rebuildPath, tool::FS_ACL_WHITE, std::move(aclWhiteEntry.value())); + _executive->storage().setRow( + rebuildPath, tool::FS_ACL_BLACK, std::move(aclBlackEntry.value())); + _executive->storage().setRow( + rebuildPath, tool::FS_KEY_EXTRA, std::move(extraEntry.value())); + + std::map bfsInfo; + auto&& out = asBytes(std::string(subEntry->get())); + codec::scale::decode(bfsInfo, gsl::make_span(out)); + + // delete sub + subEntry->setStatus(Entry::Status::DELETED); + _executive->storage().setRow(rebuildPath, tool::FS_KEY_SUB, std::move(subEntry.value())); + + // use keyPage to rewrite info + for (const auto& _sub : tool::FS_ROOT_SUBS) + { + std::string sub(_sub); + keyPageIgnoreTables->erase(sub); + } + if (!parentPath.empty()) + { + _executive->storage().setRow( + parentPath, getPathBaseName(rebuildPath), std::move(newFormEntry)); + } + + // rewrite sub info + for (const auto& [name, type] : bfsInfo) + { + Entry entry; + tool::BfsFileFactory::buildDirEntry(entry, type); + _executive->storage().setRow(rebuildPath, name, std::move(entry)); + if (type == tool::FS_TYPE_DIR) + { + rebuildQ.push( + {(rebuildPath == tool::FS_ROOT ? rebuildPath : rebuildPath + "/") + name, + rebuildPath}); + } + } + } + if (rebuildSys) + { + // build /sys/ + buildSysSubs(_executive); + } +} + +bool BFSPrecompiled::recursiveBuildDir( + const std::shared_ptr& _executive, + const std::string& _absoluteDir) +{ + if (_absoluteDir.empty()) + { + return false; + } + // transfer /usr/local/bin => ["usr", "local", "bin"] + std::vector dirList; + std::string absoluteDir = _absoluteDir; + if (absoluteDir.starts_with('/')) + { + absoluteDir = absoluteDir.substr(1); + } + if (absoluteDir.ends_with('/')) + { + absoluteDir.pop_back(); + } + boost::split(dirList, absoluteDir, boost::is_any_of("/"), boost::token_compress_on); + std::string root = "/"; + + auto version = _executive->blockContext().lock()->blockVersion(); + for (const auto& dir : dirList) + { + auto table = _executive->storage().openTable(root); + if (!table) + { + EXECUTIVE_LOG(TRACE) << LOG_BADGE("recursiveBuildDir") + << LOG_DESC("can not open path table") + << LOG_KV("tableName", root); + return false; + } + auto newTableName = ((root == "/") ? root : (root + "/")).append(dir); + + if (version >= (uint32_t)BlockVersion::V3_1_VERSION) + { + auto dirEntry = _executive->storage().getRow(root, dir); + if (!dirEntry) + { + // not exist, then set row to root, create dir + Entry newEntry; + // type, status, acl_type, acl_white, acl_black, extra + tool::BfsFileFactory::buildDirEntry(newEntry, tool::FileType::DIRECTOR); + _executive->storage().setRow(root, dir, std::move(newEntry)); + + _executive->storage().createTable(newTableName, std::string(tool::FS_DIR_FIELDS)); + root = newTableName; + continue; + } + else + { + auto dirFields = dirEntry->getObject>(); + if (dirFields[0] == tool::FS_TYPE_DIR) + { + // if dir is directory, continue + root = newTableName; + continue; + } + // exist in root, it means this dir is not a directory + EXECUTIVE_LOG(DEBUG) << LOG_BADGE("recursiveBuildDir") + << LOG_DESC("file had already existed, and not directory type") + << LOG_KV("parentDir", root) << LOG_KV("dir", dir) + << LOG_KV("type", dirFields[0]); + return false; + } + } + + // if version < 3.0.0 + auto typeEntry = _executive->storage().getRow(root, FS_KEY_TYPE); + if (typeEntry) + { + // can get type, then this type is directory + // try open root + dir + auto nextDirTable = _executive->storage().openTable(newTableName); + if (nextDirTable.has_value()) + { + // root + dir table exist, try to get type entry + auto tryGetTypeEntry = _executive->storage().getRow(newTableName, FS_KEY_TYPE); + if (tryGetTypeEntry.has_value() && tryGetTypeEntry->getField(0) == FS_TYPE_DIR) + { + // if success and dir is directory, continue + root = newTableName; + continue; + } + + // can not get type, it means this dir is not a directory + EXECUTIVE_LOG(DEBUG) << LOG_BADGE("recursiveBuildDir") + << LOG_DESC("file had already existed, and not directory type") + << LOG_KV("parentDir", root) << LOG_KV("dir", dir); + return false; + } + + // root + dir not exist, create root + dir and build bfs info in root table + auto subEntry = _executive->storage().getRow(root, FS_KEY_SUB); + auto&& out = asBytes(std::string(subEntry->getField(0))); + // codec to map + std::map bfsInfo; + codec::scale::decode(bfsInfo, gsl::make_span(out)); + + /// create table and build bfs info + bfsInfo.insert(std::make_pair(dir, FS_TYPE_DIR)); + _executive->storage().createTable(newTableName, SYS_VALUE_FIELDS); + storage::Entry tEntry, newSubEntry, aclTypeEntry, aclWEntry, aclBEntry, extraEntry; + std::map newSubMap; + tEntry.importFields({FS_TYPE_DIR}); + newSubEntry.importFields({asString(codec::scale::encode(newSubMap))}); + aclTypeEntry.importFields({"0"}); + aclWEntry.importFields({""}); + aclBEntry.importFields({""}); + extraEntry.importFields({""}); + _executive->storage().setRow(newTableName, FS_KEY_TYPE, std::move(tEntry)); + _executive->storage().setRow(newTableName, FS_KEY_SUB, std::move(newSubEntry)); + _executive->storage().setRow(newTableName, FS_ACL_TYPE, std::move(aclTypeEntry)); + _executive->storage().setRow(newTableName, FS_ACL_WHITE, std::move(aclWEntry)); + _executive->storage().setRow(newTableName, FS_ACL_BLACK, std::move(aclBEntry)); + _executive->storage().setRow(newTableName, FS_KEY_EXTRA, std::move(extraEntry)); + + // set metadata in parent dir + subEntry->setField(0, asString(codec::scale::encode(bfsInfo))); + _executive->storage().setRow(root, FS_KEY_SUB, std::move(subEntry.value())); + root = newTableName; + } + else + { + EXECUTIVE_LOG(TRACE) << LOG_BADGE("recursiveBuildDir") + << LOG_DESC("parent type not found") << LOG_KV("parentDir", root) + << LOG_KV("dir", dir); + return false; + } + } + return true; +} + +void BFSPrecompiled::buildSysSubs( + const std::shared_ptr& _executive) const +{ + for (const auto& sysSub : BFS_SYS_SUBS) + { + Entry entry; + // type, status, acl_type, acl_white, acl_black, extra + tool::BfsFileFactory::buildDirEntry(entry, tool::LINK); + _executive->storage().setRow( + tool::FS_SYS_BIN, sysSub.substr(tool::FS_SYS_BIN.length() + 1), std::move(entry)); + } + // build sys contract + for (const auto& nameAddress : SYS_NAME_ADDRESS_MAP) + { + auto linkTable = + _executive->storage().createTable(std::string(nameAddress.first), SYS_VALUE_FIELDS); + tool::BfsFileFactory::buildLink(linkTable.value(), std::string(nameAddress.second), ""); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/BFSPrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/BFSPrecompiled.h" new file mode 100644 index 00000000..9caab437 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/BFSPrecompiled.h" @@ -0,0 +1,65 @@ +/** + * Copyright (C) 2021 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 BFSPrecompiled.h + * @author: kyonRay + * @date 2021-06-10 + */ + +#pragma once +#include "../vm/Precompiled.h" + +namespace bcos::precompiled +{ +class BFSPrecompiled : public bcos::precompiled::Precompiled +{ +public: + using Ptr = std::shared_ptr; + BFSPrecompiled(crypto::Hash::Ptr _hashImpl); + ~BFSPrecompiled() override = default; + std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) override; + +private: + void listDir(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + void listDirPage(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + void makeDir(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + void link(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + void linkAdaptCNS(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + void readLink(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + void touch(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + void initBfs(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + void rebuildBfs(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + void rebuildBfs310(const std::shared_ptr& _executive); + int checkLinkParam(std::shared_ptr _executive, + std::string const& _contractAddress, std::string& _contractName, + std::string& _contractVersion, std::string const& _contractAbi); + bool recursiveBuildDir(const std::shared_ptr& _executive, + const std::string& _absoluteDir); + std::set BfsTypeSet; + void buildSysSubs(const std::shared_ptr& _executive) const; +}; +} // namespace bcos::precompiled \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/ConsensusPrecompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/ConsensusPrecompiled.cpp" new file mode 100644 index 00000000..57eb744d --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/ConsensusPrecompiled.cpp" @@ -0,0 +1,399 @@ +/** + * Copyright (C) 2021 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 ConsensusPrecompiled.cpp + * @author: kyonRay + * @date 2021-05-26 + */ + +#include "ConsensusPrecompiled.h" +#include "bcos-executor/src/precompiled/common/PrecompiledResult.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::precompiled; +using namespace bcos::ledger; + +const char* const CSS_METHOD_ADD_SEALER = "addSealer(string,uint256)"; +const char* const CSS_METHOD_ADD_SER = "addObserver(string)"; +const char* const CSS_METHOD_REMOVE = "remove(string)"; +const char* const CSS_METHOD_SET_WEIGHT = "setWeight(string,uint256)"; +const auto NODE_LENGTH = 128U; + +ConsensusPrecompiled::ConsensusPrecompiled(const crypto::Hash::Ptr& _hashImpl) + : Precompiled(_hashImpl) +{ + name2Selector[CSS_METHOD_ADD_SEALER] = getFuncSelector(CSS_METHOD_ADD_SEALER, _hashImpl); + name2Selector[CSS_METHOD_ADD_SER] = getFuncSelector(CSS_METHOD_ADD_SER, _hashImpl); + name2Selector[CSS_METHOD_REMOVE] = getFuncSelector(CSS_METHOD_REMOVE, _hashImpl); + name2Selector[CSS_METHOD_SET_WEIGHT] = getFuncSelector(CSS_METHOD_SET_WEIGHT, _hashImpl); +} + +std::shared_ptr ConsensusPrecompiled::call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) +{ + // parse function name + uint32_t func = getParamFunc(_callParameters->input()); + bytesConstRef data = _callParameters->params(); + + showConsensusTable(_executive); + + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + + if (blockContext->isAuthCheck() && !checkSenderFromAuth(_callParameters->m_sender)) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ConsensusPrecompiled") + << LOG_DESC("sender is not from sys") + << LOG_KV("sender", _callParameters->m_sender); + _callParameters->setExecResult(codec.encode(int32_t(CODE_NO_AUTHORIZED))); + return _callParameters; + } + + int result = 0; + if (func == name2Selector[CSS_METHOD_ADD_SEALER]) + { + // addSealer(string, uint256) + result = addSealer(_executive, data, codec); + } + else if (func == name2Selector[CSS_METHOD_ADD_SER]) + { + // addObserver(string) + result = addObserver(_executive, data, codec); + } + else if (func == name2Selector[CSS_METHOD_REMOVE]) + { + // remove(string) + result = removeNode(_executive, data, codec); + } + else if (func == name2Selector[CSS_METHOD_SET_WEIGHT]) + { + // setWeight(string,uint256) + result = setWeight(_executive, data, codec); + } + else + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("ConsensusPrecompiled") + << LOG_DESC("call undefined function") << LOG_KV("func", func); + BOOST_THROW_EXCEPTION( + bcos::protocol::PrecompiledError("ConsensusPrecompiled call undefined function!")); + } + + _callParameters->setExecResult(codec.encode(int32_t(result))); + return _callParameters; +} + +int ConsensusPrecompiled::addSealer( + const std::shared_ptr& _executive, bytesConstRef& _data, + const CodecWrapper& codec) +{ + // addSealer(string, uint256) + std::string nodeID; + u256 weight; + auto blockContext = _executive->blockContext().lock(); + codec.decode(_data, nodeID, weight); + // Uniform lowercase nodeID + boost::to_lower(nodeID); + + PRECOMPILED_LOG(INFO) << BLOCK_NUMBER(blockContext->number()) + << LOG_BADGE("ConsensusPrecompiled") << LOG_DESC("addSealer") + << LOG_KV("nodeID", nodeID); + if (nodeID.size() != NODE_LENGTH || + std::count_if(nodeID.begin(), nodeID.end(), + [](unsigned char _ch) { return std::isxdigit(_ch); }) != NODE_LENGTH) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ConsensusPrecompiled") + << LOG_DESC("nodeID length error") << LOG_KV("nodeID", nodeID); + return CODE_INVALID_NODE_ID; + } + if (weight == 0) + { + // u256 weight be then 0 + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ConsensusPrecompiled") << LOG_DESC("weight is 0") + << LOG_KV("nodeID", nodeID); + return CODE_INVALID_WEIGHT; + } + + auto& storage = _executive->storage(); + + ConsensusNodeList consensusList; + auto entry = storage.getRow(SYS_CONSENSUS, "key"); + if (entry) + { + consensusList = entry->getObject(); + } + else + { + entry.emplace(Entry()); + } + + auto node = std::find_if(consensusList.begin(), consensusList.end(), + [&nodeID](const ConsensusNode& node) { return node.nodeID == nodeID; }); + if (node != consensusList.end()) + { + // exist + node->weight = weight; + node->type = ledger::CONSENSUS_SEALER; + node->enableNumber = boost::lexical_cast(blockContext->number() + 1); + } + else + { + // no exist + if (blockContext->blockVersion() >= (uint32_t)protocol::BlockVersion::V3_1_VERSION) + { + // version >= 3.1.0, only allow adding sealer in observer list + return CODE_ADD_SEALER_SHOULD_IN_OBSERVER; + } + consensusList.emplace_back(nodeID, weight, std::string{ledger::CONSENSUS_SEALER}, + boost::lexical_cast(blockContext->number() + 1)); + } + + entry->setObject(consensusList); + + storage.setRow(SYS_CONSENSUS, "key", std::move(*entry)); + + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ConsensusPrecompiled") + << LOG_DESC("addSealer successfully insert") << LOG_KV("nodeID", nodeID) + << LOG_KV("weight", weight); + return 0; +} + +int ConsensusPrecompiled::addObserver( + const std::shared_ptr& _executive, bytesConstRef& _data, + const CodecWrapper& codec) +{ + // addObserver(string) + std::string nodeID; + auto blockContext = _executive->blockContext().lock(); + codec.decode(_data, nodeID); + // Uniform lowercase nodeID + boost::to_lower(nodeID); + PRECOMPILED_LOG(INFO) << BLOCK_NUMBER(blockContext->number()) + << LOG_BADGE("ConsensusPrecompiled") << LOG_DESC("addObserver") + << LOG_KV("nodeID", nodeID); + + if (nodeID.size() != NODE_LENGTH || + std::count_if(nodeID.begin(), nodeID.end(), + [](unsigned char c) { return std::isxdigit(c); }) != NODE_LENGTH) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ConsensusPrecompiled") + << LOG_DESC("nodeID length error") << LOG_KV("nodeID", nodeID); + return CODE_INVALID_NODE_ID; + } + + auto& storage = _executive->storage(); + + auto entry = storage.getRow(SYS_CONSENSUS, "key"); + + ConsensusNodeList consensusList; + if (entry) + { + consensusList = entry->getObject(); + } + else + { + entry.emplace(Entry()); + } + auto node = std::find_if(consensusList.begin(), consensusList.end(), + [&nodeID](const ConsensusNode& node) { return node.nodeID == nodeID; }); + if (node != consensusList.end()) + { + // find it in consensus list + auto sealerCount = std::count_if(consensusList.begin(), consensusList.end(), + [](auto&& node) { return node.type == ledger::CONSENSUS_SEALER; }); + if (sealerCount == 1) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ConsensusPrecompiled") + << LOG_DESC("addObserver failed, because last sealer"); + return CODE_LAST_SEALER; + } + node->weight = 0; + node->type = ledger::CONSENSUS_OBSERVER; + node->enableNumber = boost::lexical_cast(blockContext->number() + 1); + } + else + { + consensusList.emplace_back(nodeID, 0, std::string{ledger::CONSENSUS_OBSERVER}, + boost::lexical_cast(blockContext->number() + 1)); + } + + entry->setObject(consensusList); + + storage.setRow(SYS_CONSENSUS, "key", std::move(*entry)); + + return 0; +} + +int ConsensusPrecompiled::removeNode( + const std::shared_ptr& _executive, bytesConstRef& _data, + const CodecWrapper& codec) +{ + // remove(string) + std::string nodeID; + auto blockContext = _executive->blockContext().lock(); + codec.decode(_data, nodeID); + // Uniform lowercase nodeID + boost::to_lower(nodeID); + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ConsensusPrecompiled") << LOG_DESC("remove") + << LOG_KV("nodeID", nodeID); + if (nodeID.size() != NODE_LENGTH) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ConsensusPrecompiled") + << LOG_DESC("nodeID length error") << LOG_KV("nodeID", nodeID); + return CODE_INVALID_NODE_ID; + } + + auto& storage = _executive->storage(); + + ConsensusNodeList consensusList; + auto entry = storage.getRow(SYS_CONSENSUS, "key"); + if (entry) + { + consensusList = entry->getObject(); + } + else + { + entry.emplace(Entry()); + } + auto node = std::find_if(consensusList.begin(), consensusList.end(), + [&nodeID](const ConsensusNode& node) { return node.nodeID == nodeID; }); + if (node != consensusList.end()) + { + consensusList.erase(node); + } + else + { + return CODE_NODE_NOT_EXIST; // Not found + } + + auto sealerSize = std::count_if(consensusList.begin(), consensusList.end(), + [](auto&& node) { return node.type == ledger::CONSENSUS_SEALER; }); + if (sealerSize == 0) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ConsensusPrecompiled") + << LOG_DESC("removeNode failed, because last sealer"); + return CODE_LAST_SEALER; + } + + entry->setObject(consensusList); + + storage.setRow(SYS_CONSENSUS, "key", std::move(*entry)); + + return 0; +} + +int ConsensusPrecompiled::setWeight( + const std::shared_ptr& _executive, bytesConstRef& _data, + const CodecWrapper& codec) +{ + // setWeight(string,uint256) + std::string nodeID; + u256 weight; + auto blockContext = _executive->blockContext().lock(); + codec.decode(_data, nodeID, weight); + // Uniform lowercase nodeID + boost::to_lower(nodeID); + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ConsensusPrecompiled") << LOG_DESC("setWeight") + << LOG_KV("nodeID", nodeID); + if (nodeID.size() != NODE_LENGTH) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ConsensusPrecompiled") + << LOG_DESC("nodeID length error") << LOG_KV("nodeID", nodeID); + return CODE_INVALID_NODE_ID; + } + if (weight == 0) + { + // u256 weight must greater then 0 + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ConsensusPrecompiled") << LOG_DESC("weight is 0") + << LOG_KV("nodeID", nodeID); + return CODE_INVALID_WEIGHT; + } + + auto& storage = _executive->storage(); + + auto entry = storage.getRow(SYS_CONSENSUS, "key"); + + ConsensusNodeList consensusList; + if (entry) + { + auto value = entry->getField(0); + + consensusList = decodeConsensusList(value); + } + auto node = std::find_if(consensusList.begin(), consensusList.end(), + [&nodeID](const ConsensusNode& node) { return node.nodeID == nodeID; }); + if (node != consensusList.end()) + { + if (node->type != ledger::CONSENSUS_SEALER) + { + BOOST_THROW_EXCEPTION(protocol::PrecompiledError("Cannot set weight to observer.")); + } + node->weight = weight; + node->enableNumber = boost::lexical_cast(blockContext->number() + 1); + } + else + { + return CODE_NODE_NOT_EXIST; // Not found + } + + entry->setObject(consensusList); + + storage.setRow(SYS_CONSENSUS, "key", std::move(*entry)); + + return 0; +} + +void ConsensusPrecompiled::showConsensusTable( + const std::shared_ptr& _executive) +{ + auto& storage = _executive->storage(); + // SYS_CONSENSUS must exist + auto entry = storage.getRow(SYS_CONSENSUS, "key"); + + if (!entry) + { + PRECOMPILED_LOG(TRACE) << LOG_BADGE("ConsensusPrecompiled") + << LOG_DESC("showConsensusTable") << " No consensus"; + return; + } + + if (c_fileLogLevel < bcos::LogLevel::TRACE) + { + return; + } + auto consensusList = entry->getObject(); + + std::stringstream consensusTable; + consensusTable << "ConsensusPrecompiled show table:\n"; + for (auto& node : consensusList) + { + auto& [nodeID, weight, type, enableNumber] = node; + + consensusTable << "ConsensusPrecompiled: " << nodeID << "," << type << "," << enableNumber + << "," << weight << "\n"; + } + PRECOMPILED_LOG(TRACE) << LOG_BADGE("ConsensusPrecompiled") << LOG_DESC("showConsensusTable") + << LOG_KV("consensusTable", consensusTable.str()); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/ConsensusPrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/ConsensusPrecompiled.h" new file mode 100644 index 00000000..62dc1e2d --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/ConsensusPrecompiled.h" @@ -0,0 +1,57 @@ +/** + * Copyright (C) 2021 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 ConsensusPrecompiled.h + * @author: kyonRay + * @date 2021-05-26 + */ + +#pragma once +#include "../executive/BlockContext.h" +#include "../vm/Precompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include +#include +#include + +namespace bcos::precompiled +{ +class ConsensusPrecompiled : public bcos::precompiled::Precompiled +{ +public: + using Ptr = std::shared_ptr; + explicit ConsensusPrecompiled(const crypto::Hash::Ptr& _hashImpl); + ~ConsensusPrecompiled() override = default; + + std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) override; + +private: + [[nodiscard]] int addSealer(const std::shared_ptr& _executive, + bytesConstRef& _data, const CodecWrapper& codec); + + [[nodiscard]] int addObserver(const std::shared_ptr& _executive, + bytesConstRef& _data, const CodecWrapper& codec); + + [[nodiscard]] int removeNode(const std::shared_ptr& _executive, + bytesConstRef& _data, const CodecWrapper& codec); + + [[nodiscard]] int setWeight(const std::shared_ptr& _executive, + bytesConstRef& _data, const CodecWrapper& codec); + + void showConsensusTable(const std::shared_ptr& _executive); +}; +} // namespace bcos::precompiled diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/CryptoPrecompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/CryptoPrecompiled.cpp" new file mode 100644 index 00000000..f98730aa --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/CryptoPrecompiled.cpp" @@ -0,0 +1,196 @@ +/** + * Copyright (C) 2021 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 CryptoPrecompiled.cpp + * @author: kyonRay + * @date 2021-05-30 + */ + +#include "CryptoPrecompiled.h" +#include "bcos-crypto/signature/codec/SignatureDataWithPub.h" +#include "bcos-executor/src/precompiled/common/PrecompiledResult.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::codec; +using namespace bcos::crypto; +using namespace bcos::executor; +using namespace bcos::precompiled; + +// precompiled interfaces related to hash calculation +const char* const CRYPTO_METHOD_SM3_STR = "sm3(bytes)"; +// Note: the interface here can't be keccak256k1 for naming conflict +const char* const CRYPTO_METHOD_KECCAK256_STR = "keccak256Hash(bytes)"; +// precompiled interfaces related to verify +// sm2 verify: (message, publicKey, r, s) +const char* const CRYPTO_METHOD_SM2_VERIFY_STR = "sm2Verify(bytes32,bytes,bytes32,bytes32)"; +// the params are (vrfInput, vrfPublicKey, vrfProof) +const char* const CRYPTO_METHOD_CURVE25519_VRF_VERIFY_STR = + "curve25519VRFVerify(bytes,bytes,bytes)"; + +CryptoPrecompiled::CryptoPrecompiled(crypto::Hash::Ptr _hashImpl) : Precompiled(_hashImpl) +{ + name2Selector[CRYPTO_METHOD_SM3_STR] = getFuncSelector(CRYPTO_METHOD_SM3_STR, _hashImpl); + name2Selector[CRYPTO_METHOD_KECCAK256_STR] = + getFuncSelector(CRYPTO_METHOD_KECCAK256_STR, _hashImpl); + name2Selector[CRYPTO_METHOD_SM2_VERIFY_STR] = + getFuncSelector(CRYPTO_METHOD_SM2_VERIFY_STR, _hashImpl); + name2Selector[CRYPTO_METHOD_CURVE25519_VRF_VERIFY_STR] = + getFuncSelector(CRYPTO_METHOD_CURVE25519_VRF_VERIFY_STR, _hashImpl); +} + +std::shared_ptr CryptoPrecompiled::call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) +{ + auto funcSelector = getParamFunc(_callParameters->input()); + auto paramData = _callParameters->params(); + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + auto gasPricer = m_precompiledGasFactory->createPrecompiledGas(); + gasPricer->setMemUsed(paramData.size()); + auto version = blockContext->blockVersion(); + + if (funcSelector == name2Selector[CRYPTO_METHOD_SM3_STR]) + { + bytes inputData; + codec.decode(paramData, inputData); + + auto sm3Hash = crypto::sm3Hash(ref(inputData)); + PRECOMPILED_LOG(TRACE) << LOG_DESC("CryptoPrecompiled: sm3") + << LOG_KV("input", toHexString(inputData)) + << LOG_KV("result", toHexString(sm3Hash)); + _callParameters->setExecResult(codec.encode(codec::toString32(sm3Hash))); + } + else if (funcSelector == name2Selector[CRYPTO_METHOD_KECCAK256_STR]) + { + bytes inputData; + codec.decode(paramData, inputData); + auto keccak256Hash = crypto::keccak256Hash(ref(inputData)); + PRECOMPILED_LOG(TRACE) << LOG_DESC("CryptoPrecompiled: keccak256") + << LOG_KV("input", toHexString(inputData)) + << LOG_KV("result", toHexString(keccak256Hash)); + _callParameters->setExecResult(codec.encode(codec::toString32(keccak256Hash))); + } + else if (funcSelector == name2Selector[CRYPTO_METHOD_SM2_VERIFY_STR]) + { + sm2Verify(_executive, paramData, _callParameters); + } + // curve25519VRFVerify + else if (funcSelector == name2Selector[CRYPTO_METHOD_CURVE25519_VRF_VERIFY_STR] && + (version >= (uint32_t)(bcos::protocol::BlockVersion::V3_0_VERSION))) + { + curve25519VRFVerify(_executive, paramData, _callParameters); + } + else + { + // no defined function + PRECOMPILED_LOG(INFO) << LOG_DESC("CryptoPrecompiled: undefined method") + << LOG_KV("funcSelector", std::to_string(funcSelector)); + BOOST_THROW_EXCEPTION( + bcos::protocol::PrecompiledError("CryptoPrecompiled call undefined function!")); + } + gasPricer->updateMemUsed(_callParameters->m_execResult.size()); + _callParameters->setGasLeft(_callParameters->m_gasLeft - gasPricer->calTotalGas()); + return _callParameters; +} + +void CryptoPrecompiled::curve25519VRFVerify( + const std::shared_ptr& _executive, bytesConstRef _paramData, + PrecompiledExecResult::Ptr _callResult) +{ + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + bool verifySuccess = false; + u256 randomValue = 0; + try + { + bytes message; + bytes publicKey; + bytes proof; + codec.decode(_paramData, message, publicKey, proof); + CInputBuffer rawPublicKey{(const char*)publicKey.data(), publicKey.size()}; + CInputBuffer rawMsg{(const char*)message.data(), message.size()}; + CInputBuffer rawProof{(const char*)proof.data(), proof.size()}; + HashType vrfHash; + COutputBuffer outputHash{(char*)vrfHash.data(), vrfHash.size()}; + if ((wedpr_curve25519_vrf_is_valid_public_key(&rawPublicKey) == WEDPR_SUCCESS) && + (wedpr_curve25519_vrf_verify_utf8(&rawPublicKey, &rawMsg, &rawProof) == + WEDPR_SUCCESS) && + (wedpr_curve25519_vrf_proof_to_hash(&rawProof, &outputHash) == WEDPR_SUCCESS)) + { + verifySuccess = true; + randomValue = (u256)(vrfHash); + } + } + catch (std::exception const& e) + { + PRECOMPILED_LOG(INFO) << LOG_DESC("CryptoPrecompiled: curve25519VRFVerify exception") + << LOG_KV("e", boost::diagnostic_information(e)); + } + PRECOMPILED_LOG(TRACE) << LOG_DESC("CryptoPrecompiled: curve25519VRFVerify ") << verifySuccess + << LOG_KV("randomValue", randomValue); + _callResult->setExecResult(codec.encode(verifySuccess, randomValue)); +} + +void CryptoPrecompiled::sm2Verify(const std::shared_ptr& _executive, + bytesConstRef _paramData, PrecompiledExecResult::Ptr _callResult) +{ + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + try + { + string32 message; + bytes inputPublicKey; + string32 r; + string32 s; + codec.decode(_paramData, message, inputPublicKey, r, s); + auto msgHash = fromString32(message); + Address account; + bool verifySuccess = true; + auto signatureData = std::make_shared( + fromString32(r), fromString32(s), ref(inputPublicKey)); + auto publicKey = crypto::sm2Recover(msgHash, ref(*(signatureData->encode()))); + if (!publicKey) + { + PRECOMPILED_LOG(DEBUG) + << LOG_DESC("CryptoPrecompiled: sm2Verify failed for recover public key failed"); + _callResult->setExecResult(codec.encode(false, account)); + return; + } + + account = right160( + crypto::sm3Hash(bytesConstRef(publicKey->data().data(), publicKey->data().size()))); + PRECOMPILED_LOG(TRACE) << LOG_DESC("CryptoPrecompiled: sm2Verify") + << LOG_KV("verifySuccess", verifySuccess) + << LOG_KV("publicKey", publicKey->hex()) + << LOG_KV("account", account); + _callResult->setExecResult(codec.encode(verifySuccess, account)); + } + catch (std::exception const& e) + { + PRECOMPILED_LOG(INFO) << LOG_DESC("CryptoPrecompiled: sm2Verify exception") + << LOG_KV("e", boost::diagnostic_information(e)); + Address emptyAccount; + _callResult->setExecResult(codec.encode(false, emptyAccount)); + } +} diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/CryptoPrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/CryptoPrecompiled.h" new file mode 100644 index 00000000..81fef25d --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/CryptoPrecompiled.h" @@ -0,0 +1,57 @@ +/** + * Copyright (C) 2021 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 CryptoPrecompiled.h + * @author: kyonRay + * @date 2021-05-30 + */ + +#pragma once +#include "../vm/Precompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include "bcos-executor/src/precompiled/common/PrecompiledResult.h" + +namespace bcos +{ +namespace precompiled +{ +#if 0 +abstract contract Crypto +{ + function sm3(bytes memory data) public view returns(bytes32){} + function keccak256Hash(bytes memory data) public view returns(bytes32){} + function sm2Verify(bytes32 message, bytes memory publicKey, bytes32 r, bytes32 s) public view returns(bool, address){} + function curve25519VRFVerify(bytes memory message, bytes memory publicKey, bytes memory proof) public view returns(bool, uint256){} +} +#endif + +class CryptoPrecompiled : public Precompiled +{ +public: + using Ptr = std::shared_ptr; + CryptoPrecompiled(crypto::Hash::Ptr _hashImpl); + virtual ~CryptoPrecompiled() {} + std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) override; + +private: + void sm2Verify(const std::shared_ptr& _executive, + bytesConstRef _paramData, PrecompiledExecResult::Ptr _callResult); + void curve25519VRFVerify(const std::shared_ptr& _executive, + bytesConstRef _paramData, PrecompiledExecResult::Ptr _callResult); +}; +} // namespace precompiled +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/KVTablePrecompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/KVTablePrecompiled.cpp" new file mode 100644 index 00000000..eaae545a --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/KVTablePrecompiled.cpp" @@ -0,0 +1,145 @@ +/** + * Copyright (C) 2021 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 KVTablePrecompiled.cpp + * @author: kyonRay + * @date 2021-05-27 + */ + +#include "KVTablePrecompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include "bcos-executor/src/precompiled/common/PrecompiledResult.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::precompiled; +using namespace bcos::protocol; + +const char* const KV_TABLE_METHOD_SET = "set(string,string)"; +const char* const KV_TABLE_METHOD_GET = "get(string)"; + +KVTablePrecompiled::KVTablePrecompiled(crypto::Hash::Ptr _hashImpl) : Precompiled(_hashImpl) +{ + name2Selector[KV_TABLE_METHOD_SET] = getFuncSelector(KV_TABLE_METHOD_SET, _hashImpl); + name2Selector[KV_TABLE_METHOD_GET] = getFuncSelector(KV_TABLE_METHOD_GET, _hashImpl); +} + +std::shared_ptr KVTablePrecompiled::call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) +{ + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + // [tableName][actualParams] + std::vector dynamicParams; + bytes param; + codec.decode(_callParameters->input(), dynamicParams, param); + auto tableName = dynamicParams.at(0); + tableName = getActualTableName(tableName); + + // get user call actual params + auto originParam = ref(param); + uint32_t func = getParamFunc(originParam); + bytesConstRef data = getParamData(originParam); + + auto gasPricer = m_precompiledGasFactory->createPrecompiledGas(); + gasPricer->setMemUsed(_callParameters->input().size()); + + auto table = _executive->storage().openTable(tableName); + if (!table.has_value()) + { + BOOST_THROW_EXCEPTION(PrecompiledError(tableName + " does not exist")); + } + + if (func == name2Selector[KV_TABLE_METHOD_SET]) + { + /// set(string,string) + set(tableName, _executive, data, _callParameters, gasPricer); + } + else if (func == name2Selector[KV_TABLE_METHOD_GET]) + { + /// get(string) + get(tableName, _executive, data, _callParameters, gasPricer); + } + else + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("KVTablePrecompiled") + << LOG_DESC("call undefined function!"); + BOOST_THROW_EXCEPTION(PrecompiledError("KVTablePrecompiled call undefined function!")); + } + gasPricer->updateMemUsed(_callParameters->m_execResult.size()); + _callParameters->setGasLeft(_callParameters->m_gasLeft - gasPricer->calTotalGas()); + return _callParameters; +} + +void KVTablePrecompiled::get(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const std::shared_ptr& callResult, const PrecompiledGas::Ptr& gasPricer) +{ + /// get(string) => (bool, string) + std::string key; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(data, key); + PRECOMPILED_LOG(TRACE) << LOG_BADGE("KVTable") << LOG_KV("tableName", tableName) + << LOG_KV("get", key); + auto table = _executive->storage().openTable(tableName); + + auto entry = table->getRow(key); + gasPricer->appendOperation(InterfaceOpcode::Select); + if (!entry) + { + callResult->setExecResult(codec.encode(false, std::string(""))); + return; + } + + gasPricer->updateMemUsed(entry->size()); + callResult->setExecResult(codec.encode(true, std::string(entry->get()))); +} + +void KVTablePrecompiled::set(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const std::shared_ptr& callResult, const PrecompiledGas::Ptr& gasPricer) +{ + /// set(string,string) + std::string key, value; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(data, key, value); + PRECOMPILED_LOG(INFO) << LOG_BADGE("KVTable") << LOG_KV("tableName", tableName) + << LOG_KV("key", key) << LOG_KV("value", value); + + checkLengthValidate(key, USER_TABLE_KEY_VALUE_MAX_LENGTH, CODE_TABLE_KEY_VALUE_LENGTH_OVERFLOW); + + checkLengthValidate( + value, USER_TABLE_FIELD_VALUE_MAX_LENGTH, CODE_TABLE_KEY_VALUE_LENGTH_OVERFLOW); + + Entry entry; + entry.importFields({value}); + _executive->storage().setRow(tableName, key, std::move(entry)); + callResult->setExecResult(codec.encode(int32_t(1))); + gasPricer->setMemUsed(1); + gasPricer->appendOperation(InterfaceOpcode::Insert); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/KVTablePrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/KVTablePrecompiled.h" new file mode 100644 index 00000000..ecc02c43 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/KVTablePrecompiled.h" @@ -0,0 +1,58 @@ +/** + * Copyright (C) 2021 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 KVTablePrecompiled.h + * @author: kyonRay + * @date 2021-05-27 + */ + +#pragma once + +#include "../vm/Precompiled.h" +#include +#include + +namespace bcos::precompiled +{ +#if 0 +contract KVTableFactory { + function openTable(string) public constant returns (KVTable); + function createTable(string, string, string) public returns (bool,int); +} +#endif + +class KVTablePrecompiled : public bcos::precompiled::Precompiled +{ +public: + using Ptr = std::shared_ptr; + KVTablePrecompiled(crypto::Hash::Ptr _hashImpl); + virtual ~KVTablePrecompiled(){}; + + std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) override; + +private: + void get(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const std::shared_ptr& callResult, + const PrecompiledGas::Ptr& gasPricer); + void set(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const std::shared_ptr& callResult, + const PrecompiledGas::Ptr& gasPricer); +}; + +} // namespace bcos::precompiled \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/SystemConfigPrecompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/SystemConfigPrecompiled.cpp" new file mode 100644 index 00000000..7999f164 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/SystemConfigPrecompiled.cpp" @@ -0,0 +1,291 @@ +/** + * Copyright (C) 2021 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 SystemConfigPrecompiled.cpp + * @author: kyonRay + * @date 2021-05-26 + */ + +#include "SystemConfigPrecompiled.h" +#include "bcos-executor/src/precompiled/common/PrecompiledResult.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::storage; +using namespace bcos::precompiled; +using namespace bcos::executor; +using namespace bcos::ledger; +using namespace bcos::protocol; + +const char* const SYSCONFIG_METHOD_SET_STR = "setValueByKey(string,string)"; +const char* const SYSCONFIG_METHOD_GET_STR = "getValueByKey(string)"; + +SystemConfigPrecompiled::SystemConfigPrecompiled() : Precompiled(GlobalHashImpl::g_hashImpl) +{ + name2Selector[SYSCONFIG_METHOD_SET_STR] = + getFuncSelector(SYSCONFIG_METHOD_SET_STR, GlobalHashImpl::g_hashImpl); + name2Selector[SYSCONFIG_METHOD_GET_STR] = + getFuncSelector(SYSCONFIG_METHOD_GET_STR, GlobalHashImpl::g_hashImpl); + auto defaultCmp = [](std::string_view _key, int64_t _value, int64_t _minValue) { + if (_value >= _minValue) + { + return; + } + BOOST_THROW_EXCEPTION(PrecompiledError( + "Invalid value " + std::to_string(_value) + " ,the value for " + std::string{_key} + + " must be no less than " + std::to_string(_minValue))); + }; + m_sysValueCmp.insert(std::make_pair(SYSTEM_KEY_TX_GAS_LIMIT, [defaultCmp](int64_t _value) { + defaultCmp(SYSTEM_KEY_TX_GAS_LIMIT, _value, TX_GAS_LIMIT_MIN); + })); + m_sysValueCmp.insert( + std::make_pair(SYSTEM_KEY_CONSENSUS_LEADER_PERIOD, [defaultCmp](int64_t _value) { + defaultCmp(SYSTEM_KEY_CONSENSUS_LEADER_PERIOD, _value, 1); + })); + m_sysValueCmp.insert(std::make_pair(SYSTEM_KEY_TX_COUNT_LIMIT, [defaultCmp](int64_t _value) { + defaultCmp(SYSTEM_KEY_TX_COUNT_LIMIT, _value, TX_COUNT_LIMIT_MIN); + })); + // for compatibility + // Note: the compatibility_version is not compatibility + m_sysValueCmp.insert(std::make_pair(SYSTEM_KEY_COMPATIBILITY_VERSION, [](int64_t _value) { + if (_value < (uint32_t)(g_BCOSConfig.minSupportedVersion())) + { + std::stringstream errorMsg; + errorMsg << LOG_DESC("set " + std::string(SYSTEM_KEY_COMPATIBILITY_VERSION) + + " failed for lower than min_supported_version") + << LOG_KV("minSupportedVersion", g_BCOSConfig.minSupportedVersion()); + PRECOMPILED_LOG(INFO) << errorMsg.str() << LOG_KV("setValue", _value); + BOOST_THROW_EXCEPTION(PrecompiledError(errorMsg.str())); + } + })); + m_valueConverter.insert(std::make_pair(SYSTEM_KEY_COMPATIBILITY_VERSION, + [](const std::string& _value, uint32_t blockVersion) -> uint64_t { + auto version = bcos::tool::toVersionNumber(_value); + if (versionCompareTo(blockVersion, BlockVersion::V3_1_VERSION) >= 0) + { + if (version < blockVersion) + { + BOOST_THROW_EXCEPTION(PrecompiledError( + "Set compatibility version should not lower than version nowadays.")); + } + } + return version; + })); +} + +std::shared_ptr SystemConfigPrecompiled::call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) +{ + // parse function name + uint32_t func = getParamFunc(_callParameters->input()); + auto blockContext = _executive->blockContext().lock(); + + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + if (func == name2Selector[SYSCONFIG_METHOD_SET_STR]) + { + // setValueByKey(string,string) + if (blockContext->isAuthCheck() && !checkSenderFromAuth(_callParameters->m_sender)) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("SystemConfigPrecompiled") + << LOG_DESC("sender is not from sys") + << LOG_KV("sender", _callParameters->m_sender); + _callParameters->setExecResult(codec.encode(int32_t(CODE_NO_AUTHORIZED))); + } + else + { + std::string configKey; + std::string configValue; + codec.decode(_callParameters->params(), configKey, configValue); + // Uniform lowercase configKey + boost::to_lower(configKey); + PRECOMPILED_LOG(INFO) << BLOCK_NUMBER(blockContext->number()) + << LOG_BADGE("SystemConfigPrecompiled") + << LOG_DESC("setValueByKey") << LOG_KV("configKey", configKey) + << LOG_KV("configValue", configValue); + + int64_t value = checkValueValid(configKey, configValue, blockContext->blockVersion()); + auto table = _executive->storage().openTable(ledger::SYS_CONFIG); + + auto entry = table->newEntry(); + auto systemConfigEntry = SystemConfigEntry{configValue, blockContext->number() + 1}; + entry.setObject(systemConfigEntry); + + table->setRow(configKey, std::move(entry)); + + if (shouldUpgradeChain(configKey, blockContext->blockVersion(), value)) + { + upgradeChain(_executive, _callParameters, codec, value); + } + + PRECOMPILED_LOG(INFO) << LOG_BADGE("SystemConfigPrecompiled") + << LOG_DESC("set system config") << LOG_KV("configKey", configKey) + << LOG_KV("configValue", configValue) + << LOG_KV("enableNum", blockContext->number() + 1); + _callParameters->setExecResult(codec.encode(int32_t(CODE_SUCCESS))); + } + } + else if (func == name2Selector[SYSCONFIG_METHOD_GET_STR]) + { + // getValueByKey(string) + std::string configKey; + codec.decode(_callParameters->params(), configKey); + // Uniform lowercase configKey + boost::to_lower(configKey); + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("SystemConfigPrecompiled") + << LOG_DESC("getValueByKey func") << LOG_KV("configKey", configKey); + + auto valueNumberPair = getSysConfigByKey(_executive, configKey); + _callParameters->setExecResult(codec.encode(valueNumberPair.first, valueNumberPair.second)); + } + else + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("SystemConfigPrecompiled") + << LOG_DESC("call undefined function") << LOG_KV("func", func); + BOOST_THROW_EXCEPTION(PrecompiledError("SystemConfigPrecompiled call undefined function!")); + } + return _callParameters; +} + +int64_t SystemConfigPrecompiled::checkValueValid( + std::string_view _key, std::string_view value, uint32_t blockVersion) +{ + int64_t configuredValue = 0; + std::string key = std::string(_key); + if (!c_supportedKey.contains(key)) + { + BOOST_THROW_EXCEPTION(PrecompiledError("unsupported key " + key)); + } + if (value.empty()) + { + BOOST_THROW_EXCEPTION(PrecompiledError("The value for " + key + " must be non-empty.")); + } + try + { + if (m_valueConverter.contains(key)) + { + configuredValue = (m_valueConverter.at(key))(std::string(value), blockVersion); + } + else + { + configuredValue = boost::lexical_cast(value); + } + } + catch (bcos::tool::InvalidVersion const& e) + { + // Note: be careful when modify error message here + auto errorMsg = + "Invalid value for " + key + + ". The version must be in format of major_version.middle_version.minimum_version, and " + "the minimum version is optional. The major version must between " + + std::to_string(bcos::protocol::MIN_MAJOR_VERSION) + " to " + + std::to_string(bcos::protocol::MAX_MAJOR_VERSION); + PRECOMPILED_LOG(INFO) << LOG_DESC("SystemConfigPrecompiled: invalid version") + << LOG_KV("errorInfo", boost::diagnostic_information(e)); + BOOST_THROW_EXCEPTION(PrecompiledError(errorMsg)); + } + catch (std::exception const& e) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("SystemConfigPrecompiled") + << LOG_DESC("checkValueValid failed") << LOG_KV("key", _key) + << LOG_KV("value", value) + << LOG_KV("errorInfo", boost::diagnostic_information(e)); + BOOST_THROW_EXCEPTION( + PrecompiledError("The value for " + key + " must be a valid number.")); + } + if (m_sysValueCmp.contains(key)) + { + (m_sysValueCmp.at(key))(configuredValue); + } + return configuredValue; +} + +std::pair SystemConfigPrecompiled::getSysConfigByKey( + const std::shared_ptr& _executive, + const std::string& _key) const +{ + try + { + auto table = _executive->storage().openTable(ledger::SYS_CONFIG); + auto entry = table->getRow(_key); + if (entry) + { + auto [value, enableNumber] = entry->getObject(); + return {value, enableNumber}; + } + + PRECOMPILED_LOG(INFO) << LOG_BADGE("SystemConfigPrecompiled") + << LOG_DESC("get sys config failed") << LOG_KV("configKey", _key); + return {"", -1}; + } + catch (std::exception const& e) + { + auto errorMsg = + "getSysConfigByKey for " + _key + "failed, e:" + boost::diagnostic_information(e); + PRECOMPILED_LOG(INFO) << LOG_BADGE("SystemConfigPrecompiled") << errorMsg; + return {errorMsg, -1}; + } +} + +void SystemConfigPrecompiled::upgradeChain( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters, CodecWrapper const& codec, + uint32_t toVersion) const +{ + auto blockContext = _executive->blockContext().lock(); + + if (blockContext->blockVersion() <= static_cast(BlockVersion::V3_0_VERSION) && + toVersion >= static_cast(BlockVersion::V3_1_VERSION)) + { + // rebuild Bfs + auto input = codec.encodeWithSig( + "rebuildBfs(uint256,uint256)", blockContext->blockVersion(), toVersion); + std::string sender = + blockContext->isWasm() ? precompiled::SYS_CONFIG_NAME : precompiled::SYS_CONFIG_ADDRESS; + std::string toAddress = + blockContext->isWasm() ? precompiled::BFS_NAME : precompiled::BFS_ADDRESS; + auto response = externalRequest(_executive, ref(input), _callParameters->m_origin, sender, + toAddress, false, false, _callParameters->m_gasLeft); + + if (response->status != (int32_t)TransactionStatus::None) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("SystemConfigPrecompiled") + << LOG_DESC("rebuildBfs failed") + << LOG_KV("status", response->status); + BOOST_THROW_EXCEPTION(PrecompiledError("Rebuild BFS error.")); + } + + // create new system tables of 3.1.0 + // clang-format off + std::string_view tables[] = { + SYS_CODE_BINARY, bcos::ledger::SYS_VALUE, + SYS_CONTRACT_ABI, bcos::ledger::SYS_VALUE, + }; + // clang-format on + size_t total = sizeof(tables) / sizeof(std::string_view); + + for (size_t i = 0; i < total; i += 2) + { + _executive->storage().createTable(std::string(tables[i]), std::string(tables[i + 1])); + } + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/SystemConfigPrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/SystemConfigPrecompiled.h" new file mode 100644 index 00000000..04acae5a --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/SystemConfigPrecompiled.h" @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2021 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 SystemConfigPrecompiled.h + * @author: kyonRay + * @date 2021-05-26 + */ + +#pragma once +#include "../vm/Precompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include "bcos-framework/protocol/ProtocolTypeDef.h" +#include +#include +namespace bcos::precompiled +{ +class SystemConfigPrecompiled : public bcos::precompiled::Precompiled +{ +public: + using Ptr = std::shared_ptr; + + SystemConfigPrecompiled(); + ~SystemConfigPrecompiled() override = default; + std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) override; + std::pair getSysConfigByKey( + const std::shared_ptr& _executive, + const std::string& _key) const; + +private: + int64_t checkValueValid(std::string_view key, std::string_view value, uint32_t blockVersion); + inline bool shouldUpgradeChain( + std::string_view key, uint32_t fromVersion, uint32_t toVersion) const noexcept + { + return key == bcos::ledger::SYSTEM_KEY_COMPATIBILITY_VERSION && toVersion > fromVersion; + } + void upgradeChain(const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters, CodecWrapper const& codec, + uint32_t toVersion) const; + + std::map> m_valueConverter; + std::map> m_sysValueCmp; + const std::set c_supportedKey = {bcos::ledger::SYSTEM_KEY_TX_GAS_LIMIT, + bcos::ledger::SYSTEM_KEY_CONSENSUS_LEADER_PERIOD, bcos::ledger::SYSTEM_KEY_TX_COUNT_LIMIT, + bcos::ledger::SYSTEM_KEY_COMPATIBILITY_VERSION}; +}; + +} // namespace bcos::precompiled diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/TableManagerPrecompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/TableManagerPrecompiled.cpp" new file mode 100644 index 00000000..837217b2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/TableManagerPrecompiled.cpp" @@ -0,0 +1,329 @@ +/** + * Copyright (C) 2021 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 TableManagerPrecompiled.cpp + * @author: kyonGuo + * @date 2022/4/8 + */ + +#include "TableManagerPrecompiled.h" + +#include "bcos-executor/src/precompiled/common/Common.h" +#include "bcos-executor/src/precompiled/common/PrecompiledResult.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::precompiled; +using namespace bcos::protocol; + +constexpr const char* const TABLE_METHOD_CREATE = "createTable(string,(string,string[]))"; +constexpr const char* const TABLE_METHOD_CREATE_KV = "createKVTable(string,string,string)"; +constexpr const char* const TABLE_METHOD_APPEND = "appendColumns(string,string[])"; +constexpr const char* const TABLE_METHOD_OPEN = "openTable(string)"; +constexpr const char* const TABLE_METHOD_DESC = "desc(string)"; + + +TableManagerPrecompiled::TableManagerPrecompiled(crypto::Hash::Ptr _hashImpl) + : Precompiled(_hashImpl) +{ + name2Selector[TABLE_METHOD_CREATE] = getFuncSelector(TABLE_METHOD_CREATE, _hashImpl); + name2Selector[TABLE_METHOD_APPEND] = getFuncSelector(TABLE_METHOD_APPEND, _hashImpl); + name2Selector[TABLE_METHOD_CREATE_KV] = getFuncSelector(TABLE_METHOD_CREATE_KV, _hashImpl); + name2Selector[TABLE_METHOD_OPEN] = getFuncSelector(TABLE_METHOD_OPEN, _hashImpl); + name2Selector[TABLE_METHOD_DESC] = getFuncSelector(TABLE_METHOD_DESC, _hashImpl); +} + +std::shared_ptr TableManagerPrecompiled::call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) +{ + uint32_t func = getParamFunc(_callParameters->input()); + auto gasPricer = m_precompiledGasFactory->createPrecompiledGas(); + gasPricer->setMemUsed(_callParameters->input().size()); + + if (func == name2Selector[TABLE_METHOD_CREATE]) + { + /// createTable(string,string,string) + createTable(_executive, gasPricer, _callParameters); + } + else if (func == name2Selector[TABLE_METHOD_CREATE_KV]) + { + /// createKVTable(string,string,string) + createKVTable(_executive, gasPricer, _callParameters); + } + else if (func == name2Selector[TABLE_METHOD_APPEND]) + { + /// appendColumns(string,string[]) + appendColumns(_executive, gasPricer, _callParameters); + } + else if (func == name2Selector[TABLE_METHOD_DESC]) + { + /// desc(string) + desc(_executive, gasPricer, _callParameters); + } + else if (!_executive->blockContext().lock()->isWasm() && + func == name2Selector[TABLE_METHOD_OPEN]) + { + /// only solidity: openTable(string) => address + openTable(_executive, gasPricer, _callParameters); + } + else + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("TableManager") << LOG_DESC("call undefined function!"); + BOOST_THROW_EXCEPTION(PrecompiledError("TableManager call undefined function!")); + } + gasPricer->updateMemUsed(_callParameters->m_execResult.size()); + _callParameters->setGasLeft(_callParameters->m_gasLeft - gasPricer->calTotalGas()); + return _callParameters; +} + +void TableManagerPrecompiled::createTable( + const std::shared_ptr& _executive, + const PrecompiledGas::Ptr& gasPricer, const PrecompiledExecResult::Ptr& _callParameters) +{ + // createTable(string,(string,string[])) + std::string tableName; + TableInfoTuple tableInfoTuple; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_callParameters->params(), tableName, tableInfoTuple); + auto& [keyField, valueFields] = tableInfoTuple; + auto valueField = precompiled::checkCreateTableParam(tableName, keyField, valueFields); + PRECOMPILED_LOG(INFO) << BLOCK_NUMBER(blockContext->number()) + << LOG_BADGE("TableManagerPrecompiled") + << LOG_KV("createTable", tableName) << LOG_KV("keyField", keyField) + << LOG_KV("valueField", valueField); + gasPricer->appendOperation(InterfaceOpcode::CreateTable); + // /tables + tableName + auto newTableName = getTableName(tableName); + auto table = _executive->storage().openTable(newTableName); + if (table) + { + // table already exist + _callParameters->setExecResult(codec.encode(int32_t(CODE_TABLE_NAME_ALREADY_EXIST))); + return; + } + std::string tableManagerAddress = + blockContext->isWasm() ? TABLE_MANAGER_NAME : TABLE_MANAGER_ADDRESS; + std::string tableAddress = blockContext->isWasm() ? TABLE_NAME : TABLE_ADDRESS; + + // here is a trick to set table key field info + valueField = keyField + "," + valueField; + std::string codeString = getDynamicPrecompiledCodeString(tableAddress, newTableName); + auto input = codec.encode(newTableName, codeString); + std::string abi = blockContext->blockVersion() >= static_cast(BlockVersion::V3_1_VERSION) ? + std::string(TABLE_ABI) : + ""; + auto response = externalRequest(_executive, ref(input), _callParameters->m_origin, + tableManagerAddress, blockContext->isWasm() ? newTableName : "", false, true, + _callParameters->m_gasLeft - gasPricer->calTotalGas(), false, std::move(abi)); + + if (response->status != (int32_t)TransactionStatus::None) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("TableManagerPrecompiled") + << LOG_DESC("create table error") << LOG_KV("tableName", newTableName) + << LOG_KV("valueField", valueField); + BOOST_THROW_EXCEPTION(PrecompiledError("Create table error.")); + } + + _executive->storage().createTable(getActualTableName(newTableName), valueField); + _callParameters->setExecResult(codec.encode(int32_t(CODE_SUCCESS))); +} + +void TableManagerPrecompiled::createKVTable( + const std::shared_ptr& _executive, + const PrecompiledGas::Ptr& gasPricer, const PrecompiledExecResult::Ptr& _callParameters) +{ + /// createKVTable(string,string,string) + std::string tableName, key, value; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_callParameters->params(), tableName, key, value); + precompiled::checkCreateTableParam(tableName, key, value); + PRECOMPILED_LOG(INFO) << BLOCK_NUMBER(blockContext->number()) + << LOG_BADGE("TableManagerPrecompiled") + << LOG_KV("createKVTable", tableName) << LOG_KV("keyField", key) + << LOG_KV("valueField", value); + gasPricer->appendOperation(InterfaceOpcode::CreateTable); + // /tables + tableName + auto newTableName = getTableName(tableName); + auto table = _executive->storage().openTable(newTableName); + if (table) + { + // table already exist + _callParameters->setExecResult(codec.encode(int32_t(CODE_TABLE_NAME_ALREADY_EXIST))); + return; + } + std::string tableManagerAddress = + blockContext->isWasm() ? TABLE_MANAGER_NAME : TABLE_MANAGER_ADDRESS; + std::string kvTableAddress = blockContext->isWasm() ? KV_TABLE_NAME : KV_TABLE_ADDRESS; + std::string codeString = getDynamicPrecompiledCodeString(kvTableAddress, newTableName); + + auto input = codec.encode(newTableName, codeString); + std::string abi = blockContext->blockVersion() >= static_cast(BlockVersion::V3_1_VERSION) ? + std::string(KV_TABLE_ABI) : + ""; + auto response = externalRequest(_executive, ref(input), _callParameters->m_origin, + tableManagerAddress, blockContext->isWasm() ? newTableName : "", false, true, + _callParameters->m_gasLeft - gasPricer->calTotalGas(), false, std::move(abi)); + + if (response->status != (int32_t)TransactionStatus::None) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("TableManagerPrecompiled") + << LOG_DESC("create kv table error") + << LOG_KV("tableName", newTableName) << LOG_KV("valueField", value); + BOOST_THROW_EXCEPTION(PrecompiledError("Create table error.")); + } + + // here is a trick to set table key field info + value = key + "," + value; + _executive->storage().createTable(getActualTableName(newTableName), value); + _callParameters->setExecResult(codec.encode(int32_t(CODE_SUCCESS))); +} + +void TableManagerPrecompiled::appendColumns( + const std::shared_ptr& _executive, + const PrecompiledGas::Ptr& gasPricer, const PrecompiledExecResult::Ptr& _callParameters) +{ + // appendColumns(string,string[]) + std::string tableName; + std::vector newColumns; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_callParameters->params(), tableName, newColumns); + tableName = getActualTableName(getTableName(tableName)); + + PRECOMPILED_LOG(INFO) << BLOCK_NUMBER(blockContext->number()) + << LOG_BADGE("TableManagerPrecompiled") << LOG_DESC("appendColumns") + << LOG_KV("tableName", tableName) + << LOG_KV("newColumns", boost::join(newColumns, ",")); + // 1. get origin table info + auto table = _executive->storage().openTable(StorageInterface::SYS_TABLES); + auto existEntry = table->getRow(tableName); + if (!existEntry) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("TableManagerPrecompiled") + << LOG_DESC("table not exists") << LOG_KV("tableName", tableName); + _callParameters->setExecResult(codec.encode(int32_t(CODE_TABLE_NOT_EXIST))); + return; + } + // here is a trick to avoid key field dup, s_table save user table (key,fields) + std::vector originFields; + boost::split(originFields, std::string(existEntry->get()), boost::is_any_of(",")); + std::set checkDupFields(originFields.begin() + 1, originFields.end()); + // 2. check columns not duplicate + bool insertSuccess = true; + for (const auto& col : newColumns) + { + checkLengthValidate( + col, USER_TABLE_FIELD_NAME_MAX_LENGTH, CODE_TABLE_FIELD_LENGTH_OVERFLOW); + std::tie(std::ignore, insertSuccess) = checkDupFields.insert(col); + originFields.emplace_back(col); + if (!insertSuccess) + break; + } + if (!insertSuccess) + { + // dup + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("TableManagerPrecompiled") + << LOG_DESC("columns duplicate") << LOG_KV("tableName", tableName); + _callParameters->setExecResult(codec.encode(int32_t(CODE_TABLE_DUPLICATE_FIELD))); + return; + } + // 3. append new columns + // manually change sys_table + // here is a trick to update table value fields + Entry newEntry; + auto newField = boost::join(originFields, ","); + + newEntry.importFields({std::move(newField)}); + _executive->storage().setRow(StorageInterface::SYS_TABLES, tableName, std::move(newEntry)); + gasPricer->appendOperation(InterfaceOpcode::Set, 1); + _callParameters->setExecResult(codec.encode(int32_t(CODE_SUCCESS))); +} + +void TableManagerPrecompiled::openTable( + const std::shared_ptr& _executive, + const PrecompiledGas::Ptr& gasPricer, const PrecompiledExecResult::Ptr& _callParameters) +{ + /// only solidity: openTable(string) => address + std::string tableName; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_callParameters->params(), tableName); + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("TableManagerPrecompiled") + << LOG_KV("openTable", tableName); + gasPricer->appendOperation(InterfaceOpcode::OpenTable); + // /tables + tableName + auto absolutePath = getTableName(tableName); + auto table = _executive->storage().openTable(absolutePath); + + if (table) + { + // file exists, try to get type + auto typeEntry = _executive->storage().getRow(absolutePath, FS_KEY_TYPE); + if (typeEntry && typeEntry->getField(0) == FS_TYPE_LINK) + { + // if link + auto addressEntry = _executive->storage().getRow(absolutePath, FS_LINK_ADDRESS); + auto contractAddress = std::string(addressEntry->get()); + auto codecAddress = codec.encode(Address(std::move(contractAddress))); + _callParameters->setExecResult(std::move(codecAddress)); + return; + } + + _callParameters->setExecResult(codec.encode(Address())); + return; + } + _callParameters->setExecResult(codec.encode(Address())); +} + +void TableManagerPrecompiled::desc( + const std::shared_ptr& _executive, + const PrecompiledGas::Ptr& gasPricer, const PrecompiledExecResult::Ptr& _callParameters) +{ + /// desc(string) + std::string tableName; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_callParameters->params(), tableName); + + tableName = getActualTableName(getTableName(tableName)); + PRECOMPILED_LOG(DEBUG) << LOG_DESC("Table desc") << LOG_KV("tableName", tableName); + + auto sysEntry = _executive->storage().getRow(storage::StorageInterface::SYS_TABLES, tableName); + if (!sysEntry) + { + TableInfoTuple tableInfo = {}; + _callParameters->setExecResult(codec.encode(std::move(tableInfo))); + return; + } + auto keyAndValue = sysEntry->get(); + auto keyField = std::string(keyAndValue.substr(0, keyAndValue.find_first_of(','))); + auto valueFields = std::string(keyAndValue.substr(keyAndValue.find_first_of(',') + 1)); + std::vector values; + boost::split(values, std::move(valueFields), boost::is_any_of(",")); + + TableInfoTuple tableInfo = {std::move(keyField), std::move(values)}; + + gasPricer->appendOperation(InterfaceOpcode::Select); + _callParameters->setExecResult(codec.encode(std::move(tableInfo))); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/TableManagerPrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/TableManagerPrecompiled.h" new file mode 100644 index 00000000..f4d3a828 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/TableManagerPrecompiled.h" @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2021 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 TableManagerPrecompiled.h + * @author: kyonGuo + * @date 2022/4/8 + */ + +#pragma once + +#include "../executive/TransactionExecutive.h" +#include "../vm/Precompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include +#include + +namespace bcos::precompiled +{ +class TableManagerPrecompiled : public Precompiled +{ +public: + using Ptr = std::shared_ptr; + TableManagerPrecompiled(crypto::Hash::Ptr _hashImpl); + virtual ~TableManagerPrecompiled() = default; + + std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) override; + +private: + void createTable(const std::shared_ptr& _executive, + const PrecompiledGas::Ptr& gasPricer, PrecompiledExecResult::Ptr const& _callParameters); + void createKVTable(const std::shared_ptr& _executive, + const PrecompiledGas::Ptr& gasPricer, PrecompiledExecResult::Ptr const& _callParameters); + void appendColumns(const std::shared_ptr& _executive, + const PrecompiledGas::Ptr& gasPricer, PrecompiledExecResult::Ptr const& _callParameters); + void openTable(const std::shared_ptr& _executive, + const PrecompiledGas::Ptr& gasPricer, PrecompiledExecResult::Ptr const& _callParameters); + void desc(const std::shared_ptr& _executive, + const PrecompiledGas::Ptr& gasPricer, PrecompiledExecResult::Ptr const& _callParameters); +}; +} // namespace bcos::precompiled diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/TablePrecompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/TablePrecompiled.cpp" new file mode 100644 index 00000000..147af7f8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/TablePrecompiled.cpp" @@ -0,0 +1,585 @@ +/** + * 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 TablePrecompiled.cpp + * @author: kyonGuo + * @date 2022/4/20 + */ + +#include "TablePrecompiled.h" +#include "bcos-executor/src/precompiled/common/PrecompiledResult.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::precompiled; +using namespace bcos::protocol; + +constexpr const char* const TABLE_METHOD_SELECT_KEY = "select(string)"; +constexpr const char* const TABLE_METHOD_SELECT_CON = "select((uint8,string)[],(uint32,uint32))"; +constexpr const char* const TABLE_METHOD_COUNT = "count((uint8,string)[])"; +constexpr const char* const TABLE_METHOD_INSERT = "insert((string,string[]))"; +constexpr const char* const TABLE_METHOD_UPDATE_KEY = "update(string,(string,string)[])"; +constexpr const char* const TABLE_METHOD_UPDATE_CON = + "update((uint8,string)[],(uint32,uint32),(string,string)[])"; +constexpr const char* const TABLE_METHOD_REMOVE_KEY = "remove(string)"; +constexpr const char* const TABLE_METHOD_REMOVE_CON = "remove((uint8,string)[],(uint32,uint32))"; + +TablePrecompiled::TablePrecompiled(crypto::Hash::Ptr _hashImpl) : Precompiled(_hashImpl) +{ + name2Selector[TABLE_METHOD_SELECT_KEY] = getFuncSelector(TABLE_METHOD_SELECT_KEY, _hashImpl); + name2Selector[TABLE_METHOD_SELECT_CON] = getFuncSelector(TABLE_METHOD_SELECT_CON, _hashImpl); + name2Selector[TABLE_METHOD_COUNT] = getFuncSelector(TABLE_METHOD_COUNT, _hashImpl); + name2Selector[TABLE_METHOD_INSERT] = getFuncSelector(TABLE_METHOD_INSERT, _hashImpl); + name2Selector[TABLE_METHOD_UPDATE_KEY] = getFuncSelector(TABLE_METHOD_UPDATE_KEY, _hashImpl); + name2Selector[TABLE_METHOD_UPDATE_CON] = getFuncSelector(TABLE_METHOD_UPDATE_CON, _hashImpl); + name2Selector[TABLE_METHOD_REMOVE_KEY] = getFuncSelector(TABLE_METHOD_REMOVE_KEY, _hashImpl); + name2Selector[TABLE_METHOD_REMOVE_CON] = getFuncSelector(TABLE_METHOD_REMOVE_CON, _hashImpl); +} + +std::shared_ptr TablePrecompiled::call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) +{ + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + // [tableName,keyField,valueFields][actualParams] + std::vector dynamicParams; + bytes param; + codec.decode(_callParameters->input(), dynamicParams, param); + auto tableName = dynamicParams.at(0); + tableName = getActualTableName(tableName); + // get user call actual params + auto originParam = ref(param); + uint32_t func = getParamFunc(originParam); + bytesConstRef data = getParamData(originParam); + auto gasPricer = m_precompiledGasFactory->createPrecompiledGas(); + gasPricer->setMemUsed(param.size()); + + auto table = _executive->storage().openTable(tableName); + if (!table.has_value()) + { + BOOST_THROW_EXCEPTION(PrecompiledError(tableName + " does not exist")); + } + + if (func == name2Selector[TABLE_METHOD_SELECT_KEY]) + { + /// select(string) + selectByKey(tableName, _executive, data, gasPricer, _callParameters); + } + else if (func == name2Selector[TABLE_METHOD_SELECT_CON]) + { + /// select((uint8,string)[],(uint32,uint32)) + selectByCondition(tableName, _executive, data, gasPricer, _callParameters); + } + else if (func == name2Selector[TABLE_METHOD_INSERT]) + { + /// insert((string,string[])) + insert(tableName, _executive, data, gasPricer, _callParameters); + } + else if (func == name2Selector[TABLE_METHOD_UPDATE_KEY]) + { + /// update(string,(uint,string)[]) + updateByKey(tableName, _executive, data, gasPricer, _callParameters); + } + else if (func == name2Selector[TABLE_METHOD_UPDATE_CON]) + { + /// update((uint8,string)[],(uint32,uint32),(uint,string)[]) + updateByCondition(tableName, _executive, data, gasPricer, _callParameters); + } + else if (func == name2Selector[TABLE_METHOD_REMOVE_KEY]) + { + /// remove(string) + removeByKey(tableName, _executive, data, gasPricer, _callParameters); + } + else if (func == name2Selector[TABLE_METHOD_REMOVE_CON]) + { + /// remove((uint8,string)[],(uint32,uint32)) + removeByCondition(tableName, _executive, data, gasPricer, _callParameters); + } + else if (func == name2Selector[TABLE_METHOD_COUNT]) + { + /// count((uint8,string)[]) + count(tableName, _executive, data, gasPricer, _callParameters); + } + else + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("TablePrecompiled") + << LOG_DESC("call undefined function!"); + BOOST_THROW_EXCEPTION(PrecompiledError("TablePrecompiled call undefined function!")); + } + gasPricer->updateMemUsed(_callParameters->m_execResult.size()); + _callParameters->setGasLeft(_callParameters->m_gasLeft - gasPricer->calTotalGas()); + return _callParameters; +} + +void TablePrecompiled::desc(TableInfoTuple& _tableInfo, const std::string& _tableName, + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) const +{ + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + auto tableName = _tableName.substr(0, 2) == "u_" ? _tableName.substr(2) : _tableName; + PRECOMPILED_LOG(DEBUG) << LOG_DESC("TablePrecompiled desc") << LOG_KV("tableName", tableName); + + auto input = codec.encodeWithSig("desc(string)", tableName); + std::string tableManagerAddress = + blockContext->isWasm() ? TABLE_MANAGER_NAME : TABLE_MANAGER_ADDRESS; + + // external call to get desc + auto response = externalRequest(_executive, ref(input), _callParameters->m_origin, + _callParameters->m_codeAddress, tableManagerAddress, _callParameters->m_staticCall, + _callParameters->m_create, _callParameters->m_gasLeft); + + codec.decode(ref(response->data), _tableInfo); +} + +void TablePrecompiled::buildKeyCondition(std::optional& keyCondition, + const std::vector& conditions, const LimitTuple& limit) const +{ + const auto& offset = std::get<0>(limit); + const auto& count = std::get<1>(limit); + if (count > USER_TABLE_MAX_LIMIT_COUNT || offset > offset + count) + { + PRECOMPILED_LOG(INFO) << LOG_DESC("build key condition limit overflow") + << LOG_KV("offset", offset) << LOG_KV("count", count); + BOOST_THROW_EXCEPTION(PrecompiledError("Limit overflow.")); + } + if (conditions.empty()) + { + BOOST_THROW_EXCEPTION(PrecompiledError("Condition is empty")); + } + for (const auto& condition : conditions) + { + const auto& cmp = std::get<0>(condition); + const auto& value = std::get<1>(condition); + switch (cmp) + { + case 0: + keyCondition->GT(value); + break; + case 1: + keyCondition->GE(value); + break; + case 2: + keyCondition->LT(value); + break; + case 3: + keyCondition->LE(value); + break; + default: + BOOST_THROW_EXCEPTION( + PrecompiledError(std::to_string(cmp) + " ConditionOP not exist!")); + } + } + + keyCondition->limit(offset, count); +} + +void TablePrecompiled::selectByKey(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const PrecompiledGas::Ptr& gasPricer, const PrecompiledExecResult::Ptr& _callParameters) +{ + /// select(string) + std::string key; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(data, key); + PRECOMPILED_LOG(TRACE) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("SELECT") + << LOG_KV("tableName", tableName); + + auto entry = _executive->storage().getRow(tableName, key); + if (!entry.has_value()) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("SELECT") + << LOG_DESC("Table select not exist") << LOG_KV("key", key); + EntryTuple emptyEntry = {}; + _callParameters->setExecResult(codec.encode(std::move(emptyEntry))); + return; + } + auto values = entry->getObject>(); + + // update the memory gas and the computation gas + gasPricer->updateMemUsed(values.size()); + gasPricer->appendOperation(InterfaceOpcode::Select); + PRECOMPILED_LOG(TRACE) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("SELECT") + << LOG_KV("key", key) << LOG_KV("valueSize", values.size()); + + EntryTuple entryTuple = {key, std::move(values)}; + _callParameters->setExecResult(codec.encode(std::move(entryTuple))); +} + +void TablePrecompiled::selectByCondition(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const PrecompiledGas::Ptr& gasPricer, const PrecompiledExecResult::Ptr& _callParameters) +{ + /// select((uint8,string)[],(uint32,uint32)) + std::vector conditions; + precompiled::LimitTuple limit; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(data, conditions, limit); + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("SELECT") + << LOG_KV("tableName", tableName) + << LOG_KV("ConditionSize", conditions.size()) + << LOG_KV("limitOffset", std::get<0>(limit)) + << LOG_KV("limitCount", std::get<1>(limit)); + + auto keyCondition = std::make_optional(); + // will throw exception when wrong condition cmp or limit count overflow + buildKeyCondition(keyCondition, std::move(conditions), std::move(limit)); + + // merge keys from storage and eqKeys + auto tableKeyList = _executive->storage().getPrimaryKeys(tableName, keyCondition); + std::vector entries({}); + entries.reserve(tableKeyList.size()); + for (auto& key : tableKeyList) + { + auto tableEntry = _executive->storage().getRow(tableName, key); + EntryTuple entryTuple = {key, tableEntry->getObject>()}; + entries.emplace_back(std::move(entryTuple)); + } + PRECOMPILED_LOG(TRACE) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("SELECT") + << LOG_KV("entries.size", entries.size()); + // update the memory gas and the computation gas + gasPricer->updateMemUsed(entries.size()); + gasPricer->appendOperation(InterfaceOpcode::Select, entries.size()); + _callParameters->setExecResult(codec.encode(entries)); +} + +void TablePrecompiled::count(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const PrecompiledGas::Ptr& gasPricer, const PrecompiledExecResult::Ptr& _callParameters) + +{ + /// count((uint8,string)[]) + std::vector conditions; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(data, conditions); + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("COUNT") + << LOG_KV("tableName", tableName) + << LOG_KV("ConditionSize", conditions.size()); + + uint32_t totalCount = 0; + uint32_t singleCount = 0; + do + { + auto keyCondition = std::make_optional(); + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + // will throw exception when wrong condition cmp or limit count overflow + buildKeyCondition( + keyCondition, conditions, {0 + totalCount, USER_TABLE_MAX_LIMIT_COUNT}); + } + else if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_0_VERSION) <= 0) + { + /// NOTE: if version <= 3.0, here will use empty limit, which means count always return + /// 0 + buildKeyCondition(keyCondition, conditions, {}); + } + + singleCount = _executive->storage().getPrimaryKeys(tableName, keyCondition).size(); + if (totalCount > totalCount + singleCount) + { + // overflow + totalCount = UINT32_MAX; + break; + } + totalCount += singleCount; + } while (singleCount >= USER_TABLE_MAX_LIMIT_COUNT); + PRECOMPILED_LOG(TRACE) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("COUNT") + << LOG_KV("totalCount", totalCount); + // update the memory gas and the computation gas + gasPricer->appendOperation(InterfaceOpcode::Select); + _callParameters->setExecResult(codec.encode(uint32_t(totalCount))); +} + +void TablePrecompiled::insert(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const PrecompiledGas::Ptr& gasPricer, const PrecompiledExecResult::Ptr& _callParameters) +{ + /// insert((string,string[])) + precompiled::EntryTuple insertEntry; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(data, insertEntry); + + auto& key = std::get<0>(insertEntry); + auto& values = std::get<1>(insertEntry); + + PRECOMPILED_LOG(INFO) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("INSERT") + << LOG_KV("tableName", tableName) << LOG_KV("key", key) + << LOG_KV("valueSize", values.size()); + + TableInfoTuple tableInfo; + // external call table manager desc + desc(tableInfo, tableName, _executive, _callParameters); + auto columns = std::get<1>(tableInfo); + + if (values.size() != columns.size()) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("INSERT") + << LOG_DESC("Table insert entry fields number mismatch") + << LOG_KV("valueSize", values.size()) + << LOG_KV("fieldSize", columns.size()); + BOOST_THROW_EXCEPTION(PrecompiledError("Table insert entry fields number mismatch")); + } + checkLengthValidate(key, USER_TABLE_KEY_VALUE_MAX_LENGTH, CODE_TABLE_KEY_VALUE_LENGTH_OVERFLOW); + std::for_each(values.begin(), values.end(), [](std::string_view _v) { + checkLengthValidate( + _v, USER_TABLE_FIELD_VALUE_MAX_LENGTH, CODE_TABLE_FIELD_VALUE_LENGTH_OVERFLOW); + }); + + if (_executive->storage().getRow(tableName, key)) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("INSERT") + << LOG_DESC("key already exist in table, please use UPDATE method") + << LOG_KV("key", key); + _callParameters->setExecResult(codec.encode(int32_t(CODE_INSERT_KEY_EXIST))); + return; + } + + Entry entry; + entry.setObject(std::move(values)); + + gasPricer->appendOperation(InterfaceOpcode::Insert); + gasPricer->updateMemUsed(entry.size()); + _executive->storage().setRow(tableName, key, std::move(entry)); + _callParameters->setExecResult(codec.encode(int32_t(1))); +} + +void TablePrecompiled::updateByKey(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const PrecompiledGas::Ptr& gasPricer, const PrecompiledExecResult::Ptr& _callParameters) +{ + /// update(string,(string,string)[]) + std::string key; + std::vector updateFields; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(data, key, updateFields); + PRECOMPILED_LOG(INFO) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("UPDATE") + << LOG_KV("tableName", tableName) << LOG_KV("updateKey", key) + << LOG_KV("updateFieldsSize", updateFields.size()); + auto existEntry = _executive->storage().getRow(tableName, key); + if (!existEntry) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("UPDATE") + << LOG_DESC("key not exist in table, please use INSERT method") + << LOG_KV("notExistKey", key); + _callParameters->setExecResult(codec.encode(int32_t(CODE_UPDATE_KEY_NOT_EXIST))); + return; + } + + TableInfoTuple tableInfo; + // external call table manager desc + desc(tableInfo, tableName, _executive, _callParameters); + auto keyField = std::get<0>(tableInfo); + auto columns = std::get<1>(tableInfo); + auto values = existEntry->getObject>(); + for (const auto& kv : updateFields) + { + auto& field = std::get<0>(kv); + auto& value = std::get<1>(kv); + checkLengthValidate( + value, USER_TABLE_FIELD_VALUE_MAX_LENGTH, CODE_TABLE_FIELD_VALUE_LENGTH_OVERFLOW); + auto const it = std::find(columns.begin(), columns.end(), field); + if (field == keyField) + { + PRECOMPILED_LOG(DEBUG) + << LOG_BADGE("TablePrecompiled") << LOG_BADGE("UPDATE") + << LOG_DESC("Table cannot update keyField") << LOG_KV("keyField", keyField); + BOOST_THROW_EXCEPTION( + PrecompiledError("Table update fields cannot contains key field")); + } + if (it == columns.end()) + { + PRECOMPILED_LOG(DEBUG) + << LOG_BADGE("TablePrecompiled") << LOG_BADGE("UPDATE") + << LOG_DESC("Table update field not found") << LOG_KV("field", field); + BOOST_THROW_EXCEPTION(PrecompiledError("Table update fields not found")); + } + auto index = std::distance(columns.begin(), it); + values[index] = value; + } + Entry updateEntry; + updateEntry.setObject(std::move(values)); + _executive->storage().setRow(tableName, key, std::move(updateEntry)); + + gasPricer->appendOperation(InterfaceOpcode::Update); + _callParameters->setExecResult(codec.encode(int32_t(1))); +} + +void TablePrecompiled::updateByCondition(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const PrecompiledGas::Ptr& gasPricer, const PrecompiledExecResult::Ptr& _callParameters) +{ + /// update((uint8,string)[],(uint32,uint32),(uint,string)[]) + std::vector conditions; + precompiled::LimitTuple limitTuple; + std::vector updateFields; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(data, conditions, limitTuple, updateFields); + PRECOMPILED_LOG(INFO) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("UPDATE") + << LOG_KV("tableName", tableName) + << LOG_KV("ConditionSize", conditions.size()) + << LOG_KV("limitOffset", std::get<0>(limitTuple)) + << LOG_KV("limitCount", std::get<1>(limitTuple)) + << LOG_KV("updateFieldsSize", updateFields.size()); + auto keyCondition = std::make_optional(); + + // will throw exception when wrong condition cmp or limit count overflow + buildKeyCondition(keyCondition, std::move(conditions), std::move(limitTuple)); + + if (c_fileLogLevel <= LogLevel::TRACE) + { + PRECOMPILED_LOG(TRACE) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("UPDATE") + << LOG_DESC("keyCond trace ") << keyCondition->toString(); + } + + auto tableKeyList = _executive->storage().getPrimaryKeys(tableName, keyCondition); + + TableInfoTuple tableInfo; + // external call table manager desc + desc(tableInfo, tableName, _executive, _callParameters); + auto keyField = std::get<0>(tableInfo); + auto columns = std::get<1>(tableInfo); + + std::vector> updateValue; + updateValue.reserve(updateFields.size()); + for (const auto& kv : updateFields) + { + auto& field = std::get<0>(kv); + auto& value = std::get<1>(kv); + checkLengthValidate( + value, USER_TABLE_FIELD_VALUE_MAX_LENGTH, CODE_TABLE_FIELD_VALUE_LENGTH_OVERFLOW); + auto const it = std::find(columns.begin(), columns.end(), field); + if (field == keyField) + { + PRECOMPILED_LOG(DEBUG) + << LOG_BADGE("TablePrecompiled") << LOG_BADGE("UPDATE") + << LOG_DESC("Table cannot update keyField") << LOG_KV("keyField", keyField); + BOOST_THROW_EXCEPTION( + PrecompiledError("Table update fields cannot contains key field")); + } + if (it == columns.end()) + { + PRECOMPILED_LOG(DEBUG) + << LOG_BADGE("TablePrecompiled") << LOG_BADGE("UPDATE") + << LOG_DESC("Table update field not found") << LOG_KV("field", field); + BOOST_THROW_EXCEPTION(PrecompiledError("Table update fields not found")); + } + std::pair p = {std::distance(columns.begin(), it), std::move(value)}; + updateValue.emplace_back(std::move(p)); + } + + auto entries = _executive->storage().getRows(tableName, tableKeyList); + for (size_t i = 0; i < entries.size(); ++i) + { + auto&& entry = entries[i]; + auto values = entry->getObject>(); + for (auto& kv : updateValue) + { + values[kv.first] = kv.second; + } + entry->setObject(std::move(values)); + _executive->storage().setRow(tableName, tableKeyList[i], std::move(entry.value())); + } + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("UPDATE") + << LOG_KV("selectKeySize", tableKeyList.size()) + << LOG_KV("affectedRows", entries.size()); + gasPricer->setMemUsed(tableKeyList.size() * columns.size()); + gasPricer->appendOperation(InterfaceOpcode::Update, tableKeyList.size()); + _callParameters->setExecResult(codec.encode((int32_t)tableKeyList.size())); +} + +void TablePrecompiled::removeByKey(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const PrecompiledGas::Ptr& gasPricer, const PrecompiledExecResult::Ptr& _callParameters) +{ + /// remove(string) + std::string key; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(data, key); + PRECOMPILED_LOG(DEBUG) << LOG_DESC("Table remove") << LOG_KV("tableName", tableName) + << LOG_KV("removeKey", key); + + auto existEntry = _executive->storage().getRow(tableName, key); + if (!existEntry) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("REMOVE") + << LOG_DESC("key not exist in table") << LOG_KV("notExistKey", key); + _callParameters->setExecResult(codec.encode(int32_t(CODE_REMOVE_KEY_NOT_EXIST))); + return; + } + Entry deletedEntry; + deletedEntry.setStatus(Entry::DELETED); + _executive->storage().setRow(tableName, key, std::move(deletedEntry)); + + gasPricer->appendOperation(InterfaceOpcode::Remove); + _callParameters->setExecResult(codec.encode(int32_t(1))); +} + +void TablePrecompiled::removeByCondition(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const PrecompiledGas::Ptr& gasPricer, const PrecompiledExecResult::Ptr& _callParameters) +{ + /// remove((uint8,string)[],(uint32,uint32)) + std::vector conditions; + precompiled::LimitTuple limitTuple; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(data, conditions, limitTuple); + PRECOMPILED_LOG(INFO) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("REMOVE") + << LOG_KV("tableName", tableName) + << LOG_KV("ConditionSize", conditions.size()) + << LOG_KV("limitOffset", std::get<0>(limitTuple)) + << LOG_KV("limitCount", std::get<1>(limitTuple)); + + auto keyCondition = std::make_optional(); + + // will throw exception when wrong condition cmp or limit count overflow + buildKeyCondition(keyCondition, std::move(conditions), std::move(limitTuple)); + + if (c_fileLogLevel <= LogLevel::TRACE) + { + PRECOMPILED_LOG(TRACE) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("REMOVE") + << LOG_DESC("keyCond trace ") << keyCondition->toString(); + } + + auto tableKeyList = _executive->storage().getPrimaryKeys(tableName, keyCondition); + + for (auto& tableKey : tableKeyList) + { + Entry deletedEntry; + deletedEntry.setStatus(Entry::DELETED); + _executive->storage().setRow(tableName, tableKey, std::move(deletedEntry)); + } + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("TablePrecompiled") << LOG_BADGE("REMOVE") + << LOG_KV("affectedRows", tableKeyList.size()); + gasPricer->setMemUsed(tableKeyList.size()); + gasPricer->appendOperation(InterfaceOpcode::Remove, tableKeyList.size()); + _callParameters->setExecResult(codec.encode((int32_t)tableKeyList.size())); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/TablePrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/TablePrecompiled.h" new file mode 100644 index 00000000..689567e6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/TablePrecompiled.h" @@ -0,0 +1,73 @@ +/** + * 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 TablePrecompiled.h + * @author: kyonGuo + * @date 2022/4/20 + */ + +#pragma once + +#include "../vm/Precompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include +#include + +namespace bcos::precompiled +{ +class TablePrecompiled : public bcos::precompiled::Precompiled +{ +public: + using Ptr = std::shared_ptr; + TablePrecompiled(crypto::Hash::Ptr _hashImpl); + ~TablePrecompiled() override = default; + + std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) override; + +private: + void selectByKey(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const PrecompiledGas::Ptr& gasPricer, PrecompiledExecResult::Ptr const& _callParameters); + void selectByCondition(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const PrecompiledGas::Ptr& gasPricer, PrecompiledExecResult::Ptr const& _callParameters); + void count(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const PrecompiledGas::Ptr& gasPricer, PrecompiledExecResult::Ptr const& _callParameters); + void insert(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const PrecompiledGas::Ptr& gasPricer, PrecompiledExecResult::Ptr const& _callParameters); + void updateByKey(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const PrecompiledGas::Ptr& gasPricer, PrecompiledExecResult::Ptr const& _callParameters); + void updateByCondition(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const PrecompiledGas::Ptr& gasPricer, PrecompiledExecResult::Ptr const& _callParameters); + void removeByKey(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const PrecompiledGas::Ptr& gasPricer, PrecompiledExecResult::Ptr const& _callParameters); + void removeByCondition(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const PrecompiledGas::Ptr& gasPricer, PrecompiledExecResult::Ptr const& _callParameters); + void buildKeyCondition(std::optional& keyCondition, + const std::vector& conditions, + const precompiled::LimitTuple& limit) const; + void desc(TableInfoTuple& _tableInfo, const std::string& tableName, + const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters) const; +}; +} // namespace bcos::precompiled \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/common/Common.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/common/Common.h" new file mode 100644 index 00000000..38055414 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/common/Common.h" @@ -0,0 +1,167 @@ +/** + * Copyright (C) 2021 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: kyonRay + * @date 2021-05-25 + */ + +#pragma once + +#include "PrecompiledAbi.h" +#include "bcos-framework/Common.h" +#include "bcos-framework/protocol/CommonError.h" +#include "bcos-framework/protocol/Exceptions.h" +#include "bcos-framework/storage/Common.h" +#include "bcos-framework/storage/Entry.h" +#include "bcos-executor/src/Common.h" +#include +#include + +namespace bcos +{ +namespace precompiled +{ +#define PRECOMPILED_LOG(LEVEL) BCOS_LOG(LEVEL) << "[EXECUTOR][PRECOMPILED]" + +using TableInfoTuple = std::tuple>; +using ConditionTuple = std::tuple; +using LimitTuple = std::tuple; +using UpdateFieldTuple = std::tuple; +using EntryTuple = std::tuple>; +using BfsTuple = std::tuple>; + +/// Precompiled reserved code field +static constexpr const char* const PRECOMPILED_CODE_FIELD = "[PRECOMPILED]"; +static constexpr const int PRECOMPILED_CODE_FIELD_SIZE = 13; + +/// SYS_CONFIG table fields +static constexpr size_t SYS_VALUE = 0; +static constexpr const char* SYS_VALUE_FIELDS = "value"; + +/// FileSystem path limit +static const size_t FS_PATH_MAX_LENGTH = 56; +static const size_t FS_PATH_MAX_LEVEL = 6; + +const int SYS_TABLE_KEY_FIELD_NAME_MAX_LENGTH = 64; +const int SYS_TABLE_VALUE_FIELD_NAME_MAX_LENGTH = 1024; +// field +const int USER_TABLE_FIELD_NAME_MAX_LENGTH = 64; +const int USER_TABLE_NAME_MAX_LENGTH_S = 50; +// value +const int USER_TABLE_KEY_VALUE_MAX_LENGTH = 255; +const int USER_TABLE_FIELD_VALUE_MAX_LENGTH = 16 * 1024 * 1024 - 1; +const int USER_TABLE_MAX_LIMIT_COUNT = 500; + +const int CODE_NO_AUTHORIZED = -50000; +const int CODE_TABLE_NAME_ALREADY_EXIST = -50001; +const int CODE_TABLE_NAME_LENGTH_OVERFLOW = -50002; +const int CODE_TABLE_FIELD_LENGTH_OVERFLOW = -50003; +const int CODE_TABLE_FIELD_TOTAL_LENGTH_OVERFLOW = -50004; +const int CODE_TABLE_KEY_VALUE_LENGTH_OVERFLOW = -50005; +const int CODE_TABLE_FIELD_VALUE_LENGTH_OVERFLOW = -50006; +const int CODE_TABLE_DUPLICATE_FIELD = -50007; +const int CODE_TABLE_INVALIDATE_FIELD = -50008; + +const int TX_COUNT_LIMIT_MIN = 1; +const int TX_GAS_LIMIT_MIN = 100000; + +enum PrecompiledErrorCode : int +{ + // BFSPrecompiled -53099 ~ -53000 + CODE_ADDRESS_OR_VERSION_ERROR = -51202, + CODE_FILE_COUNT_ERROR = -53007, + CODE_FILE_INVALID_TYPE = -53006, + CODE_FILE_INVALID_PATH = -53005, + CODE_FILE_BUILD_DIR_FAILED = -53003, + CODE_FILE_ALREADY_EXIST = -53002, + CODE_FILE_NOT_EXIST = -53001, + + // AccountManagerPrecompiled -51999 ~ -51900 + CODE_INVALID_REVOKE_LAST_AUTHORIZATION = -51907, + CODE_INVALID_NON_EXIST_AUTHORIZATION = -51906, + CODE_INVALID_NO_AUTHORIZED = -51905, + CODE_INVALID_TABLE_NOT_EXIST = -51904, + CODE_INVALID_CONTRACT_ADDRESS = -51903, + CODE_INVALID_CONTRACT_REPEAT_AUTHORIZATION = -51902, + CODE_INVALID_CONTRACT_AVAILABLE = -51901, + CODE_ACCOUNT_ALREADY_EXIST = -51900, + + // RingSigPrecompiled -51899 ~ -51800 + VERIFY_RING_SIG_FAILED = -51800, + + // GroupSigPrecompiled -51799 ~ -51700 + VERIFY_GROUP_SIG_FAILED = -51700, + + // PaillierPrecompiled -51699 ~ -51600 + CODE_INVALID_CIPHERS = -51600, + + // CRUDPrecompiled -51599 ~ -51500 + CODE_REMOVE_KEY_NOT_EXIST = -51508, + CODE_UPDATE_KEY_NOT_EXIST = -51507, + CODE_INSERT_KEY_EXIST = -51506, + CODE_KEY_NOT_EXIST_IN_ENTRY = -51504, + CODE_INVALID_UPDATE_TABLE_KEY = -51503, + CODE_CONDITION_OPERATION_UNDEFINED = -51502, + CODE_PARSE_CONDITION_ERROR = -51501, + CODE_PARSE_ENTRY_ERROR = -51500, + + // DagTransferPrecompiled -51499 ~ -51400 + CODE_INVALID_OPENTABLE_FAILED = -51406, + CODE_INVALID_BALANCE_OVERFLOW = -51405, + CODE_INVALID_INSUFFICIENT_BALANCE = -51404, + CODE_INVALID_USER_ALREADY_EXIST = -51403, + CODE_INVALID_USER_NOT_EXIST = -51402, + CODE_INVALID_AMOUNT = -51401, + CODE_INVALID_USER_NAME = -51400, + + // SystemConfigPrecompiled -51399 ~ -51300 + + // ConsensusPrecompiled -51199 ~ -51100 + CODE_ADD_SEALER_SHOULD_IN_OBSERVER = -51104, + CODE_NODE_NOT_EXIST = -51103, + CODE_INVALID_WEIGHT = -51102, + CODE_LAST_SEALER = -51101, + CODE_INVALID_NODE_ID = -51100, + + // AuthPrecompiledTest -51099 ~ -51000 + CODE_TABLE_AUTH_TYPE_DECODE_ERROR = -51004, + CODE_TABLE_ERROR_AUTH_TYPE = -51003, + CODE_TABLE_AUTH_TYPE_NOT_EXIST = -51002, + CODE_TABLE_AUTH_ROW_NOT_EXIST = -51001, + CODE_TABLE_AGENT_ROW_NOT_EXIST = -51000, + + // Common error code among all precompiled contracts -50199 ~ -50100 + CODE_TABLE_OPEN_ERROR = -50105, + CODE_TABLE_CREATE_ERROR = -50104, + CODE_TABLE_SET_ROW_ERROR = -50103, + CODE_ADDRESS_INVALID = -50102, + CODE_UNKNOW_FUNCTION_CALL = -50101, + CODE_TABLE_NOT_EXIST = -50100, + + // correct return: code great or equal 0 + CODE_SUCCESS = 0 +}; + +enum ContractStatus +{ + Available, + Frozen, + AddressNonExistent, + NotContractAddress, + Count +}; +} // namespace precompiled +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/common/PrecompiledAbi.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/common/PrecompiledAbi.h" new file mode 100644 index 00000000..a4a68eab --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/common/PrecompiledAbi.h" @@ -0,0 +1,33 @@ +/** + * 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 PrecompiledAbi.h + * @author: kyonGuo + * @date 2022/10/25 + */ + +#pragma once +#include + +namespace bcos::precompiled +{ + +// clang-format off +constexpr static const std::string_view ACCOUNT_ABI = "[{\"inputs\":[],\"name\":\"getAccountStatus\",\"outputs\":[{\"internalType\":\"enum AccountStatus\",\"name\":\"\",\"type\":\"uint8\"}],\"selector\":[460338666,3816497635],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"enum AccountStatus\",\"name\":\"status\",\"type\":\"uint8\"}],\"name\":\"setAccountStatus\",\"outputs\":[{\"internalType\":\"int32\",\"name\":\"\",\"type\":\"int32\"}],\"selector\":[184977018,369160594],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"; +constexpr static const std::string_view TABLE_ABI = "[{\"inputs\":[{\"components\":[{\"internalType\":\"enum ConditionOP\",\"name\":\"op\",\"type\":\"uint8\"},{\"internalType\":\"string\",\"name\":\"value\",\"type\":\"string\"}],\"internalType\":\"struct Condition[]\",\"name\":\"conditions\",\"type\":\"tuple[]\"}],\"name\":\"count\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"selector\":[3625360167,2327356356],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"key\",\"type\":\"string\"},{\"internalType\":\"string[]\",\"name\":\"fields\",\"type\":\"string[]\"}],\"internalType\":\"struct Entry\",\"name\":\"entry\",\"type\":\"tuple\"}],\"name\":\"insert\",\"outputs\":[{\"internalType\":\"int32\",\"name\":\"\",\"type\":\"int32\"}],\"selector\":[1550717023,1284216112],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"enum ConditionOP\",\"name\":\"op\",\"type\":\"uint8\"},{\"internalType\":\"string\",\"name\":\"value\",\"type\":\"string\"}],\"internalType\":\"struct Condition[]\",\"name\":\"conditions\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"offset\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"count\",\"type\":\"uint32\"}],\"internalType\":\"struct Limit\",\"name\":\"limit\",\"type\":\"tuple\"}],\"name\":\"remove\",\"outputs\":[{\"internalType\":\"int32\",\"name\":\"\",\"type\":\"int32\"}],\"selector\":[1751202047,277135530],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"key\",\"type\":\"string\"}],\"name\":\"remove\",\"outputs\":[{\"internalType\":\"int32\",\"name\":\"\",\"type\":\"int32\"}],\"selector\":[2153356875,2260153337],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"enum ConditionOP\",\"name\":\"op\",\"type\":\"uint8\"},{\"internalType\":\"string\",\"name\":\"value\",\"type\":\"string\"}],\"internalType\":\"struct Condition[]\",\"name\":\"conditions\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"offset\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"count\",\"type\":\"uint32\"}],\"internalType\":\"struct Limit\",\"name\":\"limit\",\"type\":\"tuple\"}],\"name\":\"select\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"key\",\"type\":\"string\"},{\"internalType\":\"string[]\",\"name\":\"fields\",\"type\":\"string[]\"}],\"internalType\":\"struct Entry[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"selector\":[1020609838,1062557692],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"key\",\"type\":\"string\"}],\"name\":\"select\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"key\",\"type\":\"string\"},{\"internalType\":\"string[]\",\"name\":\"fields\",\"type\":\"string[]\"}],\"internalType\":\"struct Entry\",\"name\":\"\",\"type\":\"tuple\"}],\"selector\":[4242006977,1530027384],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"key\",\"type\":\"string\"},{\"components\":[{\"internalType\":\"string\",\"name\":\"columnName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"value\",\"type\":\"string\"}],\"internalType\":\"struct UpdateField[]\",\"name\":\"updateFields\",\"type\":\"tuple[]\"}],\"name\":\"update\",\"outputs\":[{\"internalType\":\"int32\",\"name\":\"\",\"type\":\"int32\"}],\"selector\":[1107285855,33194060],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"enum ConditionOP\",\"name\":\"op\",\"type\":\"uint8\"},{\"internalType\":\"string\",\"name\":\"value\",\"type\":\"string\"}],\"internalType\":\"struct Condition[]\",\"name\":\"conditions\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"offset\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"count\",\"type\":\"uint32\"}],\"internalType\":\"struct Limit\",\"name\":\"limit\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"string\",\"name\":\"columnName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"value\",\"type\":\"string\"}],\"internalType\":\"struct UpdateField[]\",\"name\":\"updateFields\",\"type\":\"tuple[]\"}],\"name\":\"update\",\"outputs\":[{\"internalType\":\"int32\",\"name\":\"\",\"type\":\"int32\"}],\"selector\":[2572410770,107820592],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"; +constexpr static const std::string_view KV_TABLE_ABI = "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"key\",\"type\":\"string\"}],\"name\":\"get\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"selector\":[1765722206,2065403395],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"key\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"value\",\"type\":\"string\"}],\"name\":\"set\",\"outputs\":[{\"internalType\":\"int32\",\"name\":\"\",\"type\":\"int32\"}],\"selector\":[3913463062,439950516],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"; +// clang-format on + +} // namespace bcos::precompiled diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/common/PrecompiledGas.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/common/PrecompiledGas.cpp" new file mode 100644 index 00000000..7c580349 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/common/PrecompiledGas.cpp" @@ -0,0 +1,80 @@ +/** + * Copyright (C) 2021 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 PrecompiledGas.cpp + * @author: kyonRay + * @date 2021-05-25 + */ + +#include "PrecompiledGas.h" +#include "bcos-executor/src/precompiled/common/Common.h" +using namespace bcos; +using namespace bcos::precompiled; + +const int64_t GasMetrics::Zero = 0; +const int64_t GasMetrics::Base = 2; +const int64_t GasMetrics::VeryLow = 3; +const int64_t GasMetrics::Low = 5; +const int64_t GasMetrics::Mid = 8; +const int64_t GasMetrics::High = 10; +// Every 256 bytes is a memory gas calculation unit +const unsigned GasMetrics::MemGas = 3; +const unsigned GasMetrics::MemUnitSize = 32; + +void PrecompiledGas::appendOperation(InterfaceOpcode const& _opType, unsigned const& _opSize) +{ + m_operationList->push_back(std::make_pair(_opType, _opSize)); +} + +void PrecompiledGas::updateMemUsed(uint64_t const& _newMemSize) +{ + if (_newMemSize > m_memUsed) + { + m_memUsed = _newMemSize; + } +} + +int64_t PrecompiledGas::calTotalGas() +{ + return (calComputationGas() + calMemGas()); +} + + +// Traverse m_operationList to calculate total gas cost +int64_t PrecompiledGas::calComputationGas() +{ + int64_t totalGas = 0; + for (auto const& it : *m_operationList) + { + if (!m_metric->OpCode2GasCost.count(it.first)) + { + PRECOMPILED_LOG(INFO) << LOG_DESC("Invalid opType:") << it.first; + continue; + } + totalGas += ((m_metric->OpCode2GasCost)[it.first]) * it.second; + } + return totalGas; +} + +// Calculating gas consumed by memory +int64_t PrecompiledGas::calMemGas() +{ + if (m_memUsed == 0) + { + return 0; + } + auto memSize = (m_memUsed + GasMetrics::MemUnitSize - 1) / GasMetrics::MemUnitSize; + return (GasMetrics::MemGas * memSize) + (memSize * memSize) / 512; +} diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/common/PrecompiledGas.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/common/PrecompiledGas.h" new file mode 100644 index 00000000..d137da65 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/common/PrecompiledGas.h" @@ -0,0 +1,163 @@ +/** + * Copyright (C) 2021 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 PrecompileGas.h + * @author: kyonRay + * @date 2021-05-25 + */ + +#pragma once +#include + +namespace bcos +{ +namespace executor +{ +using VMFlagType = uint64_t; +} + +namespace precompiled +{ +// opcode of corresponding method +enum InterfaceOpcode : int64_t +{ + EQ = 0x00, + GE = 0x01, + GT = 0x02, + LE = 0x03, + LT = 0x04, + NE = 0x05, + Limit = 0x06, + GetInt = 0x07, + GetAddr = 0x08, + Set = 0x09, + GetByte32 = 0X0a, + GetByte64 = 0x0b, + GetString = 0x0c, + CreateTable = 0x0d, + OpenTable = 0x0e, + Select = 0x0f, + Insert = 0x10, + Update = 0x11, + Remove = 0x12, + PaillierAdd = 0x13, + GroupSigVerify = 0x14, + RingSigVerify = 0x15 +}; + +struct GasMetrics +{ + // Measure the level of instruction gas + static const int64_t Zero; + static const int64_t Base; + static const int64_t VeryLow; + static const int64_t Low; + static const int64_t Mid; + static const int64_t High; + // Every 256 bytes is a memory gas calculation unit + static const unsigned MemGas; + static const unsigned MemUnitSize; + + int64_t CreateGas = 16000; + int64_t LoadGas = 200; + int64_t StoreGas = 10000; + int64_t RemoveGas = 2500; + int64_t VerifyGas = 20000; + + // opcode to gasCost mapping + std::map OpCode2GasCost; + + using Ptr = std::shared_ptr; + GasMetrics() { init(); }; + virtual ~GasMetrics() = default; + + void init() + { + OpCode2GasCost = {{InterfaceOpcode::EQ, VeryLow}, {InterfaceOpcode::GE, VeryLow}, + {InterfaceOpcode::GT, VeryLow}, {InterfaceOpcode::LE, VeryLow}, + {InterfaceOpcode::LT, VeryLow}, {InterfaceOpcode::NE, VeryLow}, + {InterfaceOpcode::Limit, VeryLow}, {InterfaceOpcode::GetInt, VeryLow}, + {InterfaceOpcode::GetAddr, VeryLow}, {InterfaceOpcode::Set, VeryLow}, + {InterfaceOpcode::GetByte32, 32 * VeryLow}, {InterfaceOpcode::GetByte64, 64 * VeryLow}, + {InterfaceOpcode::GetString, VeryLow}, {InterfaceOpcode::CreateTable, CreateGas}, + {InterfaceOpcode::OpenTable, LoadGas}, {InterfaceOpcode::Select, LoadGas}, + {InterfaceOpcode::Insert, StoreGas}, {InterfaceOpcode::Update, StoreGas}, + {InterfaceOpcode::Remove, RemoveGas}, {InterfaceOpcode::PaillierAdd, VerifyGas}, + {InterfaceOpcode::GroupSigVerify, VerifyGas}, + {InterfaceOpcode::RingSigVerify, VerifyGas}}; + } +}; + +// FreeStorageGasMetrics +struct FreeStorageGasMetrics : public GasMetrics +{ + FreeStorageGasMetrics() : GasMetrics() + { + CreateGas = 500; + LoadGas = 200; + StoreGas = 200; + RemoveGas = 200; + } + virtual ~FreeStorageGasMetrics() {} +}; + +class PrecompiledGas +{ +public: + using Ptr = std::shared_ptr; + using OpListType = std::vector>; + + PrecompiledGas() : m_operationList(std::make_shared()) {} + virtual ~PrecompiledGas() = default; + + virtual void appendOperation(InterfaceOpcode const& _opType, unsigned const& _opSize = 1); + virtual int64_t calTotalGas(); + void setMemUsed(uint64_t const& _memUsed) { m_memUsed = _memUsed; } + uint64_t const& memUsed() const { return m_memUsed; } + + void updateMemUsed(uint64_t const& _newMemSize); + void setGasMetric(GasMetrics::Ptr _metric) { m_metric = _metric; } + +protected: + // Traverse m_operationList to calculate total gas cost + virtual int64_t calComputationGas(); + // Calculating gas consumed by memory + virtual int64_t calMemGas(); + +private: + std::shared_ptr m_operationList; + GasMetrics::Ptr m_metric; + uint64_t m_memUsed = 0; +}; + +class PrecompiledGasFactory +{ +public: + using Ptr = std::shared_ptr; + PrecompiledGasFactory() { m_gasMetric = std::make_shared(); } + PrecompiledGas::Ptr createPrecompiledGas() + { + auto gasPricer = std::make_shared(); + gasPricer->setGasMetric(m_gasMetric); + return gasPricer; + } + + GasMetrics::Ptr gasMetric() { return m_gasMetric; } + +private: + GasMetrics::Ptr m_gasMetric; +}; +} // namespace precompiled +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/common/PrecompiledResult.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/common/PrecompiledResult.h" new file mode 100644 index 00000000..233a9faa --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/common/PrecompiledResult.h" @@ -0,0 +1,88 @@ +/** + * Copyright (C) 2021 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 PrecompiledResult.h + * @author: kyonRay + * @date 2021-05-25 + */ +#pragma once +#include "../../CallParameters.h" +#include "PrecompiledGas.h" +#include + +namespace bcos::precompiled +{ +struct PrecompiledExecResult +{ + using Ptr = std::shared_ptr; + PrecompiledExecResult() = default; + PrecompiledExecResult(const executor::CallParameters::UniquePtr& _callParameters) + : m_sender(_callParameters->senderAddress), + m_codeAddress(_callParameters->codeAddress), + m_precompiledAddress(_callParameters->receiveAddress), + m_origin(_callParameters->origin), + m_input(ref(_callParameters->data)), + m_gasLeft(_callParameters->gas), + m_staticCall(_callParameters->staticCall), + m_create(_callParameters->create) + {} + ~PrecompiledExecResult() = default; + PrecompiledExecResult(const PrecompiledExecResult&) = delete; + PrecompiledExecResult& operator=(const PrecompiledExecResult&) = delete; + + PrecompiledExecResult(PrecompiledExecResult&&) = delete; + PrecompiledExecResult(const PrecompiledExecResult&&) = delete; + + /** for input **/ + bytesConstRef const& input() const { return m_input; } + bytesConstRef params() const { return m_input.getCroppedData(4); } + + /** for output **/ + bytes const& execResult() const { return m_execResult; } + bytes& mutableExecResult() { return m_execResult; } + void setExecResult(bytes const& _execResult) { m_execResult = _execResult; } + void setExecResult(bytes&& _execResult) { m_execResult = std::move(_execResult); } + void setGasLeft(int64_t _gasLeft) { m_gasLeft = _gasLeft; } + inline void setExternalResult(executor::CallParameters::UniquePtr _callParameter) + { + m_execResult = std::move(_callParameter->data); + if (_callParameter->status != (int32_t)protocol::TransactionStatus::None) + { + BOOST_THROW_EXCEPTION( + protocol::PrecompiledError("External call revert: " + _callParameter->message)); + } + } + + inline void takeDataToCallParameter(executor::CallParameters::UniquePtr& callParameters) + { + callParameters->type = executor::CallParameters::FINISHED; + callParameters->gas = m_gasLeft; + callParameters->status = (int32_t)protocol::TransactionStatus::None; + callParameters->data = std::move(m_execResult); + callParameters->internalCall = false; + } + + std::string m_sender; // common field, readable format + std::string m_codeAddress; // different to 'to', this field set to precompiled origin address + std::string m_precompiledAddress; // common field, readable format + std::string m_origin; // common field, readable format + + bytesConstRef m_input; // common field, transaction data, binary format + bcos::bytes m_execResult; + int64_t m_gasLeft = 0; + bool m_staticCall = false; // common field + bool m_create = false; // by request, is creation +}; +} // namespace bcos::precompiled diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/common/Utilities.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/common/Utilities.cpp" new file mode 100644 index 00000000..bc2858ae --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/common/Utilities.cpp" @@ -0,0 +1,396 @@ +/** + * Copyright (C) 2021 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 Utilities.cpp + * @author: kyonRay + * @date 2021-05-25 + */ + +#include "Utilities.h" +#include "Common.h" +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::precompiled; +using namespace bcos::protocol; +using namespace bcos::crypto; + +static tbb::concurrent_unordered_map s_name2SelectCache; + +void bcos::precompiled::checkNameValidate(std::string_view tableName, std::string_view _keyField, + std::vector& valueFieldList) +{ + std::set valueFieldSet; + std::vector allowChar = {'$', '_', '@'}; + std::vector tableAllowChar = {'_', '/'}; + std::string allowCharString = "{$, _, @}"; + std::string tableAllowCharString = "{_, /}"; + auto checkTableNameValidate = [&tableAllowChar, &tableAllowCharString]( + std::string_view tableName) { + size_t iSize = tableName.size(); + for (size_t i = 0; i < iSize; i++) + { + if (!isalnum(tableName[i]) && + (tableAllowChar.end() == + find(tableAllowChar.begin(), tableAllowChar.end(), tableName[i]))) + { + std::stringstream errorMsg; + errorMsg << "Invalid table name \"" << tableName + << "\", the table name must be letters or numbers, and " + "only supports \"" + << tableAllowCharString << "\" as special character set"; + PRECOMPILED_LOG(INFO) << LOG_BADGE("checkNameValidate") << LOG_DESC(errorMsg.str()); + // Note: the StorageException and PrecompiledException content can't + // be modified at will for the information will be written to the + // blockchain + BOOST_THROW_EXCEPTION(PrecompiledError(errorMsg.str())); + } + } + }; + + auto checkFieldNameValidate = [&allowChar, &allowCharString]( + std::string_view tableName, std::string_view fieldName) { + if (fieldName.empty() || fieldName[0] == '_') + { + std::stringstream errorMessage; + errorMessage << "Invalid field \"" << fieldName + << "\", the size of the field must be larger than 0 and " + "the field can't start with \"_\""; + PRECOMPILED_LOG(INFO) << LOG_BADGE("checkNameValidate") << LOG_DESC(errorMessage.str()) + << LOG_KV("field name", fieldName) + << LOG_KV("table name", tableName); + BOOST_THROW_EXCEPTION(PrecompiledError("invalid field: " + std::string(fieldName))); + } + size_t iSize = fieldName.size(); + for (size_t i = 0; i < iSize; i++) + { + if (!isalnum(fieldName[i]) && + (allowChar.end() == find(allowChar.begin(), allowChar.end(), fieldName[i]))) + { + std::stringstream errorMessage; + errorMessage + << "Invalid field \"" << fieldName + << "\", the field name must be letters or numbers, and only supports \"" + << allowCharString << "\" as special character set"; + + PRECOMPILED_LOG(INFO) + << LOG_BADGE("checkNameValidate") << LOG_DESC(errorMessage.str()) + << LOG_KV("field name", fieldName) << LOG_KV("table name", tableName); + BOOST_THROW_EXCEPTION(PrecompiledError("invalid field: " + std::string(fieldName))); + } + } + }; + + checkTableNameValidate(tableName); + + checkFieldNameValidate(tableName, _keyField); + + for (auto& valueField : valueFieldList) + { + auto ret = valueFieldSet.insert(valueField); + if (!ret.second) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("checkNameValidate") << LOG_DESC("duplicated field") + << LOG_KV("field name", valueField) + << LOG_KV("table name", tableName); + BOOST_THROW_EXCEPTION(PrecompiledError("duplicated field: " + valueField)); + } + checkFieldNameValidate(tableName, valueField); + } +} + +void bcos::precompiled::checkLengthValidate( + std::string_view fieldValue, int32_t maxLength, int32_t errorCode) +{ + if (fieldValue.size() > (size_t)maxLength) + { + PRECOMPILED_LOG(DEBUG) << "key:" << fieldValue << " value size:" << fieldValue.size() + << " greater than " << maxLength; + BOOST_THROW_EXCEPTION( + PrecompiledError("size of value/key greater than" + std::to_string(maxLength) + + " error code: " + std::to_string(errorCode))); + } +} + +std::string bcos::precompiled::checkCreateTableParam(const std::string_view& _tableName, + std::string& _keyField, const std::variant>& _valueField) +{ + std::vector fieldNameList; + if (_valueField.index() == 1) + { + fieldNameList.assign(std::get<1>(_valueField).begin(), std::get<1>(_valueField).end()); + } + else + { + boost::split(fieldNameList, std::get<0>(_valueField), boost::is_any_of(",")); + } + + if (_keyField.size() > (size_t)SYS_TABLE_KEY_FIELD_NAME_MAX_LENGTH) + { // mysql TableName and fieldName length limit is 64 + BOOST_THROW_EXCEPTION( + protocol::PrecompiledError("table field name length overflow " + + std::to_string(SYS_TABLE_KEY_FIELD_NAME_MAX_LENGTH))); + } + boost::trim(_keyField); + if (_keyField.size() > (size_t)SYS_TABLE_KEY_FIELD_NAME_MAX_LENGTH) + { // mysql TableName and fieldName length limit is 64 + BOOST_THROW_EXCEPTION(protocol::PrecompiledError( + "errorCode: " + std::to_string(CODE_TABLE_FIELD_LENGTH_OVERFLOW) + + std::string("table key name length overflow ") + + std::to_string(SYS_TABLE_KEY_FIELD_NAME_MAX_LENGTH))); + } + + for (auto& str : fieldNameList) + { + boost::trim(str); + if (str.size() > (size_t)SYS_TABLE_KEY_FIELD_NAME_MAX_LENGTH) + { // mysql TableName and fieldName length limit is 64 + BOOST_THROW_EXCEPTION(protocol::PrecompiledError( + "errorCode: " + std::to_string(CODE_TABLE_FIELD_LENGTH_OVERFLOW) + + std::string("table field name length overflow ") + + std::to_string(SYS_TABLE_KEY_FIELD_NAME_MAX_LENGTH))); + } + } + + checkNameValidate(_tableName, _keyField, fieldNameList); + + auto valueField = boost::join(fieldNameList, ","); + if (valueField.size() > (size_t)SYS_TABLE_VALUE_FIELD_NAME_MAX_LENGTH) + { + BOOST_THROW_EXCEPTION( + protocol::PrecompiledError(std::string("total table field name length overflow ") + + std::to_string(SYS_TABLE_VALUE_FIELD_NAME_MAX_LENGTH))); + } + + auto tableName = precompiled::getTableName(_tableName); + if (tableName.size() > (size_t)USER_TABLE_NAME_MAX_LENGTH_S) + { + // mysql TableName and fieldName length limit is 64 + BOOST_THROW_EXCEPTION(protocol::PrecompiledError( + "errorCode: " + std::to_string(CODE_TABLE_NAME_LENGTH_OVERFLOW) + + std::string(" tableName length overflow ") + + std::to_string(USER_TABLE_NAME_MAX_LENGTH_S))); + } + return valueField; +} + +uint32_t bcos::precompiled::getFuncSelector( + std::string const& _functionName, const crypto::Hash::Ptr& _hashImpl) +{ + // global function selector cache + if (s_name2SelectCache.count(_functionName)) + { + return s_name2SelectCache[_functionName]; + } + auto selector = getFuncSelectorByFunctionName(_functionName, _hashImpl); + s_name2SelectCache.insert(std::make_pair(_functionName, selector)); + return selector; +} + +// for ut +void bcos::precompiled::clearName2SelectCache() +{ + s_name2SelectCache.clear(); +} + +uint32_t bcos::precompiled::getParamFunc(bytesConstRef _param) +{ + if (_param.size() < 4) + { + PRECOMPILED_LOG(INFO) << LOG_DESC( + "getParamFunc param too short, not enough to call precompiled") + << LOG_KV("param", toHexStringWithPrefix(_param.toBytes())); + BOOST_THROW_EXCEPTION(PrecompiledError("Empty param data in precompiled call")); + } + auto funcBytes = _param.getCroppedData(0, 4); + uint32_t func = *((uint32_t*)(funcBytes.data())); + + return ((func & 0x000000FF) << 24) | ((func & 0x0000FF00) << 8) | ((func & 0x00FF0000) >> 8) | + ((func & 0xFF000000) >> 24); +} + +uint32_t bcos::precompiled::getFuncSelectorByFunctionName( + std::string const& _functionName, const crypto::Hash::Ptr& _hashImpl) +{ + uint32_t func = *(uint32_t*)(_hashImpl->hash(_functionName).ref().getCroppedData(0, 4).data()); + uint32_t selector = ((func & 0x000000FF) << 24) | ((func & 0x0000FF00) << 8) | + ((func & 0x00FF0000) >> 8) | ((func & 0xFF000000) >> 24); + return selector; +} + +bcos::precompiled::ContractStatus bcos::precompiled::getContractStatus( + std::shared_ptr _executive, const std::string& _tableName) +{ + auto table = _executive->storage().openTable(_tableName); + if (!table) + { + return ContractStatus::AddressNonExistent; + } + + auto codeHashEntry = table->getRow(executor::ACCOUNT_CODE_HASH); + if (!codeHashEntry) + { + // this may happen when register link in contract constructor + return ContractStatus::Available; + } + auto codeHashStr = codeHashEntry->getField(0); + auto codeHash = HashType(codeHashStr, FixedBytes<32>::FromBinary); + + if (codeHash == HashType()) + { + return ContractStatus::NotContractAddress; + } + + // FIXME: frozen in BFS + auto frozenEntry = table->getRow(executor::ACCOUNT_FROZEN); + if (frozenEntry != std::nullopt && "true" == frozenEntry->getField(0)) + { + return ContractStatus::Frozen; + } + else + { + return ContractStatus::Available; + } +} + +bool precompiled::checkPathValid(std::string const& _path) +{ + if (_path.empty()) + return false; + if (_path.length() > FS_PATH_MAX_LENGTH) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("checkPathValid") + << LOG_DESC("path too long, over flow FS_PATH_MAX_LENGTH") + << LOG_KV("path", _path); + return false; + } + if (_path == "/") + return true; + std::string absoluteDir = _path; + if (absoluteDir[0] == '/') + { + absoluteDir = absoluteDir.substr(1); + } + if (absoluteDir.at(absoluteDir.size() - 1) == '/') + { + absoluteDir = absoluteDir.substr(0, absoluteDir.size() - 1); + } + std::vector pathList; + boost::split(pathList, absoluteDir, boost::is_any_of("/"), boost::token_compress_on); + if (pathList.size() > FS_PATH_MAX_LEVEL || pathList.empty()) + { + PRECOMPILED_LOG(INFO) + << LOG_BADGE("checkPathValid") + << LOG_DESC("resource path's level is too deep, level over FS_PATH_MAX_LEVEL") + << LOG_KV("path", _path); + return false; + } + // TODO: adapt Chinese + std::regex reg(R"(^[0-9a-zA-Z][^\>\<\*\?\/\=\+\(\)\$\"\']*$)"); + auto checkFieldNameValidate = [®](const std::string& fieldName) -> bool { + if (fieldName.empty()) + { + std::stringstream errorMessage; + errorMessage << "Invalid field \"" + fieldName + << "\", the size of the field must be larger than 0"; + PRECOMPILED_LOG(INFO) << LOG_DESC(errorMessage.str()) + << LOG_KV("field name", fieldName); + return false; + } + if (!std::regex_match(fieldName, reg)) + { + std::stringstream errorMessage; + errorMessage << "Invalid field \"" << fieldName << "\", the field name must be in reg: " + << R"(^[0-9a-zA-Z\u4e00-\u9fa5][^\>\<\*\?\/\=\+\(\)\$\"\']*$)"; + PRECOMPILED_LOG(INFO) << LOG_DESC(errorMessage.str()) + << LOG_KV("field name", fieldName); + return false; + } + return true; + }; + return std::all_of(pathList.begin(), pathList.end(), + [checkFieldNameValidate](const std::string& s) { return checkFieldNameValidate(s); }); +} + +std::pair precompiled::getParentDirAndBaseName( + const std::string& _absolutePath) +{ + // transfer /usr/local/bin => ["usr", "local", "bin"] + if (_absolutePath == "/") + return {"/", "/"}; + + std::string absoluteDir = _absolutePath; + + if (absoluteDir.ends_with('/')) + { + absoluteDir.pop_back(); + } + size_t index = absoluteDir.find_last_of('/'); + auto parent = absoluteDir.substr(0, index); + return {parent.empty() ? "/" : parent, absoluteDir.substr(index + 1)}; +} + +executor::CallParameters::UniquePtr precompiled::externalRequest( + const std::shared_ptr& _executive, const bytesConstRef& _param, + std::string_view _origin, std::string_view _sender, std::string_view _to, bool _isStatic, + bool _isCreate, int64_t gasLeft, bool _isInternalCall, std::string const& _abi) +{ + auto request = std::make_unique(executor::CallParameters::MESSAGE); + + request->senderAddress = _sender; + request->receiveAddress = _to; + request->origin = _origin; + request->codeAddress = request->receiveAddress; + request->status = 0; + request->data = _param.toBytes(); + request->create = _isCreate; + request->staticCall = !_isCreate && _isStatic; + request->internalCreate = _isCreate; + request->internalCall = _isInternalCall; + request->gas = gasLeft; + if (_isCreate && !_abi.empty()) + { + request->abi = _abi; + } + return _executive->externalCall(std::move(request)); +} + +s256 precompiled::externalTouchNewFile( + const std::shared_ptr& _executive, std::string_view _origin, + std::string_view _sender, std::string_view _filePath, std::string_view _fileType, + int64_t gasLeft) +{ + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + std::string bfsAddress = blockContext->isWasm() ? BFS_NAME : BFS_ADDRESS; + auto codecResult = + codec.encodeWithSig("touch(string,string)", std::string(_filePath), std::string(_fileType)); + auto response = externalRequest( + _executive, ref(codecResult), _origin, _sender, bfsAddress, false, false, gasLeft); + int32_t result = 0; + if (response->status == (int32_t)TransactionStatus::None) + { + codec.decode(ref(response->data), result); + } + else + { + result = (int)CODE_FILE_BUILD_DIR_FAILED; + } + return result; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/common/Utilities.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/common/Utilities.h" new file mode 100644 index 00000000..d62115db --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/common/Utilities.h" @@ -0,0 +1,143 @@ +/** + * Copyright (C) 2021 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 Utilities.h + * @author: kyonRay + * @date 2021-05-25 + */ + +#pragma once + +#include "Common.h" +#include "bcos-codec/abi/ContractABICodec.h" +#include "bcos-codec/wrapper/CodecWrapper.h" +#include "bcos-executor/src/executive/TransactionExecutive.h" +#include "bcos-framework/executor/PrecompiledTypeDef.h" +#include "bcos-framework/storage/Table.h" +#include "bcos-tool/BfsFileFactory.h" +#include +#include +#include +#include +#include + +namespace bcos::precompiled +{ +inline void getErrorCodeOut(bytes& out, int const& result, const CodecWrapper& _codec) +{ + if (result >= 0 && result < 128) + { + out = _codec.encode(u256(result)); + return; + } + out = _codec.encode(s256(result)); +} + +inline std::string getTableName(const std::string_view& _tableName) +{ + if (_tableName.starts_with(executor::USER_TABLE_PREFIX)) + { + return std::string(_tableName); + } + auto tableName = (_tableName[0] == '/') ? _tableName.substr(1) : _tableName; + return std::string(executor::USER_TABLE_PREFIX) + std::string(tableName); +} + +inline std::string getActualTableName(const std::string& _tableName) +{ + return "u_" + _tableName; +} + +inline std::string getAccountTableName(std::string_view _account) +{ + if (_account.starts_with(executor::USER_USR_PREFIX)) + { + return std::string(_account); + } + auto tableName = (_account[0] == '/') ? _account.substr(1) : _account; + return std::string(executor::USER_USR_PREFIX) + std::string(tableName); +} + +inline std::string getDynamicPrecompiledCodeString( + const std::string& _address, const std::string& _params) +{ + /// Prefix , address , params + return boost::join(std::vector({PRECOMPILED_CODE_FIELD, _address, _params}), ","); +} + +inline std::string trimHexPrefix(const std::string& _hex) +{ + if (_hex.size() >= 2 && _hex[1] == 'x' && _hex[0] == '0') + { + return _hex.substr(2); + } + return _hex; +} + +void checkNameValidate(std::string_view tableName, std::string_view _keyField, + std::vector& valueFieldList); +void checkLengthValidate(std::string_view field_value, int32_t max_length, int32_t errorCode); + +std::string checkCreateTableParam(const std::string_view& _tableName, std::string& _keyField, + const std::variant>& _valueField); + +uint32_t getFuncSelector(std::string const& _functionName, const crypto::Hash::Ptr& _hashImpl); +// for ut +void clearName2SelectCache(); +uint32_t getParamFunc(bytesConstRef _param); +uint32_t getFuncSelectorByFunctionName( + std::string const& _functionName, const crypto::Hash::Ptr& _hashImpl); + +bcos::precompiled::ContractStatus getContractStatus( + std::shared_ptr _executive, + std::string const& _tableName); + +inline bytesConstRef getParamData(bytesConstRef _param) +{ + return _param.getCroppedData(4); +} + + +bool checkPathValid(std::string const& _absolutePath); + +std::pair getParentDirAndBaseName(const std::string& _absolutePath); + +inline std::string_view getPathBaseName(std::string_view _absolutePath) +{ + if (_absolutePath == tool::FS_ROOT) + { + return _absolutePath; + } + if (_absolutePath.ends_with('/')) + { + return {}; + } + return _absolutePath.substr(_absolutePath.find_last_of('/') + 1); +} + +inline bool checkSenderFromAuth(std::string_view _sender) +{ + return _sender == precompiled::AUTH_COMMITTEE_ADDRESS; +} + +executor::CallParameters::UniquePtr externalRequest( + const std::shared_ptr& _executive, const bytesConstRef& _param, + std::string_view _origin, std::string_view _sender, std::string_view _to, bool _isStatic, + bool _isCreate, int64_t gasLeft, bool _isInternalCall = false, std::string const& _abi = ""); + +s256 externalTouchNewFile(const std::shared_ptr& _executive, + std::string_view _origin, std::string_view _sender, std::string_view _filePath, + std::string_view _fileType, int64_t gasLeft); +} // namespace bcos::precompiled \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/AccountManagerPrecompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/AccountManagerPrecompiled.cpp" new file mode 100644 index 00000000..7ed2c68b --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/AccountManagerPrecompiled.cpp" @@ -0,0 +1,248 @@ +/** + * 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 AccountManagerPrecompiled.cpp + * @author: kyonGuo + * @date 2022/9/26 + */ + +#include "AccountManagerPrecompiled.h" +#include "../../vm/HostContext.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include "bcos-executor/src/precompiled/common/PrecompiledResult.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include +#include +#include + +using namespace bcos; +using namespace bcos::precompiled; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::protocol; + +const char* const AM_METHOD_SET_ACCOUNT_STATUS = "setAccountStatus(address,uint8)"; +const char* const AM_METHOD_GET_ACCOUNT_STATUS = "getAccountStatus(address)"; + +AccountManagerPrecompiled::AccountManagerPrecompiled() : Precompiled(GlobalHashImpl::g_hashImpl) +{ + name2Selector[AM_METHOD_SET_ACCOUNT_STATUS] = + getFuncSelector(AM_METHOD_SET_ACCOUNT_STATUS, GlobalHashImpl::g_hashImpl); + name2Selector[AM_METHOD_GET_ACCOUNT_STATUS] = + getFuncSelector(AM_METHOD_GET_ACCOUNT_STATUS, GlobalHashImpl::g_hashImpl); +} + +std::shared_ptr AccountManagerPrecompiled::call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) +{ + // parse function name + uint32_t func = getParamFunc(_callParameters->input()); + + /// directly passthrough data to call + if (func == name2Selector[AM_METHOD_SET_ACCOUNT_STATUS]) + { + setAccountStatus(_executive, _callParameters); + } + else if (func == name2Selector[AM_METHOD_GET_ACCOUNT_STATUS]) + { + getAccountStatus(_executive, _callParameters); + } + else + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("AccountManagerPrecompiled") + << LOG_DESC("call undefined function") << LOG_KV("func", func); + BOOST_THROW_EXCEPTION( + bcos::protocol::PrecompiledError("AccountManagerPrecompiled call undefined function!")); + } + return _callParameters; +} + +void AccountManagerPrecompiled::createAccountWithStatus( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters, const CodecWrapper& codec, + std::string_view accountHex, uint8_t status) const +{ + auto accountTableName = getAccountTableName(accountHex); + + // prefix + address + tableName + std::string codeString = getDynamicPrecompiledCodeString(ACCOUNT_ADDRESS, accountTableName); + auto input = codec.encode(accountTableName, codeString); + + auto response = externalRequest(_executive, ref(input), _callParameters->m_origin, + _callParameters->m_codeAddress, accountHex, false, true, _callParameters->m_gasLeft, false, + std::string(ACCOUNT_ABI)); + + if (response->status != (int32_t)TransactionStatus::None) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("AccountManagerPrecompiled") + << LOG_DESC("createAccount failed") + << LOG_KV("accountTableName", accountTableName) + << LOG_KV("status", response->status); + BOOST_THROW_EXCEPTION(PrecompiledError("Create account error.")); + } + + auto newParams = codec.encodeWithSig("setAccountStatus(uint8)", status); + auto setStatusRes = externalRequest(_executive, ref(newParams), _callParameters->m_origin, + _callParameters->m_codeAddress, accountHex, _callParameters->m_staticCall, + _callParameters->m_create, _callParameters->m_gasLeft); + + if (setStatusRes->status != (int32_t)TransactionStatus::None) + { + PRECOMPILED_LOG(ERROR) << LOG_BADGE("AccountManagerPrecompiled") + << LOG_DESC("set status failed") + << LOG_KV("accountTableName", accountTableName) + << LOG_KV("status", response->status); + BOOST_THROW_EXCEPTION(PrecompiledError("Set account status failed.")); + } + _callParameters->setExternalResult(std::move(setStatusRes)); +} + +void AccountManagerPrecompiled::setAccountStatus( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) const +{ + // setAccountStatus(address,uint8) + Address account; + uint8_t status = 0; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_callParameters->params(), account, status); + std::string accountStr = account.hex(); + + PRECOMPILED_LOG(INFO) << BLOCK_NUMBER(blockContext->number()) + << LOG_BADGE("AccountManagerPrecompiled") << LOG_DESC("setAccountStatus") + << LOG_KV("account", accountStr) + << LOG_KV("status", std::to_string(status)); + + // check is governor + auto governors = getGovernorList(_executive, _callParameters, codec); + if (RANGES::find_if(governors, [&_callParameters](const Address& address) { + return address.hex() == _callParameters->m_sender; + }) == governors.end()) + { + // not from governor + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_NO_AUTHORIZED, codec); + return; + } + if (RANGES::find(governors, account) != governors.end()) + { + // set governor's status + PRECOMPILED_LOG(INFO) << BLOCK_NUMBER(blockContext->number()) + << LOG_BADGE("AccountManagerPrecompiled") + << LOG_DESC("set governor's status") << LOG_KV("account", accountStr) + << LOG_KV("status", std::to_string(status)); + BOOST_THROW_EXCEPTION(PrecompiledError("Should not set governor's status.")); + } + + // check account exist, if not exist, create first + auto accountTableName = getAccountTableName(account.hex()); + auto table = _executive->storage().openTable(accountTableName); + if (!table) + { + auto appsAccountTableName = getContractTableName(account.hex()); + auto appsTable = _executive->storage().openTable(appsAccountTableName); + if (appsTable) + { + PRECOMPILED_LOG(INFO) + << BLOCK_NUMBER(blockContext->number()) << LOG_BADGE("AccountManagerPrecompiled") + << LOG_DESC("account table already exist in /apps, maybe this is a contract.") + << LOG_KV("account", accountStr) << LOG_KV("status", std::to_string(status)); + _callParameters->setExecResult(codec.encode(int32_t(CODE_ACCOUNT_ALREADY_EXIST))); + return; + } + PRECOMPILED_LOG(INFO) << BLOCK_NUMBER(blockContext->number()) + << LOG_BADGE("AccountManagerPrecompiled") + << LOG_DESC("setAccountStatus table not exist, create first") + << LOG_KV("account", accountStr) + << LOG_KV("status", std::to_string(status)); + // create table + createAccountWithStatus(_executive, _callParameters, codec, accountStr, status); + return; + } + + // table must exist, then call + auto newParams = codec.encodeWithSig("setAccountStatus(uint8)", status); + auto response = externalRequest(_executive, ref(newParams), _callParameters->m_origin, + _callParameters->m_codeAddress, accountStr, _callParameters->m_staticCall, + _callParameters->m_create, _callParameters->m_gasLeft); + _callParameters->setExternalResult(std::move(response)); +} + +void AccountManagerPrecompiled::getAccountStatus( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) const +{ + // getAccountStatus(address) + Address account; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_callParameters->params(), account); + std::string accountStr = account.hex(); + + PRECOMPILED_LOG(TRACE) << BLOCK_NUMBER(blockContext->number()) << LOG_BADGE("getAccountStatus") + << LOG_KV("account", accountStr); + auto newParams = codec.encodeWithSig("getAccountStatus()"); + auto response = externalRequest(_executive, ref(newParams), _callParameters->m_origin, + _callParameters->m_codeAddress, accountStr, _callParameters->m_staticCall, + _callParameters->m_create, _callParameters->m_gasLeft); + if (response->status != (int32_t)TransactionStatus::None) + { + // maybe this address not exist in chain, return normal by default + _callParameters->setExecResult(codec.encode((uint8_t)0)); + return; + } + _callParameters->setExternalResult(std::move(response)); +} + +std::vector
AccountManagerPrecompiled::getGovernorList( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters, const CodecWrapper& codec) const +{ + auto blockContext = _executive->blockContext().lock(); + const auto* sender = blockContext->isWasm() ? ACCOUNT_MANAGER_NAME : ACCOUNT_MGR_ADDRESS; + auto getCommittee = codec.encodeWithSig("_committee()"); + auto getCommitteeResponse = externalRequest(_executive, ref(getCommittee), + _callParameters->m_origin, sender, AUTH_COMMITTEE_ADDRESS, _callParameters->m_staticCall, + false, _callParameters->m_gasLeft); + if (getCommitteeResponse->status != (int32_t)TransactionStatus::None) [[unlikely]] + { + PRECOMPILED_LOG(ERROR) << LOG_BADGE("AccountManagerPrecompiled") + << LOG_DESC("get committee failed") + << LOG_KV("status", getCommitteeResponse->status); + BOOST_THROW_EXCEPTION(PrecompiledError("Get committee failed.")); + } + + Address committee; + codec.decode(ref(getCommitteeResponse->data), committee); + + auto getInfo = codec.encodeWithSig("getCommitteeInfo()"); + auto getInfoResponse = externalRequest(_executive, ref(getInfo), _callParameters->m_origin, + sender, committee.hex(), _callParameters->m_staticCall, false, _callParameters->m_gasLeft); + + if (getInfoResponse->status != (int32_t)TransactionStatus::None) [[unlikely]] + { + PRECOMPILED_LOG(ERROR) << LOG_BADGE("AccountManagerPrecompiled") + << LOG_DESC("get committee info failed") + << LOG_KV("committee", committee.hex()); + BOOST_THROW_EXCEPTION(PrecompiledError("Get committee info failed.")); + } + uint8_t participatesRate = 0; + uint8_t winRate = 0; + std::vector
governors; + std::vector weights; + codec.decode(ref(getInfoResponse->data), participatesRate, winRate, governors, weights); + return governors; +} diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/AccountManagerPrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/AccountManagerPrecompiled.h" new file mode 100644 index 00000000..f36b69ba --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/AccountManagerPrecompiled.h" @@ -0,0 +1,55 @@ +/** + * 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 AccountManagerPrecompiled.h + * @author: kyonGuo + * @date 2022/9/26 + */ + +#pragma once +#include "../../vm/Precompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include + +namespace bcos::precompiled +{ +class AccountManagerPrecompiled : public bcos::precompiled::Precompiled +{ +public: + using Ptr = std::shared_ptr; + AccountManagerPrecompiled(); + ~AccountManagerPrecompiled() override = default; + + std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) override; + +private: + void setAccountStatus(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters) const; + + void getAccountStatus(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters) const; + + void createAccountWithStatus(const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters, const CodecWrapper& codec, + std::string_view accountHex, uint8_t status = 0) const; + + std::vector
getGovernorList( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters, const CodecWrapper& codec) const; +}; +} // namespace bcos::precompiled \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/AccountPrecompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/AccountPrecompiled.cpp" new file mode 100644 index 00000000..dd141bbb --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/AccountPrecompiled.cpp" @@ -0,0 +1,181 @@ +/** + * 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 AccountPrecompiled.cpp + * @author: kyonGuo + * @date 2022/9/26 + */ + +#include "AccountPrecompiled.h" +#include "../../vm/HostContext.h" + +using namespace bcos; +using namespace bcos::precompiled; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::protocol; + +const char* const AM_METHOD_SET_ACCOUNT_STATUS = "setAccountStatus(uint8)"; +const char* const AM_METHOD_GET_ACCOUNT_STATUS = "getAccountStatus()"; + +AccountPrecompiled::AccountPrecompiled() : Precompiled(GlobalHashImpl::g_hashImpl) +{ + name2Selector[AM_METHOD_SET_ACCOUNT_STATUS] = + getFuncSelector(AM_METHOD_SET_ACCOUNT_STATUS, GlobalHashImpl::g_hashImpl); + name2Selector[AM_METHOD_GET_ACCOUNT_STATUS] = + getFuncSelector(AM_METHOD_GET_ACCOUNT_STATUS, GlobalHashImpl::g_hashImpl); +} + +std::shared_ptr AccountPrecompiled::call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) +{ + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + // [tableName][actualParams] + std::vector dynamicParams; + bytes param; + codec.decode(_callParameters->input(), dynamicParams, param); + auto accountTableName = dynamicParams.at(0); + + // get user call actual params + auto originParam = ref(param); + uint32_t func = getParamFunc(originParam); + bytesConstRef data = getParamData(originParam); + auto table = _executive->storage().openTable(accountTableName); + if (!table.has_value()) [[unlikely]] + { + BOOST_THROW_EXCEPTION(PrecompiledError(accountTableName + " does not exist")); + } + + if (func == name2Selector[AM_METHOD_SET_ACCOUNT_STATUS]) + { + setAccountStatus(accountTableName, _executive, data, _callParameters); + } + else if (func == name2Selector[AM_METHOD_GET_ACCOUNT_STATUS]) + { + getAccountStatus(accountTableName, _executive, _callParameters); + } + else + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("AccountPrecompiled") + << LOG_DESC("call undefined function") << LOG_KV("func", func); + BOOST_THROW_EXCEPTION( + bcos::protocol::PrecompiledError("AccountPrecompiled call undefined function!")); + } + return _callParameters; +} + + +void AccountPrecompiled::setAccountStatus(const std::string& accountTableName, + const std::shared_ptr& _executive, bytesConstRef& data, + const PrecompiledExecResult::Ptr& _callParameters) const +{ + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + const auto* accountMgrSender = + blockContext->isWasm() ? ACCOUNT_MANAGER_NAME : ACCOUNT_MGR_ADDRESS; + if (_callParameters->m_sender != accountMgrSender) + { + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_NO_AUTHORIZED, codec); + return; + } + + uint8_t status = 0; + codec.decode(data, status); + + PRECOMPILED_LOG(INFO) << BLOCK_NUMBER(blockContext->number()) << LOG_BADGE("AccountPrecompiled") + << LOG_DESC("setAccountStatus") << LOG_KV("account", accountTableName) + << LOG_KV("status", std::to_string(status)); + auto existEntry = _executive->storage().getRow(accountTableName, ACCOUNT_STATUS); + // already exist status, check and move it to last status + if (existEntry.has_value()) + { + auto statusStr = std::string(existEntry->get()); + auto existStatus = boost::lexical_cast(statusStr); + // account already abolish, should not set any status to it + if (existStatus == AccountStatus::abolish && status != AccountStatus::abolish) + { + PRECOMPILED_LOG(INFO) << BLOCK_NUMBER(blockContext->number()) + << LOG_BADGE("AccountPrecompiled") + << LOG_DESC("account already abolish, should not set any status") + << LOG_KV("account", accountTableName) + << LOG_KV("status", std::to_string(status)); + BOOST_THROW_EXCEPTION( + PrecompiledError("Account already abolish, should not set any status.")); + } + _executive->storage().setRow( + accountTableName, ACCOUNT_LAST_STATUS, std::move(existEntry.value())); + } + else + { + // first time + Entry lastStatusEntry; + lastStatusEntry.importFields({"0"}); + _executive->storage().setRow( + accountTableName, ACCOUNT_LAST_STATUS, std::move(lastStatusEntry)); + } + // set status and lastUpdateNumber + Entry statusEntry; + statusEntry.importFields({boost::lexical_cast(status)}); + _executive->storage().setRow(accountTableName, ACCOUNT_STATUS, std::move(statusEntry)); + Entry lastUpdateEntry; + lastUpdateEntry.importFields({boost::lexical_cast(blockContext->number())}); + _executive->storage().setRow(accountTableName, ACCOUNT_LAST_UPDATE, std::move(lastUpdateEntry)); + _callParameters->setExecResult(codec.encode(int32_t(CODE_SUCCESS))); +} + +void AccountPrecompiled::getAccountStatus(const std::string& tableName, + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) const +{ + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + uint8_t status = getAccountStatus(tableName, _executive); + _callParameters->setExecResult(codec.encode(status)); +} + +uint8_t AccountPrecompiled::getAccountStatus(const std::string& account, + const std::shared_ptr& _executive) const +{ + auto accountTable = getAccountTableName(account); + auto entry = _executive->storage().getRow(accountTable, ACCOUNT_STATUS); + auto lastUpdateEntry = _executive->storage().getRow(accountTable, ACCOUNT_LAST_UPDATE); + if (!lastUpdateEntry.has_value()) + { + PRECOMPILED_LOG(TRACE) << LOG_BADGE("AccountPrecompiled") << LOG_DESC("getAccountStatus") + << LOG_DESC(" Status row not exist, return 0 by default"); + return 0; + } + auto lastUpdateNumber = boost::lexical_cast(std::string(lastUpdateEntry->get())); + auto blockContext = _executive->blockContext().lock(); + std::string_view statusStr; + if (blockContext->number() > lastUpdateNumber) [[likely]] + { + statusStr = entry->get(); + } + else [[unlikely]] + { + auto lastStatusEntry = _executive->storage().getRow(accountTable, ACCOUNT_LAST_STATUS); + statusStr = lastStatusEntry->get(); + } + + PRECOMPILED_LOG(TRACE) << LOG_BADGE("AccountPrecompiled") + << BLOCK_NUMBER(blockContext->number()) << LOG_DESC("getAccountStatus") + << LOG_KV("lastUpdateNumber", lastUpdateNumber) + << LOG_KV("status", statusStr); + auto status = boost::lexical_cast(statusStr); + return status; +} diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/AccountPrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/AccountPrecompiled.h" new file mode 100644 index 00000000..10a5d838 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/AccountPrecompiled.h" @@ -0,0 +1,52 @@ +/** + * 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 AccountPrecompiled.h + * @author: kyonGuo + * @date 2022/9/26 + */ + +#pragma once +#include "../../vm/Precompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include + +namespace bcos::precompiled +{ +class AccountPrecompiled : public bcos::precompiled::Precompiled +{ +public: + using Ptr = std::shared_ptr; + AccountPrecompiled(); + ~AccountPrecompiled() override = default; + + std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) override; + + uint8_t getAccountStatus(const std::string& accountTable, + const std::shared_ptr& _executive) const; + +private: + void setAccountStatus(const std::string& tableName, + const std::shared_ptr& _executive, bytesConstRef& data, + PrecompiledExecResult::Ptr const& _callParameters) const; + + void getAccountStatus(const std::string& tableName, + const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters) const; +}; +} // namespace bcos::precompiled diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/AuthManagerPrecompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/AuthManagerPrecompiled.cpp" new file mode 100644 index 00000000..6abdeee1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/AuthManagerPrecompiled.cpp" @@ -0,0 +1,782 @@ +/** + * Copyright (C) 2021 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 AuthManagerPrecompiled.cpp + * @author: kyonRay + * @date 2021-10-09 + */ + +#include "AuthManagerPrecompiled.h" +#include "../../vm/HostContext.h" +#include "ContractAuthMgrPrecompiled.h" +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::precompiled; +using namespace bcos::executor; +using namespace bcos::storage; + +/// wasm +const char* const AUTH_METHOD_GET_ADMIN = "getAdmin(string)"; +const char* const AUTH_METHOD_SET_ADMIN = "resetAdmin(string,string)"; +const char* const AUTH_METHOD_SET_AUTH_TYPE = "setMethodAuthType(string,bytes4,uint8)"; +const char* const AUTH_METHOD_OPEN_AUTH = "openMethodAuth(string,bytes4,string)"; +const char* const AUTH_METHOD_CLOSE_AUTH = "closeMethodAuth(string,bytes4,string)"; +const char* const AUTH_METHOD_CHECK_AUTH = "checkMethodAuth(string,bytes4,string)"; +const char* const AUTH_METHOD_GET_AUTH = "getMethodAuth(string,bytes4)"; +/// evm +const char* const AUTH_METHOD_GET_ADMIN_ADD = "getAdmin(address)"; +const char* const AUTH_METHOD_SET_ADMIN_ADD = "resetAdmin(address,address)"; +const char* const AUTH_METHOD_SET_AUTH_TYPE_ADD = "setMethodAuthType(address,bytes4,uint8)"; +const char* const AUTH_METHOD_OPEN_AUTH_ADD = "openMethodAuth(address,bytes4,address)"; +const char* const AUTH_METHOD_CLOSE_AUTH_ADD = "closeMethodAuth(address,bytes4,address)"; +const char* const AUTH_METHOD_CHECK_AUTH_ADD = "checkMethodAuth(address,bytes4,address)"; +const char* const AUTH_METHOD_GET_AUTH_ADD = "getMethodAuth(address,bytes4)"; + +const char* const AUTH_METHOD_SET_CONTRACT = "setContractStatus(address,bool)"; +const char* const AUTH_METHOD_GET_CONTRACT = "contractAvailable(address)"; + +/// deploy +const char* const AUTH_METHOD_GET_DEPLOY_TYPE = "deployType()"; +const char* const AUTH_METHOD_SET_DEPLOY_TYPE = "setDeployAuthType(uint8)"; +/// wasm +const char* const AUTH_OPEN_DEPLOY_ACCOUNT = "openDeployAuth(string)"; +const char* const AUTH_CLOSE_DEPLOY_ACCOUNT = "closeDeployAuth(string)"; +const char* const AUTH_CHECK_DEPLOY_ACCESS = "hasDeployAuth(string)"; +/// evm +const char* const AUTH_OPEN_DEPLOY_ACCOUNT_ADD = "openDeployAuth(address)"; +const char* const AUTH_CLOSE_DEPLOY_ACCOUNT_ADD = "closeDeployAuth(address)"; +const char* const AUTH_CHECK_DEPLOY_ACCESS_ADD = "hasDeployAuth(address)"; + +AuthManagerPrecompiled::AuthManagerPrecompiled(crypto::Hash::Ptr _hashImpl) : Precompiled(_hashImpl) +{ + /// wasm + name2Selector[AUTH_METHOD_GET_ADMIN] = getFuncSelector(AUTH_METHOD_GET_ADMIN, _hashImpl); + name2Selector[AUTH_METHOD_SET_ADMIN] = getFuncSelector(AUTH_METHOD_SET_ADMIN, _hashImpl); + name2Selector[AUTH_METHOD_SET_AUTH_TYPE] = + getFuncSelector(AUTH_METHOD_SET_AUTH_TYPE, _hashImpl); + name2Selector[AUTH_METHOD_OPEN_AUTH] = getFuncSelector(AUTH_METHOD_OPEN_AUTH, _hashImpl); + name2Selector[AUTH_METHOD_CLOSE_AUTH] = getFuncSelector(AUTH_METHOD_CLOSE_AUTH, _hashImpl); + name2Selector[AUTH_METHOD_CHECK_AUTH] = getFuncSelector(AUTH_METHOD_CHECK_AUTH, _hashImpl); + name2Selector[AUTH_METHOD_GET_AUTH] = getFuncSelector(AUTH_METHOD_GET_AUTH, _hashImpl); + + /// evm + name2Selector[AUTH_METHOD_GET_ADMIN_ADD] = + getFuncSelector(AUTH_METHOD_GET_ADMIN_ADD, _hashImpl); + name2Selector[AUTH_METHOD_SET_ADMIN_ADD] = + getFuncSelector(AUTH_METHOD_SET_ADMIN_ADD, _hashImpl); + name2Selector[AUTH_METHOD_SET_AUTH_TYPE_ADD] = + getFuncSelector(AUTH_METHOD_SET_AUTH_TYPE_ADD, _hashImpl); + name2Selector[AUTH_METHOD_OPEN_AUTH_ADD] = + getFuncSelector(AUTH_METHOD_OPEN_AUTH_ADD, _hashImpl); + name2Selector[AUTH_METHOD_CLOSE_AUTH_ADD] = + getFuncSelector(AUTH_METHOD_CLOSE_AUTH_ADD, _hashImpl); + name2Selector[AUTH_METHOD_CHECK_AUTH_ADD] = + getFuncSelector(AUTH_METHOD_CHECK_AUTH_ADD, _hashImpl); + name2Selector[AUTH_METHOD_SET_CONTRACT] = getFuncSelector(AUTH_METHOD_SET_CONTRACT, _hashImpl); + name2Selector[AUTH_METHOD_GET_CONTRACT] = getFuncSelector(AUTH_METHOD_GET_CONTRACT, _hashImpl); + name2Selector[AUTH_METHOD_GET_AUTH_ADD] = getFuncSelector(AUTH_METHOD_GET_AUTH_ADD, _hashImpl); + + /// deploy + name2Selector[AUTH_METHOD_GET_DEPLOY_TYPE] = + getFuncSelector(AUTH_METHOD_GET_DEPLOY_TYPE, _hashImpl); + name2Selector[AUTH_METHOD_SET_DEPLOY_TYPE] = + getFuncSelector(AUTH_METHOD_SET_DEPLOY_TYPE, _hashImpl); + + /// wasm deploy auth + name2Selector[AUTH_OPEN_DEPLOY_ACCOUNT] = getFuncSelector(AUTH_OPEN_DEPLOY_ACCOUNT, _hashImpl); + name2Selector[AUTH_CLOSE_DEPLOY_ACCOUNT] = + getFuncSelector(AUTH_CLOSE_DEPLOY_ACCOUNT, _hashImpl); + name2Selector[AUTH_CHECK_DEPLOY_ACCESS] = getFuncSelector(AUTH_CHECK_DEPLOY_ACCESS, _hashImpl); + /// evm deploy auth + name2Selector[AUTH_OPEN_DEPLOY_ACCOUNT_ADD] = + getFuncSelector(AUTH_OPEN_DEPLOY_ACCOUNT_ADD, _hashImpl); + name2Selector[AUTH_CLOSE_DEPLOY_ACCOUNT_ADD] = + getFuncSelector(AUTH_CLOSE_DEPLOY_ACCOUNT_ADD, _hashImpl); + name2Selector[AUTH_CHECK_DEPLOY_ACCESS_ADD] = + getFuncSelector(AUTH_CHECK_DEPLOY_ACCESS_ADD, _hashImpl); +} + +std::shared_ptr AuthManagerPrecompiled::call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) +{ + // parse function name + uint32_t func = getParamFunc(_callParameters->input()); + + /// directly passthrough data to call + if (func == name2Selector[AUTH_METHOD_GET_ADMIN] || + func == name2Selector[AUTH_METHOD_GET_ADMIN_ADD]) + { + getAdmin(_executive, _callParameters); + } + else if (func == name2Selector[AUTH_METHOD_SET_ADMIN] || + func == name2Selector[AUTH_METHOD_SET_ADMIN_ADD]) + { + resetAdmin(_executive, _callParameters); + } + else if (func == name2Selector[AUTH_METHOD_SET_AUTH_TYPE] || + func == name2Selector[AUTH_METHOD_SET_AUTH_TYPE_ADD]) + { + setMethodAuthType(_executive, _callParameters); + } + else if (func == name2Selector[AUTH_METHOD_OPEN_AUTH] || + func == name2Selector[AUTH_METHOD_OPEN_AUTH_ADD] || + func == name2Selector[AUTH_METHOD_CLOSE_AUTH] || + func == name2Selector[AUTH_METHOD_CLOSE_AUTH_ADD]) + { + setMethodAuth(_executive, _callParameters); + } + else if (func == name2Selector[AUTH_METHOD_CHECK_AUTH] || + func == name2Selector[AUTH_METHOD_CHECK_AUTH_ADD]) + { + checkMethodAuth(_executive, _callParameters); + } + else if (func == name2Selector[AUTH_METHOD_GET_AUTH] || + func == name2Selector[AUTH_METHOD_GET_AUTH_ADD]) + { + getMethodAuth(_executive, _callParameters); + } + else if (func == name2Selector[AUTH_METHOD_GET_DEPLOY_TYPE]) + { + getDeployType(_executive, _callParameters); + } + else if (func == name2Selector[AUTH_METHOD_SET_DEPLOY_TYPE]) + { + setDeployType(_executive, _callParameters); + } + else if (func == name2Selector[AUTH_OPEN_DEPLOY_ACCOUNT] || + func == name2Selector[AUTH_OPEN_DEPLOY_ACCOUNT_ADD]) + { + setDeployAuth(_executive, false, _callParameters); + } + else if (func == name2Selector[AUTH_CLOSE_DEPLOY_ACCOUNT] || + func == name2Selector[AUTH_CLOSE_DEPLOY_ACCOUNT_ADD]) + { + setDeployAuth(_executive, true, _callParameters); + } + else if (func == name2Selector[AUTH_CHECK_DEPLOY_ACCESS] || + func == name2Selector[AUTH_CHECK_DEPLOY_ACCESS_ADD]) + { + hasDeployAuth(_executive, _callParameters); + } + else if (func == name2Selector[AUTH_METHOD_SET_CONTRACT]) + { + setContractStatus(_executive, _callParameters); + } + else if (func == name2Selector[AUTH_METHOD_GET_CONTRACT]) + { + contractAvailable(_executive, _callParameters); + } + else + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("AuthManagerPrecompiled") + << LOG_DESC("call undefined function") << LOG_KV("func", func); + BOOST_THROW_EXCEPTION( + bcos::protocol::PrecompiledError("AuthManagerPrecompiled call undefined function!")); + } + return _callParameters; +} + +void AuthManagerPrecompiled::getAdmin( + const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters) + +{ + bytesConstRef data = getParamData(_callParameters->input()); + std::string path; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + if (blockContext->isWasm()) + { + codec.decode(data, path); + } + else + { + Address contractAddress; + codec.decode(data, contractAddress); + path = contractAddress.hex(); + } + + std::string adminStr = getContractAdmin(_executive, path, _callParameters); + PRECOMPILED_LOG(TRACE) << LOG_BADGE("AuthManagerPrecompiled") << LOG_DESC("getAdmin success") + << LOG_KV("contractPath", path) << LOG_KV("admin", adminStr); + _callParameters->setExecResult( + blockContext->isWasm() ? codec.encode(adminStr) : codec.encode(Address(adminStr))); +} + +void AuthManagerPrecompiled::resetAdmin( + const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters) + +{ + std::string path; + std::string admin; + bytesConstRef data = getParamData(_callParameters->input()); + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + if (!blockContext->isWasm()) + { + Address contractAddress; + Address adminAddress; + codec.decode(data, contractAddress, adminAddress); + path = contractAddress.hex(); + admin = adminAddress.hex(); + } + else + { + codec.decode(data, path, admin); + } + PRECOMPILED_LOG(DEBUG) << BLOCK_NUMBER(blockContext->number()) + << LOG_BADGE("AuthManagerPrecompiled") << LOG_DESC("resetAdmin") + << LOG_KV("path", path) << LOG_KV("admin", admin); + if (!checkSenderFromAuth(_callParameters->m_sender)) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("AuthManagerPrecompiled") + << LOG_DESC("sender is not from sys") << LOG_KV("path", path) + << LOG_KV("sender", _callParameters->m_sender); + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_NO_AUTHORIZED, codec); + return; + } + auto newParams = + codec.encode(std::string(AUTH_CONTRACT_MGR_ADDRESS), _callParameters->input().toBytes()); + std::string authMgrAddress = blockContext->isWasm() ? AUTH_MANAGER_NAME : AUTH_MANAGER_ADDRESS; + + auto response = externalRequest(_executive, ref(newParams), _callParameters->m_origin, + authMgrAddress, path, _callParameters->m_staticCall, _callParameters->m_create, + _callParameters->m_gasLeft, true); + _callParameters->setExternalResult(std::move(response)); +} + +void AuthManagerPrecompiled::setMethodAuthType( + const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters) +{ + std::string path; + string32 _func; + string32 _type; + bytesConstRef data = getParamData(_callParameters->input()); + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + auto beginT = utcTime(); + if (!blockContext->isWasm()) + { + Address contractAddress; + codec.decode(data, contractAddress, _func, _type); + path = contractAddress.hex(); + } + else + { + codec.decode(data, path, _func, _type); + } + auto admin = getContractAdmin(_executive, path, _callParameters); + if (_callParameters->m_sender != admin) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("AuthManagerPrecompiled") + << LOG_DESC("Permission denied, only admin can set contract access.") + << LOG_KV("address", path) + << LOG_KV("sender", _callParameters->m_sender); + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_NO_AUTHORIZED, codec); + return; + } + auto newParams = + codec.encode(std::string(AUTH_CONTRACT_MGR_ADDRESS), _callParameters->input().toBytes()); + std::string authMgrAddress = blockContext->isWasm() ? AUTH_MANAGER_NAME : AUTH_MANAGER_ADDRESS; + auto response = externalRequest(_executive, ref(newParams), _callParameters->m_origin, + authMgrAddress, path, _callParameters->m_staticCall, _callParameters->m_create, + _callParameters->m_gasLeft, true); + auto finishedET = utcTime() - beginT; + PRECOMPILED_LOG(TRACE) << LOG_BADGE("AuthManagerPrecompiled") << "setMethodAuthType finished" + << LOG_KV("setPath", path) << LOG_KV("finishedET", finishedET); + _callParameters->setExternalResult(std::move(response)); +} + +void AuthManagerPrecompiled::checkMethodAuth( + const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters) +{ + std::string path; + string32 _func; + std::string account; + bytesConstRef data = getParamData(_callParameters->input()); + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + if (!blockContext->isWasm()) + { + Address contractAddress; + Address accountAddress; + codec.decode(data, contractAddress, _func, accountAddress); + path = contractAddress.hex(); + account = accountAddress.hex(); + } + else + { + codec.decode(data, path, _func, account); + } + auto newParams = + codec.encode(std::string(AUTH_CONTRACT_MGR_ADDRESS), _callParameters->input().toBytes()); + std::string authMgrAddress = blockContext->isWasm() ? AUTH_MANAGER_NAME : AUTH_MANAGER_ADDRESS; + + auto response = externalRequest(_executive, ref(newParams), _callParameters->m_origin, + authMgrAddress, path, _callParameters->m_staticCall, _callParameters->m_create, + _callParameters->m_gasLeft, true); + + _callParameters->setExternalResult(std::move(response)); +} + +void AuthManagerPrecompiled::getMethodAuth( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) + +{ + std::string path; + string32 _func; + bytesConstRef data = getParamData(_callParameters->input()); + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + if (!blockContext->isWasm()) + { + Address contractAddress; + codec.decode(data, contractAddress, _func); + path = contractAddress.hex(); + } + else + { + codec.decode(data, path, _func); + } + auto newParams = + codec.encode(std::string(AUTH_CONTRACT_MGR_ADDRESS), _callParameters->input().toBytes()); + std::string authMgrAddress = blockContext->isWasm() ? AUTH_MANAGER_NAME : AUTH_MANAGER_ADDRESS; + + auto response = externalRequest(_executive, ref(newParams), _callParameters->m_origin, + authMgrAddress, path, _callParameters->m_staticCall, _callParameters->m_create, + _callParameters->m_gasLeft, true); + + _callParameters->setExternalResult(std::move(response)); +} + +void AuthManagerPrecompiled::setMethodAuth( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) + +{ + std::string path; + std::string account; + string32 _func; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + bytesConstRef data = getParamData(_callParameters->input()); + auto recordT = utcTime(); + if (!blockContext->isWasm()) + { + Address contractAddress; + Address accountAddress; + codec.decode(data, contractAddress, _func, accountAddress); + path = contractAddress.hex(); + account = accountAddress.hex(); + } + else + { + codec.decode(data, path, _func, account); + } + auto admin = getContractAdmin(_executive, path, _callParameters); + if (_callParameters->m_sender != admin) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("AuthManagerPrecompiled") + << LOG_DESC("Permission denied, only admin can set contract access.") + << LOG_KV("address", path) + << LOG_KV("sender", _callParameters->m_sender); + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_NO_AUTHORIZED, codec); + return; + } + auto newParams = + codec.encode(std::string(AUTH_CONTRACT_MGR_ADDRESS), _callParameters->input().toBytes()); + std::string authMgrAddress = blockContext->isWasm() ? AUTH_MANAGER_NAME : AUTH_MANAGER_ADDRESS; + auto response = externalRequest(_executive, ref(newParams), _callParameters->m_origin, + authMgrAddress, path, _callParameters->m_staticCall, _callParameters->m_create, + _callParameters->m_gasLeft, true); + PRECOMPILED_LOG(TRACE) << LOG_BADGE("AuthManagerPrecompiled") << "setMethodAuth finished" + << LOG_KV("setPath", path) << LOG_KV("execT", (utcTime() - recordT)); + _callParameters->setExternalResult(std::move(response)); +} + +void AuthManagerPrecompiled::setContractStatus( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) + +{ + std::string address; + bool isFreeze = false; + bytesConstRef data = getParamData(_callParameters->input()); + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + if (blockContext->isWasm()) + { + codec.decode(data, address, isFreeze); + } + else + { + Address contractAddress; + codec.decode(data, contractAddress, isFreeze); + address = contractAddress.hex(); + } + PRECOMPILED_LOG(DEBUG) << BLOCK_NUMBER(blockContext->number()) + << LOG_BADGE("AuthManagerPrecompiled") << LOG_DESC("setContractStatus") + << LOG_KV("address", address) << LOG_KV("isFreeze", isFreeze); + + /// check sender is contract admin + auto admin = getContractAdmin(_executive, address, _callParameters); + if (_callParameters->m_sender != admin) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("AuthManagerPrecompiled") + << LOG_DESC("Permission denied, only admin can set contract access.") + << LOG_KV("address", address) + << LOG_KV("sender", _callParameters->m_sender); + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_NO_AUTHORIZED, codec); + return; + } + auto newParams = + codec.encode(std::string(AUTH_CONTRACT_MGR_ADDRESS), _callParameters->input().toBytes()); + std::string authMgrAddress = blockContext->isWasm() ? AUTH_MANAGER_NAME : AUTH_MANAGER_ADDRESS; + + auto response = externalRequest(_executive, ref(newParams), _callParameters->m_origin, + authMgrAddress, address, _callParameters->m_staticCall, _callParameters->m_create, + _callParameters->m_gasLeft, true); + + _callParameters->setExternalResult(std::move(response)); +} + +void AuthManagerPrecompiled::contractAvailable( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) + +{ + std::string address; + bytesConstRef data = getParamData(_callParameters->input()); + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + if (blockContext->isWasm()) + { + codec.decode(data, address); + } + else + { + Address contractAddress; + codec.decode(data, contractAddress); + address = contractAddress.hex(); + } + PRECOMPILED_LOG(TRACE) << LOG_BADGE("AuthManagerPrecompiled") << LOG_DESC("contractAvailable") + << LOG_KV("address", address); + + auto newParams = + codec.encode(std::string(AUTH_CONTRACT_MGR_ADDRESS), _callParameters->input().toBytes()); + std::string authMgrAddress = blockContext->isWasm() ? AUTH_MANAGER_NAME : AUTH_MANAGER_ADDRESS; + + auto response = externalRequest(_executive, ref(newParams), _callParameters->m_origin, + authMgrAddress, address, _callParameters->m_staticCall, _callParameters->m_create, + _callParameters->m_gasLeft, true); + + _callParameters->setExternalResult(std::move(response)); +} + +std::string AuthManagerPrecompiled::getContractAdmin( + const std::shared_ptr& _executive, const std::string& _to, + PrecompiledExecResult::Ptr const& _callParameters) +{ + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + + std::string authMgrAddress = blockContext->isWasm() ? AUTH_MANAGER_NAME : AUTH_MANAGER_ADDRESS; + + bytes selector = blockContext->isWasm() ? + codec.encodeWithSig(AUTH_METHOD_GET_ADMIN, _to) : + codec.encodeWithSig(AUTH_METHOD_GET_ADMIN_ADD, Address(_to)); + auto data = codec.encode(std::string(AUTH_CONTRACT_MGR_ADDRESS), selector); + auto response = + externalRequest(_executive, ref(data), _callParameters->m_origin, authMgrAddress, _to, + _callParameters->m_staticCall, false, _callParameters->m_gasLeft, true); + + if (response->status != (int32_t)protocol::TransactionStatus::None) + { + PRECOMPILED_LOG(DEBUG) << "Can't get contract admin, check the contract existence." + << LOG_KV("address", _to); + BOOST_THROW_EXCEPTION( + protocol::PrecompiledError("Please check the existence of contract.")); + } + std::string admin = ""; + + codec.decode(ref(response->data), admin); + + return admin; +} + +u256 AuthManagerPrecompiled::getDeployAuthType( + const std::shared_ptr& _executive) +{ + std::string typeStr = ""; + if (_executive->blockContext().lock()->blockVersion() >= + static_cast(protocol::BlockVersion::V3_1_VERSION)) + { + auto entry = _executive->storage().getRow(tool::FS_ROOT, tool::FS_APPS.substr(1)); + // apps must exist + if (!entry) [[unlikely]] + { + PRECOMPILED_LOG(FATAL) + << LOG_BADGE("AuthManagerPrecompiled") << LOG_DESC("apps not exist"); + } + auto fields = entry->getObject>(); + typeStr.assign(fields[2]); + } + else + { + auto entry = _executive->storage().getRow(tool::FS_APPS, FS_ACL_TYPE); + // entry must exist + typeStr.assign(entry->get()); + } + u256 type = 0; + try + { + type = boost::lexical_cast(typeStr); + } + catch (...) + { + return 0; + } + PRECOMPILED_LOG(TRACE) << LOG_BADGE("getDeployAuthType") << LOG_KV("type", type); + return type; +} + +void AuthManagerPrecompiled::getDeployType( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) + + +{ + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + + u256 type = getDeployAuthType(_executive); + _callParameters->setExecResult(codec.encode(type)); +} + +void AuthManagerPrecompiled::setDeployType( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) + +{ + string32 _type; + bytesConstRef data = getParamData(_callParameters->input()); + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(data, _type); + if (!checkSenderFromAuth(_callParameters->m_sender)) + { + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_NO_AUTHORIZED, codec); + return; + } + u256 type = _type[_type.size() - 1]; + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("AuthManagerPrecompiled") << LOG_DESC("setDeployType") + << LOG_KV("type", type); + if (type > 2) [[unlikely]] + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("AuthManagerPrecompiled") + << LOG_DESC("deploy auth type must be 1 or 2."); + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_TABLE_ERROR_AUTH_TYPE, codec); + return; + } + if (blockContext->blockVersion() >= static_cast(protocol::BlockVersion::V3_1_VERSION)) + { + auto entry = _executive->storage().getRow(tool::FS_ROOT, tool::FS_APPS.substr(1)); + // apps must exist + if (!entry) [[unlikely]] + { + PRECOMPILED_LOG(FATAL) + << LOG_BADGE("AuthManagerPrecompiled") << LOG_DESC("apps not exist"); + } + auto fields = entry->getObject>(); + fields[2] = boost::lexical_cast(type); + entry->setObject(fields); + _executive->storage().setRow( + tool::FS_ROOT, tool::FS_APPS.substr(1), std::move(entry.value())); + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_SUCCESS, codec); + return; + } + Entry entry; + entry.importFields({boost::lexical_cast(type)}); + _executive->storage().setRow(tool::FS_APPS, FS_ACL_TYPE, std::move(entry)); + + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_SUCCESS, codec); +} + +void AuthManagerPrecompiled::setDeployAuth( + const std::shared_ptr& _executive, bool _isClose, + const PrecompiledExecResult::Ptr& _callParameters) + +{ + std::string account; + bytesConstRef data = getParamData(_callParameters->input()); + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + if (blockContext->isWasm()) + { + codec.decode(data, account); + } + else + { + Address accountAddress; + codec.decode(data, accountAddress); + account = accountAddress.hex(); + } + if (!checkSenderFromAuth(_callParameters->m_sender)) + { + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_NO_AUTHORIZED, codec); + return; + } + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("AuthManagerPrecompiled") << LOG_DESC("setDeployAuth") + << LOG_KV("account", account) << LOG_KV("isClose", _isClose); + auto type = getDeployAuthType(_executive); + std::map aclMap; + bool access = _isClose ? (type == (int)AuthType::BLACK_LIST_MODE) : + (type == (int)AuthType::WHITE_LIST_MODE); + + if (blockContext->blockVersion() >= static_cast(protocol::BlockVersion::V3_1_VERSION)) + { + auto entry = _executive->storage().getRow(tool::FS_ROOT, tool::FS_APPS.substr(1)); + // apps must exist + if (!entry) [[unlikely]] + { + PRECOMPILED_LOG(FATAL) + << LOG_BADGE("AuthManagerPrecompiled") << LOG_DESC("apps not exist"); + } + auto fields = entry->getObject>(); + + const auto insertIndex = (type == (int)AuthType::WHITE_LIST_MODE) ? 3 : 4; + + auto mapStr = std::string(fields.at(insertIndex)); + if (!mapStr.empty()) + { + auto&& out = asBytes(mapStr); + codec::scale::decode(aclMap, gsl::make_span(out)); + } + // covered writing + aclMap[account] = access; + fields[insertIndex] = asString(codec::scale::encode(aclMap)); + entry->setObject(fields); + + _executive->storage().setRow( + tool::FS_ROOT, tool::FS_APPS.substr(1), std::move(entry.value())); + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_SUCCESS, codec); + return; + } + + auto getAclStr = (type == (int)AuthType::BLACK_LIST_MODE) ? FS_ACL_BLACK : FS_ACL_WHITE; + auto entry = _executive->storage().getRow(tool::FS_APPS, getAclStr); + auto mapStr = std::string(entry->getField(0)); + if (!mapStr.empty()) + { + auto&& out = asBytes(mapStr); + codec::scale::decode(aclMap, gsl::make_span(out)); + } + // covered writing + aclMap[account] = access; + entry->setField(0, asString(codec::scale::encode(aclMap))); + _executive->storage().setRow(tool::FS_APPS, getAclStr, std::move(entry.value())); + + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_SUCCESS, codec); +} + +void AuthManagerPrecompiled::hasDeployAuth( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) + +{ + std::string account; + bytesConstRef data = getParamData(_callParameters->input()); + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + if (blockContext->isWasm()) + { + codec.decode(data, account); + } + else + { + Address accountAddress; + codec.decode(data, accountAddress); + account = accountAddress.hex(); + } + _callParameters->setExecResult(codec.encode(checkDeployAuth(_executive, account))); +} + +bool AuthManagerPrecompiled::checkDeployAuth( + const std::shared_ptr& _executive, const std::string& _account) +{ + auto type = getDeployAuthType(_executive); + if (type == 0) + { + return true; + } + std::map aclMap; + std::string aclMapStr = ""; + + if (_executive->blockContext().lock()->blockVersion() >= + static_cast(protocol::BlockVersion::V3_1_VERSION)) + { + auto entry = _executive->storage().getRow(tool::FS_ROOT, tool::FS_APPS.substr(1)); + // apps must exist + if (!entry) [[unlikely]] + { + PRECOMPILED_LOG(FATAL) + << LOG_BADGE("AuthManagerPrecompiled") << LOG_DESC("apps not exist"); + } + auto fields = entry->getObject>(); + auto getAclIndex = (type == (int)AuthType::WHITE_LIST_MODE) ? 3 : 4; + aclMapStr.assign(fields.at(getAclIndex)); + } + else + { + auto getAclType = (type == (int)AuthType::WHITE_LIST_MODE) ? FS_ACL_WHITE : FS_ACL_BLACK; + auto entry = _executive->storage().getRow(tool::FS_APPS, getAclType); + aclMapStr.assign(entry->get()); + } + + if (aclMapStr.empty()) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("AuthManagerPrecompiled") + << LOG_DESC("not deploy acl exist, return by default") + << LOG_KV("aclType", type); + // if entry still empty + // if white list mode, return false + // if black list mode, return true + return type == (int)AuthType::BLACK_LIST_MODE; + } + auto&& out = asBytes(aclMapStr); + codec::scale::decode(aclMap, gsl::make_span(out)); + if (aclMap.find(_account) == aclMap.end()) + { + // can't find account in map + // if white list mode, return false + // if black list mode, return true + return type == (int)AuthType::BLACK_LIST_MODE; + } + if (type == (int)AuthType::BLACK_LIST_MODE) + { + return !aclMap.at(_account); + } + return aclMap.at(_account); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/AuthManagerPrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/AuthManagerPrecompiled.h" new file mode 100644 index 00000000..271a65a0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/AuthManagerPrecompiled.h" @@ -0,0 +1,85 @@ +/** + * Copyright (C) 2021 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 AuthManagerPrecompiled.h + * @author: kyonRay + * @date 2021-10-09 + */ + +#pragma once +#include "../../vm/Precompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include + +namespace bcos::precompiled +{ +class AuthManagerPrecompiled : public bcos::precompiled::Precompiled +{ +public: + using Ptr = std::shared_ptr; + AuthManagerPrecompiled(crypto::Hash::Ptr _hashImpl); + ~AuthManagerPrecompiled() override = default; + + std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) override; + +private: + void getAdmin(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + void resetAdmin(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + void setMethodAuthType(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + void checkMethodAuth(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + void getMethodAuth(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + void setMethodAuth(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + void setContractStatus(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + void contractAvailable(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + void getDeployType(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + void setDeployType(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + void hasDeployAuth(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + void setDeployAuth(const std::shared_ptr& _executive, + bool _isClose, PrecompiledExecResult::Ptr const& _callParameters); + + std::string getContractAdmin(const std::shared_ptr& _executive, + const std::string& _address, PrecompiledExecResult::Ptr const& _callParameters); + + u256 getDeployAuthType(const std::shared_ptr& _executive); + + bool checkDeployAuth(const std::shared_ptr& _executive, + const std::string& account); +}; +} // namespace bcos::precompiled \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/ContractAuthMgrPrecompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/ContractAuthMgrPrecompiled.cpp" new file mode 100644 index 00000000..34cb70c6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/ContractAuthMgrPrecompiled.cpp" @@ -0,0 +1,732 @@ +/** + * 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 ContractAuthMgrPrecompiled.cpp + * @author: kyonGuo + * @date 2022/4/15 + */ + +#include "ContractAuthMgrPrecompiled.h" +#include "AuthManagerPrecompiled.h" + +using namespace bcos; +using namespace bcos::precompiled; +using namespace bcos::executor; +using namespace bcos::storage; + +/// contract ACL +/// wasm +const char* const AUTH_METHOD_GET_ADMIN = "getAdmin(string)"; +const char* const AUTH_METHOD_SET_ADMIN = "resetAdmin(string,string)"; +const char* const AUTH_METHOD_SET_AUTH_TYPE = "setMethodAuthType(string,bytes4,uint8)"; +const char* const AUTH_METHOD_OPEN_AUTH = "openMethodAuth(string,bytes4,string)"; +const char* const AUTH_METHOD_CLOSE_AUTH = "closeMethodAuth(string,bytes4,string)"; +const char* const AUTH_METHOD_CHECK_AUTH = "checkMethodAuth(string,bytes4,string)"; +const char* const AUTH_METHOD_GET_AUTH = "getMethodAuth(string,bytes4)"; +/// evm +const char* const AUTH_METHOD_GET_ADMIN_ADD = "getAdmin(address)"; +const char* const AUTH_METHOD_SET_ADMIN_ADD = "resetAdmin(address,address)"; +const char* const AUTH_METHOD_SET_AUTH_TYPE_ADD = "setMethodAuthType(address,bytes4,uint8)"; +const char* const AUTH_METHOD_OPEN_AUTH_ADD = "openMethodAuth(address,bytes4,address)"; +const char* const AUTH_METHOD_CLOSE_AUTH_ADD = "closeMethodAuth(address,bytes4,address)"; +const char* const AUTH_METHOD_CHECK_AUTH_ADD = "checkMethodAuth(address,bytes4,address)"; +const char* const AUTH_METHOD_GET_AUTH_ADD = "getMethodAuth(address,bytes4)"; + +/// contract status +const char* const AUTH_METHOD_SET_CONTRACT = "setContractStatus(address,bool)"; +const char* const AUTH_METHOD_GET_CONTRACT = "contractAvailable(address)"; + +ContractAuthMgrPrecompiled::ContractAuthMgrPrecompiled(crypto::Hash::Ptr _hashImpl) + : bcos::precompiled::Precompiled(_hashImpl) +{ + /// wasm + name2Selector[AUTH_METHOD_GET_ADMIN] = getFuncSelector(AUTH_METHOD_GET_ADMIN, _hashImpl); + name2Selector[AUTH_METHOD_SET_ADMIN] = getFuncSelector(AUTH_METHOD_SET_ADMIN, _hashImpl); + name2Selector[AUTH_METHOD_SET_AUTH_TYPE] = + getFuncSelector(AUTH_METHOD_SET_AUTH_TYPE, _hashImpl); + name2Selector[AUTH_METHOD_OPEN_AUTH] = getFuncSelector(AUTH_METHOD_OPEN_AUTH, _hashImpl); + name2Selector[AUTH_METHOD_CLOSE_AUTH] = getFuncSelector(AUTH_METHOD_CLOSE_AUTH, _hashImpl); + name2Selector[AUTH_METHOD_CHECK_AUTH] = getFuncSelector(AUTH_METHOD_CHECK_AUTH, _hashImpl); + name2Selector[AUTH_METHOD_GET_AUTH] = getFuncSelector(AUTH_METHOD_GET_AUTH, _hashImpl); + + /// evm + name2Selector[AUTH_METHOD_GET_ADMIN_ADD] = + getFuncSelector(AUTH_METHOD_GET_ADMIN_ADD, _hashImpl); + name2Selector[AUTH_METHOD_SET_ADMIN_ADD] = + getFuncSelector(AUTH_METHOD_SET_ADMIN_ADD, _hashImpl); + name2Selector[AUTH_METHOD_SET_AUTH_TYPE_ADD] = + getFuncSelector(AUTH_METHOD_SET_AUTH_TYPE_ADD, _hashImpl); + name2Selector[AUTH_METHOD_OPEN_AUTH_ADD] = + getFuncSelector(AUTH_METHOD_OPEN_AUTH_ADD, _hashImpl); + name2Selector[AUTH_METHOD_CLOSE_AUTH_ADD] = + getFuncSelector(AUTH_METHOD_CLOSE_AUTH_ADD, _hashImpl); + name2Selector[AUTH_METHOD_CHECK_AUTH_ADD] = + getFuncSelector(AUTH_METHOD_CHECK_AUTH_ADD, _hashImpl); + name2Selector[AUTH_METHOD_GET_AUTH_ADD] = getFuncSelector(AUTH_METHOD_GET_AUTH_ADD, _hashImpl); + + name2Selector[AUTH_METHOD_SET_CONTRACT] = getFuncSelector(AUTH_METHOD_SET_CONTRACT, _hashImpl); + name2Selector[AUTH_METHOD_GET_CONTRACT] = getFuncSelector(AUTH_METHOD_GET_CONTRACT, _hashImpl); +} + +std::shared_ptr ContractAuthMgrPrecompiled::call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) +{ + // parse function name + uint32_t func = getParamFunc(_callParameters->input()); + + auto blockContext = _executive->blockContext().lock(); + auto authAddress = blockContext->isWasm() ? AUTH_MANAGER_NAME : AUTH_MANAGER_ADDRESS; + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + PRECOMPILED_LOG(TRACE) << BLOCK_NUMBER(blockContext->number()) + << LOG_BADGE("ContractAuthMgrPrecompiled") << LOG_DESC("call") + << LOG_KV("func", func); + + if (_callParameters->m_sender != authAddress) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("sender is not from AuthManager") + << LOG_KV("sender", _callParameters->m_sender); + BOOST_THROW_EXCEPTION(BCOS_ERROR( + CODE_NO_AUTHORIZED, "ContractAuthMgrPrecompiled: sender is not from AuthManager")); + } + if (func == name2Selector[AUTH_METHOD_GET_ADMIN] || + func == name2Selector[AUTH_METHOD_GET_ADMIN_ADD]) + { + getAdmin(_executive, _callParameters); + } + else if (func == name2Selector[AUTH_METHOD_SET_ADMIN] || + func == name2Selector[AUTH_METHOD_SET_ADMIN_ADD]) + { + resetAdmin(_executive, _callParameters); + } + else if (func == name2Selector[AUTH_METHOD_SET_AUTH_TYPE] || + func == name2Selector[AUTH_METHOD_SET_AUTH_TYPE_ADD]) + { + setMethodAuthType(_executive, _callParameters); + } + else if (func == name2Selector[AUTH_METHOD_OPEN_AUTH] || + func == name2Selector[AUTH_METHOD_OPEN_AUTH_ADD]) + { + setMethodAuth(_executive, false, _callParameters); + } + else if (func == name2Selector[AUTH_METHOD_CLOSE_AUTH] || + func == name2Selector[AUTH_METHOD_CLOSE_AUTH_ADD]) + { + setMethodAuth(_executive, true, _callParameters); + } + else if (func == name2Selector[AUTH_METHOD_CHECK_AUTH] || + func == name2Selector[AUTH_METHOD_CHECK_AUTH_ADD]) + { + checkMethodAuth(_executive, _callParameters); + } + else if (func == name2Selector[AUTH_METHOD_GET_AUTH] || + func == name2Selector[AUTH_METHOD_GET_AUTH_ADD]) + { + getMethodAuth(_executive, _callParameters); + } + else if (func == name2Selector[AUTH_METHOD_SET_CONTRACT]) + { + setContractStatus(_executive, _callParameters); + } + else if (func == name2Selector[AUTH_METHOD_GET_CONTRACT]) + { + contractAvailable(_executive, _callParameters); + } + else + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("call undefined function") << LOG_KV("func", func); + BOOST_THROW_EXCEPTION(bcos::protocol::PrecompiledError( + "ContractAuthMgrPrecompiled call undefined function!")); + } + return _callParameters; +} + +void ContractAuthMgrPrecompiled::getAdmin( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) + +{ + /// evm: getAdmin(address) => string admin + /// wasm: getAdmin(string) => string admin + + std::string path; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + if (blockContext->isWasm()) + { + codec.decode(_callParameters->params(), path); + } + else + { + Address contractAddress; + codec.decode(_callParameters->params(), contractAddress); + path = contractAddress.hex(); + } + PRECOMPILED_LOG(TRACE) << LOG_BADGE("ContractAuthMgrPrecompiled") << LOG_DESC("getAdmin") + << LOG_KV("path", path); + path = getAuthTableName(path); + + auto table = _executive->storage().openTable(path); + if (!table) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("contract ACL table not found") << LOG_KV("table", path); + BOOST_THROW_EXCEPTION(protocol::PrecompiledError("Contract address not found.")); + } + auto entry = table->getRow(ADMIN_FIELD); + if (!entry) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("contract ACL admin entry not found") + << LOG_KV("path", path); + BOOST_THROW_EXCEPTION(protocol::PrecompiledError("Contract Admin row not found.")); + } + std::string adminStr = std::string(entry->getField(0)); + PRECOMPILED_LOG(TRACE) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("getAdmin success") << LOG_KV("admin", adminStr); + _callParameters->setExecResult(codec.encode(adminStr)); +} + +void ContractAuthMgrPrecompiled::resetAdmin( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) +{ + /// evm: resetAdmin(address path,address admin) => int256 + /// wasm: resetAdmin(string path,string admin) => int256 + + std::string path; + std::string admin; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + if (!blockContext->isWasm()) + { + Address contractAddress; + Address adminAddress; + codec.decode(_callParameters->params(), contractAddress, adminAddress); + path = contractAddress.hex(); + admin = adminAddress.hex(); + } + else + { + codec.decode(_callParameters->params(), path, admin); + } + PRECOMPILED_LOG(DEBUG) << BLOCK_NUMBER(blockContext->number()) + << LOG_BADGE("ContractAuthMgrPrecompiled") << LOG_DESC("resetAdmin") + << LOG_KV("path", path) << LOG_KV("admin", admin); + path = getAuthTableName(path); + auto table = _executive->storage().openTable(path); + if (!table || !table->getRow(ADMIN_FIELD)) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("contract ACL table not found") << LOG_KV("path", path); + BOOST_THROW_EXCEPTION(protocol::PrecompiledError("Contract address not found.")); + } + auto newEntry = table->newEntry(); + newEntry.setField(SYS_VALUE, admin); + table->setRow(ADMIN_FIELD, std::move(newEntry)); + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_SUCCESS, codec); +} + +void ContractAuthMgrPrecompiled::setMethodAuthType( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) + +{ + /// evm: setMethodAuthType(address path, bytes4 func, uint8 type) => int256 + /// wasm: setMethodAuthType(string path, bytes4 func, uint8 type) => int256 + + std::string path; + string32 _func; + string32 _type; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + if (!blockContext->isWasm()) + { + Address contractAddress; + codec.decode(_callParameters->params(), contractAddress, _func, _type); + path = contractAddress.hex(); + } + else + { + codec.decode(_callParameters->params(), path, _func, _type); + } + bytes func = codec::fromString32(_func).ref().getCroppedData(0, 4).toBytes(); + uint8_t type = _type[_type.size() - 1]; + PRECOMPILED_LOG(DEBUG) << BLOCK_NUMBER(blockContext->number()) + << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("setMethodAuthType") << LOG_KV("path", path) + << LOG_KV("func", toHexStringWithPrefix(func)) + << LOG_KV("type", (uint32_t)type); + path = getAuthTableName(path); + auto table = _executive->storage().openTable(path); + if (!table) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("contract ACL table not found") << LOG_KV("path", path); + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_TABLE_NOT_EXIST, codec); + return; + } + auto entry = table->getRow(METHOD_AUTH_TYPE); + if (!entry) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("contract ACL table method_auth_type row not found") + << LOG_KV("path", path); + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_TABLE_AUTH_ROW_NOT_EXIST, codec); + return; + } + + std::string authTypeStr = std::string(entry->getField(SYS_VALUE)); + std::map methAuthTypeMap; + if (!authTypeStr.empty()) + { + auto&& out = asBytes(authTypeStr); + codec::scale::decode(methAuthTypeMap, gsl::make_span(out)); + } + // covered writing + methAuthTypeMap[func] = type; + entry->setField(SYS_VALUE, asString(codec::scale::encode(methAuthTypeMap))); + table->setRow(METHOD_AUTH_TYPE, std::move(entry.value())); + + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_SUCCESS, codec); +} + +void ContractAuthMgrPrecompiled::checkMethodAuth( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) + +{ + /// evm: checkMethodAuth(address path, bytes4 func, address account) => bool + /// wasm: checkMethodAuth(string path, bytes4 func, string account) => bool + + std::string path; + string32 _func; + std::string account; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + if (!blockContext->isWasm()) + { + Address contractAddress; + Address accountAddress; + codec.decode(_callParameters->params(), contractAddress, _func, accountAddress); + path = contractAddress.hex(); + account = accountAddress.hex(); + } + else + { + codec.decode(_callParameters->params(), path, _func, account); + } + auto result = checkMethodAuth( + _executive, path, codec::fromString32(_func).ref().getCroppedData(0, 4), account); + _callParameters->setExecResult(codec.encode(result)); +} + +bool ContractAuthMgrPrecompiled::checkMethodAuth( + const std::shared_ptr& _executive, const std::string& _path, + bytesRef func, const std::string& account) +{ + auto path = _path; + path = getAuthTableName(path); + auto table = _executive->storage().openTable(path); + if (!table) + { + // only precompiled contract in /sys/, or pre-built-in contract + PRECOMPILED_LOG(TRACE) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("auth table not found, auth pass through by default.") + << LOG_KV("path", path); + return true; + } + try + { + auto getMethodType = getMethodAuthType(_executive, path, func); + if (getMethodType == (int)CODE_TABLE_AUTH_TYPE_NOT_EXIST) + { + // this method not set type + return true; + } + auto&& authMap = getMethodAuth(_executive, path, getMethodType); + + if (authMap.empty()) + { + PRECOMPILED_LOG(TRACE) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("auth row not found, no method set acl") + << LOG_KV("path", path) << LOG_KV("authType", getMethodType); + // if entry still empty + // if white list mode, return false + // if black list mode, return true + return getMethodType == (int)AuthType::BLACK_LIST_MODE; + } + if (authMap.find(func.toBytes()) == authMap.end()) + { + // func not set acl, pass through + return true; + } + if (authMap.at(func.toBytes()).find(account) == authMap.at(func.toBytes()).end()) + { + // can't find account in user map + // if white list mode, return false + // if black list mode, return true + return getMethodType == (int)AuthType::BLACK_LIST_MODE; + } + if (getMethodType == (int)AuthType::BLACK_LIST_MODE) + { + return !authMap.at(func.toBytes()).at(account); + } + return authMap.at(func.toBytes()).at(account); + } + catch (...) + { + // getMethodAuth will throw PrecompiledError + return true; + } +} + +void ContractAuthMgrPrecompiled::getMethodAuth( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) + +{ + /// evm: getMethodAuth(address,bytes4) => (uint8,string[],string[]) + /// wasm: getMethodAuth(string,bytes4) => (uint8,string[],string[]) + + std::string path; + string32 _func; + bytes funcBytes; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + if (!blockContext->isWasm()) + { + Address contractAddress; + codec.decode(_callParameters->params(), contractAddress, _func); + path = contractAddress.hex(); + } + else + { + codec.decode(_callParameters->params(), path, _func); + } + funcBytes = codec::fromString32(_func).ref().getCroppedData(0, 4).toBytes(); + std::vector accessList = {}; + std::vector blockList = {}; + path = getAuthTableName(path); + auto getMethodType = getMethodAuthType(_executive, path, ref(funcBytes)); + if (getMethodType < 0) + { + // no acl strategy + PRECOMPILED_LOG(TRACE) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("no acl strategy") << LOG_KV("path", path); + _callParameters->setExecResult( + codec.encode(uint8_t(0), std::move(accessList), std::move(blockList))); + return; + } + auto&& authMap = getMethodAuth(_executive, path, getMethodType); + + auto it = authMap.find(funcBytes); + if (it == authMap.end()) + { + PRECOMPILED_LOG(TRACE) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("auth row not found, no method set acl") + << LOG_KV("path", path); + // func not set acl, return empty + _callParameters->setExecResult( + codec.encode(uint8_t(getMethodType), std::move(accessList), std::move(blockList))); + } + else + { + for (const auto& addressPair : it->second) + { + bool access = addressPair.second ? (getMethodType == (int)AuthType::WHITE_LIST_MODE) : + (getMethodType == (int)AuthType::BLACK_LIST_MODE); + if (access) + { + accessList.emplace_back(std::move(addressPair.first)); + } + else + { + blockList.emplace_back(std::move(addressPair.first)); + } + } + _callParameters->setExecResult( + codec.encode(uint8_t(getMethodType), std::move(accessList), std::move(blockList))); + } +} + +void ContractAuthMgrPrecompiled::setMethodAuth( + const std::shared_ptr& _executive, bool _isClose, + const PrecompiledExecResult::Ptr& _callParameters) + +{ + std::string path; + std::string account; + string32 _func; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_callParameters->params(), path, _func, account); + if (!blockContext->isWasm()) + { + Address contractAddress; + Address accountAddress; + codec.decode(_callParameters->params(), contractAddress, _func, accountAddress); + path = contractAddress.hex(); + account = accountAddress.hex(); + } + else + { + codec.decode(_callParameters->params(), path, _func, account); + } + bytes func = codec::fromString32(_func).ref().getCroppedData(0, 4).toBytes(); + PRECOMPILED_LOG(DEBUG) << BLOCK_NUMBER(blockContext->number()) + << LOG_BADGE("ContractAuthMgrPrecompiled") << LOG_DESC("setAuth") + << LOG_KV("path", path) << LOG_KV("func", toHexStringWithPrefix(func)) + << LOG_KV("account", account); + path = getAuthTableName(path); + auto table = _executive->storage().openTable(path); + if (!table) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("setMethodAuth: contract ACL table not found") + << LOG_KV("path", path); + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_TABLE_NOT_EXIST, codec); + return; + } + s256 authType = getMethodAuthType(_executive, path, ref(func)); + std::string getTypeStr; + if (authType == (int)AuthType::WHITE_LIST_MODE) + getTypeStr = METHOD_AUTH_WHITE; + else if (authType == (int)AuthType::BLACK_LIST_MODE) + getTypeStr = METHOD_AUTH_BLACK; + else + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("error auth type") << LOG_KV("path", path) + << LOG_KV("type", authType); + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_TABLE_ERROR_AUTH_TYPE, codec); + return; + } + auto entry = table->getRow(getTypeStr); + if (!entry) + { + PRECOMPILED_LOG(TRACE) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("auth row not found, try to set new one") + << LOG_KV("path", path) << LOG_KV("Type", getTypeStr); + entry = table->newEntry(); + entry->setField(SYS_VALUE, ""); + } + + bool access = _isClose ? (authType == (int)AuthType::BLACK_LIST_MODE) : + (authType == (int)AuthType::WHITE_LIST_MODE); + + auto userAuth = std::make_pair(account, access); + + MethodAuthMap authMap; + if (entry->getField(SYS_VALUE).empty()) + { + // first insert func + authMap.insert({func, {std::move(userAuth)}}); + } + else + { + try + { + auto&& out = asBytes(std::string(entry->getField(SYS_VALUE))); + codec::scale::decode(authMap, gsl::make_span(out)); + if (authMap.find(func) != authMap.end()) + { + authMap.at(func)[account] = access; + } + else + { + authMap.insert({func, {std::move(userAuth)}}); + } + } + catch (...) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("auth map parse error") << LOG_KV("path", path); + getErrorCodeOut( + _callParameters->mutableExecResult(), CODE_TABLE_AUTH_ROW_NOT_EXIST, codec); + return; + } + } + entry->setField(SYS_VALUE, asString(codec::scale::encode(authMap))); + table->setRow(getTypeStr, std::move(entry.value())); + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_SUCCESS, codec); +} + +int32_t ContractAuthMgrPrecompiled::getMethodAuthType( + const std::shared_ptr& _executive, const std::string& _path, + bytesConstRef _func) +{ + auto table = _executive->storage().openTable(_path); + // _table can't be nullopt + auto entry = table->getRow(METHOD_AUTH_TYPE); + if (!entry || entry->getField(SYS_VALUE).empty()) + { + PRECOMPILED_LOG(TRACE) + << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("no acl strategy exist, should set the method access auth type firstly"); + return (int)CODE_TABLE_AUTH_TYPE_NOT_EXIST; + } + std::string authTypeStr = std::string(entry->getField(SYS_VALUE)); + std::map authTypeMap; + auto s = _func.toString(); + try + { + auto&& out = asBytes(authTypeStr); + codec::scale::decode(authTypeMap, gsl::make_span(out)); + auto it = authTypeMap.find(_func.toBytes()); + if (it == authTypeMap.end()) + { + return (int)CODE_TABLE_AUTH_TYPE_NOT_EXIST; + } + return it->second; + } + catch (...) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("decode method type error"); + return (int)CODE_TABLE_AUTH_TYPE_DECODE_ERROR; + } +} + +MethodAuthMap ContractAuthMgrPrecompiled::getMethodAuth( + const std::shared_ptr& _executive, const std::string& path, + int32_t getMethodType) const +{ + auto table = _executive->storage().openTable(path); + if (!table) + { + // only precompiled contract in /sys/, or pre-built-in contract + PRECOMPILED_LOG(TRACE) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("auth table not found.") << LOG_KV("path", path); + BOOST_THROW_EXCEPTION(protocol::PrecompiledError("Auth table not found")); + } + std::string getTypeStr = + getMethodType == (int)AuthType::WHITE_LIST_MODE ? METHOD_AUTH_WHITE : METHOD_AUTH_BLACK; + + auto entry = table->getRow(getTypeStr); + MethodAuthMap authMap = {}; + if (!entry || entry->getField(SYS_VALUE).empty()) + { + PRECOMPILED_LOG(TRACE) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("auth row not found, no method set acl") + << LOG_KV("path", path) << LOG_KV("authType", getTypeStr); + return authMap; + } + bytes&& out = asBytes(std::string(entry->getField(SYS_VALUE))); + codec::scale::decode(authMap, gsl::make_span(out)); + return authMap; +} + +void ContractAuthMgrPrecompiled::setContractStatus( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) + +{ + /// setContractStatus(address _addr, bool isFreeze) => int256 + + std::string address; + bool isFreeze = false; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + if (blockContext->isWasm()) + { + codec.decode(_callParameters->params(), address, isFreeze); + } + else + { + Address contractAddress; + codec.decode(_callParameters->params(), contractAddress, isFreeze); + address = contractAddress.hex(); + } + PRECOMPILED_LOG(DEBUG) << BLOCK_NUMBER(blockContext->number()) + << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("setContractStatus") << LOG_KV("address", address) + << LOG_KV("isFreeze", isFreeze); + auto path = getAuthTableName(address); + auto table = _executive->storage().openTable(path); + if (!table) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("contract ACL table not found") << LOG_KV("path", path); + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_TABLE_NOT_EXIST, codec); + return; + } + auto status = isFreeze ? CONTRACT_FROZEN : CONTRACT_NORMAL; + Entry entry = {}; + entry.importFields({status}); + table->setRow("status", std::move(entry)); + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_SUCCESS, codec); +} + +void ContractAuthMgrPrecompiled::contractAvailable( + const std::shared_ptr& _executive, + const PrecompiledExecResult::Ptr& _callParameters) + +{ + /// contractAvailable(address _addr) => bool + + std::string address; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + if (blockContext->isWasm()) + { + codec.decode(_callParameters->params(), address); + } + else + { + Address contractAddress; + codec.decode(_callParameters->params(), contractAddress); + address = contractAddress.hex(); + } + PRECOMPILED_LOG(TRACE) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("contractAvailable") << LOG_KV("address", address); + auto result = getContractStatus(_executive, address); + // result !=0 && result != 1 + if (result >> 1) + { + BOOST_THROW_EXCEPTION(protocol::PrecompiledError("Cannot get contract status")); + } + _callParameters->setExecResult(codec.encode(result)); +} + +int32_t ContractAuthMgrPrecompiled::getContractStatus( + const std::shared_ptr& _executive, const std::string& _path) +{ + auto path = getAuthTableName(_path); + auto table = _executive->storage().openTable(path); + if (!table) + { + // only precompiled contract in /sys/, or pre-built-in contract + PRECOMPILED_LOG(TRACE) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("auth table not found, auth pass through by default.") + << LOG_KV("path", path); + return (int)CODE_TABLE_NOT_EXIST; + } + auto entry = table->getRow(STATUS_FIELD); + if (!entry) + { + PRECOMPILED_LOG(TRACE) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC( + "auth status row not found, auth pass through by default."); + return (int)CODE_TABLE_AUTH_ROW_NOT_EXIST; + } + auto status = entry->get(); + PRECOMPILED_LOG(TRACE) << LOG_BADGE("ContractAuthMgrPrecompiled") + << LOG_DESC("get contract status success") << LOG_KV("contract", path) + << LOG_KV("status", status); + return status == CONTRACT_NORMAL; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/ContractAuthMgrPrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/ContractAuthMgrPrecompiled.h" new file mode 100644 index 00000000..b5285321 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/ContractAuthMgrPrecompiled.h" @@ -0,0 +1,91 @@ +/** + * 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 ContractAuthMgrPrecompiled.h + * @author: kyonGuo + * @date 2022/4/15 + */ + +#pragma once + +#include "../../vm/Precompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include + +namespace bcos::precompiled +{ +/// func => (address, access) +using MethodAuthMap = std::map>; + +enum AuthType : int +{ + WHITE_LIST_MODE = 1, + BLACK_LIST_MODE = 2 +}; + +class ContractAuthMgrPrecompiled : public bcos::precompiled::Precompiled +{ +public: + ContractAuthMgrPrecompiled(crypto::Hash::Ptr _hashImpl); + ~ContractAuthMgrPrecompiled() override = default; + + std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) override; + + bool checkMethodAuth(const std::shared_ptr& _executive, + const std::string& path, bytesRef func, const std::string& account); + + int32_t getContractStatus(const std::shared_ptr& _executive, + const std::string& _path); + +private: + void getAdmin(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + void resetAdmin(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + void setMethodAuthType(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + void checkMethodAuth(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + void getMethodAuth(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + void setMethodAuth(const std::shared_ptr& _executive, + bool _isClose, PrecompiledExecResult::Ptr const& _callParameters); + + void setContractStatus(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + void contractAvailable(const std::shared_ptr& _executive, + PrecompiledExecResult::Ptr const& _callParameters); + + int32_t getMethodAuthType(const std::shared_ptr& _executive, + const std::string& _path, bytesConstRef _func); + + MethodAuthMap getMethodAuth(const std::shared_ptr& _executive, + const std::string& path, int32_t authType) const; + + inline std::string getAuthTableName(const std::string& _name) + { + return std::string(executor::USER_APPS_PREFIX) + _name + executor::CONTRACT_SUFFIX; + } +}; +} // namespace bcos::precompiled \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/CpuHeavyPrecompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/CpuHeavyPrecompiled.cpp" new file mode 100644 index 00000000..2b0147a7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/CpuHeavyPrecompiled.cpp" @@ -0,0 +1,104 @@ +/** + * Copyright (C) 2021 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 CpuHeavyPrecompiled.cpp + * @author: jimmyshi + * @date 2021-02-23 + */ + +#include "CpuHeavyPrecompiled.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" + +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::precompiled; + +/* +contract HelloWorld { + function get() public constant returns(string); + function set(string _m) public; +} +*/ + +// set interface +const char* const METHOD_SORT = "sort(uint256,uint256)"; + +CpuHeavyPrecompiled::CpuHeavyPrecompiled(crypto::Hash::Ptr _hashImpl) : Precompiled(_hashImpl) +{ + name2Selector[METHOD_SORT] = getFuncSelector(METHOD_SORT, _hashImpl); +} + + +void quickSort(std::vector& arr, int left, int right) +{ + int i = left; + int j = right; + if (i == j) + return; + unsigned int pivot = arr[left + (right - left) / 2]; + while (i <= j) + { + while (arr[i] < pivot) + i++; + while (pivot < arr[j]) + j--; + if (i <= j) + { + std::swap(arr[i], arr[j]); + i++; + j--; + } + } + if (left < j) + quickSort(arr, left, j); + if (i < right) + quickSort(arr, i, right); +} + +void sort(int size) +{ + std::vector data = std::vector(size); + for (int x = 0; x < size; x++) + { + data[x] = size - x; + } + quickSort(data, 0, size - 1); +} + + +std::shared_ptr CpuHeavyPrecompiled::call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) +{ + PRECOMPILED_LOG(TRACE) << LOG_BADGE("CpuHeavyPrecompiled") << LOG_DESC("call") + << LOG_KV("param", toHexString(_callParameters->input())); + + // parse function name + // uint32_t func = getParamFunc(_param); + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + + u256 size, signature; + codec.decode(_callParameters->params(), size, signature); + + auto gasPricer = m_precompiledGasFactory->createPrecompiledGas(); + gasPricer->setMemUsed(_callParameters->input().size()); + + sort(size.convert_to()); + + _callParameters->setExecResult(bytes()); + return _callParameters; +} diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/CpuHeavyPrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/CpuHeavyPrecompiled.h" new file mode 100644 index 00000000..ee55c254 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/CpuHeavyPrecompiled.h" @@ -0,0 +1,78 @@ +/** + * Copyright (C) 2021 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 CpuHeavyPrecompiled.h + * @author: jimmyshi + * @date 2022-02-23 + */ + +#pragma once + +#include "../../vm/Precompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" + +namespace bcos +{ +namespace precompiled +{ +class CpuHeavyPrecompiled : public bcos::precompiled::Precompiled +{ +public: + using Ptr = std::shared_ptr; + + CpuHeavyPrecompiled(crypto::Hash::Ptr _hashImpl); + + virtual ~CpuHeavyPrecompiled(){}; + + std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) override; + + // is this precompiled need parallel processing, default false. + virtual bool isParallelPrecompiled() override { return true; } + + virtual std::vector getParallelTag(bytesConstRef param, bool _isWasm) override + { + (void)param; + (void)_isWasm; + return std::vector(); + }; + + static inline std::string getAddress(unsigned int id) + { + u160 address = u160(CPU_HEAVY_START_ADDRESS); + address += id; + h160 addressBytes = h160(address); + return addressBytes.hex(); + } + + static void registerPrecompiled( + std::shared_ptr>> + registeredMap, + crypto::Hash::Ptr _hashImpl) + { + for (int id = 0; id < CPU_HEAVY_CONTRACT_NUM; id++) + { + std::string&& address = getAddress(id); + registeredMap->insert({std::move(address), + std::make_shared(_hashImpl)}); + } + BCOS_LOG(TRACE) << LOG_BADGE("CpuHeavy") << "Register CpuHeavyPrecompiled complete" + << LOG_KV("addressFrom", getAddress(0)) + << LOG_KV("addressTo", getAddress(CPU_HEAVY_CONTRACT_NUM - 1)); + } +}; +} // namespace precompiled +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/DagTransferPrecompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/DagTransferPrecompiled.cpp" new file mode 100644 index 00000000..82fe2fa9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/DagTransferPrecompiled.cpp" @@ -0,0 +1,519 @@ +/** + * Copyright (C) 2021 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 DagTransferPrecompiled.cpp + * @author: kyonRay + * @date 2021-05-30 + */ + +#include "DagTransferPrecompiled.h" +#include "bcos-codec/wrapper/CodecWrapper.h" +#include "bcos-executor/src/precompiled/common/PrecompiledResult.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include +#include + +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::precompiled; +using namespace bcos::ledger; + +// interface of DagTransferPrecompiled +/* +contract DagTransfer{ + function userAdd(string user, uint256 balance) public returns(); + function userSave(string user, uint256 balance) public returns(uint256); + function userDraw(string user, uint256 balance) public returns(uint256); + function userBalance(string user) public constant returns(uint256,uint256); + function userTransfer(string user_a, string user_b, uint256 amount) public returns(uint256); +} +*/ +const char* const DAG_TRANSFER_METHOD_ADD_STR_UINT = "userAdd(string,uint256)"; +const char* const DAG_TRANSFER_METHOD_SAV_STR_UINT = "userSave(string,uint256)"; +const char* const DAG_TRANSFER_METHOD_DRAW_STR_UINT = "userDraw(string,uint256)"; +const char* const DAG_TRANSFER_METHOD_TRS_STR2_UINT = "userTransfer(string,string,uint256)"; +const char* const DAG_TRANSFER_METHOD_BAL_STR = "userBalance(string)"; + +// fields of table '_dag_transfer_' +// const char* const DAG_TRANSFER_FIELD_NAME = "user_name"; +const size_t DAG_TRANSFER_FIELD_BALANCE = 0; + +DagTransferPrecompiled::DagTransferPrecompiled(crypto::Hash::Ptr _hashImpl) : Precompiled(_hashImpl) +{ + name2Selector[DAG_TRANSFER_METHOD_ADD_STR_UINT] = + getFuncSelector(DAG_TRANSFER_METHOD_ADD_STR_UINT, _hashImpl); + name2Selector[DAG_TRANSFER_METHOD_SAV_STR_UINT] = + getFuncSelector(DAG_TRANSFER_METHOD_SAV_STR_UINT, _hashImpl); + name2Selector[DAG_TRANSFER_METHOD_DRAW_STR_UINT] = + getFuncSelector(DAG_TRANSFER_METHOD_DRAW_STR_UINT, _hashImpl); + name2Selector[DAG_TRANSFER_METHOD_TRS_STR2_UINT] = + getFuncSelector(DAG_TRANSFER_METHOD_TRS_STR2_UINT, _hashImpl); + name2Selector[DAG_TRANSFER_METHOD_BAL_STR] = + getFuncSelector(DAG_TRANSFER_METHOD_BAL_STR, _hashImpl); +} + +std::vector DagTransferPrecompiled::getParallelTag(bytesConstRef _param, bool _isWasm) +{ + // parse function name + uint32_t func = getParamFunc(_param); + bytesConstRef data = getParamData(_param); + + std::vector results; + auto codec = CodecWrapper(m_hashImpl, _isWasm); + // user_name user_balance 2 fields in table, the key of table is user_name field + if (func == name2Selector[DAG_TRANSFER_METHOD_ADD_STR_UINT]) + { // userAdd(string,uint256) + std::string user; + u256 amount; + codec.decode(data, user, amount); + // if params is invalid , parallel process can be done + if (!user.empty()) + { + results.push_back(user); + } + } + else if (func == name2Selector[DAG_TRANSFER_METHOD_SAV_STR_UINT]) + { // userSave(string,uint256) + std::string user; + u256 amount; + codec.decode(data, user, amount); + // if params is invalid , parallel process can be done + if (!user.empty()) + { + results.push_back(user); + } + } + else if (func == name2Selector[DAG_TRANSFER_METHOD_DRAW_STR_UINT]) + { // userDraw(string,uint256) + std::string user; + u256 amount; + codec.decode(data, user, amount); + // if params is invalid , parallel process can be done + if (!user.empty()) + { + results.push_back(user); + } + } + else if (func == name2Selector[DAG_TRANSFER_METHOD_TRS_STR2_UINT]) + { + // userTransfer(string,string,uint256) + std::string fromUser, toUser; + u256 amount; + codec.decode(data, fromUser, toUser, amount); + // if params is invalid , parallel process can be done + if (!fromUser.empty() && !toUser.empty()) + { + results.push_back(fromUser); + results.push_back(toUser); + } + } + else if (func == name2Selector[DAG_TRANSFER_METHOD_BAL_STR]) + { + // query interface has no parallel processing conflict. + // do nothing + } + + return results; +} + +std::shared_ptr DagTransferPrecompiled::call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) +{ + // parse function name + uint32_t func = getParamFunc(_callParameters->input()); + bytesConstRef data = _callParameters->params(); + auto gasPricer = m_precompiledGasFactory->createPrecompiledGas(); + + // user_name user_balance 2 fields in table, the key of table is user_name field + if (func == name2Selector[DAG_TRANSFER_METHOD_ADD_STR_UINT]) + { // userAdd(string,uint256) + userAddCall( + _executive, data, _callParameters->m_origin, _callParameters->mutableExecResult()); + } + else if (func == name2Selector[DAG_TRANSFER_METHOD_SAV_STR_UINT]) + { // userSave(string,uint256) + userSaveCall( + _executive, data, _callParameters->m_origin, _callParameters->mutableExecResult()); + } + else if (func == name2Selector[DAG_TRANSFER_METHOD_DRAW_STR_UINT]) + { // userDraw(string,uint256) + userDrawCall( + _executive, data, _callParameters->m_origin, _callParameters->mutableExecResult()); + } + else if (func == name2Selector[DAG_TRANSFER_METHOD_TRS_STR2_UINT]) + { // userTransfer(string,string,uint256) + userTransferCall( + _executive, data, _callParameters->m_origin, _callParameters->mutableExecResult()); + } + else if (func == name2Selector[DAG_TRANSFER_METHOD_BAL_STR]) + { // userBalance(string user) + userBalanceCall(_executive, data, _callParameters->mutableExecResult()); + } + else + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("DagTransferPrecompiled") << LOG_DESC("error func") + << LOG_KV("func", func); + } + gasPricer->updateMemUsed(_callParameters->m_execResult.size()); + _callParameters->setGasLeft(_callParameters->m_gasLeft - gasPricer->calTotalGas()); + return _callParameters; +} + +void DagTransferPrecompiled::userAddCall(std::shared_ptr _executive, + bytesConstRef _data, std::string const&, bytes& _out) +{ + // userAdd(string,uint256) + std::string user; + u256 amount; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_data, user, amount); + + int ret; + std::string strErrorMsg; + do + { + if (user.empty()) + { + strErrorMsg = "invalid user name"; + ret = CODE_INVALID_USER_NAME; + break; + } + auto table = _executive->storage().openTable(DAG_TRANSFER); + if (!table) + { + strErrorMsg = "openTable failed."; + ret = CODE_INVALID_OPENTABLE_FAILED; + break; + } + auto entry = table->getRow(user); + if (entry) + { + strErrorMsg = "user already exist"; + ret = CODE_INVALID_USER_ALREADY_EXIST; + break; + } + + // user not exist, insert user into it. + auto newEntry = table->newEntry(); + newEntry.setField(DAG_TRANSFER_FIELD_BALANCE, amount.str()); + table->setRow(user, newEntry); + ret = 0; + } while (false); + if (!strErrorMsg.empty()) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("DagTransferPrecompiled") << LOG_DESC(strErrorMsg) + << LOG_KV("code", ret); + } + _out = codec.encode(u256(ret)); +} + +void DagTransferPrecompiled::userSaveCall( + std::shared_ptr _executive, bytesConstRef _data, + std::string const&, bytes& _out) +{ + // userSave(string,uint256) + std::string user; + u256 amount; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_data, user, amount); + + int ret; + u256 balance; + std::string strErrorMsg; + do + { + if (user.empty()) + { + strErrorMsg = "invalid user name"; + ret = CODE_INVALID_USER_NAME; + break; + } + + // check amount valid + if (0 == amount) + { + strErrorMsg = "invalid save amount"; + ret = CODE_INVALID_AMOUNT; + break; + } + + auto table = _executive->storage().openTable(DAG_TRANSFER); + if (!table) + { + strErrorMsg = "openTable failed."; + ret = CODE_INVALID_OPENTABLE_FAILED; + break; + } + + auto entry = table->getRow(user); + if (!entry) + { + // If user is not exist, insert it. With this strategy, we can also add user by save + // operation. + auto newEntry = table->newEntry(); + newEntry.setField(DAG_TRANSFER_FIELD_BALANCE, amount.str()); + table->setRow(user, newEntry); + } + else + { + balance = u256(entry->getField(DAG_TRANSFER_FIELD_BALANCE)); + + // if overflow + auto new_balance = balance + amount; + if (new_balance < balance) + { + strErrorMsg = "save overflow"; + ret = CODE_INVALID_BALANCE_OVERFLOW; + break; + } + + auto updateEntry = table->newEntry(); + updateEntry.setField(DAG_TRANSFER_FIELD_BALANCE, new_balance.str()); + table->setRow(user, updateEntry); + } + + ret = 0; + } while (false); + if (!strErrorMsg.empty()) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("DagTransferPrecompiled") << LOG_DESC(strErrorMsg) + << LOG_KV("code", ret); + } + _out = codec.encode(u256(ret)); +} + +void DagTransferPrecompiled::userDrawCall( + std::shared_ptr _executive, bytesConstRef _data, + std::string const&, bytes& _out) +{ + std::string user; + u256 amount; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_data, user, amount); + + u256 balance; + int ret; + std::string strErrorMsg; + do + { + if (user.empty()) + { + strErrorMsg = "invalid user name"; + ret = CODE_INVALID_USER_NAME; + break; + } + if (amount == 0) + { + strErrorMsg = "draw invalid amount"; + ret = CODE_INVALID_AMOUNT; + break; + } + auto table = _executive->storage().openTable(DAG_TRANSFER); + if (!table) + { + strErrorMsg = "openTable failed."; + ret = CODE_INVALID_OPENTABLE_FAILED; + break; + } + + auto entry = table->getRow(user); + if (!entry) + { + strErrorMsg = "user not exist"; + ret = CODE_INVALID_USER_NOT_EXIST; + break; + } + + // only one record for every user + balance = u256(entry->getField(DAG_TRANSFER_FIELD_BALANCE)); + if (balance < amount) + { + strErrorMsg = "insufficient balance"; + ret = CODE_INVALID_INSUFFICIENT_BALANCE; + break; + } + + auto new_balance = balance - amount; + auto newEntry = table->newEntry(); + newEntry.setField(DAG_TRANSFER_FIELD_BALANCE, new_balance.str()); + table->setRow(user, newEntry); + ret = 0; + } while (false); + if (!strErrorMsg.empty()) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("DagTransferPrecompiled") << LOG_DESC(strErrorMsg) + << LOG_KV("code", ret); + } + _out = codec.encode(u256(ret)); +} + +void DagTransferPrecompiled::userBalanceCall( + std::shared_ptr _executive, bytesConstRef _data, bytes& _out) +{ + std::string user; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_data, user); + + u256 balance; + int ret; + std::string strErrorMsg; + + do + { + if (user.empty()) + { + strErrorMsg = " invalid user name"; + ret = CODE_INVALID_USER_NAME; + break; + } + + auto table = _executive->storage().openTable(DAG_TRANSFER); + if (!table) + { + strErrorMsg = "openTable failed."; + ret = CODE_INVALID_OPENTABLE_FAILED; + break; + } + + auto entry = table->getRow(user); + if (!entry) + { + strErrorMsg = "user not exist"; + ret = CODE_INVALID_USER_NOT_EXIST; + break; + } + + // only one record for every user + balance = u256(entry->getField(DAG_TRANSFER_FIELD_BALANCE)); + ret = 0; + } while (false); + if (!strErrorMsg.empty()) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("DagTransferPrecompiled") << LOG_DESC(strErrorMsg) + << LOG_KV("code", ret); + } + _out = codec.encode(u256(ret), balance); +} + +void DagTransferPrecompiled::userTransferCall( + std::shared_ptr _executive, bytesConstRef _data, + std::string const&, bytes& _out) +{ + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + std::string fromUser, toUser; + u256 amount; + codec.decode(_data, fromUser, toUser, amount); + + u256 fromUserBalance, newFromUserBalance; + u256 toUserBalance, newToUserBalance; + + std::string strErrorMsg; + int ret; + do + { + // parameters check + if (fromUser.empty() || toUser.empty()) + { + strErrorMsg = "invalid user name"; + ret = CODE_INVALID_USER_NAME; + break; + } + if (amount == 0) + { + strErrorMsg = "invalid amount"; + ret = CODE_INVALID_AMOUNT; + break; + } + // transfer self, do nothing + if (fromUser == toUser) + { + ret = 0; + break; + } + auto table = _executive->storage().openTable(DAG_TRANSFER); + if (!table) + { + strErrorMsg = "openTable failed."; + ret = CODE_INVALID_OPENTABLE_FAILED; + break; + } + + auto entry = table->getRow(fromUser); + if (!entry) + { + strErrorMsg = "from user not exist"; + ret = CODE_INVALID_USER_NOT_EXIST; + break; + } + + fromUserBalance = u256(entry->getField(DAG_TRANSFER_FIELD_BALANCE)); + if (fromUserBalance < amount) + { + strErrorMsg = "from user insufficient balance"; + ret = CODE_INVALID_INSUFFICIENT_BALANCE; + break; + } + + entry = table->getRow(toUser); + if (!entry) + { + // If to user not exist, add it first. + auto newEntry = table->newEntry(); + newEntry.setField(DAG_TRANSFER_FIELD_BALANCE, u256(0).str()); + table->setRow(toUser, newEntry); + toUserBalance = 0; + } + else + { + toUserBalance = u256(entry->getField(DAG_TRANSFER_FIELD_BALANCE)); + } + + // overflow check + if (toUserBalance + amount < toUserBalance) + { + strErrorMsg = "to user balance overflow."; + ret = CODE_INVALID_BALANCE_OVERFLOW; + break; + } + + newFromUserBalance = fromUserBalance - amount; + newToUserBalance = toUserBalance + amount; + + // update fromUser balance info. + entry = table->newEntry(); + entry->setField(DAG_TRANSFER_FIELD_BALANCE, newFromUserBalance.str()); + table->setRow(fromUser, *entry); + + // update toUser balance info. + entry = table->newEntry(); + entry->setField(DAG_TRANSFER_FIELD_BALANCE, newToUserBalance.str()); + table->setRow(toUser, *entry); + // end with success + ret = 0; + } while (false); + if (!strErrorMsg.empty()) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("DagTransferPrecompiled") << LOG_DESC(strErrorMsg) + << LOG_KV("code", ret); + } + _out = codec.encode(u256(ret)); +} diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/DagTransferPrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/DagTransferPrecompiled.h" new file mode 100644 index 00000000..04263d0e --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/DagTransferPrecompiled.h" @@ -0,0 +1,76 @@ +/** + * Copyright (C) 2021 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 DagTransferPrecompiled.h + * @author: kyonRay + * @date 2021-05-30 + */ + +#pragma once +#include "../../vm/Precompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include + +namespace bcos +{ +namespace precompiled +{ +class DagTransferPrecompiled : public bcos::precompiled::Precompiled +{ +public: + using Ptr = std::shared_ptr; + DagTransferPrecompiled(crypto::Hash::Ptr _hashImpl); + virtual ~DagTransferPrecompiled(){}; + + std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) override; + +public: + // is this precompiled need parallel processing, default false. + bool isParallelPrecompiled() override { return true; } + std::vector getParallelTag(bytesConstRef param, bool _isWasm) override; + + static void createDagTable(std::shared_ptr storageInterface) + { + std::string _tableName(ledger::DAG_TRANSFER); + // create table first + std::promise createPromise; + storageInterface->asyncCreateTable(_tableName, SYS_VALUE_FIELDS, + [&createPromise](Error::UniquePtr _e, std::optional) { + createPromise.set_value(std::move(_e)); + }); + Error::UniquePtr _e = createPromise.get_future().get(); + BCOS_LOG(TRACE) << LOG_BADGE("DAG TRANSFER") << "create DAG Transfer table" + << LOG_KV("table", _tableName) + << (_e == nullptr ? "" : " withError: " + _e->errorMessage()); + } + +public: + void userAddCall(std::shared_ptr _executive, + bytesConstRef _data, std::string const& _origin, bytes& _out); + void userSaveCall(std::shared_ptr _executive, + bytesConstRef _data, std::string const& _origin, bytes& _out); + void userDrawCall(std::shared_ptr _executive, + bytesConstRef _data, std::string const& _origin, bytes& _out); + void userBalanceCall(std::shared_ptr _executive, + bytesConstRef _data, bytes& _out); + void userTransferCall(std::shared_ptr _executive, + bytesConstRef _data, std::string const& _origin, bytes& _out); +}; + +} // namespace precompiled + +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/GroupSigPrecompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/GroupSigPrecompiled.cpp" new file mode 100644 index 00000000..6e50e838 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/GroupSigPrecompiled.cpp" @@ -0,0 +1,99 @@ +/** + * Copyright (C) 2021 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 GroupSigPrecompiled.cpp + * @author: yklu + * @date 2022-04-12 + */ +#include "GroupSigPrecompiled.h" +#include "../../executive/BlockContext.h" +#include "bcos-executor/src/precompiled/common/PrecompiledResult.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include + +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::precompiled; + +/* +contract GroupSig +{ + function groupSigVerify(string signature, string message, string gpkInfo, string paramInfo) +public constant returns(int, bool); +} +*/ + +const char* const GROUP_SIG_METHOD_SET_STR = "groupSigVerify(string,string,string,string)"; + +GroupSigPrecompiled::GroupSigPrecompiled(crypto::Hash::Ptr _hashImpl) : Precompiled(_hashImpl) +{ + name2Selector[GROUP_SIG_METHOD_SET_STR] = getFuncSelector(GROUP_SIG_METHOD_SET_STR, _hashImpl); +} + +std::shared_ptr GroupSigPrecompiled::call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) +{ + // parse function name + uint32_t func = getParamFunc(_callParameters->input()); + bytesConstRef data = _callParameters->params(); + auto blockContext = _executive->blockContext().lock(); + auto codec = + std::make_shared(blockContext->hashHandler(), blockContext->isWasm()); + auto gasPricer = m_precompiledGasFactory->createPrecompiledGas(); + + if (func == name2Selector[GROUP_SIG_METHOD_SET_STR]) + { + // groupSigVerify(string) + std::string signature, message, gpkInfo, paramInfo; + codec->decode(data, signature, message, gpkInfo, paramInfo); + bool result = false; + try + { + result = GroupSigApi::group_verify(signature, message, gpkInfo, paramInfo); + gasPricer->appendOperation(InterfaceOpcode::GroupSigVerify); + } + catch (std::exception& error) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("GroupSigPrecompiled") << LOG_DESC(error.what()) + << LOG_KV("signature", signature) << LOG_KV("message", message) + << LOG_KV("gpkInfo", gpkInfo) << LOG_KV("paramInfo", paramInfo); + result = false; + } + catch (std::string& error) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("GroupSigPrecompiled") << LOG_DESC(error) + << LOG_KV("signature", signature) << LOG_KV("message", message) + << LOG_KV("gpkInfo", gpkInfo) << LOG_KV("paramInfo", paramInfo); + result = false; + } + int32_t retCode = CODE_SUCCESS; + if (!result) + { + retCode = VERIFY_GROUP_SIG_FAILED; + } + _callParameters->setExecResult(codec->encode(retCode, result)); + } + else + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("GroupSigPrecompiled") + << LOG_DESC("call undefined function") << LOG_KV("func", func); + _callParameters->setExecResult(codec->encode((int32_t)CODE_UNKNOW_FUNCTION_CALL, false)); + } + gasPricer->updateMemUsed(_callParameters->m_execResult.size()); + _callParameters->setGasLeft(_callParameters->m_gasLeft - gasPricer->calTotalGas()); + return _callParameters; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/GroupSigPrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/GroupSigPrecompiled.h" new file mode 100644 index 00000000..167f1bff --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/GroupSigPrecompiled.h" @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2021 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 GroupSigPrecompiled.h + * @author: yklu + * @date 2022-04-12 + */ +#pragma once + +#include "../../vm/Precompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" + +namespace bcos +{ +namespace precompiled +{ +class GroupSigPrecompiled : public bcos::precompiled::Precompiled +{ +public: + using Ptr = std::shared_ptr; + GroupSigPrecompiled(crypto::Hash::Ptr _hashImpl); + virtual ~GroupSigPrecompiled(){}; + + std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) override; +}; +} // namespace precompiled +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/HelloWorldPrecompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/HelloWorldPrecompiled.cpp" new file mode 100644 index 00000000..85664e1c --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/HelloWorldPrecompiled.cpp" @@ -0,0 +1,128 @@ +/** + * Copyright (C) 2021 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 HelloWorldPrecompiled.cpp + * @author: kyonRay + * @date 2021-05-30 + */ + +#include "HelloWorldPrecompiled.h" +#include "../../executive/BlockContext.h" +#include "bcos-executor/src/precompiled/common/PrecompiledResult.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" + +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::precompiled; + +/* +contract HelloWorld { + function get() public constant returns(string); + function set(string _m) public; +} +*/ + +// HelloWorldPrecompiled table name +const std::string HELLO_WORLD_TABLE_NAME = "hello_world"; +// key field +const std::string HELLO_WORLD_KEY_FIELD = "key"; +const std::string HELLO_WORLD_KEY_FIELD_NAME = "hello_key"; +// value field +const std::string HELLO_WORLD_VALUE_FIELD = "value"; + +// get interface +const char* const HELLO_WORLD_METHOD_GET = "get()"; +// set interface +const char* const HELLO_WORLD_METHOD_SET = "set(string)"; + +HelloWorldPrecompiled::HelloWorldPrecompiled(crypto::Hash::Ptr _hashImpl) : Precompiled(_hashImpl) +{ + name2Selector[HELLO_WORLD_METHOD_GET] = getFuncSelector(HELLO_WORLD_METHOD_GET, _hashImpl); + name2Selector[HELLO_WORLD_METHOD_SET] = getFuncSelector(HELLO_WORLD_METHOD_SET, _hashImpl); +} + +std::shared_ptr HelloWorldPrecompiled::call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) +{ + PRECOMPILED_LOG(TRACE) << LOG_BADGE("HelloWorldPrecompiled") << LOG_DESC("call") + << LOG_KV("param", toHexString(_callParameters->input())); + + // parse function name + uint32_t func = getParamFunc(_callParameters->input()); + bytesConstRef data = _callParameters->params(); + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + auto gasPricer = m_precompiledGasFactory->createPrecompiledGas(); + gasPricer->setMemUsed(_callParameters->input().size()); + + auto table = _executive->storage().openTable(precompiled::getTableName(HELLO_WORLD_TABLE_NAME)); + gasPricer->appendOperation(InterfaceOpcode::OpenTable); + if (!table) + { + // table is not exist, create it. + table = _executive->storage().createTable( + precompiled::getTableName(HELLO_WORLD_TABLE_NAME), HELLO_WORLD_VALUE_FIELD); + gasPricer->appendOperation(InterfaceOpcode::CreateTable); + if (!table) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("HelloWorldPrecompiled") << LOG_DESC("set") + << LOG_DESC("open table failed."); + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_NO_AUTHORIZED, codec); + return _callParameters; + } + } + if (func == name2Selector[HELLO_WORLD_METHOD_GET]) + { // get() function call + // default retMsg + std::string retValue = "Hello World!"; + + auto entry = table->getRow(HELLO_WORLD_KEY_FIELD_NAME); + if (!entry) + { + gasPricer->updateMemUsed(entry->size()); + gasPricer->appendOperation(InterfaceOpcode::Select, 1); + + retValue = entry->getField(0); + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("HelloWorldPrecompiled") << LOG_DESC("get") + << LOG_KV("value", retValue); + } + _callParameters->setExecResult(codec.encode(retValue)); + } + else if (func == name2Selector[HELLO_WORLD_METHOD_SET]) + { // set(string) function call + std::string strValue; + codec.decode(data, strValue); + auto entry = table->getRow(HELLO_WORLD_KEY_FIELD_NAME); + gasPricer->updateMemUsed(entry->size()); + gasPricer->appendOperation(InterfaceOpcode::Select, 1); + entry->setField(0, strValue); + + table->setRow(HELLO_WORLD_KEY_FIELD_NAME, *entry); + gasPricer->appendOperation(InterfaceOpcode::Update, 1); + gasPricer->updateMemUsed(entry->size()); + getErrorCodeOut(_callParameters->mutableExecResult(), 1, codec); + } + else + { // unknown function call + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("HelloWorldPrecompiled") + << LOG_DESC(" unknown function ") << LOG_KV("func", func); + _callParameters->setExecResult(codec.encode(u256((int)CODE_UNKNOW_FUNCTION_CALL))); + } + gasPricer->updateMemUsed(_callParameters->m_execResult.size()); + _callParameters->setGasLeft(_callParameters->m_gasLeft - gasPricer->calTotalGas()); + return _callParameters; +} diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/HelloWorldPrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/HelloWorldPrecompiled.h" new file mode 100644 index 00000000..62c290f5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/HelloWorldPrecompiled.h" @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2021 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 HelloWorldPrecompiled.h + * @author: kyonRay + * @date 2021-05-30 + */ + +#pragma once + +#include "../../vm/Precompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" + +namespace bcos +{ +namespace precompiled +{ +class HelloWorldPrecompiled : public bcos::precompiled::Precompiled +{ +public: + using Ptr = std::shared_ptr; + HelloWorldPrecompiled(crypto::Hash::Ptr _hashImpl); + virtual ~HelloWorldPrecompiled(){}; + + std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) override; +}; +} // namespace precompiled +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/PermissionPrecompiledInterface.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/PermissionPrecompiledInterface.h" new file mode 100644 index 00000000..9043e756 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/PermissionPrecompiledInterface.h" @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2021 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 PermissionPrecompiledInterface.h + * @author: kyonRay + * @date 2021-09-08 + */ + +#pragma once +#include "../../vm/Precompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" + +namespace bcos::precompiled +{ +const char* const PER_METHOD_LOGIN = "login(string,string[])"; +const char* const PER_METHOD_LOGOUT = "logout(string,string[])"; +const char* const PER_METHOD_CREATE = "create(string,string,string,bytes)"; +const char* const PER_METHOD_CALL = "call(string,string,string,bytes)"; +const char* const PER_METHOD_SEND = "sendTransaction(string,string,string,bytes)"; +struct PermissionRet +{ + using Ptr = std::shared_ptr; + s256 code; + std::string message; + std::string path; + PermissionRet(s256&& _code, std::string&& _msg) : code(_code), message(_msg) {} + PermissionRet(s256&& _code, std::string&& _msg, std::string&& _path) + : code(_code), message(_msg), path(_path) + {} +}; +class PermissionPrecompiledInterface : public bcos::precompiled::Precompiled +{ +public: + using Ptr = std::shared_ptr; + PermissionPrecompiledInterface(crypto::Hash::Ptr _hashImpl) : Precompiled(_hashImpl){}; + virtual ~PermissionPrecompiledInterface(){}; + + std::shared_ptr call( + std::shared_ptr, PrecompiledExecResult::Ptr) override + { + return nullptr; + } + + virtual PermissionRet::Ptr login( + const std::string& nonce, const std::vector& params) = 0; + + virtual PermissionRet::Ptr logout( + const std::string& path, const std::vector& params) = 0; + + virtual PermissionRet::Ptr create(const std::string& userPath, const std::string& origin, + const std::string& to, bytesConstRef params) = 0; + + virtual PermissionRet::Ptr call(const std::string& userPath, const std::string& origin, + const std::string& to, bytesConstRef params) = 0; + + virtual PermissionRet::Ptr sendTransaction(const std::string& userPath, + const std::string& origin, const std::string& to, bytesConstRef params) = 0; +}; +} // namespace bcos::precompiled diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/RingSigPrecompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/RingSigPrecompiled.cpp" new file mode 100644 index 00000000..e4d15144 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/RingSigPrecompiled.cpp" @@ -0,0 +1,99 @@ +/** + * Copyright (C) 2021 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 RingSigPrecompiled.cpp + * @author: yklu + * @date 2022-04-12 + */ +#include "RingSigPrecompiled.h" +#include "../../executive/BlockContext.h" +#include "bcos-executor/src/precompiled/common/PrecompiledResult.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include + +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::precompiled; + +/* +contract RingSig +{ + function ringSigVerify(string signature, string message, string paramInfo) public constant +returns(int, bool); +} +*/ + +const char* const RING_SIG_METHOD_SET_STR = "ringSigVerify(string,string,string)"; + +RingSigPrecompiled::RingSigPrecompiled(crypto::Hash::Ptr _hashImpl) : Precompiled(_hashImpl) +{ + name2Selector[RING_SIG_METHOD_SET_STR] = getFuncSelector(RING_SIG_METHOD_SET_STR, _hashImpl); +} + +std::shared_ptr RingSigPrecompiled::call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) +{ + // parse function name + uint32_t func = getParamFunc(_callParameters->input()); + bytesConstRef data = _callParameters->params(); + auto blockContext = _executive->blockContext().lock(); + auto codec = + std::make_shared(blockContext->hashHandler(), blockContext->isWasm()); + auto gasPricer = m_precompiledGasFactory->createPrecompiledGas(); + + if (func == name2Selector[RING_SIG_METHOD_SET_STR]) + { + // ringSigVerify(string,string,string) + std::string signature, message, paramInfo; + codec->decode(data, signature, message, paramInfo); + bool result = false; + try + { + result = RingSigApi::LinkableRingSig::ring_verify(signature, message, paramInfo); + gasPricer->appendOperation(InterfaceOpcode::GroupSigVerify); + } + catch (std::exception& error) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("RingSigPrecompiled") << LOG_DESC(error.what()) + << LOG_KV("signature", signature) << LOG_KV("message", message) + << LOG_KV("paramInfo", paramInfo); + result = false; + } + catch (std::string& error) + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("RingSigPrecompiled") << LOG_DESC(error) + << LOG_KV("signature", signature) << LOG_KV("message", message) + << LOG_KV("paramInfo", paramInfo); + result = false; + } + int32_t retCode = CODE_SUCCESS; + if (!result) + { + retCode = VERIFY_RING_SIG_FAILED; + } + _callParameters->setExecResult(codec->encode(retCode, result)); + } + else + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("RingSigPrecompiled") + << LOG_DESC("call undefined function") << LOG_KV("func", func); + _callParameters->setExecResult(codec->encode((int32_t)CODE_UNKNOW_FUNCTION_CALL, false)); + } + gasPricer->updateMemUsed(_callParameters->m_execResult.size()); + _callParameters->setGasLeft(_callParameters->m_gasLeft - gasPricer->calTotalGas()); + return _callParameters; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/RingSigPrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/RingSigPrecompiled.h" new file mode 100644 index 00000000..0c669027 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/RingSigPrecompiled.h" @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2021 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 RingSigPrecompiled.h + * @author: yklu + * @date 2022-04-12 + */ +#pragma once + +#include "../../vm/Precompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" + +namespace bcos +{ +namespace precompiled +{ +class RingSigPrecompiled : public bcos::precompiled::Precompiled +{ +public: + using Ptr = std::shared_ptr; + RingSigPrecompiled(crypto::Hash::Ptr _hashImpl); + virtual ~RingSigPrecompiled(){}; + + std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) override; +}; +} // namespace precompiled +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/SmallBankPrecompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/SmallBankPrecompiled.cpp" new file mode 100644 index 00000000..8d2fd2a4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/SmallBankPrecompiled.cpp" @@ -0,0 +1,300 @@ +/** + * copyright (C) 2021 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 CpuHeavyPrecompiled.cpp + * @author: wenlinli + * @date 2021-03-17 + */ + +#include "SmallBankPrecompiled.h" +#include "../../executive/BlockContext.h" +#include "../TableManagerPrecompiled.h" +#include "DagTransferPrecompiled.h" +#include "bcos-executor/src/precompiled/common/PrecompiledResult.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include +#include + + +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::precompiled; +using namespace bcos::ledger; + + +const char* const SMALL_BANK_METHOD_ADD_STR_UINT = "updateBalance(string,uint256)"; +const char* const SMALL_BANK_METHOD_TRS_STR2_UINT = "sendPayment(string,string,uint256)"; +const size_t SMALLBANK_TRANSFER_FIELD_BALANCE = 0; + +SmallBankPrecompiled::SmallBankPrecompiled(crypto::Hash::Ptr _hashImpl, std::string _tableName) + : Precompiled(_hashImpl), m_tableName(_tableName) +{ + name2Selector[SMALL_BANK_METHOD_ADD_STR_UINT] = + getFuncSelector(SMALL_BANK_METHOD_ADD_STR_UINT, _hashImpl); + name2Selector[SMALL_BANK_METHOD_TRS_STR2_UINT] = + getFuncSelector(SMALL_BANK_METHOD_TRS_STR2_UINT, _hashImpl); +} + + +std::vector SmallBankPrecompiled::getParallelTag(bytesConstRef _param, bool _isWasm) +{ + // parse function name + uint32_t func = getParamFunc(_param); + bytesConstRef data = getParamData(_param); + std::vector results; + auto codec = CodecWrapper(m_hashImpl, _isWasm); + + // user_name user_balance 2 fields in table, the key of table is user_name field + if (func == name2Selector[SMALL_BANK_METHOD_ADD_STR_UINT]) + { // updateBalance(string,uint256) + std::string user; + u256 amount; + codec.decode(data, user, amount); + // if params is invalid , parallel process can be done + if (!user.empty()) + { + results.push_back(user); + } + } + + else if (func == name2Selector[SMALL_BANK_METHOD_TRS_STR2_UINT]) + { + // sendPayment(string,string,uint256) + std::string fromUser, toUser; + u256 amount; + codec.decode(data, fromUser, toUser, amount); + // if params is invalid , parallel process can be done + if (!fromUser.empty() && !toUser.empty()) + { + results.push_back(fromUser); + results.push_back(toUser); + } + } + // std::cout << "SmallBank getParallelTag done." << std::endl; + return results; +} + +std::shared_ptr SmallBankPrecompiled::call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) +{ + PRECOMPILED_LOG(TRACE) << LOG_BADGE("SmallBankPrecompiled") << LOG_DESC("call") + << LOG_KV("param", toHexString(_callParameters->input())); + // parse function name + uint32_t func = getParamFunc(_callParameters->input()); + bytesConstRef data = _callParameters->params(); + auto gasPricer = m_precompiledGasFactory->createPrecompiledGas(); + + auto table = _executive->storage().openTable(m_tableName); + gasPricer->appendOperation(InterfaceOpcode::OpenTable); + if (!table) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("SmallBankPrecompiled") << LOG_DESC("call") + << LOG_DESC("open table failed.") + << LOG_KV("tableName", m_tableName); + auto blockContext = _executive->blockContext().lock(); + getErrorCodeOut(_callParameters->mutableExecResult(), CODE_TABLE_OPEN_ERROR, + CodecWrapper(blockContext->hashHandler(), blockContext->isWasm())); + return _callParameters; + } + + // user_name user_balance 2 fields in table, the key of table is user_name field + if (func == name2Selector[SMALL_BANK_METHOD_ADD_STR_UINT]) + { // updateBalance(string,uint256) + updateBalanceCall( + _executive, data, _callParameters->m_origin, _callParameters->mutableExecResult()); + } + else if (func == name2Selector[SMALL_BANK_METHOD_TRS_STR2_UINT]) + { // sendPayment(string,string,uint256) + sendPaymentCall( + _executive, data, _callParameters->m_origin, _callParameters->mutableExecResult()); + } + else + { + PRECOMPILED_LOG(INFO) << LOG_BADGE("SmallBankPrecompiled") << LOG_DESC("error func") + << LOG_KV("func", func); + } + + _callParameters->setGasLeft(_callParameters->m_gasLeft - gasPricer->calTotalGas()); + _callParameters->setExecResult(bytes()); + // std::cout << "SmallBank Precompiled call done." << std::endl; + return _callParameters; +} + +void SmallBankPrecompiled::updateBalanceCall( + std::shared_ptr _executive, bytesConstRef _data, + std::string const&, bytes& _out) +{ + // userAdd(string,uint256) + std::string user; + u256 amount; + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + codec.decode(_data, user, amount); + + int ret; + std::string strErrorMsg; + do + { + if (user.empty()) + { + strErrorMsg = "invalid user name"; + ret = CODE_INVALID_USER_NAME; + break; + } + auto table = _executive->storage().openTable(m_tableName); + // std::cout << "SmallBank ---------- updateBalancecall tableName is " << m_tableName << + // std::endl; + if (!table) + { + strErrorMsg = "openTable failed."; + ret = CODE_INVALID_OPENTABLE_FAILED; + break; + } + auto entry = table->getRow(user); + if (entry) + { + strErrorMsg = "user already exist"; + ret = CODE_INVALID_USER_ALREADY_EXIST; + break; + } + + // user not exist, insert user into it. + auto newEntry = table->newEntry(); + newEntry.setField(SMALLBANK_TRANSFER_FIELD_BALANCE, amount.str()); + // std::cout << "SmallBank ---------- user message has insert tableName: " << m_tableName + // << ", userName is" << user << ", balance is " << amount.str() << std::endl; + table->setRow(user, newEntry); + ret = 0; + } while (false); + if (!strErrorMsg.empty()) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("SmallBankPrecompiled") << LOG_DESC(strErrorMsg) + << LOG_KV("code", ret); + } + _out = codec.encode(u256(ret)); +} + +void SmallBankPrecompiled::sendPaymentCall( + std::shared_ptr _executive, bytesConstRef _data, + std::string const&, bytes& _out) +{ + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + std::string fromUser, toUser; + u256 amount; + codec.decode(_data, fromUser, toUser, amount); + + u256 fromUserBalance, newFromUserBalance; + u256 toUserBalance, newToUserBalance; + + std::string strErrorMsg; + int ret; + do + { + // parameters check + if (fromUser.empty() || toUser.empty()) + { + strErrorMsg = "invalid user name"; + ret = CODE_INVALID_USER_NAME; + break; + } + if (amount == 0) + { + strErrorMsg = "invalid amount"; + ret = CODE_INVALID_AMOUNT; + break; + } + // transfer self, do nothing + if (fromUser == toUser) + { + ret = 0; + break; + } + auto table = _executive->storage().openTable(m_tableName); + // std::cout << "SmallBank ---------- sendPaymentCall tableName is " << m_tableName << + // std::endl; + if (!table) + { + strErrorMsg = "openTable failed."; + ret = CODE_INVALID_OPENTABLE_FAILED; + break; + } + + auto entry = table->getRow(fromUser); + if (!entry) + { + strErrorMsg = "from user not exist"; + ret = CODE_INVALID_USER_NOT_EXIST; + break; + } + + fromUserBalance = u256(entry->getField(SMALLBANK_TRANSFER_FIELD_BALANCE)); + if (fromUserBalance < amount) + { + strErrorMsg = "from user insufficient balance"; + ret = CODE_INVALID_INSUFFICIENT_BALANCE; + break; + } + + entry = table->getRow(toUser); + if (!entry) + { + // If to user not exist, add it first. + auto newEntry = table->newEntry(); + newEntry.setField(SMALLBANK_TRANSFER_FIELD_BALANCE, u256(0).str()); + table->setRow(toUser, newEntry); + toUserBalance = 0; + } + else + { + toUserBalance = u256(entry->getField(SMALLBANK_TRANSFER_FIELD_BALANCE)); + } + + // overflow check + if (toUserBalance + amount < toUserBalance) + { + strErrorMsg = "to user balance overflow."; + ret = CODE_INVALID_BALANCE_OVERFLOW; + break; + } + + newFromUserBalance = fromUserBalance - amount; + newToUserBalance = toUserBalance + amount; + + // std::cout << "SmallBank ---------- user transfer message has insert tableName: " << + // m_tableName << ", fromUser is" << fromUser << ",fromUserBalance is" << fromUserBalance + // <<", newFromUserBalance is "<< newFromUserBalance << ", toUser is" << toUser << ", + // toUserBalance is" << toUserBalance << ", newToUserBalance is "<< newToUserBalance << + // std::endl; update fromUser balance info. + entry = table->newEntry(); + entry->setField(SMALLBANK_TRANSFER_FIELD_BALANCE, newFromUserBalance.str()); + table->setRow(fromUser, *entry); + + // update toUser balance info. + entry = table->newEntry(); + entry->setField(SMALLBANK_TRANSFER_FIELD_BALANCE, newToUserBalance.str()); + table->setRow(toUser, *entry); + // end with success + ret = 0; + } while (false); + if (!strErrorMsg.empty()) + { + PRECOMPILED_LOG(DEBUG) << LOG_BADGE("SmallBankPrecompiled") << LOG_DESC(strErrorMsg) + << LOG_KV("code", ret); + } + _out = codec.encode(u256(ret)); +} diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/SmallBankPrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/SmallBankPrecompiled.h" new file mode 100644 index 00000000..f7804933 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/SmallBankPrecompiled.h" @@ -0,0 +1,96 @@ +/** + * copyright (C) 2021 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 SmallBankPrecompiled.h + * @author: wenlinli + * @date 2022-03-17 + */ + +#pragma once + +#include "../../vm/Precompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include +#include + +namespace bcos +{ +namespace precompiled +{ +class SmallBankPrecompiled : public bcos::precompiled::Precompiled +{ +public: + using Ptr = std::shared_ptr; + + SmallBankPrecompiled(crypto::Hash::Ptr _hashImpl, std::string _tableName); + + virtual ~SmallBankPrecompiled(){}; + + std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) override; + +public: + // is this precompiled need parallel processing, default false. + bool isParallelPrecompiled() override { return true; } + std::vector getParallelTag(bytesConstRef param, bool _isWasm) override; + + static inline std::string getAddress(unsigned int id) + { + u160 address = u160(SMALLBANK_START_ADDRESS); + address += id; + h160 addressBytes = h160(address); + return addressBytes.hex(); + } + + static void registerPrecompiled(std::shared_ptr storageInterface, + std::shared_ptr>> + registeredMap, + crypto::Hash::Ptr _hashImpl) + { + for (int id = 0; id < SMALLBANK_CONTRACT_NUM; id++) + { + std::string _tableName = std::to_string(id); + std::string path = std::string{bcos::ledger::SMALLBANK_TRANSFER}; + _tableName = path + _tableName; + // create table first + std::promise createPromise; + storageInterface->asyncCreateTable(_tableName, SYS_VALUE_FIELDS, + [&createPromise](Error::UniquePtr _e, std::optional) { + createPromise.set_value(std::move(_e)); + }); + Error::UniquePtr _e = createPromise.get_future().get(); + std::string&& address = getAddress(id); + registeredMap->insert({std::move(address), + std::make_shared(_hashImpl, _tableName)}); + } + BCOS_LOG(TRACE) << LOG_BADGE("SmallBank") << "Register SmallBankPrecompiled complete" + << LOG_KV("addressFrom", getAddress(0)) + << LOG_KV("addressTo", getAddress(SMALLBANK_CONTRACT_NUM - 1)) + << LOG_KV("tableNameBase", bcos::ledger::SMALLBANK_TRANSFER); + } + +public: + void updateBalanceCall(std::shared_ptr _executive, + bytesConstRef _data, std::string const& _origin, bytes& _out); + void sendPaymentCall(std::shared_ptr _executive, + bytesConstRef _data, std::string const& _origin, bytes& _out); + +private: + std::string m_tableName; +}; +} // namespace precompiled +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/UserPrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/UserPrecompiled.h" new file mode 100644 index 00000000..7d859309 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/UserPrecompiled.h" @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2021 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 UserPrecompiled.h + * @author: kyonRay + * @date 2021-05-30 + */ +#pragma once +#include "CpuHeavyPrecompiled.h" +#include "DagTransferPrecompiled.h" +#include "HelloWorldPrecompiled.h" +#include "PermissionPrecompiledInterface.h" +#include "SmallBankPrecompiled.h" + +namespace bcos::precompiled +{ +const char DEFAULT_PERMISSION_ADDRESS[] = "0000000000000000000000000000000000001005"; +// FIXME: 1005 is default, use static configurable address for more powerful permission +} // namespace bcos::precompiled diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/ZkpPrecompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/ZkpPrecompiled.cpp" new file mode 100644 index 00000000..a51ab767 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/ZkpPrecompiled.cpp" @@ -0,0 +1,303 @@ +/** + * Copyright (C) 2021 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 ZkpPrecompiled.cpp + * @author: yujiechen + * @date 2022-07-18 + */ +#include "ZkpPrecompiled.h" +#include "bcos-codec/wrapper/CodecWrapper.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include + +using namespace bcos; +using namespace bcos::precompiled; +// wedpr_verify_either_equality_relationship_proof(c1_point, c2_point, c3_point, proof, c_basepoint, +// blinding_basepoint) +const char* const VERIFY_EITHER_EQUALITY_PROOF_STR = + "verifyEitherEqualityProof(bytes,bytes,bytes,bytes,bytes,bytes)"; +// wedpr_verify_knowledge_proof(c_point, proof, c_basepoint, blinding_basepoint) +const char* const VERIFY_KNOWLEDGE_PROOF_STR = "verifyKnowledgeProof(bytes,bytes,bytes,bytes)"; +// wedpr_verify_format_proof(c1_point, c2_point, proof, c1_basepoint, c2_basepoint, +// blinding_basepoint) +const char* const VERIFY_FORMAT_PROOF = "verifyFormatProof(bytes,bytes,bytes,bytes,bytes,bytes)"; +// wedpr_verify_sum_relationship(c1_point, c2_point, c3_point, proof, value_basepoint, +// blinding_basepoint) +const char* const VERIFY_SUM_PROOF = "verifySumProof(bytes,bytes,bytes,bytes,bytes,bytes)"; +// wedpr_verify_product_relationship(c1_point, c2_point, c3_point, proof, value_basepoint, +// blinding_basepoint) +const char* const VERIFY_PRODUCT_PROOF = + "verifyProductProof(bytes, bytes, bytes, bytes, bytes, bytes)"; +// wedpr_verify_equality_relationship_proof(c1_point, c2_point, proof, basepoint1, basepoint2) +const char* const VERIFY_EQUALITY_PROOF = "verifyEqualityProof(bytes,bytes,bytes,bytes,bytes)"; +// wedpr_aggregate_ristretto_point(*point_sum, *point_share,result); +const char* const AGGREGATE_POINT = "aggregatePoint(bytes,bytes)"; + +ZkpPrecompiled::ZkpPrecompiled(bcos::crypto::Hash::Ptr _hashImpl) : Precompiled(_hashImpl) +{ + m_zkpImpl = std::make_shared(); + + name2Selector[VERIFY_EITHER_EQUALITY_PROOF_STR] = + getFuncSelector(VERIFY_EITHER_EQUALITY_PROOF_STR, _hashImpl); + name2Selector[VERIFY_KNOWLEDGE_PROOF_STR] = + getFuncSelector(VERIFY_KNOWLEDGE_PROOF_STR, _hashImpl); + name2Selector[VERIFY_FORMAT_PROOF] = getFuncSelector(VERIFY_FORMAT_PROOF, _hashImpl); + name2Selector[VERIFY_SUM_PROOF] = getFuncSelector(VERIFY_SUM_PROOF, _hashImpl); + name2Selector[VERIFY_PRODUCT_PROOF] = getFuncSelector(VERIFY_PRODUCT_PROOF, _hashImpl); + name2Selector[VERIFY_EQUALITY_PROOF] = getFuncSelector(VERIFY_EQUALITY_PROOF, _hashImpl); + name2Selector[AGGREGATE_POINT] = getFuncSelector(AGGREGATE_POINT, _hashImpl); +} +std::shared_ptr ZkpPrecompiled::call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) +{ + auto funcSelector = getParamFunc(_callParameters->input()); + auto paramData = _callParameters->params(); + auto blockContext = _executive->blockContext().lock(); + auto codec = CodecWrapper(blockContext->hashHandler(), blockContext->isWasm()); + auto gasPricer = m_precompiledGasFactory->createPrecompiledGas(); + gasPricer->setMemUsed(paramData.size()); + + if (name2Selector[VERIFY_EITHER_EQUALITY_PROOF_STR] == funcSelector) + { + verifyEitherEqualityProof(codec, paramData, _callParameters); + } + else if (name2Selector[VERIFY_KNOWLEDGE_PROOF_STR] == funcSelector) + { + // verifyKnowledgeProof + verifyKnowledgeProof(codec, paramData, _callParameters); + } + else if (name2Selector[VERIFY_FORMAT_PROOF] == funcSelector) + { + // verifyFormatProof + verifyFormatProof(codec, paramData, _callParameters); + } + else if (name2Selector[VERIFY_SUM_PROOF] == funcSelector) + { + // verifySumProof + verifySumProof(codec, paramData, _callParameters); + } + else if (name2Selector[VERIFY_PRODUCT_PROOF] == funcSelector) + { + // verifyProductProof + verifyProductProof(codec, paramData, _callParameters); + } + else if (name2Selector[VERIFY_EQUALITY_PROOF] == funcSelector) + { + // verifyEqualityProof + verifyEqualityProof(codec, paramData, _callParameters); + } + else if (name2Selector[AGGREGATE_POINT] == funcSelector) + { + // aggregateRistrettoPoint + aggregateRistrettoPoint(codec, paramData, _callParameters); + } + else + { + // no defined function + PRECOMPILED_LOG(INFO) << LOG_DESC("ZkpPrecompiled: undefined method") + << LOG_KV("funcSelector", std::to_string(funcSelector)); + BOOST_THROW_EXCEPTION( + bcos::protocol::PrecompiledError("ZkpPrecompiled call undefined function!")); + } + gasPricer->updateMemUsed(_callParameters->m_execResult.size()); + _callParameters->setGasLeft(_callParameters->m_gasLeft - gasPricer->calTotalGas()); + return _callParameters; +} + +// verifyEitherEqualityProof +void ZkpPrecompiled::verifyEitherEqualityProof( + CodecWrapper const& _codec, bytesConstRef _paramData, PrecompiledExecResult::Ptr _callResult) +{ + bool verifyResult = false; + try + { + bytes c1Point; + bytes c2Point; + bytes c3Point; + bytes equalityProof; + bytes basePoint; + bytes blindingBasePoint; + _codec.decode( + _paramData, c1Point, c2Point, c3Point, equalityProof, basePoint, blindingBasePoint); + verifyResult = m_zkpImpl->verifyEitherEqualityProof( + c1Point, c2Point, c3Point, equalityProof, basePoint, blindingBasePoint); + PRECOMPILED_LOG(TRACE) << LOG_DESC("verifyEitherEqualityProof") + << LOG_KV("c1", toHex(c1Point)) << LOG_KV("c2", toHex(c2Point)) + << LOG_KV("c3", toHex(c3Point)) + << LOG_KV("proof", toHex(equalityProof)) + << LOG_KV("basePoint", toHex(basePoint)) + << LOG_KV("blindingBasePoint", toHex(blindingBasePoint)); + } + catch (std::exception const& e) + { + PRECOMPILED_LOG(DEBUG) << LOG_DESC("verifyEitherEqualityProof exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + _callResult->setExecResult(_codec.encode(verifyResult)); + PRECOMPILED_LOG(TRACE) << LOG_DESC("verifyEitherEqualityProof: ") << verifyResult; +} + +void ZkpPrecompiled::verifyKnowledgeProof( + CodecWrapper const& _codec, bytesConstRef _paramData, PrecompiledExecResult::Ptr _callResult) +{ + bool verifyResult = false; + try + { + bytes pointData; + bytes knowledgeProof; + bytes basePoint; + bytes blindingBasePoint; + _codec.decode(_paramData, pointData, knowledgeProof, basePoint, blindingBasePoint); + verifyResult = m_zkpImpl->verifyKnowledgeProof( + pointData, knowledgeProof, basePoint, blindingBasePoint); + } + catch (std::exception const& e) + { + PRECOMPILED_LOG(DEBUG) << LOG_DESC("verifyKnowledgeProof exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + _callResult->setExecResult(_codec.encode(verifyResult)); + PRECOMPILED_LOG(TRACE) << LOG_DESC("verifyKnowledgeProof: ") << verifyResult; +} + +void ZkpPrecompiled::verifyFormatProof( + CodecWrapper const& _codec, bytesConstRef _paramData, PrecompiledExecResult::Ptr _callResult) +{ + bool verifyResult = false; + try + { + bytes c1Point; + bytes c2Point; + bytes formatProof; + bytes c1BasePoint; + bytes c2BasePoint; + bytes blindingBasePoint; + _codec.decode( + _paramData, c1Point, c2Point, formatProof, c1BasePoint, c2BasePoint, blindingBasePoint); + verifyResult = m_zkpImpl->verifyFormatProof( + c1Point, c2Point, formatProof, c1BasePoint, c2BasePoint, blindingBasePoint); + PRECOMPILED_LOG(TRACE) << LOG_DESC("verifyFormatProof") << LOG_KV("c1", toHex(c1Point)) + << LOG_KV("c2", toHex(c2Point)) + << LOG_KV("proof", toHex(formatProof)) + << LOG_KV("c1BasePoint", toHex(c1BasePoint)) + << LOG_KV("c2BasePoint", toHex(c2BasePoint)) + << LOG_KV("blindingBasePoint", toHex(c2BasePoint)); + } + catch (std::exception const& e) + { + PRECOMPILED_LOG(DEBUG) << LOG_DESC("verifyFormatProof exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + _callResult->setExecResult(_codec.encode(verifyResult)); + PRECOMPILED_LOG(TRACE) << LOG_DESC("verifyFormatProof: ") << verifyResult; +} + +void ZkpPrecompiled::verifySumProof( + CodecWrapper const& _codec, bytesConstRef _paramData, PrecompiledExecResult::Ptr _callResult) +{ + bool verifyResult = false; + try + { + bytes c1Point; + bytes c2Point; + bytes c3Point; + bytes arithmeticProof; + bytes valueBasePoint; + bytes blindingBasePoint; + _codec.decode(_paramData, c1Point, c2Point, c3Point, arithmeticProof, valueBasePoint, + blindingBasePoint); + verifyResult = m_zkpImpl->verifySumProof( + c1Point, c2Point, c3Point, arithmeticProof, valueBasePoint, blindingBasePoint); + } + catch (std::exception const& e) + { + PRECOMPILED_LOG(DEBUG) << LOG_DESC("verifySumProof exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + _callResult->setExecResult(_codec.encode(verifyResult)); + PRECOMPILED_LOG(TRACE) << LOG_DESC("verifySumProof: ") << verifyResult; +} + +void ZkpPrecompiled::verifyProductProof( + CodecWrapper const& _codec, bytesConstRef _paramData, PrecompiledExecResult::Ptr _callResult) +{ + bool verifyResult = false; + try + { + bytes c1Point; + bytes c2Point; + bytes c3Point; + bytes arithmeticProof; + bytes valueBasePoint; + bytes blindingBasePoint; + _codec.decode(_paramData, c1Point, c2Point, c3Point, arithmeticProof, valueBasePoint, + blindingBasePoint); + verifyResult = m_zkpImpl->verifyProductProof( + c1Point, c2Point, c3Point, arithmeticProof, valueBasePoint, blindingBasePoint); + } + catch (std::exception const& e) + { + PRECOMPILED_LOG(DEBUG) << LOG_DESC("verifyProductProof exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + _callResult->setExecResult(_codec.encode(verifyResult)); + PRECOMPILED_LOG(TRACE) << LOG_DESC("verifyProductProof: ") << verifyResult; +} + +void ZkpPrecompiled::verifyEqualityProof( + CodecWrapper const& _codec, bytesConstRef _paramData, PrecompiledExecResult::Ptr _callResult) +{ + bool verifyResult = false; + try + { + bytes c1Point; + bytes c2Point; + bytes equalityProof; + bytes basePoint1; + bytes basePoint2; + _codec.decode(_paramData, c1Point, c2Point, equalityProof, basePoint1, basePoint2); + verifyResult = + m_zkpImpl->verifyEqualityProof(c1Point, c2Point, equalityProof, basePoint1, basePoint2); + } + catch (std::exception const& e) + { + PRECOMPILED_LOG(DEBUG) << LOG_DESC("verifyEqualityProof exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + _callResult->setExecResult(_codec.encode(verifyResult)); + PRECOMPILED_LOG(TRACE) << LOG_DESC("verifyEqualityProof: ") << verifyResult; +} + +void ZkpPrecompiled::aggregateRistrettoPoint( + CodecWrapper const& _codec, bytesConstRef _paramData, PrecompiledExecResult::Ptr _callResult) +{ + int retCode = 0; + bytes result; + try + { + bytes sumPoint; + bytes pointShare; + _codec.decode(_paramData, sumPoint, pointShare); + result = m_zkpImpl->aggregateRistrettoPoint(sumPoint, pointShare); + } + catch (std::exception const& e) + { + retCode = -1; + PRECOMPILED_LOG(DEBUG) << LOG_DESC("aggregateRistrettoPoint exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + _callResult->setExecResult(_codec.encode(retCode, result)); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/extension/ZkpPrecompiled.h" "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/ZkpPrecompiled.h" new file mode 100644 index 00000000..6a6b991c --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/extension/ZkpPrecompiled.h" @@ -0,0 +1,82 @@ +/** + * Copyright (C) 2021 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 ZkpPrecompiled.h + * @author: yujiechen + * @date 2022-07-18 + */ + +#pragma once + +#include "../../vm/Precompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include + +namespace bcos +{ +namespace precompiled +{ +#if 0 +contract ZkpPrecompiled +{ + function verifyEitherEqualityProof(bytes memory c1_point, bytes memory c2_point, bytes memory c3_point, bytes memory proof, bytes memory c_basepoint, bytes memory blinding_basepoint) public virtual view returns(bool); + function verifyKnowledgeProof(bytes memory c_point, bytes memory proof, bytes memory c_basepoint, bytes memory blinding_basepoint) public virtual view returns(bool); + function verifyFormatProof(bytes memory c1_point, bytes memory c2_point, bytes memory proof, bytes memory c1_basepoint, bytes memory c2_basepoint, bytes memory blinding_basepoint) public virtual view returns(bool); + function verifySumProof(bytes memory c1_point, bytes memory c2_point, bytes memory c3_point, bytes memory proof, bytes memory value_basepoint, bytes memory blinding_basepoint)public virtual view returns(bool); + function verifyProductProof(bytes memory c1_point, bytes memory c2_point, bytes memory c3_point, bytes memory proof, bytes memory value_basepoint, bytes memory blinding_basepoint) public virtual view returns(bool); + function verifyEqualityProof(bytes memory c1_point, bytes memory c2_point, bytes memory proof, bytes memory basepoint1, bytes memory basepoint2)public virtual view returns(bool); + function aggregatePoint(bytes memory point1, bytes memory point2) public virtual view returns(int, bytes memory); +} +#endif + +class ZkpPrecompiled : public bcos::precompiled::Precompiled +{ +public: + using Ptr = std::shared_ptr; + ZkpPrecompiled(bcos::crypto::Hash::Ptr _hashImpl); + ~ZkpPrecompiled() override{}; + + std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) override; + +private: + void verifyEitherEqualityProof(CodecWrapper const& _codec, bytesConstRef _paramData, + PrecompiledExecResult::Ptr _callResult); + + void verifyKnowledgeProof(CodecWrapper const& _codec, bytesConstRef _paramData, + PrecompiledExecResult::Ptr _callResult); + + void verifyFormatProof(CodecWrapper const& _codec, bytesConstRef _paramData, + PrecompiledExecResult::Ptr _callResult); + + void verifySumProof(CodecWrapper const& _codec, bytesConstRef _paramData, + PrecompiledExecResult::Ptr _callResult); + + void verifyProductProof(CodecWrapper const& _codec, bytesConstRef _paramData, + PrecompiledExecResult::Ptr _callResult); + + + void verifyEqualityProof(CodecWrapper const& _codec, bytesConstRef _paramData, + PrecompiledExecResult::Ptr _callResult); + + void aggregateRistrettoPoint(CodecWrapper const& _codec, bytesConstRef _paramData, + PrecompiledExecResult::Ptr _callResult); + +private: + bcos::crypto::DiscreteLogarithmZkp::Ptr m_zkpImpl; +}; +} // namespace precompiled +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/Account.sol" "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/Account.sol" new file mode 100644 index 00000000..70912750 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/Account.sol" @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.6.10 <0.8.20; +pragma experimental ABIEncoderV2; + +enum AccountStatus{ + normal, + freeze, + abolish +} + +abstract contract AccountManager { + // 设置账户状态,只有治理委员可以调用,0 - normal, others - abnormal, 如果账户不存在会先创建 + function setAccountStatus(address addr, AccountStatus status) public virtual returns (int32); + // 任何用户都可以调用 + function getAccountStatus(address addr) public view virtual returns (AccountStatus); +} + +abstract contract Account { + // 设置账户状态,只有AccountManager可以调用, 0 - normal, others - abnormal + function setAccountStatus(AccountStatus status) public virtual returns (int32); + // 任何用户都可以调用 + function getAccountStatus() public view virtual returns (AccountStatus); +} diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/BfsPrecompiled.sol" "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/BfsPrecompiled.sol" new file mode 100644 index 00000000..a4932e3a --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/BfsPrecompiled.sol" @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.6.0; +pragma experimental ABIEncoderV2; + + struct BfsInfo { + string file_name; + string file_type; + string[] ext; + } + +abstract contract BfsPrecompiled { + // @return return BfsInfo at most 500, if you want more, try list with paging interface + function list(string memory absolutePath) public view returns (int32, BfsInfo[] memory); + // @return int, >=0 -> BfsInfo left, <0 -> errorCode + function list(string memory absolutePath, uint offset, uint limit) public view returns (int, BfsInfo[] memory); + + function mkdir(string memory absolutePath) public returns (int32); + + function link(string memory absolutePath, address _address, string memory _abi) public returns (int); + // for cns compatibility + function link(string memory name, string memory version, address _address, string memory _abi) public returns (int32); + + function readlink(string memory absolutePath) public view returns (address); + + function touch(string memory absolutePath, string memory fileType) public returns (int32); + + function rebuildBfs() public returns (int); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/ConsensusPrecompiled.sol" "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/ConsensusPrecompiled.sol" new file mode 100644 index 00000000..a01b9d93 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/ConsensusPrecompiled.sol" @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.6.0; + +contract ConsensusPrecompiled { + function addSealer(string memory,uint256) public returns (int32){} + function addObserver(string memory) public returns (int32){} + function remove(string memory) public returns (int32){} + function setWeight(string memory,uint256) public returns (int32){} +} diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/ContractAuthPrecompiled.sol" "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/ContractAuthPrecompiled.sol" new file mode 100644 index 00000000..4c76ece7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/ContractAuthPrecompiled.sol" @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.6.0; + +abstract contract ContractAuthPrecompiled { + function getAdmin(address path) public view returns (address); + function resetAdmin(address path, address admin) public returns (int256); + function setMethodAuthType(address path, bytes4 func, uint8 authType) public returns (int256); + function openMethodAuth(address path, bytes4 func, address account) public returns (int256); + function closeMethodAuth(address path, bytes4 func, address account) public returns (int256); + function checkMethodAuth(address path, bytes4 func, address account) public view returns (bool); + function getMethodAuth(address path, bytes4 func) public view returns (uint8,string[] memory,string[] memory); + function setContractStatus(address _address, bool isFreeze) public returns(int); + function contractAvailable(address _address) public view returns (bool); + + function deployType() public view returns (uint256); + function setDeployAuthType(uint8 _type) public returns (int256); + function openDeployAuth(address account) public returns (int256); + function closeDeployAuth(address account) public returns (int256); + function hasDeployAuth(address account) public view returns (bool); +} diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/Crypto.sol" "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/Crypto.sol" new file mode 100644 index 00000000..5de8c7bd --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/Crypto.sol" @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.6.10 <0.8.20; +pragma experimental ABIEncoderV2; + +abstract contract Crypto +{ + function sm3(bytes memory data) public view returns(bytes32){} + function keccak256Hash(bytes memory data) public view returns(bytes32){} + function sm2Verify(bytes32 message, bytes memory publicKey, bytes32 r, bytes32 s) public view returns(bool, address){} + function curve25519VRFVerify(bytes memory message, bytes memory publicKey, bytes memory proof) public view returns(bool, uint256){} +} diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/GroupSigPrecompiled.sol" "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/GroupSigPrecompiled.sol" new file mode 100644 index 00000000..a80e8992 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/GroupSigPrecompiled.sol" @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.6.10 <0.8.20; + +abstract contract GroupSigPrecompiled { + function groupSigVerify(string memory, string memory, string memory, string memory) public view returns (int32,bool); +} diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/RingSigPrecompiled.sol" "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/RingSigPrecompiled.sol" new file mode 100644 index 00000000..a3c1ea9e --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/RingSigPrecompiled.sol" @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.6.10 <0.8.20; + +abstract contract RingSigPrecompiled { + function ringSigVerify(string memory, string memory, string memory) public view returns (int32,bool); +} diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/SystemConfigPrecompiled.sol" "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/SystemConfigPrecompiled.sol" new file mode 100644 index 00000000..9168fc74 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/SystemConfigPrecompiled.sol" @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.6.0; + +contract SystemConfigPrecompiled +{ + function setValueByKey(string memory key, string memory value) public returns(int32){} + function getValueByKey(string memory key) public view returns(string memory,int256){} +} diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/Table.sol" "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/Table.sol" new file mode 100644 index 00000000..be6c349c --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/Table.sol" @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.6.10 <0.8.20; +pragma experimental ABIEncoderV2; + +// KeyOrder指定Key的排序规则,字典序和数字序,如果指定为数字序,key只能为数字 +// enum KeyOrder {Lexicographic, Numerical} +struct TableInfo { + string keyColumn; + string[] valueColumns; +} + +// 记录,用于select和insert +struct Entry { + string key; + string[] fields; // 考虑2.0的Entry接口,临时Precompiled的问题,考虑加工具类接口 +} + +// 更新字段,用于update +struct UpdateField { + string columnName; + // 考虑工具类 + string value; +} + +// 筛选条件,大于、大于等于、小于、小于等于 +enum ConditionOP {GT, GE, LT, LE} +struct Condition { + ConditionOP op; + // string field; + string value; +} + +// 数量限制 +struct Limit { + uint32 offset; + // count limit max is 500 + uint32 count; +} + +// 表管理合约,是静态Precompiled,有固定的合约地址 +abstract contract TableManager { + // 创建表,传入TableInfo + function createTable(string memory path, TableInfo memory tableInfo) public virtual returns (int32); + + // 创建KV表,传入key和value字段名 + function createKVTable(string memory tableName, string memory keyField, string memory valueField) public virtual returns (int32); + + // 只提供给Solidity合约调用时使用 + function openTable(string memory path) public view virtual returns (address); + + // 变更表字段 + // 只能新增字段,不能删除字段,新增的字段默认值为空,不能与原有字段重复 + function appendColumns(string memory path, string[] memory newColumns) public virtual returns (int32); + + // 获取表信息 + function desc(string memory tableName) public view virtual returns (TableInfo memory); +} + +// 表合约,是动态Precompiled,TableManager创建时指定地址 +abstract contract Table { + // 按key查询entry + function select(string memory key) public virtual view returns (Entry memory); + + // 按条件批量查询entry,condition为空则查询所有记录 + function select(Condition[] memory conditions, Limit memory limit) public virtual view returns (Entry[] memory); + + // 按照条件查询count数据 + function count(Condition[] memory conditions) public virtual view returns (uint32); + + // 插入数据 + function insert(Entry memory entry) public virtual returns (int32); + + // 按key更新entry + function update(string memory key, UpdateField[] memory updateFields) public virtual returns (int32); + + // 按条件批量更新entry,condition为空则更新所有记录 + function update(Condition[] memory conditions, Limit memory limit, UpdateField[] memory updateFields) public virtual returns (int32); + + // 按key删除entry + function remove(string memory key) public virtual returns (int32); + // 按条件批量删除entry,condition为空则删除所有记录 + function remove(Condition[] memory conditions, Limit memory limit) public virtual returns (int32); +} + +abstract contract KVTable { + function get(string memory key) public view virtual returns (bool, string memory); + + function set(string memory key, string memory value) public virtual returns (int32); +} diff --git "a/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/ZkpPrecompiled.sol" "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/ZkpPrecompiled.sol" new file mode 100644 index 00000000..5d3c8e7f --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/precompiled/solidity/ZkpPrecompiled.sol" @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.6.10 <0.8.20; +pragma experimental ABIEncoderV2; + +abstract contract ZkpPrecompiled +{ + function verifyEitherEqualityProof(bytes memory c1_point, bytes memory c2_point, bytes memory c3_point, bytes memory proof, bytes memory c_basepoint, bytes memory blinding_basepoint) public virtual view returns(bool); + function verifyKnowledgeProof(bytes memory c_point, bytes memory proof, bytes memory c_basepoint, bytes memory blinding_basepoint) public virtual view returns(bool); + function verifyFormatProof(bytes memory c1_point, bytes memory c2_point, bytes memory proof, bytes memory c1_basepoint, bytes memory c2_basepoint, bytes memory blinding_basepoint) public virtual view returns(bool); + function verifySumProof(bytes memory c1_point, bytes memory c2_point, bytes memory c3_point, bytes memory proof, bytes memory value_basepoint, bytes memory blinding_basepoint)public virtual view returns(bool); + function verifyProductProof(bytes memory c1_point, bytes memory c2_point, bytes memory c3_point, bytes memory proof, bytes memory value_basepoint, bytes memory blinding_basepoint) public virtual view returns(bool); + function verifyEqualityProof(bytes memory c1_point, bytes memory c2_point, bytes memory proof, bytes memory basepoint1, bytes memory basepoint2)public virtual view returns(bool); + + function aggregatePoint(bytes memory point1, bytes memory point2) public virtual view returns(bool, bytes memory); +} diff --git "a/BFPL\345\243\271/bcos-executor/src/vm/DelegateHostContext.cpp" "b/BFPL\345\243\271/bcos-executor/src/vm/DelegateHostContext.cpp" new file mode 100644 index 00000000..6646537f --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/vm/DelegateHostContext.cpp" @@ -0,0 +1,35 @@ +#include "DelegateHostContext.h" +using namespace bcos; +using namespace bcos::executor; + +DelegateHostContext::DelegateHostContext(CallParameters::UniquePtr callParameters, + std::shared_ptr executive, std::string tableName) + : HostContext(std::move(callParameters), executive, tableName) +{ + if (!getCallParameters()->delegateCall) + { + EXECUTOR_LOG(FATAL) << "Construct a DelegateHostContext using non delegateCall params" + << getCallParameters()->toFullString(); + exit(1); + } + setCode(getCallParameters()->delegateCallCode); + m_delegateCallSender = getCallParameters()->delegateCallSender; +} + +std::optional DelegateHostContext::code() +{ + return m_code; +} + +bool DelegateHostContext::setCode(bytes code) +{ + storage::Entry codeEntry; + codeEntry.importFields({code}); + m_code = codeEntry; + return true; +} + +std::string_view DelegateHostContext::caller() const +{ + return m_delegateCallSender; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/vm/DelegateHostContext.h" "b/BFPL\345\243\271/bcos-executor/src/vm/DelegateHostContext.h" new file mode 100644 index 00000000..a04dd3e0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/vm/DelegateHostContext.h" @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2021 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. + * + * @brief host context for delegateCall + * @file DelegateHostContext.h + * @author: xingqiangbai + * @date: 2022-09-30 + */ +#pragma once +#include "HostContext.h" +#include + +namespace bcos +{ +namespace executor +{ +class DelegateHostContext : public HostContext +{ +public: + DelegateHostContext(CallParameters::UniquePtr callParameters, + std::shared_ptr executive, std::string tableName); + + virtual ~DelegateHostContext() = default; + std::optional code() override; + bool setCode(bytes code) override; + std::string_view caller() const override; + +private: + storage::Entry m_code; + std::string m_delegateCallSender; +}; + +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/vm/EVMHostInterface.cpp" "b/BFPL\345\243\271/bcos-executor/src/vm/EVMHostInterface.cpp" new file mode 100644 index 00000000..8e945706 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/vm/EVMHostInterface.cpp" @@ -0,0 +1,461 @@ +/* + * Copyright (C) 2021 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. + * + * @brief host context + * @file EVMHostInterface.cpp + * @author: xingqiangbai + * @date: 2021-05-24 + * @brief host context + * @file EVMHostInterface.cpp + * @author: ancelmo + * @date: 2021-09-13 + */ + +#include "EVMHostInterface.h" +#include "../Common.h" +#include "HostContext.h" +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace bcos +{ +namespace executor +{ +namespace +{ +static_assert(sizeof(Address) == sizeof(evmc_address), "Address types size mismatch"); +static_assert(alignof(Address) == alignof(evmc_address), "Address types alignment mismatch"); +static_assert(sizeof(h256) == sizeof(evmc_bytes32), "Hash types size mismatch"); +static_assert(alignof(h256) == alignof(evmc_bytes32), "Hash types alignment mismatch"); + +bool accountExists(evmc_host_context* _context, const evmc_address* _addr) noexcept +{ + auto& hostContext = static_cast(*_context); + auto addr = fromEvmC(*_addr); + return hostContext.exists(addr); +} + +evmc_bytes32 getStorage( + evmc_host_context* _context, const evmc_address* _addr, const evmc_bytes32* _key) +{ + boost::ignore_unused(_addr); + auto& hostContext = static_cast(*_context); + + // programming assert for debug + assert(fromEvmC(*_addr) == boost::algorithm::unhex(std::string(hostContext.myAddress()))); + + return toEvmC(hostContext.store(fromEvmC(*_key))); +} + +evmc_storage_status setStorage(evmc_host_context* _context, const evmc_address* _addr, + const evmc_bytes32* _key, const evmc_bytes32* _value) +{ + boost::ignore_unused(_addr); + auto& hostContext = static_cast(*_context); + + assert(fromEvmC(*_addr) == boost::algorithm::unhex(std::string(hostContext.myAddress()))); + + u256 index = fromEvmC(*_key); + u256 value = fromEvmC(*_value); + + auto status = EVMC_STORAGE_MODIFIED; + if (value == 0) + { + status = EVMC_STORAGE_DELETED; + hostContext.sub().refunds += hostContext.vmSchedule().sstoreRefundGas; + } + hostContext.setStore(index, value); // Interface uses native endianness + return status; +} + +evmc_bytes32 getBalance(evmc_host_context* _context, const evmc_address* _addr) noexcept +{ + // auto &hostContext = static_cast(*_context); + // return toEvmC(hostContext.balance(fromEvmC(*_addr))); + + // always return 0 + (void)_context; + (void)_addr; + return toEvmC(h256(0)); +} + +size_t getCodeSize(evmc_host_context* _context, const evmc_address* _addr) +{ + auto& hostContext = static_cast(*_context); + return hostContext.codeSizeAt(fromEvmC(*_addr)); +} + +evmc_bytes32 getCodeHash(evmc_host_context* _context, const evmc_address* _addr) +{ + auto& hostContext = static_cast(*_context); + return toEvmC(hostContext.codeHashAt(fromEvmC(*_addr))); +} + +/** + * @brief : copy code between [_codeOffset, _codeOffset + _bufferSize] to + * bufferData if _codeOffset is larger than code length, then return 0; if + * _codeOffset + _bufferSize is larger than the end of the code, than only copy + * [_codeOffset, _codeEnd] + * @param _context : evm context, including to myAddress, caller, gas, origin, + * value, etc. + * @param _addr: the evmc-address of the code + * @param _codeOffset: the offset begin to copy code + * @param _bufferData : buffer store the copied code + * @param _bufferSize : code size to copy + * @return size_t : return copied code size(in byte) + */ +size_t copyCode(evmc_host_context* _context, const evmc_address*, size_t, uint8_t* _bufferData, + size_t _bufferSize) +{ + auto& hostContext = static_cast(*_context); + + hostContext.setCode(bytes((bcos::byte*)_bufferData, (bcos::byte*)_bufferData + _bufferSize)); + return _bufferSize; + // auto addr = fromEvmC(*_addr); + // auto code = hostContext.codeAt(addr); + + // // Handle "big offset" edge case. + // if (_codeOffset >= code->size()) + // return 0; + + // size_t maxToCopy = code->size() - _codeOffset; + // size_t numToCopy = std::min(maxToCopy, _bufferSize); + // std::copy_n(code->data() + _codeOffset, numToCopy, _bufferData); + // return numToCopy; +} + +void selfdestruct(evmc_host_context* _context, const evmc_address* _addr, + const evmc_address* _beneficiary) noexcept +{ + (void)_addr; + (void)_beneficiary; + auto& hostContext = static_cast(*_context); + + hostContext.suicide(); // FISCO BCOS has no _beneficiary +} + +void log(evmc_host_context* _context, const evmc_address* _addr, uint8_t const* _data, + size_t _dataSize, const evmc_bytes32 _topics[], size_t _numTopics) noexcept +{ + (void)_addr; + auto& hostContext = static_cast(*_context); + assert(fromEvmC(*_addr) == boost::algorithm::unhex(std::string(hostContext.myAddress()))); + h256 const* pTopics = reinterpret_cast(_topics); + hostContext.log(h256s{pTopics, pTopics + _numTopics}, bytesConstRef{_data, _dataSize}); +} + +evmc_access_status access_account(evmc_host_context* _context, const evmc_address* _addr) +{ + std::ignore = _context; + std::ignore = _addr; + return EVMC_ACCESS_COLD; +} + + +evmc_access_status access_storage( + evmc_host_context* _context, const evmc_address* _addr, const evmc_bytes32* _key) +{ + std::ignore = _context; + std::ignore = _addr; + std::ignore = _key; + return EVMC_ACCESS_COLD; +} + +evmc_tx_context getTxContext(evmc_host_context* _context) noexcept +{ + auto& hostContext = static_cast(*_context); + evmc_tx_context result; + if (hostContext.isWasm()) + { + result.tx_origin = toEvmC(hostContext.origin()); + } + else + { + auto origin = fromHex(hostContext.origin()); + result.tx_origin = toEvmC(std::string_view((char*)origin.data(), origin.size())); + } + result.block_number = hostContext.blockNumber(); + result.block_timestamp = hostContext.timestamp(); + result.block_gas_limit = hostContext.blockGasLimit(); + + memset(result.tx_gas_price.bytes, 0, 32); + memset(result.block_coinbase.bytes, 0, 20); + memset(result.block_difficulty.bytes, 0, 32); + memset(result.chain_id.bytes, 0, 32); + return result; +} + +evmc_bytes32 getBlockHash(evmc_host_context* _txContextPtr, int64_t _number) +{ + auto& hostContext = static_cast(*_txContextPtr); + return toEvmC(hostContext.blockHash(_number)); +} + +// evmc_result create(HostContext& _txContext, evmc_message const* _msg) noexcept +// { +// return _txContext.externalRequest(_msg); +// int64_t gas = _msg->gas; +// // u256 value = fromEvmC(_msg->value); +// bytesConstRef init = {_msg->input_data, _msg->input_size}; +// u256 salt = fromEvmC(_msg->create2_salt); +// evmc_opcode opcode = +// _msg->kind == EVMC_CREATE ? evmc_opcode::OP_CREATE : evmc_opcode::OP_CREATE2; + +// // HostContext::create takes the sender address from .myAddress(). +// assert(fromEvmC(_msg->sender) == _txContext.myAddress()); + +// return _txContext.create(gas, init, opcode, salt); +// } + +evmc_result call(evmc_host_context* _context, const evmc_message* _msg) noexcept +{ + // gas maybe smaller than 0 since outside gas is u256 and evmc_message is + // int64_t so gas maybe smaller than 0 in some extreme cases + // * origin code: assert(_msg->gas >= 0) + if (_msg->gas < 0) + { + EXECUTIVE_LOG(INFO) << LOG_DESC("EVM Gas overflow") << LOG_KV("cur gas", _msg->gas); + BOOST_THROW_EXCEPTION(protocol::GasOverflow()); + } + + auto& hostContext = static_cast(*_context); + + return hostContext.externalRequest(_msg); +} + +/// function table +// clang-format off +evmc_host_interface const fnTable = { + accountExists, + getStorage, + setStorage, + getBalance, + getCodeSize, + getCodeHash, + copyCode, + selfdestruct, + call, + getTxContext, + getBlockHash, + log, + access_account, + access_storage, +}; +// clang-format on + +// for wasm + +bool wasmAccountExists( + evmc_host_context* _context, const uint8_t* address, int32_t addressLength) noexcept +{ + auto& hostContext = static_cast(*_context); + return hostContext.exists(string_view((char*)address, addressLength)); +} + +int32_t get(evmc_host_context* _context, const uint8_t* _addr, int32_t _addressLength, + const uint8_t* _key, int32_t _keyLength, uint8_t* _value, int32_t _valueLength) +{ + boost::ignore_unused(_addr, _addressLength); + auto& hostContext = static_cast(*_context); + + // programming assert for debug + assert(string_view((char*)_addr, _addressLength) == hostContext.myAddress()); + auto value = hostContext.get(string((char*)_key, _keyLength)); + if (value.size() > (size_t)_valueLength) + { + return -1; + } + memcpy(_value, value.data(), value.size()); + return value.size(); +} + +evmc_storage_status set(evmc_host_context* _context, const uint8_t* _addr, int32_t _addressLength, + const uint8_t* _key, int32_t _keyLength, const uint8_t* _value, int32_t _valueLength) +{ + boost::ignore_unused(_addr, _addressLength); + auto& hostContext = static_cast(*_context); + + // IF (!HOSTCONTEXT.ISPERMITTED()) + // { // FIXME: RETURN STATUS INSTEAD OF THROW EXCEPTION + // BOOST_THROW_EXCEPTION(PERMISSIONDENIED()); + // } + assert(string_view((char*)_addr, _addressLength) == hostContext.myAddress()); + string key((char*)_key, _keyLength); + string value((char*)_value, _valueLength); + + auto status = EVMC_STORAGE_MODIFIED; + if (value.size() == 0) + { + status = EVMC_STORAGE_DELETED; + hostContext.sub().refunds += hostContext.vmSchedule().sstoreRefundGas; + } + hostContext.set(key, value); // Interface uses native endianness + return status; +} + +size_t wasmGetCodeSize(evmc_host_context* _context, const uint8_t* _addr, int32_t _addressLength) +{ + auto& hostContext = static_cast(*_context); + return hostContext.codeSizeAt(string_view((char*)_addr, _addressLength)); +} + +evmc_bytes32 wasmGetCodeHash( + evmc_host_context* _context, const uint8_t* _addr, int32_t _addressLength) +{ + auto& hostContext = static_cast(*_context); + return toEvmC(hostContext.codeHashAt(string_view((char*)_addr, _addressLength))); +} + +size_t wasmCopyCode(evmc_host_context* _context, const uint8_t*, int32_t, size_t, + uint8_t* _bufferData, size_t _bufferSize) +{ + auto& hostContext = static_cast(*_context); + + hostContext.setCode(bytes((bcos::byte*)_bufferData, (bcos::byte*)_bufferData + _bufferSize)); + return _bufferSize; + + // hostContext.setCode(bcos::bytes(_bufferData, _bufferSize)); + + // auto code = hostContext.codeAt(string_view((char *)_addr, _addressLength)); + + // // Handle "big offset" edge case. + // if (_codeOffset >= code->size()) + // return 0; + + // size_t maxToCopy = code->size() - _codeOffset; + // size_t numToCopy = std::min(maxToCopy, _bufferSize); + // std::copy_n(code->data() + _codeOffset, numToCopy, _bufferData); + // return numToCopy; +} + +void wasmLog(evmc_host_context* _context, const uint8_t* _addr, int32_t _addressLength, + uint8_t const* _data, size_t _dataSize, const evmc_bytes32 _topics[], + size_t _numTopics) noexcept +{ + boost::ignore_unused(_addr, _addressLength); + + auto& hostContext = static_cast(*_context); + assert(string_view((char*)_addr, _addressLength) == hostContext.myAddress()); + h256 const* pTopics = reinterpret_cast(_topics); + hostContext.log(h256s{pTopics, pTopics + _numTopics}, bytesConstRef{_data, _dataSize}); +} + +bool registerAsset(evmc_host_context* _context, const char* _assetName, int32_t _nameLength, + const evmc_address* _issuer, bool _fungible, uint64_t _total, const char* _desc, + int32_t _descLength) +{ + auto& hostContext = static_cast(*_context); + return hostContext.registerAsset(string(_assetName, _nameLength), fromEvmC(*_issuer), _fungible, + _total, string(_desc, _descLength)); +} + +bool issueFungibleAsset(evmc_host_context* _context, const uint8_t* _to, int32_t _toLength, + const char* _assetName, int32_t _nameLength, uint64_t _amount) +{ + auto& hostContext = static_cast(*_context); + return hostContext.issueFungibleAsset( + string_view((char*)_to, _toLength), string(_assetName, _nameLength), _amount); +} + +uint64_t issueNotFungibleAsset(evmc_host_context* _context, const uint8_t* _to, int32_t _toLength, + const char* _assetName, int32_t _nameLength, const char* _uri, int32_t _uriLength) +{ + auto& hostContext = static_cast(*_context); + return hostContext.issueNotFungibleAsset(string_view((char*)_to, _toLength), + string(_assetName, _nameLength), string(_uri, _uriLength)); +} + +bool transferAsset(evmc_host_context* _context, const uint8_t* _to, int32_t _toLength, + const char* _assetName, int32_t _nameLength, uint64_t _amountOrID, bool _fromSelf) +{ + auto& hostContext = static_cast(*_context); + return hostContext.transferAsset(string_view((char*)_to, _toLength), + string(_assetName, _nameLength), _amountOrID, _fromSelf); +} + +uint64_t getAssetBanlance(evmc_host_context* _context, const uint8_t* _addr, int32_t _addressLength, + const char* _assetName, int32_t _nameLength) +{ + auto& hostContext = static_cast(*_context); + return hostContext.getAssetBanlance( + string_view((char*)_addr, _addressLength), string(_assetName, _nameLength)); +} + +int32_t getNotFungibleAssetIDs(evmc_host_context* _context, const uint8_t* _addr, + int32_t _addressLength, const char* _assetName, int32_t _nameLength, char* _value, + int32_t _valueLength) +{ + auto& hostContext = static_cast(*_context); + auto tokenIDs = hostContext.getNotFungibleAssetIDs( + string_view((char*)_addr, _addressLength), string(_assetName, _nameLength)); + if (tokenIDs.size() > (size_t)_valueLength / sizeof(uint64_t)) + { + return -1; + } + int32_t length = tokenIDs.size() * sizeof(uint64_t); + memcpy(_value, tokenIDs.data(), length); + return length; +} + +int32_t getNotFungibleAssetInfo(evmc_host_context* _context, const uint8_t* _addr, + int32_t _addressLength, const char* _assetName, int32_t _nameLength, uint64_t _assetId, + char* _value, int32_t _valueLength) +{ + auto& hostContext = static_cast(*_context); + auto info = hostContext.getNotFungibleAssetInfo( + string_view((char*)_addr, _addressLength), string(_assetName, _nameLength), _assetId); + if (info.size() > (size_t)_valueLength) + { + return -1; + } + memcpy(_value, info.data(), info.size()); + return info.size(); +} + +wasm_host_interface const wasmFnTable = { + wasmAccountExists, + get, + set, + wasmGetCodeSize, + wasmGetCodeHash, + wasmCopyCode, + wasmLog, + registerAsset, + issueFungibleAsset, + issueNotFungibleAsset, + transferAsset, + getAssetBanlance, + getNotFungibleAssetInfo, + getNotFungibleAssetIDs, +}; + +} // namespace + +const evmc_host_interface* getHostInterface() +{ + return &fnTable; +} +const wasm_host_interface* getWasmHostInterface() +{ + return &wasmFnTable; +} +} // namespace executor +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/vm/EVMHostInterface.h" "b/BFPL\345\243\271/bcos-executor/src/vm/EVMHostInterface.h" new file mode 100644 index 00000000..3f4f0f67 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/vm/EVMHostInterface.h" @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 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. + * + * @brief host context + * @file EVMHostInterface.h + * @author: xingqiangbai + * @date: 2021-05-24 + */ + +#pragma once + +#include "../Common.h" +#include "bcos-framework/protocol/BlockHeader.h" +#include "evmc/evmc.h" +#include "evmc/instructions.h" +#include +#include +#include +#include + +namespace bcos +{ +namespace executor +{ +const evmc_host_interface* getHostInterface(); +const wasm_host_interface* getWasmHostInterface(); +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/vm/HostContext.cpp" "b/BFPL\345\243\271/bcos-executor/src/vm/HostContext.cpp" new file mode 100644 index 00000000..a87ff233 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/vm/HostContext.cpp" @@ -0,0 +1,718 @@ +/* + * Copyright (C) 2021 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. + * + * @brief host context + * @file HostContext.cpp + * @author: xingqiangbai + * @date: 2021-05-24 + */ + +#include "HostContext.h" +#include "../Common.h" +#include "../executive/TransactionExecutive.h" +#include "EVMHostInterface.h" +#include "bcos-framework/bcos-framework/ledger/LedgerTypeDef.h" +#include "bcos-framework/storage/Table.h" +#include "bcos-table/src/StateStorage.h" +#include "evmc/evmc.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace std; +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::protocol; + +namespace // anonymous +{ +/// Upper bound of stack space needed by single CALL/CREATE execution. Set +/// experimentally. +// static size_t const c_singleExecutionStackSize = 100 * 1024; + +static const std::string SYS_ASSET_NAME = "name"; +static const std::string SYS_ASSET_FUNGIBLE = "fungible"; +static const std::string SYS_ASSET_TOTAL = "total"; +static const std::string SYS_ASSET_SUPPLIED = "supplied"; +static const std::string SYS_ASSET_ISSUER = "issuer"; +static const std::string SYS_ASSET_DESCRIPTION = "description"; +static const std::string SYS_ASSET_INFO = "_sys_asset_info_"; + +} // anonymous namespace + +namespace bcos +{ +namespace executor +{ +namespace +{ +evmc_gas_metrics ethMetrics{32000, 20000, 5000, 200, 9000, 2300, 25000}; + +evmc_bytes32 evm_hash_fn(const uint8_t* data, size_t size) +{ + return toEvmC(HostContext::hashImpl()->hash(bytesConstRef(data, size))); +} +} // namespace + +// crypto::Hash::Ptr g_hashImpl = nullptr; + +HostContext::HostContext(CallParameters::UniquePtr callParameters, + std::shared_ptr executive, std::string tableName) + : m_callParameters(std::move(callParameters)), + m_executive(std::move(executive)), + m_tableName(std::move(tableName)) +{ + interface = getHostInterface(); + wasm_interface = getWasmHostInterface(); + + hash_fn = evm_hash_fn; + version = m_executive->blockContext().lock()->blockVersion(); + isSMCrypto = false; + + if (hashImpl() && hashImpl()->getHashImplType() == crypto::HashImplType::Sm3Hash) + { + isSMCrypto = true; + } + metrics = ðMetrics; + m_startTime = utcTimeUs(); +} + +std::string HostContext::get(const std::string_view& _key) +{ + auto start = utcTimeUs(); + auto entry = m_executive->storage().getRow(m_tableName, _key); + if (entry) + { + m_getTimeUsed.fetch_add(utcTimeUs() - start); + return std::string(entry->getField(0)); + } + m_getTimeUsed.fetch_add(utcTimeUs() - start); + return {}; +} + +void HostContext::set(const std::string_view& _key, std::string _value) +{ + auto start = utcTimeUs(); + Entry entry; + entry.importFields({std::move(_value)}); + + m_executive->storage().setRow(m_tableName, _key, std::move(entry)); + m_setTimeUsed.fetch_add(utcTimeUs() - start); +} + + +std::string addressBytesStr2String(std::string_view receiveAddressBytes) +{ + std::string strAddress; + strAddress.reserve(receiveAddressBytes.size() * 2); + boost::algorithm::hex_lower( + receiveAddressBytes.begin(), receiveAddressBytes.end(), std::back_inserter(strAddress)); + + return strAddress; +} + +std::string evmAddress2String(const evmc_address& address) +{ + auto receiveAddressBytes = fromEvmC(address); + return addressBytesStr2String(receiveAddressBytes); +} + +evmc_result HostContext::externalRequest(const evmc_message* _msg) +{ + // Convert evmc_message to CallParameters + auto request = std::make_unique(CallParameters::MESSAGE); + + request->senderAddress = myAddress(); + request->origin = origin(); + request->status = 0; + + auto blockContext = m_executive->blockContext().lock(); + + switch (_msg->kind) + { + case EVMC_CREATE2: + request->createSalt = fromEvmC(_msg->create2_salt); + break; + case EVMC_CALL: + if (m_executive->blockContext().lock()->isWasm()) + { + request->receiveAddress.assign((char*)_msg->destination_ptr, _msg->destination_len); + } + else + { + request->receiveAddress = evmAddress2String(_msg->destination); + } + + request->codeAddress = request->receiveAddress; + request->data.assign(_msg->input_data, _msg->input_data + _msg->input_size); + break; + case EVMC_DELEGATECALL: + case EVMC_CALLCODE: + { + if (!m_executive->blockContext().lock()->isWasm()) + { + if (blockContext->blockVersion() >= + (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION) + { + request->delegateCall = true; + request->codeAddress = evmAddress2String(_msg->destination); + request->delegateCallSender = evmAddress2String(_msg->sender); + request->receiveAddress = codeAddress(); + request->data.assign(_msg->input_data, _msg->input_data + _msg->input_size); + break; + } + } + + // old logic + evmc_result result; + result.status_code = evmc_status_code(EVMC_INVALID_INSTRUCTION); + result.release = nullptr; // no output to release + result.gas_left = 0; + return result; + } + case EVMC_CREATE: + request->data.assign(_msg->input_data, _msg->input_data + _msg->input_size); + request->create = true; + break; + } + if (versionCompareTo(blockContext->blockVersion(), BlockVersion::V3_1_VERSION) >= 0) + { + request->logEntries = std::move(m_callParameters->logEntries); + } + request->gas = _msg->gas; + // if (built in precompiled) then execute locally + + if (m_executive->isBuiltInPrecompiled(request->receiveAddress)) + { + return callBuiltInPrecompiled(request, false); + } + if (!blockContext->isWasm() && m_executive->isEthereumPrecompiled(request->receiveAddress)) + { + return callBuiltInPrecompiled(request, true); + } + + request->staticCall = m_callParameters->staticCall; + + auto response = m_executive->externalCall(std::move(request)); + + // Convert CallParameters to evmc_resultx + evmc_result result; + result.status_code = toEVMStatus(response, result, blockContext); + + result.create_address = + toEvmC(boost::algorithm::unhex(response->newEVMContractAddress)); // TODO: check if ok + + // TODO: check if the response data need to release + result.output_data = response->data.data(); + result.output_size = response->data.size(); + result.release = nullptr; // Response own by HostContext + result.gas_left = response->gas; + + // Put response to store in order to avoid data lost + m_responseStore.emplace_back(std::move(response)); + + return result; +} + +evmc_status_code HostContext::toEVMStatus(std::unique_ptr const& _response, + evmc_result _result, std::shared_ptr _blockContext) +{ + if (_blockContext->blockVersion() >= (uint32_t)(bcos::protocol::BlockVersion::V3_1_VERSION)) + { + _result.status_code = evmc_status_code(_response->evmStatus); + return _result.status_code; + } + else + { + _result.status_code = evmc_status_code(_response->status); + return _result.status_code; + } +} + +evmc_result HostContext::callBuiltInPrecompiled( + std::unique_ptr const& _request, bool _isEvmPrecompiled) +{ + auto callResults = std::make_unique(CallParameters::FINISHED); + evmc_result preResult{}; + int32_t resultCode; + bytes resultData; + + if (_isEvmPrecompiled) + { + auto gasUsed = + m_executive->costOfPrecompiled(_request->receiveAddress, ref(_request->data)); + /// NOTE: this assignment is wrong, will cause out of gas, should not use evm precompiled + /// before 3.1.0 + callResults->gas = gasUsed; + if (versionCompareTo(version, BlockVersion::V3_1_VERSION) >= 0) + { + callResults->gas = _request->gas - gasUsed; + } + auto [success, output] = + m_executive->executeOriginPrecompiled(_request->receiveAddress, ref(_request->data)); + resultCode = + (int32_t)(success ? TransactionStatus::None : TransactionStatus::RevertInstruction); + resultData.swap(output); + } + else + { + try + { + auto precompiledCallParams = + std::make_shared(_request); + precompiledCallParams = m_executive->execPrecompiled(precompiledCallParams); + callResults->gas = precompiledCallParams->m_gasLeft; + resultCode = (int32_t)TransactionStatus::None; + resultData = std::move(precompiledCallParams->m_execResult); + } + catch (protocol::PrecompiledError& e) + { + resultCode = (int32_t)TransactionStatus::PrecompiledError; + } + catch (std::exception& e) + { + resultCode = (int32_t)TransactionStatus::Unknown; + } + } + + if (resultCode != (int32_t)TransactionStatus::None) + { + callResults->type = CallParameters::REVERT; + callResults->status = resultCode; + preResult.status_code = EVMC_INTERNAL_ERROR; + preResult.gas_left = 0; + m_responseStore.emplace_back(std::move(callResults)); + return preResult; + } + + preResult.gas_left = callResults->gas; + if (preResult.gas_left < 0) + { + callResults->type = CallParameters::REVERT; + callResults->status = (int32_t)TransactionStatus::OutOfGas; + preResult.status_code = EVMC_OUT_OF_GAS; + preResult.gas_left = 0; + return preResult; + } + callResults->status = (int32_t)TransactionStatus::None; + callResults->data.swap(resultData); + preResult.output_size = callResults->data.size(); + preResult.output_data = callResults->data.data(); + preResult.release = nullptr; + m_responseStore.emplace_back(std::move(callResults)); + return preResult; +} + +bool HostContext::setCode(bytes code) +{ + // set code will cause exception when exec revert + // new logic + if (blockVersion() >= uint32_t(bcos::protocol::BlockVersion::V3_1_VERSION)) + { + auto contractTable = m_executive->storage().openTable(m_tableName); + // set code hash in contract table + auto codeHash = hashImpl()->hash(code); + if (contractTable) + { + Entry codeHashEntry; + codeHashEntry.importFields({codeHash.asBytes()}); + + auto codeEntry = m_executive->storage().getRow( + bcos::ledger::SYS_CODE_BINARY, codeHashEntry.getField(0)); + + if (!codeEntry) + { + codeEntry = std::make_optional(); + codeEntry->importFields({std::move(code)}); + + // set code in code binary table + m_executive->storage().setRow(bcos::ledger::SYS_CODE_BINARY, + codeHashEntry.getField(0), std::move(codeEntry.value())); + } + + // dry code hash in account table + m_executive->storage().setRow(m_tableName, ACCOUNT_CODE_HASH, std::move(codeHashEntry)); + return true; + } + return false; + } + // old logic + auto contractTable = m_executive->storage().openTable(m_tableName); + if (contractTable) + { + Entry codeHashEntry; + auto codeHash = hashImpl()->hash(code); + codeHashEntry.importFields({codeHash.asBytes()}); + m_executive->storage().setRow(m_tableName, ACCOUNT_CODE_HASH, std::move(codeHashEntry)); + + Entry codeEntry; + codeEntry.importFields({std::move(code)}); + m_executive->storage().setRow(m_tableName, ACCOUNT_CODE, std::move(codeEntry)); + return true; + } + return false; +} + +void HostContext::setCodeAndAbi(bytes code, string abi) +{ + EXECUTOR_LOG(TRACE) << LOG_DESC("save code and abi") << LOG_KV("tableName", m_tableName) + << LOG_KV("codeSize", code.size()) << LOG_KV("abiSize", abi.size()); + if (setCode(std::move(code))) + { + // new logic + if (blockVersion() >= uint32_t(bcos::protocol::BlockVersion::V3_1_VERSION)) + { + // set abi in abi table + auto codeEntry = m_executive->storage().getRow(m_tableName, ACCOUNT_CODE_HASH); + auto codeHash = codeEntry->getField(0); + + EXECUTOR_LOG(TRACE) << LOG_DESC("set abi") << LOG_KV("codeHash", codeHash) + << LOG_KV("abiSize", abi.size()); + + auto abiEntry = m_executive->storage().getRow(bcos::ledger::SYS_CONTRACT_ABI, codeHash); + + if (!abiEntry) + { + abiEntry = std::make_optional(); + abiEntry->importFields({std::move(abi)}); + + m_executive->storage().setRow( + bcos::ledger::SYS_CONTRACT_ABI, codeHash, std::move(abiEntry.value())); + } + + return; + } + // old logic + Entry abiEntry; + abiEntry.importFields({std::move(abi)}); + m_executive->storage().setRow(m_tableName, ACCOUNT_ABI, abiEntry); + } +} + +bcos::bytes HostContext::externalCodeRequest(const std::string_view& _a) +{ + auto request = std::make_unique(CallParameters::MESSAGE); + request->gas = gas(); + request->senderAddress = myAddress(); + request->receiveAddress = myAddress(); + request->data = bcos::protocol::GET_CODE_INPUT_BYTES; + request->origin = origin(); + request->status = 0; + request->delegateCall = false; + request->codeAddress = addressBytesStr2String(_a); + auto response = m_executive->externalCall(std::move(request)); + return std::move(response->data); +} + +size_t HostContext::codeSizeAt(const std::string_view& _a) +{ + auto blockContext = m_executive->blockContext().lock(); + if (blockContext->blockVersion() >= (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION) + { + /* + Note: + evm precompiled(0x1 ~ 0x9): return 0 (Is the same as eth) + FISCO BCOS precompiled: return 1 + + Because evm precompiled is call by build-in opcode, no need to get code size before + called, but FISCO BCOS precompiled is call like contract, so it need to get code size. + */ + + if (m_executive->isPrecompiled(addressBytesStr2String(_a))) + { + // Only FISCO BCOS precompile: constant precompiled or build-in precompiled + // evm precompiled address will go down to externalCodeRequest() and get empty code + return 1; + } + + auto code = externalCodeRequest(_a); + return code.size(); // OPCODE num is bytes.size + } + return 1; +} + +h256 HostContext::codeHashAt(const std::string_view& _a) +{ + auto blockContext = m_executive->blockContext().lock(); + if (blockContext->blockVersion() >= (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION) + { + // precompiled return 0 hash; + if (m_executive->isPrecompiled(addressBytesStr2String(_a))) + { + return h256(0); + } + auto code = externalCodeRequest(_a); + auto hash = hashImpl()->hash(code).asBytes(); + return h256(hash); + } + return h256(0); +} + +VMSchedule const& HostContext::vmSchedule() const +{ + return m_executive->vmSchedule(); +} + +u256 HostContext::store(const u256& _n) +{ + auto start = utcTimeUs(); + + auto key = toEvmC(_n); + auto keyView = std::string_view((char*)key.bytes, sizeof(key.bytes)); + + auto entry = m_executive->storage().getRow(m_tableName, keyView); + if (entry) + { + // if (c_fileLogLevel <= bcos::LogLevel::TRACE) + // { // FIXME: this log is only for debug, comment it when release + // EXECUTOR_LOG(TRACE) << LOG_DESC("store") << LOG_KV("key", toHex(keyView)) + // << LOG_KV("value", toHex(entry->get())); + // } + m_getTimeUsed.fetch_add(utcTimeUs() - start); + return fromBigEndian(entry->getField(0)); + } + // else + // {// FIXME: this log is only for debug, comment it when release + // EXECUTOR_LOG(TRACE) << LOG_DESC("store") << LOG_KV("key", toHex(keyView)) + // << LOG_KV("value", "not found"); + // } + m_getTimeUsed.fetch_add(utcTimeUs() - start); + return u256(); +} + +void HostContext::setStore(u256 const& _n, u256 const& _v) +{ + auto start = utcTimeUs(); + auto key = toEvmC(_n); + auto keyView = std::string_view((char*)key.bytes, sizeof(key.bytes)); + + auto value = toEvmC(_v); + bytes valueBytes(value.bytes, value.bytes + sizeof(value.bytes)); + + // if (c_fileLogLevel <= bcos::LogLevel::TRACE) + // { // FIXME: this log is only for debug, comment it when release + // EXECUTOR_LOG(TRACE) << LOG_DESC("setStore") << LOG_KV("key", toHex(keyView)) + // << LOG_KV("value", toHex(valueBytes)); + // } + Entry entry; + entry.importFields({std::move(valueBytes)}); + m_executive->storage().setRow(m_tableName, keyView, std::move(entry)); + m_setTimeUsed.fetch_add(utcTimeUs() - start); +} + +void HostContext::log(h256s&& _topics, bytesConstRef _data) +{ + // if (m_isWasm || myAddress().empty()) + // { + // m_sub.logs->push_back( + // protocol::LogEntry(bytes(myAddress().data(), myAddress().data() + + // myAddress().size()), + // std::move(_topics), _data.toBytes())); + // } + // else + // { + // // convert solidity address to hex string + // auto hexAddress = *toHexString(myAddress()); + // boost::algorithm::to_lower(hexAddress); // this is in case of toHexString be modified + // toChecksumAddress(hexAddress, hashImpl()->hash(hexAddress).hex()); + // m_sub.logs->push_back( + // protocol::LogEntry(asBytes(hexAddress), std::move(_topics), _data.toBytes())); + // } + m_callParameters->logEntries.emplace_back( + bytes(myAddress().data(), myAddress().data() + myAddress().size()), std::move(_topics), + _data.toBytes()); +} + +h256 HostContext::blockHash(int64_t _number) const +{ + if (m_executive->blockContext().lock()->blockVersion() >= + (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION) + { + if (_number >= blockNumber() || _number < 0) + { + return h256(""); + } + else + { + return m_executive->blockContext().lock()->blockHash(_number); + } + } + else + { + return m_executive->blockContext().lock()->hash(); + } +} +int64_t HostContext::blockNumber() const +{ + return m_executive->blockContext().lock()->number(); +} +uint32_t HostContext::blockVersion() const +{ + return m_executive->blockContext().lock()->blockVersion(); +} +int64_t HostContext::timestamp() const +{ + return m_executive->blockContext().lock()->timestamp(); +} + +std::string_view HostContext::myAddress() const +{ + return m_executive->contractAddress(); +} + +std::optional HostContext::code() +{ + auto start = utcTimeUs(); + if (blockVersion() >= uint32_t(bcos::protocol::BlockVersion::V3_1_VERSION)) + { + auto codehash = codeHash(); + Entry codeHashEntry; + codeHashEntry.importFields({codehash.asBytes()}); + auto entry = + m_executive->storage().getRow(bcos::ledger::SYS_CODE_BINARY, codeHashEntry.getField(0)); + if (!entry || entry->get().empty()) + { + auto codeEntry = m_executive->storage().getRow(m_tableName, ACCOUNT_CODE); + m_getTimeUsed.fetch_add(utcTimeUs() - start); + return codeEntry; + } + m_getTimeUsed.fetch_add(utcTimeUs() - start); + return entry; + } + auto entry = m_executive->storage().getRow(m_tableName, ACCOUNT_CODE); + m_getTimeUsed.fetch_add(utcTimeUs() - start); + return entry; +} + +h256 HostContext::codeHash() +{ + auto entry = m_executive->storage().getRow(m_tableName, ACCOUNT_CODE_HASH); + if (entry) + { + auto code = entry->getField(0); + + return h256(code, FixedBytes<32>::StringDataType::FromBinary); // TODO: h256 support decode + // from string_view + } + + return h256(); +} + +bool HostContext::registerAsset(const std::string& _assetName, const std::string_view& _addr, + bool _fungible, uint64_t _total, const std::string& _description) +{ + (void)_assetName; + (void)_addr; + (void)_fungible; + (void)_total; + (void)_description; + + return true; +} + +bool HostContext::issueFungibleAsset( + const std::string_view& _to, const std::string& _assetName, uint64_t _amount) +{ + (void)_to; + (void)_assetName; + (void)_amount; + + return true; +} + +uint64_t HostContext::issueNotFungibleAsset( + const std::string_view& _to, const std::string& _assetName, const std::string& _uri) +{ + (void)_to; + (void)_assetName; + (void)_uri; + return 0; +} + +void HostContext::depositFungibleAsset( + const std::string_view& _to, const std::string& _assetName, uint64_t _amount) +{ + (void)_to; + (void)_assetName; + (void)_amount; +} + +void HostContext::depositNotFungibleAsset(const std::string_view& _to, + const std::string& _assetName, uint64_t _assetID, const std::string& _uri) +{ + (void)_to; + (void)_assetName; + (void)_assetID; + (void)_uri; +} + +bool HostContext::transferAsset(const std::string_view& _to, const std::string& _assetName, + uint64_t _amountOrID, bool _fromSelf) +{ + (void)_to; + (void)_assetName; + (void)_amountOrID; + (void)_fromSelf; + return true; +} + +uint64_t HostContext::getAssetBanlance( + const std::string_view& _account, const std::string& _assetName) +{ + (void)_account; + (void)_assetName; + return 0; +} + +std::string HostContext::getNotFungibleAssetInfo( + const std::string_view& _owner, const std::string& _assetName, uint64_t _assetID) +{ + (void)_owner; + (void)_assetName; + (void)_assetID; + return {}; +} + +std::vector HostContext::getNotFungibleAssetIDs( + const std::string_view& _account, const std::string& _assetName) +{ + (void)_account; + (void)_assetName; + return std::vector(); +} + +bool HostContext::isWasm() +{ + return m_executive->isWasm(); +} + + +} // namespace executor +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/vm/HostContext.h" "b/BFPL\345\243\271/bcos-executor/src/vm/HostContext.h" new file mode 100644 index 00000000..fffa50db --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/vm/HostContext.h" @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2021 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. + * + * @brief host context + * @file HostContext.h + * @author: xingqiangbai + * @date: 2021-05-24 + */ + +#pragma once + +#include "../Common.h" +#include "../executive/BlockContext.h" +#include "../executive/TransactionExecutive.h" +#include "bcos-framework/protocol/BlockHeader.h" +#include "bcos-framework/storage/Table.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace executor +{ +class TransactionExecutive; + +class HostContext : public evmc_host_context +{ +public: + using UniquePtr = std::unique_ptr; + using UniqueConstPtr = std::unique_ptr; + + /// Full constructor. + HostContext(CallParameters::UniquePtr callParameters, + std::shared_ptr executive, std::string tableName); + virtual ~HostContext(){ + // auto total = utcTimeUs() - m_startTime; + // EXECUTIVE_LOG(DEBUG) << LOG_DESC("TxExecution time(us)") << LOG_KV("total", total) + // << LOG_KV("storageTimeProportion", + // (m_getTimeUsed + m_setTimeUsed) / (double)total) + // << LOG_KV("get", m_getTimeUsed) << LOG_KV("set", m_setTimeUsed); + }; + + HostContext(HostContext const&) = delete; + HostContext& operator=(HostContext const&) = delete; + HostContext(HostContext&&) = delete; + HostContext& operator=(HostContext&&) = delete; + + std::string get(const std::string_view& _key); + + void set(const std::string_view& _key, std::string _value); + + bool registerAsset(const std::string& _assetName, const std::string_view& _issuer, + bool _fungible, uint64_t _total, const std::string& _description); + bool issueFungibleAsset( + const std::string_view& _to, const std::string& _assetName, uint64_t _amount); + uint64_t issueNotFungibleAsset( + const std::string_view& _to, const std::string& _assetName, const std::string& _uri); + std::string getNotFungibleAssetInfo( + const std::string_view& _owner, const std::string& _assetName, uint64_t _id); + bool transferAsset(const std::string_view& _to, const std::string& _assetName, + uint64_t _amountOrID, bool _fromSelf); + + // if NFT return counts, else return value + uint64_t getAssetBanlance(const std::string_view& _account, const std::string& _assetName); + + std::vector getNotFungibleAssetIDs( + const std::string_view& _account, const std::string& _assetName); + + /// Read storage location. + u256 store(const u256& _n); + + /// Write a value in storage. + void setStore(const u256& _n, const u256& _v); + + /// Create a new contract. + evmc_result externalRequest(const evmc_message* _msg); + + evmc_status_code toEVMStatus(std::unique_ptr const& _response, + evmc_result _result, std::shared_ptr _blockContext); + + evmc_result callBuiltInPrecompiled( + std::unique_ptr const& _request, bool _isEvmPrecompiled); + + virtual bool setCode(bytes code); + + void setCodeAndAbi(bytes code, std::string abi); + + size_t codeSizeAt(const std::string_view& _a); + + h256 codeHashAt(const std::string_view& _a); + + /// Does the account exist? + bool exists(const std::string_view&) { return true; } + + /// Return the EVM gas-price schedule for this execution context. + VMSchedule const& vmSchedule() const; + + /// Hash of a block if within the last 256 blocks, or h256() otherwise. + h256 blockHash(int64_t _number) const; + int64_t blockNumber() const; + uint32_t blockVersion() const; + int64_t timestamp() const; + int64_t blockGasLimit() const + { + if (m_executive->blockContext().lock()->blockVersion() >= + (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION) + { + // FISCO BCOS only has tx Gas limit. We use it as block gas limit + return m_executive->blockContext().lock()->txGasLimit(); + } + else + { + return 3000000000; // TODO: add config + } + } + + /// Revert any changes made (by any of the other calls). + void log(h256s&& _topics, bytesConstRef _data); + + /// ------ get interfaces related to HostContext------ + std::string_view myAddress() const; + virtual std::string_view caller() const { return m_callParameters->senderAddress; } + std::string_view origin() const { return m_callParameters->origin; } + std::string_view codeAddress() const { return m_callParameters->codeAddress; } + bytesConstRef data() const { return ref(m_callParameters->data); } + virtual std::optional code(); + bool isCodeHasPrefix(std::string_view _prefix) const; + h256 codeHash(); + u256 salt() const { return m_salt; } + SubState& sub() { return m_sub; } + bool isCreate() const { return m_callParameters->create; } + bool staticCall() const { return m_callParameters->staticCall; } + int64_t gas() const { return m_callParameters->gas; } + void suicide() + { + if (m_executive->blockContext().lock()->blockVersion() >= + (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION) + { + m_executive->blockContext().lock()->suicide(m_tableName); + } + } + + CallParameters::UniquePtr&& takeCallParameters() + { + if (m_executive->blockContext().lock()->blockVersion() >= + (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION) + { + for (const auto& response : m_responseStore) + { + m_callParameters->logEntries.insert(m_callParameters->logEntries.end(), + std::make_move_iterator(response->logEntries.begin()), + std::make_move_iterator(response->logEntries.end())); + } + } + return std::move(m_callParameters); + } + + static crypto::Hash::Ptr hashImpl() { return GlobalHashImpl::g_hashImpl; } + + uint64_t getStorageTimeUsed() { return m_getTimeUsed; } + uint64_t setStorageTimeUsed() { return m_setTimeUsed; } + + bool isWasm(); + +protected: + const CallParameters::UniquePtr& getCallParameters() const { return m_callParameters; } + virtual bcos::bytes externalCodeRequest(const std::string_view& _a); + +private: + void depositFungibleAsset( + const std::string_view& _to, const std::string& _assetName, uint64_t _amount); + void depositNotFungibleAsset(const std::string_view& _to, const std::string& _assetName, + uint64_t _assetID, const std::string& _uri); + + CallParameters::UniquePtr m_callParameters; + std::shared_ptr m_executive; + std::string m_tableName; + + u256 m_salt; ///< Values used in new address construction by CREATE2 + SubState m_sub; ///< Sub-band VM state (suicides, refund counter, logs). + + std::list m_responseStore; + std::atomic_uint64_t m_getTimeUsed = {0}; // microsecond + std::atomic_uint64_t m_setTimeUsed = {0}; // microsecond + std::atomic_uint64_t m_startTime = {0}; // microsecond +}; + +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/vm/Precompiled.cpp" "b/BFPL\345\243\271/bcos-executor/src/vm/Precompiled.cpp" new file mode 100644 index 00000000..d660da11 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/vm/Precompiled.cpp" @@ -0,0 +1,494 @@ +/* + * Copyright (C) 2021 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. + * + * @brief evm precompiled + * @file Precompiled.cpp + * @author: xingqiangbai + * @date: 2021-05-24 + */ + +#include "../vm/Precompiled.h" +#include "../Common.h" +#include "wedpr-crypto/WedprBn128.h" +#include "wedpr-crypto/WedprCrypto.h" +#include + +using namespace std; +using namespace bcos; +using namespace bcos::crypto; + +namespace bcos +{ +namespace executor +{ +PrecompiledRegistrar* PrecompiledRegistrar::s_this = nullptr; + +PrecompiledExecutor const& PrecompiledRegistrar::executor(std::string const& _name) +{ + if (!get()->m_execs.count(_name)) + BOOST_THROW_EXCEPTION(ExecutorNotFound()); + return get()->m_execs[_name]; +} + +PrecompiledPricer const& PrecompiledRegistrar::pricer(std::string const& _name) +{ + if (!get()->m_pricers.count(_name)) + BOOST_THROW_EXCEPTION(PricerNotFound()); + return get()->m_pricers[_name]; +} + +} // namespace executor +} // namespace bcos + +namespace +{ +ETH_REGISTER_PRECOMPILED(ecrecover)(bytesConstRef _in) +{ + // When supported_version> = v2.4.0, ecRecover uniformly calls the ECDSA verification function + return bcos::crypto::ecRecover(_in); +} + +ETH_REGISTER_PRECOMPILED(sha256)(bytesConstRef _in) +{ + return {true, bcos::crypto::sha256(_in).asBytes()}; +} + +ETH_REGISTER_PRECOMPILED(ripemd160)(bytesConstRef _in) +{ + return {true, h256(bcos::crypto::ripemd160(_in), h256::AlignRight).asBytes()}; +} + +ETH_REGISTER_PRECOMPILED(identity)(bytesConstRef _in) +{ + return {true, _in.toBytes()}; +} + +// Parse _count bytes of _in starting with _begin offset as big endian int. +// If there's not enough bytes in _in, consider it infinitely right-padded with zeroes. +bigint parseBigEndianRightPadded(bytesConstRef _in, bigint const& _begin, bigint const& _count) +{ + if (_begin > _in.count()) + return 0; + assert(_count <= numeric_limits::max() / 8); // Otherwise, the return value would not + // fit in the memory. + + size_t const begin{_begin}; + size_t const count{_count}; + + // crop _in, not going beyond its size + bytesConstRef cropped = _in.getCroppedData(begin, min(count, _in.count() - begin)); + + bigint ret = fromBigEndian(cropped); + // shift as if we had right-padding zeroes + assert(count - cropped.count() <= numeric_limits::max() / 8); + ret <<= 8 * (count - cropped.count()); + + return ret; +} + +ETH_REGISTER_PRECOMPILED(modexp)(bytesConstRef _in) +{ + // This is a protocol of bignumber modular + // Described here: + // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-198.md + bigint const baseLength(parseBigEndianRightPadded(_in, 0, 32)); + bigint const expLength(parseBigEndianRightPadded(_in, 32, 32)); + bigint const modLength(parseBigEndianRightPadded(_in, 64, 32)); + assert(modLength <= numeric_limits::max() / 8); // Otherwise gas should be too + // expensive. + assert(baseLength <= numeric_limits::max() / 8); // Otherwise, gas should be too + // expensive. + if (modLength == 0 && baseLength == 0) + return {true, bytes{}}; // This is a special case where expLength can be very big. + assert(expLength <= numeric_limits::max() / 8); + + bigint const base(parseBigEndianRightPadded(_in, 96, baseLength)); + bigint const exp(parseBigEndianRightPadded(_in, 96 + baseLength, expLength)); + bigint const mod(parseBigEndianRightPadded(_in, 96 + baseLength + expLength, modLength)); + + bigint const result = mod != 0 ? boost::multiprecision::powm(base, exp, mod) : bigint{0}; + + size_t const retLength(modLength); + bytes ret(retLength); + toBigEndian(result, ret); + + return {true, ret}; +} + +namespace +{ +bigint expLengthAdjust(bigint const& _expOffset, bigint const& _expLength, bytesConstRef _in) +{ + if (_expLength <= 32) + { + bigint const exp(parseBigEndianRightPadded(_in, _expOffset, _expLength)); + return exp ? msb(exp) : 0; + } + else + { + bigint const expFirstWord(parseBigEndianRightPadded(_in, _expOffset, 32)); + size_t const highestBit(expFirstWord ? msb(expFirstWord) : 0); + return 8 * (_expLength - 32) + highestBit; + } +} + +bigint multComplexity(bigint const& _x) +{ + if (_x <= 64) + return _x * _x; + if (_x <= 1024) + return (_x * _x) / 4 + 96 * _x - 3072; + else + return (_x * _x) / 16 + 480 * _x - 199680; +} +} // namespace + +ETH_REGISTER_PRECOMPILED_PRICER(modexp)(bytesConstRef _in) +{ + bigint const baseLength(parseBigEndianRightPadded(_in, 0, 32)); + bigint const expLength(parseBigEndianRightPadded(_in, 32, 32)); + bigint const modLength(parseBigEndianRightPadded(_in, 64, 32)); + + bigint const maxLength(max(modLength, baseLength)); + bigint const adjustedExpLength(expLengthAdjust(baseLength + 96, expLength, _in)); + + return multComplexity(maxLength) * max(adjustedExpLength, 1) / 20; +} + +ETH_REGISTER_PRECOMPILED(alt_bn128_G1_add)(bytesConstRef _in) +{ + pair ret{false, bytes(64, 0)}; + CInputBuffer in{(const char*)_in.data(), _in.size()}; + COutputBuffer result{(char*)ret.second.data(), 64}; + if (wedpr_fb_alt_bn128_g1_add(&in, &result) != 0) + { + return ret; + } + ret.first = true; + return ret; +} + +ETH_REGISTER_PRECOMPILED(alt_bn128_G1_mul)(bytesConstRef _in) +{ + pair ret{false, bytes(64, 0)}; + CInputBuffer in{(const char*)_in.data(), _in.size()}; + COutputBuffer result{(char*)ret.second.data(), 64}; + if (wedpr_fb_alt_bn128_g1_mul(&in, &result) != 0) + { + return ret; + } + ret.first = true; + return ret; +} + +ETH_REGISTER_PRECOMPILED(alt_bn128_pairing_product)(bytesConstRef _in) +{ + // Input: list of pairs of G1 and G2 points + // Output: 1 if pairing evaluates to 1, 0 otherwise (left-padded to 32 bytes) + pair ret{false, bytes(32, 0)}; + size_t constexpr pairSize = 2 * 32 + 2 * 64; + size_t const pairs = _in.size() / pairSize; + if (pairs * pairSize != _in.size()) + { + // Invalid length. + return ret; + } + + CInputBuffer in{(const char*)_in.data(), _in.size()}; + COutputBuffer result{(char*)ret.second.data(), 32}; + if (wedpr_fb_alt_bn128_pairing_product(&in, &result) != 0) + { + return ret; + } + ret.first = true; + return ret; +} + +ETH_REGISTER_PRECOMPILED_PRICER(alt_bn128_pairing_product) +(bytesConstRef _in) +{ + auto const k = _in.size() / 192; + return 45000 + k * 34000; +} + +ETH_REGISTER_PRECOMPILED(blake2_compression)(bytesConstRef _in) +{ + static constexpr size_t roundsSize = 4; + static constexpr size_t stateVectorSize = 8 * 8; + static constexpr size_t messageBlockSize = 16 * 8; + static constexpr size_t offsetCounterSize = 8; + static constexpr size_t finalBlockIndicatorSize = 1; + static constexpr size_t totalInputSize = roundsSize + stateVectorSize + messageBlockSize + + 2 * offsetCounterSize + finalBlockIndicatorSize; + + if (_in.size() != totalInputSize) + return {false, {}}; + + auto const rounds = fromBigEndian(_in.getCroppedData(0, roundsSize)); + auto const stateVector = _in.getCroppedData(roundsSize, stateVectorSize); + auto const messageBlockVector = + _in.getCroppedData(roundsSize + stateVectorSize, messageBlockSize); + auto const offsetCounter0 = + _in.getCroppedData(roundsSize + stateVectorSize + messageBlockSize, offsetCounterSize); + auto const offsetCounter1 = _in.getCroppedData( + roundsSize + stateVectorSize + messageBlockSize + offsetCounterSize, offsetCounterSize); + uint8_t const finalBlockIndicator = + _in[roundsSize + stateVectorSize + messageBlockSize + 2 * offsetCounterSize]; + + if (finalBlockIndicator != 0 && finalBlockIndicator != 1) + return {false, {}}; + + return {true, bcos::crypto::blake2FCompression(rounds, stateVector, offsetCounter0, + offsetCounter1, finalBlockIndicator, messageBlockVector)}; +} + +ETH_REGISTER_PRECOMPILED_PRICER(blake2_compression) +(bytesConstRef _in) +{ + auto const rounds = fromBigEndian(_in.getCroppedData(0, 4)); + return rounds; +} + + +} // namespace + +namespace bcos +{ +namespace precompiled +{ +std::optional Precompiled::createTable( + storage::StateStorageInterface::Ptr _tableFactory, const std::string& tableName, + const std::string& valueField) +{ + auto ret = _tableFactory->createTable(tableName, valueField); + return ret ? _tableFactory->openTable(tableName) : nullopt; +} +} // namespace precompiled + + +namespace crypto +{ +// add sha2 -- sha256 to this file begin +h256 sha256(bytesConstRef _in) noexcept +{ + h256 ret; + CInputBuffer in{(const char*)_in.data(), _in.size()}; + COutputBuffer result{(char*)ret.data(), h256::SIZE}; + if (wedpr_sha256_hash(&in, &result) != 0) + { // TODO: add some log + return ret; + } + return ret; +} + +h160 ripemd160(bytesConstRef _in) +{ + h160 ret; + CInputBuffer in{(const char*)_in.data(), _in.size()}; + COutputBuffer result{(char*)ret.data(), h160::SIZE}; + if (wedpr_ripemd160_hash(&in, &result) != 0) + { // TODO: add some log + return ret; + } + return ret; +} + +namespace +{ +// The Blake 2 F compression function implemenation is based on the reference implementation, +// see https://github.com/BLAKE2/BLAKE2/blob/master/ref/blake2b-ref.c +// The changes in original code were done mostly to accommodate variable round number and to remove +// unnecessary big endian support. +constexpr size_t BLAKE2B_BLOCKBYTES = 128; + +struct blake2b_state +{ + uint64_t h[8]; + uint64_t t[2]; + uint64_t f[2]; + uint8_t buf[BLAKE2B_BLOCKBYTES]; + size_t buflen; + size_t outlen; + uint8_t last_node; +}; + +// clang-format off +constexpr uint64_t blake2b_IV[8] = +{ + 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL +}; + +constexpr uint8_t blake2b_sigma[12][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , +}; +// clang-format on + +inline uint64_t load64(const void* src) noexcept +{ + uint64_t w; + memcpy(&w, src, sizeof w); + return w; +} + +inline constexpr uint64_t rotr64(uint64_t w, unsigned c) noexcept +{ + return (w >> c) | (w << (64 - c)); +} + +inline void G(uint8_t r, uint8_t i, uint64_t& a, uint64_t& b, uint64_t& c, uint64_t& d, + const uint64_t* m) noexcept +{ + a = a + b + m[blake2b_sigma[r][2 * i + 0]]; + d = rotr64(d ^ a, 32); + c = c + d; + b = rotr64(b ^ c, 24); + a = a + b + m[blake2b_sigma[r][2 * i + 1]]; + d = rotr64(d ^ a, 16); + c = c + d; + b = rotr64(b ^ c, 63); +} + +inline void ROUND(uint32_t round, uint64_t* v, const uint64_t* m) noexcept +{ + uint8_t const r = round % 10; + G(r, 0, v[0], v[4], v[8], v[12], m); + G(r, 1, v[1], v[5], v[9], v[13], m); + G(r, 2, v[2], v[6], v[10], v[14], m); + G(r, 3, v[3], v[7], v[11], v[15], m); + G(r, 4, v[0], v[5], v[10], v[15], m); + G(r, 5, v[1], v[6], v[11], v[12], m); + G(r, 6, v[2], v[7], v[8], v[13], m); + G(r, 7, v[3], v[4], v[9], v[14], m); +} + + +void blake2b_compress( + uint32_t rounds, blake2b_state* S, const uint8_t block[BLAKE2B_BLOCKBYTES]) noexcept +{ + uint64_t m[16]; + uint64_t v[16]; + + for (size_t i = 0; i < 16; ++i) + m[i] = load64(block + i * sizeof(m[i])); + + for (size_t i = 0; i < 8; ++i) + v[i] = S->h[i]; + + v[8] = blake2b_IV[0]; + v[9] = blake2b_IV[1]; + v[10] = blake2b_IV[2]; + v[11] = blake2b_IV[3]; + v[12] = blake2b_IV[4] ^ S->t[0]; + v[13] = blake2b_IV[5] ^ S->t[1]; + v[14] = blake2b_IV[6] ^ S->f[0]; + v[15] = blake2b_IV[7] ^ S->f[1]; + + for (uint32_t r = 0; r < rounds; ++r) + ROUND(r, v, m); + + for (size_t i = 0; i < 8; ++i) + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; +} + +} // namespace + +bytes blake2FCompression(uint32_t _rounds, bytesConstRef _stateVector, bytesConstRef _t0, + bytesConstRef _t1, bool _lastBlock, bytesConstRef _messageBlockVector) +{ + if (_stateVector.size() != sizeof(blake2b_state::h)) + BOOST_THROW_EXCEPTION(InvalidInputSize()); + + blake2b_state s{}; + std::memcpy(&s.h, _stateVector.data(), _stateVector.size()); + + if (_t0.size() != sizeof(s.t[0]) || _t1.size() != sizeof(s.t[1])) + BOOST_THROW_EXCEPTION(InvalidInputSize()); + + s.t[0] = load64(_t0.data()); + s.t[1] = load64(_t1.data()); + s.f[0] = _lastBlock ? std::numeric_limits::max() : 0; + + if (_messageBlockVector.size() != BLAKE2B_BLOCKBYTES) + BOOST_THROW_EXCEPTION(InvalidInputSize()); + + uint8_t block[BLAKE2B_BLOCKBYTES]; + std::copy(_messageBlockVector.begin(), _messageBlockVector.end(), &block[0]); + + blake2b_compress(_rounds, &s, block); + + bytes result(sizeof(s.h)); + std::memcpy(&result[0], &s.h[0], result.size()); + + return result; +} + +const int RSV_LENGTH = 65; +const int PUBLIC_KEY_LENGTH = 64; +pair ecRecover(bytesConstRef _in) +{ // _in is hash(32),v(32),r(32),s(32), return address + BCOS_LOG(TRACE) << LOG_BADGE("Precompiled") << LOG_DESC("ecRecover: ") << _in.size(); + byte rawRSV[RSV_LENGTH]; + memcpy(rawRSV, _in.data() + 64, RSV_LENGTH - 1); + rawRSV[RSV_LENGTH - 1] = (byte)((int)_in[63] - 27); + CInputBuffer msgHash{(const char*)_in.data(), crypto::HashType::SIZE}; + CInputBuffer rsv{(const char*)rawRSV, RSV_LENGTH}; + + pair ret{true, bytes(crypto::HashType::SIZE, 0)}; + bytes publicKeyBytes(64, 0); + COutputBuffer publicKey{(char*)publicKeyBytes.data(), PUBLIC_KEY_LENGTH}; + + BCOS_LOG(TRACE) << LOG_BADGE("Precompiled") << LOG_DESC("wedpr_secp256k1_recover_public_key") + << LOG_KV("hash", *toHexString(msgHash.data, msgHash.data + msgHash.len)) + << LOG_KV("rsv", *toHexString(rsv.data, rsv.data + rsv.len)) + << LOG_KV("publicKey", + *toHexString(publicKey.data, publicKey.data + publicKey.len)); + auto retCode = wedpr_secp256k1_recover_public_key(&msgHash, &rsv, &publicKey); + if (retCode != 0) + { + BCOS_LOG(TRACE) << LOG_BADGE("Precompiled") << LOG_DESC("ecRecover publicKey failed"); + return {true, {}}; + } + BCOS_LOG(TRACE) << LOG_BADGE("Precompiled") + << LOG_DESC("wedpr_secp256k1_recover_public_key success"); + // keccak256 and set first 12 byte to zero + CInputBuffer pubkeyBuffer{(const char*)publicKeyBytes.data(), PUBLIC_KEY_LENGTH}; + COutputBuffer pubkeyHash{(char*)ret.second.data(), crypto::HashType::SIZE}; + retCode = wedpr_keccak256_hash(&pubkeyBuffer, &pubkeyHash); + if (retCode != 0) + { + return {true, {}}; + } + memset(ret.second.data(), 0, 12); + BCOS_LOG(TRACE) << LOG_BADGE("Precompiled") << LOG_DESC("ecRecover success"); + return ret; +} + + +} // namespace crypto + +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/vm/Precompiled.h" "b/BFPL\345\243\271/bcos-executor/src/vm/Precompiled.h" new file mode 100644 index 00000000..8b4c6324 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/vm/Precompiled.h" @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2021 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. + * + * @brief evm precompiled + * @file Precompiled.h + * @author: xingqiangbai + * @date: 2021-05-24 + */ + +#pragma once +#include "../executive/TransactionExecutive.h" +#include "bcos-codec/wrapper/CodecWrapper.h" +#include "bcos-executor/src/precompiled/common/PrecompiledGas.h" +#include "bcos-framework/storage/Table.h" +#include "bcos-table/src/StateStorage.h" +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace executor +{ +class BlockContext; +using PrecompiledExecutor = std::function(bytesConstRef _in)>; +using PrecompiledPricer = std::function; + +DERIVE_BCOS_EXCEPTION(ExecutorNotFound); +DERIVE_BCOS_EXCEPTION(PricerNotFound); + +class PrecompiledRegistrar +{ +public: + /// Get the executor object for @a _name function or @throw ExecutorNotFound if not found. + static PrecompiledExecutor const& executor(std::string const& _name); + + /// Get the price calculator object for @a _name function or @throw PricerNotFound if not found. + static PrecompiledPricer const& pricer(std::string const& _name); + + /// Register an executor. In general just use ETH_REGISTER_PRECOMPILED. + static PrecompiledExecutor registerExecutor( + std::string const& _name, PrecompiledExecutor const& _exec) + { + return (get()->m_execs[_name] = _exec); + } + /// Unregister an executor. Shouldn't generally be necessary. + static void unregisterExecutor(std::string const& _name) { get()->m_execs.erase(_name); } + + /// Register a pricer. In general just use ETH_REGISTER_PRECOMPILED_PRICER. + static PrecompiledPricer registerPricer( + std::string const& _name, PrecompiledPricer const& _exec) + { + return (get()->m_pricers[_name] = _exec); + } + /// Unregister a pricer. Shouldn't generally be necessary. + static void unregisterPricer(std::string const& _name) { get()->m_pricers.erase(_name); } + +private: + static PrecompiledRegistrar* get() + { + if (!s_this) + s_this = new PrecompiledRegistrar; + return s_this; + } + + std::unordered_map m_execs; + std::unordered_map m_pricers; + static PrecompiledRegistrar* s_this; +}; + +// TODO: unregister on unload with a static object. +#define ETH_REGISTER_PRECOMPILED(Name) \ + static std::pair __eth_registerPrecompiledFunction##Name(bytesConstRef _in); \ + static bcos::executor::PrecompiledExecutor __eth_registerPrecompiledFactory##Name = \ + ::bcos::executor::PrecompiledRegistrar::registerExecutor( \ + #Name, &__eth_registerPrecompiledFunction##Name); \ + static std::pair __eth_registerPrecompiledFunction##Name +#define ETH_REGISTER_PRECOMPILED_PRICER(Name) \ + static bigint __eth_registerPricerFunction##Name(bytesConstRef _in); \ + static bcos::executor::PrecompiledPricer __eth_registerPricerFactory##Name = \ + ::bcos::executor::PrecompiledRegistrar::registerPricer( \ + #Name, &__eth_registerPricerFunction##Name); \ + static bigint __eth_registerPricerFunction##Name + +class PrecompiledContract +{ +public: + typedef std::shared_ptr Ptr; + PrecompiledContract() = default; + PrecompiledContract(PrecompiledPricer const& _cost, PrecompiledExecutor const& _exec, + u256 const& _startingBlock = 0) + : m_cost(_cost), m_execute(_exec), m_startingBlock(_startingBlock) + {} + + PrecompiledContract(unsigned _base, unsigned _word, PrecompiledExecutor const& _exec, + u256 const& _startingBlock = 0) + : PrecompiledContract( + [=](bytesConstRef _in) -> bigint { + bigint s = _in.size(); + bigint b = _base; + bigint w = _word; + return b + (s + 31) / 32 * w; + }, + _exec, _startingBlock) + {} + + bigint cost(bytesConstRef _in) const { return m_cost(_in); } + std::pair execute(bytesConstRef _in) const { return m_execute(_in); } + + u256 const& startingBlock() const { return m_startingBlock; } + +private: + PrecompiledPricer m_cost; + PrecompiledExecutor m_execute; + u256 m_startingBlock = 0; +}; + +} // namespace executor +namespace precompiled +{ +struct PrecompiledExecResult; +class PrecompiledGasFactory; +class Precompiled : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + + Precompiled(crypto::Hash::Ptr _hashImpl) : m_hashImpl(std::move(_hashImpl)) + { + assert(m_hashImpl); + m_precompiledGasFactory = std::make_shared(); + assert(m_precompiledGasFactory); + } + virtual ~Precompiled() = default; + virtual std::shared_ptr call( + std::shared_ptr _executive, + PrecompiledExecResult::Ptr _callParameters) = 0; + + virtual bool isParallelPrecompiled() { return false; } + virtual std::vector getParallelTag(bytesConstRef, bool) { return {}; } + +protected: + std::map name2Selector; + crypto::Hash::Ptr m_hashImpl; + +protected: + std::optional createTable( + storage::StateStorageInterface::Ptr _tableFactory, const std::string& _tableName, + const std::string& _valueField); + + std::shared_ptr m_precompiledGasFactory; +}; + +} // namespace precompiled + +namespace crypto +{ +// sha2 - sha256 replace Hash.h begin +h256 sha256(bytesConstRef _input) noexcept; + +h160 ripemd160(bytesConstRef _input); + +/// Calculates the compression function F used in the BLAKE2 cryptographic hashing algorithm +/// Throws exception in case input data has incorrect size. +/// @param _rounds the number of rounds +/// @param _stateVector the state vector - 8 unsigned 64-bit little-endian words +/// @param _t0, _t1 offset counters - unsigned 64-bit little-endian words +/// @param _lastBlock the final block indicator flag +/// @param _messageBlock the message block vector - 16 unsigned 64-bit little-endian words +/// @returns updated state vector with unchanged encoding (little-endian) +bytes blake2FCompression(uint32_t _rounds, bytesConstRef _stateVector, bytesConstRef _t0, + bytesConstRef _t1, bool _lastBlock, bytesConstRef _messageBlock); + +std::pair ecRecover(bytesConstRef _in); +} // namespace crypto +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/vm/VMFactory.cpp" "b/BFPL\345\243\271/bcos-executor/src/vm/VMFactory.cpp" new file mode 100644 index 00000000..6bc8722f --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/vm/VMFactory.cpp" @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2021 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. + * + * @brief factory of vm + * @file VMFactory.cpp + * @author: xingqiangbai + * @date: 2021-05-24 + */ + + +#include "VMFactory.h" +#include "VMInstance.h" +#include +#include +#include +#include + +namespace po = boost::program_options; + +namespace bcos +{ +namespace executor +{ +namespace +{ +auto g_kind = VMKind::evmone; + +/// The pointer to VMInstance create function in DLL VMInstance VM. +/// +/// This variable is only written once when processing command line arguments, +/// so access is thread-safe. +evmc_create_fn g_evmcCreateFn; + +/// A helper type to build the tabled of VM implementations. +/// +/// More readable than std::tuple. +/// Fields are not initialized to allow usage of construction with initializer lists {}. +struct VMKindTableEntry +{ + VMKind kind; + const char* name; +}; + +/// The table of available VM implementations. + +#if 0 +VMKindTableEntry vmKindsTable[] = {{VMKind::BcosWasm, "bcos wasm"}, {VMKind::evmone, "evmone"}}; +void setVMKind(const std::string& _name) +{ + for (auto& entry : vmKindsTable) + { + // Try to find a match in the table of VMs. + if (_name == entry.name) + { + g_kind = entry.kind; + return; + } + } + // If not match for predefined VM names, try loading it as an VMInstance DLL. + evmc_loader_error_code ec; + g_evmcCreateFn = evmc_load(_name.c_str(), &ec); + switch (ec) + { + case EVMC_LOADER_SUCCESS: + break; + case EVMC_LOADER_CANNOT_OPEN: + BOOST_THROW_EXCEPTION( + po::validation_error(po::validation_error::invalid_option_value, "vm", _name, 1)); + case EVMC_LOADER_SYMBOL_NOT_FOUND: + BOOST_THROW_EXCEPTION(std::system_error(std::make_error_code(std::errc::invalid_seek), + "loading " + _name + " failed: VMInstance create function not found")); + default: + BOOST_THROW_EXCEPTION( + std::system_error(std::error_code(static_cast(ec), std::generic_category()), + "loading " + _name + " failed")); + } + g_kind = VMKind::DLL; +} +#endif +} // namespace + +VMInstance VMFactory::create() +{ + return create(g_kind); +} + +VMInstance VMFactory::create(VMKind _kind) +{ + switch (_kind) + { + case VMKind::BcosWasm: + return VMInstance{evmc_create_bcoswasm()}; + case VMKind::evmone: + return VMInstance{evmc_create_evmone()}; + case VMKind::DLL: + return VMInstance{g_evmcCreateFn()}; + default: + return VMInstance{evmc_create_evmone()}; + } +} +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/vm/VMFactory.h" "b/BFPL\345\243\271/bcos-executor/src/vm/VMFactory.h" new file mode 100644 index 00000000..1cde5f4d --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/vm/VMFactory.h" @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2021 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. + * + * @brief factory of vm + * @file VMFactory.h + * @author: xingqiangbai + * @date: 2021-05-24 + */ + +#pragma once +#include +#include +#include + +namespace bcos +{ +namespace executor +{ +class VMInstance; +enum class VMKind +{ + evmone, + BcosWasm, + DLL +}; + +class VMFactory +{ +public: + VMFactory() = delete; + ~VMFactory() = delete; + + /// Creates a VM instance of the global kind. + static VMInstance create(); + + /// Creates a VM instance of the kind provided. + static VMInstance create(VMKind _kind); +}; +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/vm/VMInstance.cpp" "b/BFPL\345\243\271/bcos-executor/src/vm/VMInstance.cpp" new file mode 100644 index 00000000..b2090acc --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/vm/VMInstance.cpp" @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2021 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. + * + * @brief c++ wrapper of vm + * @file VMInstance.cpp + * @author: xingqiangbai + * @date: 2021-05-24 + */ + +#include "VMInstance.h" +#include "HostContext.h" + +using namespace std; +namespace bcos +{ +namespace executor +{ +namespace +{ +/// The list of EVM-C options stored as pairs of (name, value). +std::vector> s_evmcOptions; + +} // namespace + +std::vector>& evmcOptions() noexcept +{ + return s_evmcOptions; +} + +VMInstance::VMInstance(evmc_vm* _instance) noexcept : m_instance(_instance) +{ + assert(m_instance != nullptr); + // the abi_version of intepreter is EVMC_ABI_VERSION when callback VMFactory::create() + assert(m_instance->abi_version == EVMC_ABI_VERSION); + + // Set the options. + if (m_instance->set_option) + for (auto& pair : evmcOptions()) + m_instance->set_option(m_instance, pair.first.c_str(), pair.second.c_str()); +} + +Result VMInstance::exec(HostContext& _hostContext, evmc_revision _rev, evmc_message* _msg, + const uint8_t* _code, size_t _code_size) +{ + Result result = Result(m_instance->execute( + m_instance, _hostContext.interface, &_hostContext, _rev, _msg, _code, _code_size)); + return result; +} + +void VMInstance::enableDebugOutput() +{ + // m_instance->set_option(m_instance, "histogram", "1"); +} + +evmc_revision toRevision(VMSchedule const& _schedule) +{ + if (_schedule.enableLondon) + return EVMC_LONDON; + if (_schedule.enableIstanbul) + return EVMC_ISTANBUL; + if (_schedule.haveCreate2) + return EVMC_CONSTANTINOPLE; + if (_schedule.haveRevert) + return EVMC_BYZANTIUM; + if (_schedule.eip158Mode) + return EVMC_SPURIOUS_DRAGON; + if (_schedule.eip150Mode) + return EVMC_TANGERINE_WHISTLE; + if (_schedule.haveDelegateCall) + return EVMC_HOMESTEAD; + return EVMC_FRONTIER; +} +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/vm/VMInstance.h" "b/BFPL\345\243\271/bcos-executor/src/vm/VMInstance.h" new file mode 100644 index 00000000..b3928fa7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/vm/VMInstance.h" @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2021 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. + * + * @brief c++ wrapper of vm + * @file VMInstance.h + * @author: xingqiangbai + * @date: 2021-05-24 + */ + +#pragma once +#include "../Common.h" +#include +#include + +namespace bcos +{ +namespace executor +{ +class HostContext; + +class Result : public evmc_result +{ +public: + explicit Result(evmc_result const& _result) : evmc_result(_result) {} + + ~Result() + { + if (release) + release(this); + } + + Result(Result&& _other) noexcept : evmc_result(_other) { _other.release = nullptr; } + + Result& operator=(Result&&) = delete; + Result(Result const&) = delete; + Result& operator=(Result const&) = delete; + + evmc_status_code status() const { return status_code; } + int64_t gasLeft() const { return gas_left; } + bytesConstRef output() const { return {output_data, output_size}; } +}; + + +/// Returns the EVM-C options parsed from command line. +std::vector>& evmcOptions() noexcept; + +/// Translate the VMSchedule to VMInstance-C revision. +evmc_revision toRevision(VMSchedule const& _schedule); + +/// The RAII wrapper for an VMInstance-C instance. +class VMInstance +{ +public: + explicit VMInstance(evmc_vm* _instance) noexcept; + ~VMInstance() { m_instance->destroy(m_instance); } + + VMInstance(VMInstance const&) = delete; + VMInstance& operator=(VMInstance) = delete; + + Result exec(HostContext& _hostContext, evmc_revision _rev, evmc_message* _msg, + const uint8_t* _code, size_t _code_size); + + void enableDebugOutput(); + +private: + /// The VM instance created with VMInstance-C _create() function. + evmc_vm* m_instance = nullptr; +}; + +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/vm/gas_meter/GasInjector.cpp" "b/BFPL\345\243\271/bcos-executor/src/vm/gas_meter/GasInjector.cpp" new file mode 100644 index 00000000..ad60e017 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/vm/gas_meter/GasInjector.cpp" @@ -0,0 +1,564 @@ +/* + * Copyright (C) 2021 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 GasInjector.cpp + * @author xingqiangbai + * @date 20200921 + */ + +#include "GasInjector.h" +#include "src/binary-reader-ir.h" +#include "src/binary-reader.h" +#include "src/binary-writer.h" +#include "src/cast.h" +#include "src/ir.h" +#include "src/stream.h" +#include +#include + +using namespace std; +using namespace wabt; + +#define METER_LOG(LEVEL) BCOS_LOG(LEVEL) << "[METER]" + +namespace bcos +{ +namespace wasm +{ +const char* const MODULE_NAME = "bcos"; +const char* const OUT_OF_GAS_NAME = "outOfGas"; +const char* const GLOBAL_GAS_NAME = "gas"; + +// write wasm will not use loc, so wrong loc doesn't matter +void GasInjector::InjectMeterExprList(ExprList* exprs, const ImportsInfo& info) +{ + auto insertPoint = exprs->begin(); + int64_t gasCost = 0; + auto subAndCheck = [&](ExprList::iterator loc) { + // sub + auto sub = MakeUnique(Opcode::I64Sub); + exprs->insert(loc, std::move(sub)); + auto set = MakeUnique(Var(info.globalGasIndex)); + exprs->insert(loc, std::move(set)); + // check if gas < 0, then call outOfGas + auto getGas = MakeUnique(Var(info.globalGasIndex)); + exprs->insert(loc, std::move(getGas)); + auto zero = MakeUnique(Const::I64(0)); + exprs->insert(loc, std::move(zero)); + auto i64le = MakeUnique(Opcode::I64LeS); + exprs->insert(loc, std::move(i64le)); + auto ifExpr = MakeUnique(); + ifExpr->true_.decl.has_func_type = false; + ifExpr->true_.decl.sig.param_types.clear(); + // ifExpr->true_.end_loc; + auto voidType = Type(Type::Void); + ifExpr->true_.decl.sig.result_types = voidType.GetInlineVector(); + auto outOfGas = MakeUnique(Var(info.gasFuncIndex)); + ifExpr->true_.exprs.insert(ifExpr->true_.exprs.begin(), std::move(outOfGas)); + exprs->insert(loc, std::move(ifExpr)); + }; + auto insertUseGasLogic = [&](ExprList::iterator loc, int64_t& gas, + ExprList::iterator current) -> ExprList::iterator { + if (gas > 0) + { + auto getGas = MakeUnique(Var(info.globalGasIndex)); + exprs->insert(loc, std::move(getGas)); + auto constGas = MakeUnique(Const::I64(gas)); + exprs->insert(loc, std::move(constGas)); + subAndCheck(loc); + gas = 0; + } + return ++current; + }; + auto isFloatOpcode = [](const Opcode& opcode) -> bool { +#ifdef WASM_FLOAT_ENABLE + return false; +#endif + uint32_t op = opcode.GetCode(); + if ((op >= 0x2A && op <= 0x2B) || (op >= 0x38 && op <= 0x39) || + (op >= 0x43 && op <= 0x44) || (op >= 0x5B && op <= 0x66) || + (op >= 0x8B && op <= 0xA6) || (op >= 0xB2 && op <= 0xBF)) + { + METER_LOG(DEBUG) << LOG_BADGE("float instruction") << LOG_KV("name", opcode.GetName()) + << LOG_KV("instruction", opcode.GetCode()); + return true; + } + return false; + }; + for (auto it = exprs->begin(); it != exprs->end(); ++it) + { + switch (it->type()) + { + case ExprType::Binary: + { // float op throw exception + auto& op = cast(&*it)->opcode; + if (op.HasPrefix()) + { + METER_LOG(WARNING) << LOG_BADGE("instruction has perfix"); + } + if (isFloatOpcode(op)) + { + METER_LOG(WARNING) << LOG_BADGE("invalid Binary instruction") + << LOG_KV("instruction", op.GetName()); + throw InvalidInstruction(op.GetName(), ((Expr*)(&*it))->loc.offset); + } + gasCost += m_costTable[op.GetCode()].Cost; + break; + } + case ExprType::Block: + { + auto& block = cast(&*it)->block; + insertPoint = insertUseGasLogic(insertPoint, gasCost, it); + InjectMeterExprList(&block.exprs, info); + break; + } + case ExprType::Br: + { + gasCost += m_costTable[Instruction::Enum::Br].Cost; + insertPoint = insertUseGasLogic(insertPoint, gasCost, it); + break; + } + case ExprType::BrIf: + { + gasCost += m_costTable[Instruction::Enum::BrIf].Cost; + insertPoint = insertUseGasLogic(insertPoint, gasCost, it); + break; + } + case ExprType::BrTable: + { + gasCost += m_costTable[Instruction::Enum::BrTable].Cost; + insertPoint = insertUseGasLogic(insertPoint, gasCost, it); + break; + } + case ExprType::Call: + { + auto& var = cast(&*it)->var; + if (var.index() >= info.originSize && !info.foundGasFunction) + { // add outOfGas import, so update call func index + var.set_index(var.index() + 1); + } + + gasCost += m_costTable[Instruction::Enum::Call].Cost; + insertPoint = insertUseGasLogic(insertPoint, gasCost, it); + break; + } + case ExprType::CallIndirect: + { + gasCost += m_costTable[Instruction::Enum::CallIndirect].Cost; + insertPoint = insertUseGasLogic(insertPoint, gasCost, it); + break; + } + case ExprType::Compare: + { // float op throw exception + auto& op = cast(&*it)->opcode; + if (isFloatOpcode(op)) + { + METER_LOG(WARNING) << LOG_BADGE("invalid Compare instruction") + << LOG_KV("instruction", op.GetName()); + throw InvalidInstruction(op.GetName(), ((Expr*)(&*it))->loc.offset); + } + gasCost += m_costTable[op.GetCode()].Cost; + break; + } + case ExprType::Const: + { + auto& constValue = cast(&*it)->const_; + if (constValue.type() == Type::I32) + { + gasCost += m_costTable[Instruction::Enum::I32Const].Cost; + } + else if (constValue.type() == Type::I64) + { + gasCost += m_costTable[Instruction::Enum::I64Const].Cost; + } + else + { +#ifndef WASM_FLOAT_ENABLE + METER_LOG(WARNING) << LOG_BADGE("invalid Const instruction"); + throw InvalidInstruction(constValue.type() == Type::F32 ? "f32.const" : "f64.const", + ((Expr*)(&*it))->loc.offset); +#endif + } + + break; + } + case ExprType::Convert: + { // float op throw exception + auto& op = cast(&*it)->opcode; + if (isFloatOpcode(op)) + { + METER_LOG(WARNING) << LOG_BADGE("invalid Convert instruction") + << LOG_KV("instruction", op.GetName()); + throw InvalidInstruction(op.GetName(), ((Expr*)(&*it))->loc.offset); + } + gasCost += m_costTable[op.GetCode()].Cost; + break; + } + case ExprType::Drop: + { + gasCost += m_costTable[Instruction::Enum::Drop].Cost; + break; + } + case ExprType::GlobalGet: + { + auto& var = cast(&*it)->var; + // add globalGas import, so update global index + var.set_index(var.index() + 1); + gasCost += m_costTable[Instruction::Enum::GlobalGet].Cost; + break; + } + case ExprType::GlobalSet: + { + auto& var = cast(&*it)->var; + // add globalGas import, so update global index + var.set_index(var.index() + 1); + gasCost += m_costTable[Instruction::Enum::GlobalSet].Cost; + break; + } + case ExprType::If: + { + auto ifExpr = cast(&*it); + InjectMeterExprList(&ifExpr->true_.exprs, info); + if (!ifExpr->false_.empty()) + { + gasCost += m_costTable[Instruction::Enum::Else].Cost; + InjectMeterExprList(&ifExpr->false_, info); + } + insertPoint = insertUseGasLogic(insertPoint, gasCost, it); + break; + } + case ExprType::Load: + { + auto& op = cast(&*it)->opcode; + gasCost += m_costTable[op.GetCode()].Cost; + break; + } + case ExprType::LocalGet: + { + gasCost += m_costTable[Instruction::Enum::LocalGet].Cost; + break; + } + case ExprType::LocalSet: + { + gasCost += m_costTable[Instruction::Enum::LocalSet].Cost; + break; + } + case ExprType::LocalTee: + { + gasCost += m_costTable[Instruction::Enum::LocalTee].Cost; + break; + } + case ExprType::Loop: + { + insertPoint = insertUseGasLogic(insertPoint, gasCost, it); + auto& block = cast(&*it)->block; + InjectMeterExprList(&block.exprs, info); + break; + } + case ExprType::MemoryGrow: + { +#if 0 +// TODO: memoryGrow is not charged for now + auto localTee = MakeUnique(Var(info.tempVarForMemoryGasIndex)); + exprs->insert(it, std::move(localTee)); + auto getGas = MakeUnique(Var(info.globalGasIndex)); + exprs->insert(it, std::move(getGas)); + auto localGet = MakeUnique(Var(info.tempVarForMemoryGasIndex)); + exprs->insert(it, std::move(localGet)); + auto constGas = + MakeUnique(Const::I32(m_costTable[Instruction::Enum::MemoryGrow].Cost)); + exprs->insert(it, std::move(constGas)); + auto mul = MakeUnique(Opcode::I32Mul); + exprs->insert(it, std::move(mul)); + auto i64Convert = MakeUnique(Opcode::I64ExtendI32S); + exprs->insert(it, std::move(i64Convert)); + subAndCheck(it); +#endif + break; + } + case ExprType::MemorySize: + { + gasCost += m_costTable[Instruction::Enum::MemorySize].Cost; + break; + } + case ExprType::Nop: + { + gasCost += m_costTable[Instruction::Enum::Nop].Cost; + break; + } + case ExprType::Return: + { + gasCost += m_costTable[Instruction::Enum::Return].Cost; + insertPoint = insertUseGasLogic(insertPoint, gasCost, it); + break; + } + case ExprType::Select: + { + gasCost += m_costTable[Instruction::Enum::Select].Cost; + break; + } + case ExprType::Store: + { + auto& op = cast(&*it)->opcode; + gasCost += m_costTable[op.GetCode()].Cost; + break; + } + case ExprType::Unary: + { + auto& op = cast(&*it)->opcode; + if (isFloatOpcode(op)) + { + METER_LOG(WARNING) << LOG_BADGE("invalid Unary instruction") + << LOG_KV("instruction", op.GetName()); + throw InvalidInstruction(op.GetName(), ((Expr*)(&*it))->loc.offset); + } + gasCost += m_costTable[op.GetCode()].Cost; + break; + } + case ExprType::Unreachable: + { + gasCost += m_costTable[Instruction::Enum::Unreachable].Cost; + break; + } + case ExprType::AtomicLoad: + case ExprType::AtomicRmw: + case ExprType::AtomicRmwCmpxchg: + case ExprType::AtomicStore: + case ExprType::AtomicNotify: + case ExprType::AtomicFence: + case ExprType::AtomicWait: + case ExprType::MemoryCopy: + case ExprType::DataDrop: + case ExprType::MemoryFill: + case ExprType::MemoryInit: + case ExprType::RefIsNull: + case ExprType::RefFunc: + case ExprType::RefNull: + case ExprType::Rethrow: + case ExprType::ReturnCall: + case ExprType::ReturnCallIndirect: + // case ExprType::SimdLaneOp: + // case ExprType::SimdShuffleOp: + case ExprType::LoadSplat: + case ExprType::TableCopy: + case ExprType::ElemDrop: + case ExprType::TableInit: + case ExprType::TableGet: + case ExprType::TableGrow: + case ExprType::TableSize: + case ExprType::TableSet: + case ExprType::TableFill: + case ExprType::Ternary: // v128.bitselect + case ExprType::Throw: + case ExprType::Try: + default: + { + METER_LOG(WARNING) << LOG_BADGE("unsupported instruction in MVP"); + throw InvalidInstruction( + m_costTable[Instruction::Enum::Unreachable].Name, ((Expr*)(&*it))->loc.offset); + break; + } + } + } + if (gasCost != 0) + { // should not happen + insertUseGasLogic(insertPoint, gasCost, insertPoint); + } +} + +GasInjector::Result GasInjector::InjectMeter(const std::vector& byteCode) +{ + GasInjector::Result injectResult; + // parse wasm use wabt + Errors errors; + Module module; + bool s_read_debug_names = false; + const bool kStopOnFirstError = true; + const bool kFailOnCustomSectionError = true; + ReadBinaryOptions options( + Features(), nullptr, s_read_debug_names, kStopOnFirstError, kFailOnCustomSectionError); + auto result = ReadBinaryIr("", byteCode.data(), byteCode.size(), options, &errors, &module); + if (!Succeeded(result)) + { + injectResult.status = Status::InvalidFormat; + return injectResult; + } +#if 0 + if (Succeeded(result)) { + ValidateOptions options(s_features); + result = ValidateModule(&module, &errors, options); + result |= GenerateNames(&module); + } + + if (Succeeded(result)) { + /* TODO(binji): This shouldn't fail; if a name can't be applied + * (because the index is invalid, say) it should just be skipped. */ + Result dummy_result = ApplyNames(&module); + WABT_USE(dummy_result); + } +#endif + // check if import outOfGas function + Index outOfGasIndex = 0; + bool foundGasFunction = false; + uint32_t originImportSize = module.imports.size(); + for (size_t i = 0; i < module.imports.size(); ++i) + { + const Import* import = module.imports[i]; + if (import->kind() == ExternalKind::Func && import->module_name == MODULE_NAME && + import->field_name == OUT_OF_GAS_NAME) + { + foundGasFunction = true; + outOfGasIndex = i; + } + } + if (!foundGasFunction) + { // import outOfGas + TypeVector params{}; + TypeVector result; + FuncSignature outOfGasSignature{params, result}; + Index sig_index = 0; + bool foundUseGasSignature = false; + for (size_t i = 0; i < module.types.size(); ++i) + { + if (module.types[i]->kind() == TypeEntryKind::Func) + { + const FuncType* func = cast(module.types[i]); + if (func->sig == outOfGasSignature) + { + foundUseGasSignature = true; + sig_index = i; + } + } + } + if (!foundUseGasSignature) + { // insert outOfGas type BinaryReaderIR::OnFuncType + auto field = MakeUnique(); + auto func_type = MakeUnique(); + func_type->sig.param_types = params; + func_type->sig.result_types = result; + field->type = std::move(func_type); + module.AppendField(std::move(field)); + sig_index = module.types.size() - 1; + } + // add outOfGas import + // BinaryReaderIR::OnImportFunc + auto import = MakeUnique(OUT_OF_GAS_NAME); + import->module_name = MODULE_NAME; + import->field_name = OUT_OF_GAS_NAME; + // import->func.decl.has_func_type = false; + import->func.decl.type_var = Var(sig_index); + import->func.decl.sig = outOfGasSignature; + // Module::AppendField, + // module.AppendField(MakeUnique(std::move(import))); + module.func_bindings.emplace(import->func.name, Binding(Location(), module.funcs.size())); + module.funcs.insert(module.funcs.begin(), &import->func); + ++module.num_func_imports; + module.imports.push_back(import.get()); + module.fields.push_back(MakeUnique(std::move(import))); + outOfGasIndex = originImportSize; + for (Export* exportItem : module.exports) + { + if (exportItem->kind == ExternalKind::Func) + { + exportItem->var.set_index(exportItem->var.index() + 1); + } + // cout << "Export:" << exportItem->name << ", type:" << (int)exportItem->kind + // << ", index:" << exportItem->var.index() << endl; + } + for (ElemSegment* elem : module.elem_segments) + { // ElemSegment has func indexes, so update it + for (auto& expr : elem->elem_exprs) + { + if (expr.var.index() >= outOfGasIndex) + { + expr.var.set_index(expr.var.index() + 1); + } + } + } + } + + // add global var gas + auto globalGas = MakeUnique(GLOBAL_GAS_NAME); + globalGas->module_name = MODULE_NAME; + globalGas->field_name = GLOBAL_GAS_NAME; + globalGas->global.name = GLOBAL_GAS_NAME; + globalGas->global.type = wabt::Type::I64; + globalGas->global.mutable_ = true; + auto zero = MakeUnique(Const::I64(0)); + globalGas->global.init_expr.push_back(std::move(zero)); + module.globals.insert(module.globals.begin(), &globalGas->global); + ++module.num_global_imports; + module.imports.push_back(globalGas.get()); + module.fields.push_back(MakeUnique(std::move(globalGas))); + // module.AppendField(MakeUnique(std::move(globalGas))); + + // set memory limit + auto memory = module.memories[0]; + if (memory->page_limits.initial < WASM_MEMORY_PAGES_INIT) + { + memory->page_limits.initial = WASM_MEMORY_PAGES_INIT; + } + memory->page_limits.max = WASM_MEMORY_PAGES_MAX; + memory->page_limits.has_max = true; + try + { + ImportsInfo info{foundGasFunction, outOfGasIndex, 0, 0, originImportSize}; + // FIXME: main and deploy of wasm should charge memory gas first + for (Func* func : module.funcs) + { // scan opcode and add meter logic + if (func->exprs.empty()) + { + continue; + } + Index tempVarIndex = func->GetNumParamsAndLocals(); + func->local_types.AppendDecl(Type::Enum::I32, 1); + info.tempVarForMemoryGasIndex = tempVarIndex; + // cout << "Func:" << func->name << ", type index:" << func->decl.type_var.index() + // << ", expr size:" << func->exprs.size() << ",info.tempVarForMemoryGasIndex:" << + // info.tempVarForMemoryGasIndex + // << "/" << func->local_types.size() << endl; + InjectMeterExprList(&func->exprs, info); + } + } + catch (const InvalidInstruction& e) + { + injectResult.status = Status::ForbiddenOpcode; + METER_LOG(WARNING) << LOG_BADGE("InjectMeter failed, because of invalid instruction") + << LOG_KV("message", e.ErrorMessage()); + return injectResult; + } + + WriteBinaryOptions writeOptions; + writeOptions.relocatable = false; + // writeOptions.canonicalize_lebs = false; +#if FISCO_DEBUG + FileStream log("wasm.log"); + MemoryStream memoryStream(&log); +#else + MemoryStream memoryStream; +#endif + WriteBinaryModule((wabt::Stream*)&memoryStream, &module, writeOptions); + + // return result + injectResult.status = Status::Success; + auto resultBytes = make_shared>(); + resultBytes->swap(memoryStream.output_buffer().data); + injectResult.byteCode = resultBytes; + return injectResult; +} + + +} // namespace wasm +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/vm/gas_meter/GasInjector.h" "b/BFPL\345\243\271/bcos-executor/src/vm/gas_meter/GasInjector.h" new file mode 100644 index 00000000..94b07ba6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/vm/gas_meter/GasInjector.h" @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2021 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 GasInjector.h + * @author xingqiangbai + * @date 20200921 + */ +#pragma once +#include "Metric.h" +#include +#include +#include + +namespace wabt +{ +class Expr; +template +class intrusive_list; +typedef intrusive_list ExprList; +} // namespace wabt +namespace bcos +{ +namespace wasm +{ +const uint64_t WASM_MEMORY_PAGES_INIT = 16; +const uint64_t WASM_MEMORY_PAGES_MAX = 1024; +class GasInjector +{ +public: + class InvalidInstruction : std::exception + { + public: + InvalidInstruction(const std::string& _opName, uint32_t _loc) + : m_opName(_opName), m_location(_loc){}; + InvalidInstruction(const char* _opName, uint32_t _loc) + : m_opName(_opName), m_location(_loc){}; + std::string ErrorMessage() const noexcept + { + return "Unsupported opcode " + m_opName + ", location:" + std::to_string(m_location); + } + + private: + std::string m_opName; + uint32_t m_location; + }; + enum Status + { + Success = 0, + InvalidFormat = 1, + ForbiddenOpcode = 2, + }; + struct Result + { + Status status; + std::shared_ptr> byteCode; + }; + GasInjector(const InstructionTable costTable) : m_costTable(costTable) {} + + Result InjectMeter(const std::vector& byteCode); + +private: + struct ImportsInfo + { + bool foundGasFunction = false; + uint32_t gasFuncIndex; + uint32_t globalGasIndex; + uint32_t tempVarForMemoryGasIndex; + uint32_t originSize; + }; + void InjectMeterExprList(wabt::ExprList* exprs, const ImportsInfo& info); + const InstructionTable m_costTable; +}; +} // namespace wasm +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/src/vm/gas_meter/Metric.cpp" "b/BFPL\345\243\271/bcos-executor/src/vm/gas_meter/Metric.cpp" new file mode 100644 index 00000000..e7f17b72 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/vm/gas_meter/Metric.cpp" @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2021 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 Metric.cpp + * @author xingqiangbai + * @date 20200921 + */ +#include "Metric.h" +#include + +namespace bcos +{ +namespace wasm +{ +InstructionTable GetInstructionTable() +{ + auto defaultInstructionTable = InstructionTable{}; +#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, text, decomp) \ + defaultInstructionTable[Instruction::Enum::Name] = \ + Instruction{text, code, std::numeric_limits::max()}; +#include "src/opcode.def" +#undef WABT_OPCODE + // Only allow instructions in wasm v1.0 standard + // https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/ + // http://webassembly.org.cn/docs/binary-encoding/ + + // Registers + defaultInstructionTable[Instruction::Enum::LocalGet].Cost = 3; + defaultInstructionTable[Instruction::Enum::LocalSet].Cost = 3; + defaultInstructionTable[Instruction::Enum::LocalTee].Cost = 3; + defaultInstructionTable[Instruction::Enum::GlobalGet].Cost = 3; + defaultInstructionTable[Instruction::Enum::GlobalSet].Cost = 3; + + // Memory + defaultInstructionTable[Instruction::Enum::I32Load].Cost = 3; + defaultInstructionTable[Instruction::Enum::I32Load8S].Cost = 3; + defaultInstructionTable[Instruction::Enum::I32Load8U].Cost = 3; + defaultInstructionTable[Instruction::Enum::I32Load16S].Cost = 3; + defaultInstructionTable[Instruction::Enum::I32Load16U].Cost = 3; + defaultInstructionTable[Instruction::Enum::I64Load].Cost = 3; + defaultInstructionTable[Instruction::Enum::I64Load8S].Cost = 3; + defaultInstructionTable[Instruction::Enum::I64Load8U].Cost = 3; + defaultInstructionTable[Instruction::Enum::I64Load16S].Cost = 3; + defaultInstructionTable[Instruction::Enum::I64Load16U].Cost = 3; + defaultInstructionTable[Instruction::Enum::I64Load32S].Cost = 3; + defaultInstructionTable[Instruction::Enum::I64Load32U].Cost = 3; + + defaultInstructionTable[Instruction::Enum::I32Store].Cost = 3; + defaultInstructionTable[Instruction::Enum::I32Store8].Cost = 3; + defaultInstructionTable[Instruction::Enum::I32Store16].Cost = 3; + defaultInstructionTable[Instruction::Enum::I64Store].Cost = 3; + defaultInstructionTable[Instruction::Enum::I64Store8].Cost = 3; + defaultInstructionTable[Instruction::Enum::I64Store16].Cost = 3; + defaultInstructionTable[Instruction::Enum::I64Store32].Cost = 3; + + defaultInstructionTable[Instruction::Enum::MemorySize].Cost = 3; + defaultInstructionTable[Instruction::Enum::MemoryGrow].Cost = 5 * 64 * 1024; + + // Flow Control + defaultInstructionTable[Instruction::Enum::Unreachable].Cost = 0; + defaultInstructionTable[Instruction::Enum::Nop].Cost = 0; + defaultInstructionTable[Instruction::Enum::Block].Cost = 0; + defaultInstructionTable[Instruction::Enum::Loop].Cost = 0; + defaultInstructionTable[Instruction::Enum::If].Cost = 0; + defaultInstructionTable[Instruction::Enum::Else].Cost = 2; + // defaultInstructionTable[Instruction::Enum::Try].Cost = 0; + // defaultInstructionTable[Instruction::Enum::Catch].Cost = 0; + // defaultInstructionTable[Instruction::Enum::Throw].Cost = 0; + // defaultInstructionTable[Instruction::Enum::Rethrow].Cost = 0; + // defaultInstructionTable[Instruction::Enum::BrOnExn].Cost = 0; + defaultInstructionTable[Instruction::Enum::End].Cost = 0; + defaultInstructionTable[Instruction::Enum::Br].Cost = 2; + defaultInstructionTable[Instruction::Enum::BrIf].Cost = 3; + defaultInstructionTable[Instruction::Enum::BrTable].Cost = 2; + defaultInstructionTable[Instruction::Enum::Return].Cost = 2; + + // Calls + defaultInstructionTable[Instruction::Enum::Call].Cost = 3; + defaultInstructionTable[Instruction::Enum::CallIndirect].Cost = 2; + // defaultInstructionTable[Instruction::Enum::ReturnCall].Cost = 2; + // defaultInstructionTable[Instruction::Enum::ReturnCallIndirect].Cost = 2; + + // Constants + defaultInstructionTable[Instruction::Enum::I32Const].Cost = 0; + defaultInstructionTable[Instruction::Enum::I64Const].Cost = 0; + + // 32-bit Integer operators + defaultInstructionTable[Instruction::Enum::I32Clz].Cost = 3; + defaultInstructionTable[Instruction::Enum::I32Ctz].Cost = 6; + defaultInstructionTable[Instruction::Enum::I32Popcnt].Cost = 3; + + defaultInstructionTable[Instruction::Enum::I32Add].Cost = 1; + defaultInstructionTable[Instruction::Enum::I32Sub].Cost = 1; + defaultInstructionTable[Instruction::Enum::I32Mul].Cost = 3; + defaultInstructionTable[Instruction::Enum::I32DivS].Cost = 80; + defaultInstructionTable[Instruction::Enum::I32DivU].Cost = 80; + defaultInstructionTable[Instruction::Enum::I32RemS].Cost = 80; + defaultInstructionTable[Instruction::Enum::I32RemU].Cost = 80; + defaultInstructionTable[Instruction::Enum::I32And].Cost = 1; + defaultInstructionTable[Instruction::Enum::I32Or].Cost = 1; + defaultInstructionTable[Instruction::Enum::I32Xor].Cost = 1; + defaultInstructionTable[Instruction::Enum::I32Shl].Cost = 2; + defaultInstructionTable[Instruction::Enum::I32ShrS].Cost = 2; + defaultInstructionTable[Instruction::Enum::I32ShrU].Cost = 2; + defaultInstructionTable[Instruction::Enum::I32Rotl].Cost = 2; + defaultInstructionTable[Instruction::Enum::I32Rotr].Cost = 2; + defaultInstructionTable[Instruction::Enum::I32Eqz].Cost = 1; + defaultInstructionTable[Instruction::Enum::I32Eq].Cost = 1; + defaultInstructionTable[Instruction::Enum::I32Ne].Cost = 1; + defaultInstructionTable[Instruction::Enum::I32LtS].Cost = 1; + defaultInstructionTable[Instruction::Enum::I32LtU].Cost = 1; + defaultInstructionTable[Instruction::Enum::I32GtS].Cost = 1; + defaultInstructionTable[Instruction::Enum::I32GtU].Cost = 1; + defaultInstructionTable[Instruction::Enum::I32LeS].Cost = 1; + defaultInstructionTable[Instruction::Enum::I32LeU].Cost = 1; + defaultInstructionTable[Instruction::Enum::I32GeS].Cost = 1; + defaultInstructionTable[Instruction::Enum::I32GeU].Cost = 1; + + // 64-bit Integer operators + defaultInstructionTable[Instruction::Enum::I64Clz].Cost = 3; + defaultInstructionTable[Instruction::Enum::I64Ctz].Cost = 6; + defaultInstructionTable[Instruction::Enum::I64Popcnt].Cost = 3; + + defaultInstructionTable[Instruction::Enum::I64Add].Cost = 1; + defaultInstructionTable[Instruction::Enum::I64Sub].Cost = 1; + defaultInstructionTable[Instruction::Enum::I64Mul].Cost = 3; + defaultInstructionTable[Instruction::Enum::I64DivS].Cost = 80; + defaultInstructionTable[Instruction::Enum::I64DivU].Cost = 80; + defaultInstructionTable[Instruction::Enum::I64RemS].Cost = 80; + defaultInstructionTable[Instruction::Enum::I64RemU].Cost = 80; + defaultInstructionTable[Instruction::Enum::I64And].Cost = 1; + defaultInstructionTable[Instruction::Enum::I64Or].Cost = 1; + defaultInstructionTable[Instruction::Enum::I64Xor].Cost = 1; + defaultInstructionTable[Instruction::Enum::I64Shl].Cost = 2; + defaultInstructionTable[Instruction::Enum::I64ShrS].Cost = 2; + defaultInstructionTable[Instruction::Enum::I64ShrU].Cost = 2; + defaultInstructionTable[Instruction::Enum::I64Rotl].Cost = 2; + defaultInstructionTable[Instruction::Enum::I64Rotr].Cost = 2; + defaultInstructionTable[Instruction::Enum::I64Eqz].Cost = 1; + defaultInstructionTable[Instruction::Enum::I64Eq].Cost = 1; + defaultInstructionTable[Instruction::Enum::I64Ne].Cost = 1; + defaultInstructionTable[Instruction::Enum::I64LtS].Cost = 1; + defaultInstructionTable[Instruction::Enum::I64LtU].Cost = 1; + defaultInstructionTable[Instruction::Enum::I64GtS].Cost = 1; + defaultInstructionTable[Instruction::Enum::I64GtU].Cost = 1; + defaultInstructionTable[Instruction::Enum::I64LeS].Cost = 1; + defaultInstructionTable[Instruction::Enum::I64LeU].Cost = 1; + defaultInstructionTable[Instruction::Enum::I64GeS].Cost = 1; + defaultInstructionTable[Instruction::Enum::I64GeU].Cost = 1; + + // Datatype conversions + defaultInstructionTable[Instruction::Enum::I32WrapI64].Cost = 3; + defaultInstructionTable[Instruction::Enum::I64ExtendI32S].Cost = 3; + defaultInstructionTable[Instruction::Enum::I64ExtendI32U].Cost = 3; + +#ifdef WASM_FLOAT_ENABLE + // 32-bit Float operators + // TODO: the price need to reconsider carefully, for now it make no sense + defaultInstructionTable[Instruction::Enum::F32Load].Cost = 3; + defaultInstructionTable[Instruction::Enum::F32Store].Cost = 3; + defaultInstructionTable[Instruction::Enum::F32Const].Cost = 0; + + defaultInstructionTable[Instruction::Enum::F32Eq].Cost = 1; + defaultInstructionTable[Instruction::Enum::F32Ne].Cost = 1; + defaultInstructionTable[Instruction::Enum::F32Lt].Cost = 1; + defaultInstructionTable[Instruction::Enum::F32Gt].Cost = 1; + defaultInstructionTable[Instruction::Enum::F32Le].Cost = 1; + defaultInstructionTable[Instruction::Enum::F32Ge].Cost = 1; + + defaultInstructionTable[Instruction::Enum::F32Abs].Cost = 3; + defaultInstructionTable[Instruction::Enum::F32Neg].Cost = 6; + defaultInstructionTable[Instruction::Enum::F32Ceil].Cost = 3; + + defaultInstructionTable[Instruction::Enum::F32Floor].Cost = 1; + defaultInstructionTable[Instruction::Enum::F32Trunc].Cost = 1; + defaultInstructionTable[Instruction::Enum::F32Nearest].Cost = 3; + defaultInstructionTable[Instruction::Enum::F32Sqrt].Cost = 80; + defaultInstructionTable[Instruction::Enum::F32Add].Cost = 80; + defaultInstructionTable[Instruction::Enum::F32Sub].Cost = 1; + defaultInstructionTable[Instruction::Enum::F32Mul].Cost = 3; + defaultInstructionTable[Instruction::Enum::F32Div].Cost = 80; + defaultInstructionTable[Instruction::Enum::F32Min].Cost = 80; + defaultInstructionTable[Instruction::Enum::F32Max].Cost = 80; + defaultInstructionTable[Instruction::Enum::F32Copysign].Cost = 80; + + defaultInstructionTable[Instruction::Enum::F32ConvertI32S].Cost = 80; + defaultInstructionTable[Instruction::Enum::F32ConvertI32U].Cost = 80; + defaultInstructionTable[Instruction::Enum::F32ConvertI64S].Cost = 1; + defaultInstructionTable[Instruction::Enum::F32ConvertI64U].Cost = 1; + defaultInstructionTable[Instruction::Enum::F32DemoteF64].Cost = 1; + defaultInstructionTable[Instruction::Enum::F32ReinterpretI32].Cost = 2; + + // 64-bit Float operators + defaultInstructionTable[Instruction::Enum::F64Load].Cost = 3; + defaultInstructionTable[Instruction::Enum::F64Store].Cost = 3; + defaultInstructionTable[Instruction::Enum::F64Const].Cost = 0; + + defaultInstructionTable[Instruction::Enum::F64Eq].Cost = 1; + defaultInstructionTable[Instruction::Enum::F64Ne].Cost = 1; + defaultInstructionTable[Instruction::Enum::F64Lt].Cost = 1; + defaultInstructionTable[Instruction::Enum::F64Gt].Cost = 1; + defaultInstructionTable[Instruction::Enum::F64Le].Cost = 1; + defaultInstructionTable[Instruction::Enum::F64Ge].Cost = 1; + + defaultInstructionTable[Instruction::Enum::F64Abs].Cost = 3; + defaultInstructionTable[Instruction::Enum::F64Neg].Cost = 6; + defaultInstructionTable[Instruction::Enum::F64Ceil].Cost = 3; + defaultInstructionTable[Instruction::Enum::F64Floor].Cost = 1; + defaultInstructionTable[Instruction::Enum::F64Trunc].Cost = 1; + defaultInstructionTable[Instruction::Enum::F64Nearest].Cost = 3; + defaultInstructionTable[Instruction::Enum::F64Sqrt].Cost = 80; + defaultInstructionTable[Instruction::Enum::F64Add].Cost = 80; + defaultInstructionTable[Instruction::Enum::F64Sub].Cost = 1; + defaultInstructionTable[Instruction::Enum::F64Mul].Cost = 3; + defaultInstructionTable[Instruction::Enum::F64Div].Cost = 80; + defaultInstructionTable[Instruction::Enum::F64Min].Cost = 80; + defaultInstructionTable[Instruction::Enum::F64Max].Cost = 80; + defaultInstructionTable[Instruction::Enum::F64Copysign].Cost = 80; + + defaultInstructionTable[Instruction::Enum::F64ConvertI32S].Cost = 80; + defaultInstructionTable[Instruction::Enum::F64ConvertI32U].Cost = 80; + defaultInstructionTable[Instruction::Enum::F64ConvertI64S].Cost = 1; + defaultInstructionTable[Instruction::Enum::F64ConvertI64U].Cost = 1; + defaultInstructionTable[Instruction::Enum::F64PromoteF32].Cost = 1; + defaultInstructionTable[Instruction::Enum::F64ReinterpretI64].Cost = 2; +#endif + + // Type-parametric operators + defaultInstructionTable[Instruction::Enum::Drop].Cost = 3; + defaultInstructionTable[Instruction::Enum::Select].Cost = 3; + + return defaultInstructionTable; +} + +} // namespace wasm +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/src/vm/gas_meter/Metric.h" "b/BFPL\345\243\271/bcos-executor/src/vm/gas_meter/Metric.h" new file mode 100644 index 00000000..86d3a9a7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/src/vm/gas_meter/Metric.h" @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 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 Metric.h + * @author xingqiangbai + * @date 20200921 + */ +#pragma once +#include +#include + +// #define WASM_FLOAT_ENABLE +namespace bcos +{ +namespace wasm +{ +struct Instruction +{ + std::string Name; + uint8_t Opcode; + uint32_t Cost; + // Instruction() = default; + + enum Enum : uint32_t + { +#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, text, decomp) \ + Name = code, +#include "src/opcode.def" +#undef WABT_OPCODE + }; +}; + +using InstructionTable = std::array; + +InstructionTable GetInstructionTable(); + +} // namespace wasm +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-executor/test/CMakeLists.txt" new file mode 100644 index 00000000..48aefd2c --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/CMakeLists.txt" @@ -0,0 +1,3 @@ +add_subdirectory(unittest) +# add_subdirectory(flow-graph) + diff --git "a/BFPL\345\243\271/bcos-executor/test/flow-graph/CMakeLists.txt" "b/BFPL\345\243\271/bcos-executor/test/flow-graph/CMakeLists.txt" new file mode 100644 index 00000000..c55fc8b4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/flow-graph/CMakeLists.txt" @@ -0,0 +1,4 @@ +add_executable(flow-graph-test main.cpp) +target_include_directories(flow-graph-test PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../src/dag/) +target_link_libraries(flow-graph-test PUBLIC executor TBB::tbb) + diff --git "a/BFPL\345\243\271/bcos-executor/test/flow-graph/main.cpp" "b/BFPL\345\243\271/bcos-executor/test/flow-graph/main.cpp" new file mode 100644 index 00000000..c567f904 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/flow-graph/main.cpp" @@ -0,0 +1,81 @@ +#include "../../src/dag/CriticalFields.h" +#include "../../src/dag/TxDAG2.h" +#include + +using namespace std; +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::executor::critical; +using namespace tbb::flow; + +using Critical = vector>; + +CriticalFieldsInterface::Ptr makeCriticals(int _totalTx) +{ + Critical originMap = {{"111"}, {"222"}, {"333"}, {"444"}, {"555"}, {"666"}, {"777"}, {"888"}, + {"999"}, {"101"}, {"102"}, {"103"}, {"104"}, {"105"}, {"106"}, {"107"}, {"108"}, {"109"}, + {"120"}, {"121"}, {"122"}, {"123"}, {"124"}, {"125"}}; + CriticalFields::Ptr criticals = make_shared>(_totalTx); + for (int i = 0; i < _totalTx; i++) + { + int rand1 = random() % originMap.size(); + int rand2 = random() % originMap.size(); + vector> critical = {{originMap[rand1]}, {originMap[rand2]}}; + criticals->put(i, make_shared::CriticalField>(std::move(critical))); + /* + stringstream ss; + ss << i; + res.push_back({string(ss.str())}); + */ + } + return criticals; +} + +void testTxDAG( + CriticalFieldsInterface::Ptr criticals, shared_ptr _txDag, string name) +{ + auto startTime = utcSteadyTime(); + cout << endl << name << " test start" << endl; + _txDag->init(criticals, [&](ID id) { + if (id % 100000 == 0) + { + std::cout << " [" << id << "] "; + } + }); + auto initTime = utcSteadyTime(); + try + { + _txDag->run(8); + } + catch (exception& e) + { + std::cout << "Exception" << boost::diagnostic_information(e) << std::endl; + } + auto endTime = utcSteadyTime(); + cout << endl + << name << " cost(ms): initDAG=" << initTime - startTime << " run=" << endTime - initTime + << " total=" << endTime - startTime << endl; +} + +int main(int argc, const char* argv[]) +{ + (void)argc; + + int _totalTx = stoi(argv[1]); + + auto criticals = makeCriticals(_totalTx); + + shared_ptr txDag = make_shared(); + testTxDAG(criticals, txDag, "TxDAG"); + + shared_ptr txDag2 = make_shared(); + testTxDAG(criticals, txDag2, "flowGraph"); + + shared_ptr txDag3 = make_shared(); + testTxDAG(criticals, txDag3, "TxDAG"); + + shared_ptr txDag4 = make_shared(); + testTxDAG(criticals, txDag4, "flowGraph"); + + return 0; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/liquid/hello_world.h" "b/BFPL\345\243\271/bcos-executor/test/liquid/hello_world.h" new file mode 100644 index 00000000..a33bf8a7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/liquid/hello_world.h" @@ -0,0 +1,894 @@ +#pragma once + +constexpr unsigned char hello_world_wasm[] = { + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x46, 0x0c, 0x60, + 0x02, 0x7f, 0x7f, 0x00, 0x60, 0x01, 0x7f, 0x00, 0x60, 0x03, 0x7f, 0x7f, + 0x7f, 0x00, 0x60, 0x03, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x04, 0x7f, + 0x7f, 0x7f, 0x7f, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x01, 0x7f, 0x60, + 0x05, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x60, 0x01, 0x7f, 0x01, 0x7f, + 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x04, 0x7f, 0x7f, 0x7f, 0x7f, + 0x01, 0x7f, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7e, 0x02, 0x6b, 0x06, 0x04, + 0x62, 0x63, 0x6f, 0x73, 0x06, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x00, + 0x00, 0x04, 0x62, 0x63, 0x6f, 0x73, 0x0a, 0x67, 0x65, 0x74, 0x53, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x00, 0x03, 0x04, 0x62, 0x63, 0x6f, 0x73, + 0x06, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x00, 0x00, 0x04, 0x62, 0x63, + 0x6f, 0x73, 0x0a, 0x73, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x00, 0x04, 0x04, 0x62, 0x63, 0x6f, 0x73, 0x0f, 0x67, 0x65, 0x74, + 0x43, 0x61, 0x6c, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x53, 0x69, 0x7a, 0x65, + 0x00, 0x06, 0x04, 0x62, 0x63, 0x6f, 0x73, 0x0b, 0x67, 0x65, 0x74, 0x43, + 0x61, 0x6c, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x00, 0x01, 0x03, 0x37, 0x36, + 0x01, 0x01, 0x00, 0x02, 0x02, 0x02, 0x01, 0x00, 0x02, 0x09, 0x02, 0x00, + 0x00, 0x02, 0x04, 0x01, 0x04, 0x04, 0x07, 0x0a, 0x0b, 0x01, 0x00, 0x08, + 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x05, 0x00, 0x01, 0x00, 0x01, + 0x02, 0x02, 0x05, 0x01, 0x00, 0x01, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, + 0x02, 0x03, 0x01, 0x02, 0x00, 0x03, 0x05, 0x03, 0x01, 0x00, 0x11, 0x06, + 0x09, 0x01, 0x7f, 0x01, 0x41, 0x80, 0x80, 0xc0, 0x00, 0x0b, 0x07, 0x26, + 0x04, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x09, 0x68, + 0x61, 0x73, 0x68, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x00, 0x24, 0x06, 0x64, + 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x00, 0x25, 0x04, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x2c, 0x0a, 0xc0, 0x4e, 0x36, 0x33, 0x01, 0x01, 0x7f, 0x20, 0x00, + 0x10, 0x07, 0x20, 0x00, 0x41, 0x14, 0x6a, 0x2d, 0x00, 0x00, 0x41, 0x02, + 0x47, 0x04, 0x40, 0x20, 0x00, 0x28, 0x02, 0x10, 0x22, 0x01, 0x28, 0x02, + 0x00, 0x04, 0x7f, 0x20, 0x01, 0x10, 0x07, 0x20, 0x00, 0x28, 0x02, 0x10, + 0x05, 0x20, 0x01, 0x0b, 0x41, 0x0c, 0x10, 0x08, 0x0b, 0x0b, 0x26, 0x01, + 0x01, 0x7f, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x00, 0x20, + 0x00, 0x28, 0x02, 0x04, 0x22, 0x00, 0x1b, 0x22, 0x01, 0x45, 0x0d, 0x00, + 0x20, 0x00, 0x45, 0x0d, 0x00, 0x20, 0x01, 0x20, 0x00, 0x10, 0x08, 0x0b, + 0x0b, 0x85, 0x02, 0x01, 0x03, 0x7f, 0x20, 0x00, 0x04, 0x40, 0x20, 0x01, + 0x20, 0x01, 0x41, 0x04, 0x6a, 0x22, 0x03, 0x4d, 0x41, 0x00, 0x20, 0x03, + 0x41, 0x01, 0x6b, 0x20, 0x03, 0x4d, 0x1b, 0x45, 0x04, 0x40, 0x00, 0x0b, + 0x41, 0xe4, 0x82, 0xc0, 0x00, 0x28, 0x02, 0x00, 0x21, 0x03, 0x20, 0x00, + 0x41, 0x08, 0x6b, 0x22, 0x01, 0x20, 0x01, 0x28, 0x02, 0x00, 0x22, 0x04, + 0x41, 0x7e, 0x71, 0x36, 0x02, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, + 0x41, 0x7c, 0x71, 0x22, 0x02, 0x20, 0x00, 0x6b, 0x20, 0x02, 0x4d, 0x04, + 0x40, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x04, + 0x6b, 0x28, 0x02, 0x00, 0x41, 0x7c, 0x71, 0x22, 0x02, 0x45, 0x0d, 0x01, + 0x20, 0x02, 0x2d, 0x00, 0x00, 0x41, 0x01, 0x71, 0x0d, 0x01, 0x20, 0x01, + 0x10, 0x38, 0x20, 0x02, 0x28, 0x02, 0x00, 0x21, 0x00, 0x20, 0x01, 0x2d, + 0x00, 0x00, 0x41, 0x02, 0x71, 0x04, 0x40, 0x20, 0x02, 0x20, 0x00, 0x41, + 0x02, 0x72, 0x22, 0x00, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x03, 0x21, 0x01, + 0x20, 0x00, 0x41, 0x7c, 0x71, 0x22, 0x00, 0x20, 0x02, 0x6b, 0x41, 0x08, + 0x6b, 0x20, 0x00, 0x4d, 0x0d, 0x02, 0x0b, 0x00, 0x0b, 0x02, 0x40, 0x20, + 0x04, 0x41, 0x7c, 0x71, 0x22, 0x02, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x20, + 0x02, 0x20, 0x04, 0x41, 0x02, 0x71, 0x1b, 0x22, 0x02, 0x45, 0x0d, 0x00, + 0x20, 0x02, 0x2d, 0x00, 0x00, 0x41, 0x01, 0x71, 0x0d, 0x00, 0x20, 0x00, + 0x20, 0x02, 0x28, 0x02, 0x08, 0x41, 0x7c, 0x71, 0x36, 0x02, 0x00, 0x20, + 0x02, 0x20, 0x01, 0x41, 0x01, 0x72, 0x36, 0x02, 0x08, 0x20, 0x03, 0x21, + 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x00, 0x0b, + 0x41, 0xe4, 0x82, 0xc0, 0x00, 0x20, 0x01, 0x36, 0x02, 0x00, 0x0b, 0x0b, + 0x48, 0x01, 0x01, 0x7f, 0x02, 0x40, 0x02, 0x40, 0x20, 0x02, 0x20, 0x02, + 0x41, 0x01, 0x6b, 0x22, 0x02, 0x49, 0x0d, 0x00, 0x20, 0x01, 0x28, 0x02, + 0x08, 0x1a, 0x20, 0x01, 0x28, 0x02, 0x10, 0x1a, 0x20, 0x01, 0x29, 0x03, + 0x00, 0x1a, 0x20, 0x01, 0x28, 0x02, 0x14, 0x22, 0x03, 0x20, 0x02, 0x20, + 0x03, 0x6a, 0x4b, 0x0d, 0x00, 0x20, 0x01, 0x41, 0x00, 0x36, 0x02, 0x14, + 0x0c, 0x01, 0x0b, 0x00, 0x0b, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x00, + 0x0b, 0x46, 0x01, 0x02, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x03, + 0x24, 0x00, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x20, 0x02, 0x41, 0x00, 0x10, + 0x0b, 0x20, 0x03, 0x28, 0x02, 0x08, 0x21, 0x04, 0x20, 0x00, 0x20, 0x03, + 0x28, 0x02, 0x0c, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, + 0x00, 0x20, 0x04, 0x20, 0x01, 0x20, 0x02, 0x10, 0x39, 0x20, 0x00, 0x20, + 0x02, 0x36, 0x02, 0x08, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, + 0x66, 0x01, 0x01, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, + 0x00, 0x02, 0x40, 0x20, 0x01, 0x41, 0x00, 0x4e, 0x04, 0x40, 0x02, 0x7f, + 0x20, 0x02, 0x45, 0x04, 0x40, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x20, 0x01, + 0x10, 0x0d, 0x20, 0x03, 0x28, 0x02, 0x0c, 0x21, 0x02, 0x20, 0x03, 0x28, + 0x02, 0x08, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x20, 0x01, 0x41, 0x01, 0x10, + 0x0e, 0x20, 0x03, 0x28, 0x02, 0x04, 0x21, 0x02, 0x20, 0x03, 0x28, 0x02, + 0x00, 0x0b, 0x22, 0x01, 0x0d, 0x01, 0x0b, 0x00, 0x0b, 0x20, 0x00, 0x20, + 0x01, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x04, 0x20, + 0x03, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x57, 0x01, 0x03, 0x7f, 0x23, + 0x00, 0x41, 0x10, 0x6b, 0x22, 0x01, 0x24, 0x00, 0x20, 0x01, 0x41, 0x08, + 0x6a, 0x41, 0x04, 0x41, 0x00, 0x10, 0x0b, 0x20, 0x01, 0x28, 0x02, 0x08, + 0x21, 0x02, 0x20, 0x01, 0x28, 0x02, 0x0c, 0x21, 0x03, 0x20, 0x00, 0x41, + 0x14, 0x6a, 0x41, 0x02, 0x3a, 0x00, 0x00, 0x20, 0x00, 0x42, 0x04, 0x37, + 0x02, 0x08, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, + 0x02, 0x36, 0x02, 0x00, 0x20, 0x02, 0x41, 0xee, 0xc2, 0xb5, 0xab, 0x06, + 0x36, 0x00, 0x00, 0x20, 0x01, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x33, + 0x01, 0x01, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, 0x00, + 0x20, 0x02, 0x41, 0x08, 0x6a, 0x20, 0x01, 0x41, 0x00, 0x10, 0x0e, 0x20, + 0x00, 0x20, 0x02, 0x28, 0x02, 0x08, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, + 0x02, 0x28, 0x02, 0x0c, 0x36, 0x02, 0x04, 0x20, 0x02, 0x41, 0x10, 0x6a, + 0x24, 0x00, 0x0b, 0x4a, 0x01, 0x01, 0x7f, 0x41, 0x01, 0x21, 0x03, 0x02, + 0x40, 0x20, 0x01, 0x45, 0x04, 0x40, 0x41, 0x00, 0x21, 0x01, 0x0c, 0x01, + 0x0b, 0x20, 0x01, 0x41, 0x01, 0x10, 0x0f, 0x21, 0x03, 0x02, 0x40, 0x20, + 0x02, 0x04, 0x40, 0x20, 0x03, 0x45, 0x0d, 0x01, 0x20, 0x03, 0x20, 0x01, + 0x10, 0x3a, 0x0c, 0x02, 0x0b, 0x20, 0x03, 0x0d, 0x01, 0x0b, 0x41, 0x00, + 0x21, 0x03, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x04, 0x20, 0x00, + 0x20, 0x03, 0x36, 0x02, 0x00, 0x0b, 0x9c, 0x01, 0x01, 0x02, 0x7f, 0x23, + 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x02, 0x40, 0x20, 0x00, + 0x20, 0x00, 0x41, 0x04, 0x6a, 0x22, 0x03, 0x4d, 0x04, 0x40, 0x20, 0x03, + 0x41, 0x01, 0x6b, 0x22, 0x00, 0x20, 0x03, 0x4d, 0x0d, 0x01, 0x0b, 0x00, + 0x0b, 0x20, 0x00, 0x41, 0x02, 0x76, 0x21, 0x00, 0x20, 0x02, 0x41, 0xe4, + 0x82, 0xc0, 0x00, 0x28, 0x02, 0x00, 0x36, 0x02, 0x0c, 0x02, 0x40, 0x20, + 0x00, 0x20, 0x01, 0x20, 0x02, 0x41, 0x0c, 0x6a, 0x10, 0x37, 0x22, 0x03, + 0x0d, 0x00, 0x20, 0x02, 0x20, 0x00, 0x20, 0x01, 0x10, 0x36, 0x41, 0x00, + 0x21, 0x03, 0x20, 0x02, 0x28, 0x02, 0x00, 0x0d, 0x00, 0x20, 0x02, 0x28, + 0x02, 0x04, 0x22, 0x03, 0x20, 0x02, 0x28, 0x02, 0x0c, 0x36, 0x02, 0x08, + 0x20, 0x02, 0x20, 0x03, 0x36, 0x02, 0x0c, 0x20, 0x00, 0x20, 0x01, 0x20, + 0x02, 0x41, 0x0c, 0x6a, 0x10, 0x37, 0x21, 0x03, 0x0b, 0x41, 0xe4, 0x82, + 0xc0, 0x00, 0x20, 0x02, 0x28, 0x02, 0x0c, 0x36, 0x02, 0x00, 0x20, 0x02, + 0x41, 0x10, 0x6a, 0x24, 0x00, 0x20, 0x03, 0x0b, 0xd0, 0x01, 0x01, 0x03, + 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, 0x00, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x41, 0x00, 0x4e, 0x04, 0x40, 0x20, + 0x02, 0x28, 0x02, 0x00, 0x22, 0x04, 0x0d, 0x01, 0x20, 0x03, 0x20, 0x01, + 0x10, 0x0d, 0x20, 0x03, 0x28, 0x02, 0x04, 0x21, 0x04, 0x20, 0x03, 0x28, + 0x02, 0x00, 0x21, 0x02, 0x0c, 0x02, 0x0b, 0x20, 0x00, 0x41, 0x01, 0x36, + 0x02, 0x00, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, + 0x0c, 0x02, 0x0b, 0x20, 0x02, 0x28, 0x02, 0x04, 0x22, 0x05, 0x45, 0x04, + 0x40, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x20, 0x01, 0x41, 0x00, 0x10, 0x0e, + 0x20, 0x03, 0x28, 0x02, 0x0c, 0x21, 0x04, 0x20, 0x03, 0x28, 0x02, 0x08, + 0x21, 0x02, 0x0c, 0x01, 0x0b, 0x20, 0x01, 0x41, 0x01, 0x10, 0x0f, 0x22, + 0x02, 0x45, 0x04, 0x40, 0x41, 0x00, 0x21, 0x02, 0x0c, 0x01, 0x0b, 0x20, + 0x02, 0x20, 0x04, 0x20, 0x05, 0x10, 0x39, 0x20, 0x04, 0x20, 0x05, 0x10, + 0x08, 0x20, 0x01, 0x21, 0x04, 0x0b, 0x20, 0x00, 0x02, 0x7f, 0x20, 0x02, + 0x04, 0x40, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x04, 0x41, 0x00, 0x0c, + 0x01, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x04, 0x41, 0x01, 0x21, + 0x04, 0x41, 0x01, 0x0b, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x08, 0x6a, + 0x20, 0x04, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, + 0x00, 0x0b, 0x52, 0x01, 0x03, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, + 0x02, 0x24, 0x00, 0x20, 0x01, 0x28, 0x02, 0x00, 0x21, 0x04, 0x20, 0x02, + 0x41, 0x08, 0x6a, 0x20, 0x01, 0x28, 0x02, 0x08, 0x22, 0x01, 0x41, 0x00, + 0x10, 0x0b, 0x20, 0x02, 0x28, 0x02, 0x08, 0x21, 0x03, 0x20, 0x00, 0x20, + 0x02, 0x28, 0x02, 0x0c, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x03, 0x36, + 0x02, 0x00, 0x20, 0x03, 0x20, 0x04, 0x20, 0x01, 0x10, 0x39, 0x20, 0x00, + 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, + 0x0b, 0xa7, 0x01, 0x01, 0x03, 0x7f, 0x23, 0x00, 0x41, 0x20, 0x6b, 0x22, + 0x02, 0x24, 0x00, 0x02, 0x40, 0x20, 0x01, 0x20, 0x00, 0x28, 0x02, 0x04, + 0x22, 0x03, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x04, 0x6b, 0x4b, 0x04, + 0x40, 0x20, 0x01, 0x20, 0x04, 0x6a, 0x22, 0x01, 0x20, 0x04, 0x49, 0x0d, + 0x01, 0x20, 0x03, 0x20, 0x03, 0x6a, 0x22, 0x04, 0x20, 0x03, 0x49, 0x0d, + 0x01, 0x20, 0x04, 0x20, 0x01, 0x20, 0x01, 0x20, 0x04, 0x49, 0x1b, 0x22, + 0x01, 0x41, 0x08, 0x20, 0x01, 0x41, 0x08, 0x4b, 0x1b, 0x21, 0x01, 0x02, + 0x40, 0x20, 0x03, 0x04, 0x40, 0x20, 0x02, 0x41, 0x18, 0x6a, 0x41, 0x01, + 0x36, 0x02, 0x00, 0x20, 0x02, 0x20, 0x03, 0x36, 0x02, 0x14, 0x20, 0x02, + 0x20, 0x00, 0x28, 0x02, 0x00, 0x36, 0x02, 0x10, 0x0c, 0x01, 0x0b, 0x20, + 0x02, 0x41, 0x00, 0x36, 0x02, 0x10, 0x0b, 0x20, 0x02, 0x20, 0x01, 0x20, + 0x02, 0x41, 0x10, 0x6a, 0x10, 0x10, 0x20, 0x02, 0x28, 0x02, 0x00, 0x41, + 0x01, 0x46, 0x0d, 0x01, 0x20, 0x00, 0x20, 0x02, 0x29, 0x02, 0x04, 0x37, + 0x02, 0x00, 0x0b, 0x20, 0x02, 0x41, 0x20, 0x6a, 0x24, 0x00, 0x0f, 0x0b, + 0x00, 0x0b, 0x32, 0x01, 0x01, 0x7f, 0x20, 0x00, 0x20, 0x02, 0x10, 0x12, + 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x03, 0x20, 0x00, 0x28, 0x02, 0x00, + 0x6a, 0x20, 0x01, 0x20, 0x02, 0x10, 0x39, 0x20, 0x03, 0x20, 0x02, 0x20, + 0x03, 0x6a, 0x22, 0x01, 0x4b, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x00, 0x20, + 0x01, 0x36, 0x02, 0x08, 0x0b, 0x14, 0x00, 0x20, 0x01, 0x20, 0x03, 0x46, + 0x04, 0x40, 0x20, 0x00, 0x20, 0x02, 0x20, 0x01, 0x10, 0x39, 0x0f, 0x0b, + 0x00, 0x0b, 0xc6, 0x01, 0x01, 0x04, 0x7f, 0x02, 0x40, 0x20, 0x00, 0x0d, + 0x00, 0x20, 0x00, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x45, + 0x0d, 0x00, 0x20, 0x00, 0x45, 0x0d, 0x00, 0x0c, 0x01, 0x0b, 0x41, 0x00, + 0x21, 0x00, 0x0b, 0x03, 0x40, 0x02, 0x40, 0x41, 0x00, 0x20, 0x00, 0x04, + 0x7f, 0x20, 0x00, 0x0d, 0x01, 0x41, 0x00, 0x05, 0x41, 0x00, 0x0b, 0x22, + 0x00, 0x6b, 0x22, 0x02, 0x0d, 0x02, 0x20, 0x02, 0x45, 0x0d, 0x02, 0x20, + 0x00, 0x41, 0x88, 0x80, 0xc0, 0x00, 0x6a, 0x22, 0x00, 0x2c, 0x00, 0x00, + 0x22, 0x03, 0x41, 0x7f, 0x4a, 0x0d, 0x02, 0x20, 0x00, 0x20, 0x02, 0x6a, + 0x22, 0x04, 0x21, 0x01, 0x20, 0x02, 0x41, 0x01, 0x47, 0x04, 0x40, 0x20, + 0x00, 0x2d, 0x00, 0x01, 0x1a, 0x20, 0x00, 0x41, 0x02, 0x6a, 0x21, 0x01, + 0x0b, 0x20, 0x03, 0x41, 0xff, 0x01, 0x71, 0x41, 0xe0, 0x01, 0x49, 0x0d, + 0x02, 0x20, 0x04, 0x22, 0x00, 0x20, 0x01, 0x47, 0x04, 0x7f, 0x20, 0x01, + 0x41, 0x01, 0x6a, 0x21, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x05, 0x41, + 0x00, 0x0b, 0x1a, 0x20, 0x03, 0x41, 0xff, 0x01, 0x71, 0x41, 0xf0, 0x01, + 0x49, 0x0d, 0x02, 0x20, 0x00, 0x20, 0x04, 0x47, 0x04, 0x40, 0x20, 0x00, + 0x2d, 0x00, 0x00, 0x1a, 0x0b, 0x0c, 0x02, 0x0b, 0x20, 0x00, 0x41, 0x01, + 0x6b, 0x21, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0xec, 0x01, + 0x01, 0x06, 0x7f, 0x41, 0x01, 0x21, 0x07, 0x41, 0x01, 0x21, 0x04, 0x03, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, 0x22, 0x06, 0x20, + 0x05, 0x6a, 0x22, 0x04, 0x20, 0x06, 0x49, 0x0d, 0x00, 0x20, 0x02, 0x20, + 0x04, 0x4d, 0x0d, 0x01, 0x20, 0x05, 0x20, 0x08, 0x6a, 0x22, 0x09, 0x20, + 0x08, 0x49, 0x0d, 0x00, 0x20, 0x02, 0x20, 0x09, 0x4d, 0x0d, 0x00, 0x02, + 0x40, 0x02, 0x40, 0x20, 0x01, 0x20, 0x04, 0x6a, 0x2d, 0x00, 0x00, 0x22, + 0x04, 0x20, 0x01, 0x20, 0x09, 0x6a, 0x2d, 0x00, 0x00, 0x22, 0x09, 0x4b, + 0x20, 0x03, 0x71, 0x0d, 0x00, 0x20, 0x04, 0x20, 0x09, 0x4f, 0x20, 0x03, + 0x72, 0x45, 0x0d, 0x00, 0x20, 0x04, 0x20, 0x09, 0x46, 0x0d, 0x01, 0x20, + 0x06, 0x41, 0x01, 0x6a, 0x22, 0x04, 0x20, 0x06, 0x49, 0x0d, 0x02, 0x41, + 0x01, 0x21, 0x07, 0x41, 0x00, 0x21, 0x05, 0x20, 0x06, 0x21, 0x08, 0x0c, + 0x05, 0x0b, 0x20, 0x05, 0x41, 0x01, 0x6a, 0x22, 0x04, 0x20, 0x05, 0x49, + 0x0d, 0x01, 0x20, 0x04, 0x20, 0x06, 0x6a, 0x22, 0x04, 0x20, 0x06, 0x49, + 0x0d, 0x01, 0x20, 0x04, 0x20, 0x08, 0x6b, 0x22, 0x07, 0x20, 0x04, 0x4b, + 0x0d, 0x01, 0x0c, 0x03, 0x0b, 0x20, 0x05, 0x20, 0x05, 0x41, 0x01, 0x6a, + 0x22, 0x05, 0x4b, 0x0d, 0x00, 0x20, 0x06, 0x21, 0x04, 0x20, 0x05, 0x20, + 0x07, 0x47, 0x0d, 0x03, 0x20, 0x04, 0x20, 0x04, 0x20, 0x07, 0x6a, 0x22, + 0x04, 0x4d, 0x0d, 0x02, 0x0b, 0x00, 0x0b, 0x20, 0x00, 0x20, 0x07, 0x36, + 0x02, 0x04, 0x20, 0x00, 0x20, 0x08, 0x36, 0x02, 0x00, 0x0f, 0x0b, 0x41, + 0x00, 0x21, 0x05, 0x0c, 0x00, 0x0b, 0x00, 0x0b, 0x37, 0x01, 0x01, 0x7f, + 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x04, 0x24, 0x00, 0x20, 0x04, 0x41, + 0x08, 0x6a, 0x41, 0x00, 0x20, 0x03, 0x20, 0x01, 0x20, 0x02, 0x10, 0x18, + 0x20, 0x00, 0x20, 0x04, 0x28, 0x02, 0x08, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x20, 0x04, 0x28, 0x02, 0x0c, 0x36, 0x02, 0x04, 0x20, 0x04, 0x41, 0x10, + 0x6a, 0x24, 0x00, 0x0b, 0x36, 0x00, 0x02, 0x40, 0x20, 0x01, 0x20, 0x02, + 0x4d, 0x04, 0x40, 0x20, 0x02, 0x20, 0x04, 0x4d, 0x04, 0x40, 0x20, 0x02, + 0x20, 0x02, 0x20, 0x01, 0x6b, 0x22, 0x04, 0x49, 0x0d, 0x02, 0x20, 0x00, + 0x20, 0x04, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x01, 0x20, 0x03, 0x6a, + 0x36, 0x02, 0x00, 0x0f, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0xa0, + 0x02, 0x01, 0x07, 0x7f, 0x41, 0x01, 0x21, 0x09, 0x41, 0x01, 0x21, 0x04, + 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, + 0x22, 0x07, 0x20, 0x05, 0x6a, 0x22, 0x04, 0x20, 0x07, 0x49, 0x0d, 0x00, + 0x20, 0x01, 0x20, 0x04, 0x4d, 0x0d, 0x03, 0x20, 0x07, 0x41, 0x01, 0x6a, + 0x22, 0x04, 0x20, 0x07, 0x49, 0x0d, 0x00, 0x20, 0x04, 0x20, 0x05, 0x6a, + 0x22, 0x08, 0x20, 0x04, 0x49, 0x0d, 0x00, 0x20, 0x01, 0x20, 0x08, 0x6b, + 0x22, 0x08, 0x20, 0x01, 0x4b, 0x0d, 0x00, 0x20, 0x01, 0x20, 0x08, 0x4d, + 0x0d, 0x00, 0x20, 0x0a, 0x41, 0x01, 0x6a, 0x22, 0x06, 0x20, 0x0a, 0x49, + 0x0d, 0x00, 0x20, 0x06, 0x20, 0x05, 0x20, 0x06, 0x6a, 0x22, 0x06, 0x4b, + 0x0d, 0x00, 0x20, 0x01, 0x20, 0x06, 0x6b, 0x22, 0x06, 0x20, 0x01, 0x4b, + 0x0d, 0x00, 0x20, 0x01, 0x20, 0x06, 0x4d, 0x0d, 0x00, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x00, 0x20, 0x08, 0x6a, 0x2d, 0x00, 0x00, 0x22, 0x08, 0x20, + 0x00, 0x20, 0x06, 0x6a, 0x2d, 0x00, 0x00, 0x22, 0x06, 0x4b, 0x20, 0x03, + 0x71, 0x0d, 0x00, 0x20, 0x06, 0x20, 0x08, 0x4d, 0x20, 0x03, 0x72, 0x45, + 0x0d, 0x00, 0x20, 0x06, 0x20, 0x08, 0x46, 0x0d, 0x01, 0x41, 0x01, 0x21, + 0x09, 0x41, 0x00, 0x21, 0x05, 0x20, 0x07, 0x21, 0x0a, 0x0c, 0x04, 0x0b, + 0x20, 0x05, 0x41, 0x01, 0x6a, 0x22, 0x04, 0x20, 0x05, 0x49, 0x0d, 0x01, + 0x20, 0x04, 0x20, 0x07, 0x6a, 0x22, 0x04, 0x20, 0x07, 0x49, 0x0d, 0x01, + 0x20, 0x04, 0x20, 0x0a, 0x6b, 0x22, 0x09, 0x20, 0x04, 0x4b, 0x0d, 0x01, + 0x0c, 0x02, 0x0b, 0x20, 0x05, 0x20, 0x05, 0x41, 0x01, 0x6a, 0x22, 0x05, + 0x4b, 0x0d, 0x00, 0x20, 0x05, 0x20, 0x09, 0x47, 0x04, 0x40, 0x20, 0x07, + 0x21, 0x04, 0x0c, 0x03, 0x0b, 0x20, 0x07, 0x20, 0x09, 0x6a, 0x22, 0x04, + 0x20, 0x07, 0x4f, 0x0d, 0x01, 0x0b, 0x00, 0x0b, 0x41, 0x00, 0x21, 0x05, + 0x0b, 0x20, 0x02, 0x20, 0x09, 0x47, 0x0d, 0x01, 0x0b, 0x0b, 0x20, 0x0a, + 0x0b, 0x2b, 0x01, 0x01, 0x7e, 0x03, 0x40, 0x20, 0x01, 0x04, 0x40, 0x20, + 0x01, 0x41, 0x01, 0x6b, 0x21, 0x01, 0x42, 0x01, 0x20, 0x00, 0x31, 0x00, + 0x00, 0x86, 0x20, 0x02, 0x84, 0x21, 0x02, 0x20, 0x00, 0x41, 0x01, 0x6a, + 0x21, 0x00, 0x0c, 0x01, 0x0b, 0x0b, 0x20, 0x02, 0x0b, 0x2a, 0x01, 0x01, + 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x01, 0x24, 0x00, 0x20, 0x01, + 0x20, 0x00, 0x10, 0x1c, 0x20, 0x01, 0x28, 0x02, 0x00, 0x20, 0x01, 0x28, + 0x02, 0x08, 0x10, 0x00, 0x20, 0x01, 0x10, 0x07, 0x20, 0x01, 0x41, 0x10, + 0x6a, 0x24, 0x00, 0x0b, 0xb7, 0x01, 0x02, 0x03, 0x7f, 0x01, 0x7e, 0x23, + 0x00, 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, 0x00, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x01, 0x28, 0x02, 0x08, 0x22, 0x02, 0x41, 0x04, 0x6a, 0x22, 0x04, + 0x20, 0x02, 0x4f, 0x04, 0x40, 0x20, 0x01, 0x28, 0x02, 0x00, 0x21, 0x01, + 0x20, 0x03, 0x20, 0x04, 0x41, 0x00, 0x10, 0x0b, 0x20, 0x03, 0x29, 0x03, + 0x00, 0x21, 0x05, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x08, 0x20, 0x00, + 0x20, 0x05, 0x37, 0x02, 0x00, 0x20, 0x02, 0x41, 0x3f, 0x4d, 0x04, 0x40, + 0x20, 0x00, 0x20, 0x02, 0x41, 0x02, 0x74, 0x10, 0x34, 0x0c, 0x03, 0x0b, + 0x20, 0x02, 0x41, 0xff, 0xff, 0x00, 0x4d, 0x04, 0x40, 0x20, 0x03, 0x20, + 0x02, 0x41, 0x02, 0x74, 0x41, 0x01, 0x72, 0x3b, 0x01, 0x0e, 0x20, 0x00, + 0x20, 0x03, 0x41, 0x0e, 0x6a, 0x41, 0x02, 0x10, 0x13, 0x0c, 0x03, 0x0b, + 0x20, 0x02, 0x41, 0xff, 0xff, 0xff, 0xff, 0x03, 0x4b, 0x0d, 0x01, 0x20, + 0x02, 0x41, 0x02, 0x74, 0x41, 0x02, 0x72, 0x20, 0x00, 0x10, 0x35, 0x0c, + 0x02, 0x0b, 0x00, 0x0b, 0x20, 0x00, 0x41, 0x03, 0x10, 0x34, 0x20, 0x02, + 0x20, 0x00, 0x10, 0x35, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x10, + 0x13, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x26, 0x00, 0x20, + 0x00, 0x10, 0x1e, 0x20, 0x00, 0x41, 0x10, 0x6a, 0x22, 0x00, 0x2d, 0x00, + 0x04, 0x41, 0x02, 0x46, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x00, 0x28, 0x02, + 0x00, 0x22, 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x1b, 0x0b, + 0xc7, 0x02, 0x02, 0x05, 0x7f, 0x01, 0x7e, 0x23, 0x00, 0x41, 0x40, 0x6a, + 0x22, 0x01, 0x24, 0x00, 0x20, 0x01, 0x41, 0x20, 0x6a, 0x20, 0x00, 0x41, + 0x0c, 0x6a, 0x22, 0x04, 0x10, 0x1f, 0x02, 0x40, 0x20, 0x01, 0x28, 0x02, + 0x24, 0x22, 0x02, 0x28, 0x02, 0x00, 0x22, 0x03, 0x41, 0x01, 0x6b, 0x22, + 0x05, 0x20, 0x03, 0x4e, 0x0d, 0x00, 0x20, 0x01, 0x28, 0x02, 0x20, 0x2d, + 0x00, 0x04, 0x20, 0x02, 0x20, 0x05, 0x36, 0x02, 0x00, 0x41, 0x02, 0x46, + 0x04, 0x40, 0x20, 0x00, 0x28, 0x02, 0x08, 0x21, 0x03, 0x20, 0x00, 0x28, + 0x02, 0x00, 0x41, 0x00, 0x21, 0x02, 0x20, 0x01, 0x41, 0x18, 0x6a, 0x41, + 0x00, 0x41, 0xe8, 0x82, 0xc0, 0x00, 0x28, 0x02, 0x00, 0x10, 0x20, 0x20, + 0x03, 0x20, 0x01, 0x28, 0x02, 0x18, 0x10, 0x01, 0x22, 0x00, 0x04, 0x40, + 0x20, 0x00, 0x41, 0x81, 0x80, 0x01, 0x4f, 0x0d, 0x02, 0x41, 0xe8, 0x82, + 0xc0, 0x00, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x01, 0x41, 0x10, 0x6a, + 0x20, 0x00, 0x10, 0x21, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x21, 0x02, 0x20, + 0x01, 0x28, 0x02, 0x10, 0x21, 0x03, 0x20, 0x00, 0x20, 0x01, 0x28, 0x02, + 0x14, 0x4b, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x02, 0x20, 0x00, 0x36, 0x02, + 0x04, 0x20, 0x02, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, 0x01, + 0x29, 0x03, 0x08, 0x37, 0x03, 0x38, 0x20, 0x01, 0x41, 0x28, 0x6a, 0x20, + 0x01, 0x41, 0x38, 0x6a, 0x10, 0x22, 0x20, 0x01, 0x29, 0x02, 0x2c, 0x21, + 0x06, 0x20, 0x01, 0x28, 0x02, 0x28, 0x21, 0x02, 0x0b, 0x20, 0x04, 0x22, + 0x00, 0x28, 0x02, 0x00, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x00, 0x41, 0x7f, + 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, 0x00, 0x36, 0x02, 0x04, 0x20, 0x01, + 0x20, 0x00, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x01, 0x28, 0x02, + 0x04, 0x21, 0x00, 0x20, 0x01, 0x28, 0x02, 0x00, 0x20, 0x01, 0x20, 0x06, + 0x37, 0x02, 0x2c, 0x20, 0x01, 0x20, 0x02, 0x36, 0x02, 0x28, 0x20, 0x01, + 0x41, 0x28, 0x6a, 0x10, 0x23, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x04, + 0x41, 0x01, 0x6a, 0x22, 0x02, 0x20, 0x04, 0x48, 0x0d, 0x01, 0x20, 0x00, + 0x20, 0x02, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x01, 0x41, 0x40, 0x6b, 0x24, + 0x00, 0x0f, 0x0b, 0x00, 0x0b, 0x2d, 0x01, 0x01, 0x7f, 0x20, 0x01, 0x28, + 0x02, 0x00, 0x41, 0x01, 0x6a, 0x22, 0x02, 0x41, 0x00, 0x4c, 0x04, 0x40, + 0x00, 0x0b, 0x20, 0x01, 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, + 0x01, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x01, 0x41, 0x04, 0x6a, 0x36, + 0x02, 0x00, 0x0b, 0x3d, 0x01, 0x01, 0x7f, 0x02, 0x40, 0x20, 0x01, 0x20, + 0x02, 0x4d, 0x04, 0x40, 0x20, 0x02, 0x41, 0x80, 0x80, 0x01, 0x4d, 0x04, + 0x40, 0x20, 0x02, 0x20, 0x02, 0x20, 0x01, 0x6b, 0x22, 0x03, 0x49, 0x0d, + 0x02, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x01, + 0x41, 0xec, 0x82, 0xc0, 0x00, 0x6a, 0x36, 0x02, 0x00, 0x0f, 0x0b, 0x00, + 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x4b, 0x01, 0x02, 0x7f, 0x23, 0x00, 0x41, + 0x10, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x20, 0x02, 0x41, 0x08, 0x6a, 0x21, + 0x03, 0x20, 0x01, 0x41, 0x80, 0x80, 0x01, 0x4b, 0x04, 0x40, 0x00, 0x0b, + 0x20, 0x03, 0x20, 0x01, 0x36, 0x02, 0x04, 0x20, 0x03, 0x41, 0xec, 0x82, + 0xc0, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x02, 0x28, 0x02, 0x08, + 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x02, 0x28, 0x02, 0x0c, 0x36, 0x02, + 0x04, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x8b, 0x09, 0x02, + 0x08, 0x7f, 0x03, 0x7e, 0x23, 0x00, 0x41, 0x20, 0x6b, 0x22, 0x03, 0x24, + 0x00, 0x20, 0x03, 0x41, 0x00, 0x3a, 0x00, 0x08, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x7e, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x01, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x41, 0x01, 0x10, 0x31, 0x0d, + 0x00, 0x02, 0x40, 0x20, 0x03, 0x2d, 0x00, 0x08, 0x22, 0x02, 0x41, 0x03, + 0x71, 0x22, 0x04, 0x41, 0x03, 0x47, 0x04, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x20, 0x04, 0x41, 0x01, 0x6b, 0x0e, 0x02, 0x02, 0x01, 0x00, + 0x0b, 0x20, 0x02, 0x41, 0x02, 0x76, 0x21, 0x02, 0x0c, 0x03, 0x0b, 0x20, + 0x03, 0x20, 0x02, 0x3a, 0x00, 0x0d, 0x20, 0x03, 0x41, 0x01, 0x3a, 0x00, + 0x0c, 0x20, 0x03, 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, 0x03, 0x41, 0x00, + 0x36, 0x02, 0x1c, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x20, 0x03, 0x41, 0x1c, + 0x6a, 0x41, 0x04, 0x10, 0x33, 0x0d, 0x03, 0x20, 0x03, 0x28, 0x02, 0x1c, + 0x22, 0x02, 0x41, 0xff, 0xff, 0x03, 0x4d, 0x0d, 0x03, 0x20, 0x02, 0x41, + 0x02, 0x76, 0x21, 0x02, 0x0c, 0x02, 0x0b, 0x20, 0x03, 0x20, 0x02, 0x3a, + 0x00, 0x0d, 0x20, 0x03, 0x41, 0x01, 0x3a, 0x00, 0x0c, 0x20, 0x03, 0x20, + 0x01, 0x36, 0x02, 0x08, 0x20, 0x03, 0x41, 0x00, 0x3b, 0x01, 0x1c, 0x20, + 0x03, 0x41, 0x08, 0x6a, 0x20, 0x03, 0x41, 0x1c, 0x6a, 0x41, 0x02, 0x10, + 0x33, 0x0d, 0x02, 0x20, 0x03, 0x2f, 0x01, 0x1c, 0x22, 0x02, 0x41, 0xff, + 0x01, 0x4d, 0x0d, 0x02, 0x20, 0x02, 0x41, 0x02, 0x76, 0x21, 0x02, 0x0c, + 0x01, 0x0b, 0x20, 0x02, 0x41, 0x04, 0x4f, 0x0d, 0x01, 0x20, 0x03, 0x41, + 0x00, 0x36, 0x02, 0x08, 0x20, 0x01, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x41, + 0x04, 0x10, 0x31, 0x0d, 0x01, 0x20, 0x03, 0x28, 0x02, 0x08, 0x22, 0x02, + 0x41, 0xff, 0xff, 0xff, 0xff, 0x03, 0x4d, 0x0d, 0x01, 0x0b, 0x20, 0x01, + 0x28, 0x02, 0x04, 0x20, 0x02, 0x49, 0x0d, 0x00, 0x20, 0x03, 0x41, 0x08, + 0x6a, 0x20, 0x02, 0x10, 0x30, 0x20, 0x01, 0x20, 0x03, 0x28, 0x02, 0x08, + 0x22, 0x04, 0x20, 0x03, 0x28, 0x02, 0x10, 0x10, 0x31, 0x04, 0x40, 0x20, + 0x03, 0x41, 0x08, 0x6a, 0x10, 0x07, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x20, + 0x03, 0x29, 0x02, 0x0c, 0x22, 0x0c, 0x42, 0x20, 0x88, 0xa7, 0x22, 0x05, + 0x41, 0x07, 0x6b, 0x22, 0x01, 0x20, 0x01, 0x20, 0x05, 0x4b, 0x1b, 0x21, + 0x09, 0x20, 0x04, 0x41, 0x03, 0x6a, 0x41, 0x7c, 0x71, 0x20, 0x04, 0x6b, + 0x21, 0x08, 0x41, 0x00, 0x21, 0x01, 0x03, 0x40, 0x20, 0x01, 0x20, 0x05, + 0x4f, 0x0d, 0x07, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x20, + 0x04, 0x6a, 0x2d, 0x00, 0x00, 0x22, 0x06, 0x41, 0x18, 0x74, 0x41, 0x18, + 0x75, 0x22, 0x07, 0x41, 0x00, 0x4e, 0x04, 0x40, 0x20, 0x08, 0x41, 0x7f, + 0x46, 0x0d, 0x03, 0x20, 0x08, 0x20, 0x01, 0x6b, 0x41, 0x03, 0x71, 0x0d, + 0x03, 0x03, 0x40, 0x20, 0x01, 0x20, 0x09, 0x4f, 0x0d, 0x03, 0x20, 0x01, + 0x20, 0x04, 0x6a, 0x22, 0x02, 0x41, 0x04, 0x6a, 0x28, 0x02, 0x00, 0x20, + 0x02, 0x28, 0x02, 0x00, 0x72, 0x41, 0x80, 0x81, 0x82, 0x84, 0x78, 0x71, + 0x0d, 0x03, 0x20, 0x01, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x22, 0x01, 0x4d, + 0x0d, 0x00, 0x0b, 0x0c, 0x01, 0x0b, 0x42, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x20, 0x21, 0x0a, 0x42, 0x80, 0x80, 0x80, 0x80, 0x10, 0x21, 0x0b, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x06, 0x41, + 0x88, 0x80, 0xc0, 0x00, 0x6a, 0x2d, 0x00, 0x00, 0x41, 0x02, 0x6b, 0x0e, + 0x03, 0x00, 0x02, 0x01, 0x0e, 0x0b, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x22, + 0x02, 0x20, 0x05, 0x49, 0x0d, 0x02, 0x42, 0x00, 0x21, 0x0a, 0x0c, 0x0c, + 0x0b, 0x42, 0x00, 0x21, 0x0a, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x22, 0x02, + 0x20, 0x05, 0x4f, 0x0d, 0x0b, 0x20, 0x02, 0x20, 0x04, 0x6a, 0x2d, 0x00, + 0x00, 0x21, 0x02, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x06, 0x41, 0xf0, 0x01, 0x6b, 0x0e, 0x05, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x0b, 0x20, 0x02, 0x41, 0xbf, 0x01, 0x4b, 0x0d, 0x0c, 0x20, 0x07, + 0x41, 0x0f, 0x6a, 0x41, 0xff, 0x01, 0x71, 0x41, 0x02, 0x4b, 0x0d, 0x0c, + 0x20, 0x02, 0x41, 0x18, 0x74, 0x41, 0x18, 0x75, 0x41, 0x00, 0x4e, 0x0d, + 0x0c, 0x0c, 0x02, 0x0b, 0x20, 0x02, 0x41, 0xf0, 0x00, 0x6a, 0x41, 0xff, + 0x01, 0x71, 0x41, 0x30, 0x4f, 0x0d, 0x0b, 0x0c, 0x01, 0x0b, 0x20, 0x02, + 0x41, 0x18, 0x74, 0x41, 0x18, 0x75, 0x41, 0x7f, 0x4a, 0x0d, 0x0a, 0x20, + 0x02, 0x41, 0x8f, 0x01, 0x4b, 0x0d, 0x0a, 0x0b, 0x20, 0x01, 0x41, 0x02, + 0x6a, 0x22, 0x02, 0x20, 0x05, 0x4f, 0x0d, 0x0b, 0x20, 0x02, 0x20, 0x04, + 0x6a, 0x2d, 0x00, 0x00, 0x41, 0xc0, 0x01, 0x71, 0x41, 0x80, 0x01, 0x47, + 0x0d, 0x08, 0x42, 0x00, 0x21, 0x0b, 0x20, 0x01, 0x41, 0x03, 0x6a, 0x22, + 0x02, 0x20, 0x05, 0x4f, 0x0d, 0x0c, 0x20, 0x02, 0x20, 0x04, 0x6a, 0x2d, + 0x00, 0x00, 0x41, 0xc0, 0x01, 0x71, 0x41, 0x80, 0x01, 0x46, 0x0d, 0x02, + 0x42, 0x80, 0x80, 0x80, 0x80, 0x80, 0xe0, 0x00, 0x0c, 0x0a, 0x0b, 0x42, + 0x00, 0x21, 0x0a, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x22, 0x02, 0x20, 0x05, + 0x4f, 0x0d, 0x0a, 0x20, 0x02, 0x20, 0x04, 0x6a, 0x2d, 0x00, 0x00, 0x21, + 0x02, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x06, 0x41, 0xe0, 0x01, + 0x47, 0x04, 0x40, 0x20, 0x06, 0x41, 0xed, 0x01, 0x46, 0x0d, 0x01, 0x20, + 0x07, 0x41, 0x1f, 0x6a, 0x41, 0xff, 0x01, 0x71, 0x41, 0x0c, 0x49, 0x0d, + 0x02, 0x20, 0x02, 0x41, 0xbf, 0x01, 0x4b, 0x0d, 0x0c, 0x20, 0x07, 0x41, + 0xfe, 0x01, 0x71, 0x41, 0xee, 0x01, 0x47, 0x0d, 0x0c, 0x20, 0x02, 0x41, + 0x18, 0x74, 0x41, 0x18, 0x75, 0x41, 0x00, 0x4e, 0x0d, 0x0c, 0x0c, 0x03, + 0x0b, 0x20, 0x02, 0x41, 0xe0, 0x01, 0x71, 0x41, 0xa0, 0x01, 0x47, 0x0d, + 0x0b, 0x0c, 0x02, 0x0b, 0x20, 0x02, 0x41, 0x18, 0x74, 0x41, 0x18, 0x75, + 0x41, 0x7f, 0x4a, 0x0d, 0x0a, 0x20, 0x02, 0x41, 0xa0, 0x01, 0x4f, 0x0d, + 0x0a, 0x0c, 0x01, 0x0b, 0x20, 0x02, 0x41, 0x18, 0x74, 0x41, 0x18, 0x75, + 0x41, 0x7f, 0x4a, 0x0d, 0x09, 0x20, 0x02, 0x41, 0xbf, 0x01, 0x4b, 0x0d, + 0x09, 0x0b, 0x42, 0x00, 0x21, 0x0b, 0x20, 0x01, 0x41, 0x02, 0x6a, 0x22, + 0x02, 0x20, 0x05, 0x4f, 0x0d, 0x0b, 0x20, 0x02, 0x20, 0x04, 0x6a, 0x2d, + 0x00, 0x00, 0x41, 0xc0, 0x01, 0x71, 0x41, 0x80, 0x01, 0x47, 0x0d, 0x07, + 0x0c, 0x01, 0x0b, 0x20, 0x02, 0x20, 0x04, 0x6a, 0x2d, 0x00, 0x00, 0x41, + 0xc0, 0x01, 0x71, 0x41, 0x80, 0x01, 0x47, 0x0d, 0x0a, 0x0b, 0x20, 0x02, + 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, 0x03, 0x0b, 0x00, 0x0b, 0x20, 0x01, + 0x20, 0x05, 0x20, 0x01, 0x20, 0x05, 0x4b, 0x1b, 0x21, 0x02, 0x03, 0x40, + 0x20, 0x01, 0x20, 0x02, 0x46, 0x04, 0x40, 0x20, 0x02, 0x21, 0x01, 0x0c, + 0x03, 0x0b, 0x20, 0x01, 0x20, 0x04, 0x6a, 0x2c, 0x00, 0x00, 0x41, 0x00, + 0x48, 0x0d, 0x02, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, 0x00, + 0x0b, 0x00, 0x0b, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, 0x00, + 0x0b, 0x00, 0x0b, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0c, 0x06, + 0x0b, 0x42, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc0, 0x00, 0x0c, 0x01, 0x0b, + 0x42, 0x80, 0x80, 0x80, 0x80, 0x80, 0x20, 0x0b, 0x21, 0x0a, 0x42, 0x80, + 0x80, 0x80, 0x80, 0x10, 0x21, 0x0b, 0x0c, 0x01, 0x0b, 0x42, 0x00, 0x21, + 0x0b, 0x0b, 0x20, 0x01, 0xad, 0x20, 0x0a, 0x20, 0x0b, 0x84, 0x84, 0x21, + 0x0a, 0x0b, 0x20, 0x01, 0x20, 0x05, 0x4f, 0x04, 0x40, 0x20, 0x00, 0x20, + 0x0c, 0x37, 0x02, 0x04, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, 0x00, 0x0c, + 0x01, 0x0b, 0x20, 0x03, 0x20, 0x0a, 0x37, 0x02, 0x14, 0x20, 0x03, 0x20, + 0x0c, 0x37, 0x02, 0x0c, 0x20, 0x03, 0x20, 0x04, 0x36, 0x02, 0x08, 0x20, + 0x03, 0x41, 0x08, 0x6a, 0x10, 0x07, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, + 0x00, 0x0b, 0x20, 0x03, 0x41, 0x20, 0x6a, 0x24, 0x00, 0x0b, 0xa0, 0x01, + 0x01, 0x02, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, 0x00, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x04, 0x41, 0x02, 0x46, + 0x04, 0x40, 0x41, 0x0c, 0x41, 0x04, 0x10, 0x0f, 0x22, 0x02, 0x45, 0x0d, + 0x02, 0x20, 0x00, 0x41, 0x00, 0x3a, 0x00, 0x04, 0x20, 0x00, 0x20, 0x02, + 0x36, 0x02, 0x00, 0x20, 0x02, 0x20, 0x01, 0x29, 0x02, 0x00, 0x37, 0x02, + 0x00, 0x20, 0x02, 0x41, 0x08, 0x6a, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x28, + 0x02, 0x00, 0x36, 0x02, 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x41, 0x08, + 0x6a, 0x22, 0x02, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x28, 0x02, 0x00, 0x36, + 0x02, 0x00, 0x20, 0x03, 0x20, 0x01, 0x29, 0x02, 0x00, 0x37, 0x03, 0x00, + 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x00, 0x28, 0x02, 0x00, 0x04, 0x40, + 0x20, 0x00, 0x10, 0x07, 0x0b, 0x20, 0x00, 0x20, 0x03, 0x29, 0x03, 0x00, + 0x37, 0x02, 0x00, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x20, 0x02, 0x28, 0x02, + 0x00, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x00, + 0x0f, 0x0b, 0x00, 0x0b, 0x04, 0x00, 0x41, 0x00, 0x0b, 0xa7, 0x04, 0x02, + 0x05, 0x7f, 0x01, 0x7e, 0x23, 0x00, 0x41, 0xa0, 0x01, 0x6b, 0x22, 0x00, + 0x24, 0x00, 0x20, 0x00, 0x41, 0x18, 0x6a, 0x10, 0x0c, 0x20, 0x00, 0x41, + 0x30, 0x6a, 0x41, 0x00, 0x10, 0x26, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x00, 0x2d, 0x00, 0x30, 0x45, 0x04, 0x40, 0x20, 0x00, 0x41, 0xd0, + 0x00, 0x6a, 0x20, 0x00, 0x41, 0x3c, 0x6a, 0x29, 0x02, 0x00, 0x37, 0x03, + 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x02, 0x34, 0x37, 0x03, 0x48, 0x20, + 0x00, 0x41, 0xe0, 0x00, 0x6a, 0x20, 0x00, 0x41, 0xd4, 0x00, 0x6a, 0x28, + 0x02, 0x00, 0x22, 0x01, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, + 0x02, 0x4c, 0x22, 0x05, 0x37, 0x03, 0x58, 0x20, 0x00, 0x20, 0x01, 0x36, + 0x02, 0x6c, 0x20, 0x00, 0x20, 0x05, 0x3e, 0x02, 0x68, 0x20, 0x00, 0x41, + 0x90, 0x01, 0x6a, 0x20, 0x00, 0x41, 0xe8, 0x00, 0x6a, 0x10, 0x22, 0x20, + 0x00, 0x28, 0x02, 0x90, 0x01, 0x45, 0x0d, 0x02, 0x20, 0x00, 0x41, 0xf8, + 0x00, 0x6a, 0x20, 0x00, 0x41, 0x98, 0x01, 0x6a, 0x28, 0x02, 0x00, 0x22, + 0x01, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0x90, 0x01, + 0x22, 0x05, 0x37, 0x03, 0x70, 0x20, 0x00, 0x41, 0x88, 0x01, 0x6a, 0x20, + 0x01, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x05, 0x37, 0x03, 0x80, 0x01, + 0x02, 0x40, 0x20, 0x00, 0x41, 0x18, 0x6a, 0x10, 0x1d, 0x45, 0x04, 0x40, + 0x20, 0x00, 0x41, 0x10, 0x6a, 0x21, 0x03, 0x20, 0x00, 0x41, 0x24, 0x6a, + 0x22, 0x01, 0x22, 0x02, 0x28, 0x02, 0x00, 0x04, 0x40, 0x00, 0x0b, 0x20, + 0x02, 0x41, 0x7f, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x02, 0x36, 0x02, + 0x04, 0x20, 0x03, 0x20, 0x02, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, + 0x00, 0x28, 0x02, 0x14, 0x21, 0x02, 0x20, 0x00, 0x28, 0x02, 0x10, 0x20, + 0x00, 0x41, 0x98, 0x01, 0x6a, 0x20, 0x00, 0x41, 0xf8, 0x00, 0x6a, 0x28, + 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0x70, + 0x37, 0x03, 0x90, 0x01, 0x20, 0x00, 0x41, 0x90, 0x01, 0x6a, 0x10, 0x23, + 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x03, 0x41, 0x01, 0x6a, 0x22, 0x04, + 0x20, 0x03, 0x48, 0x0d, 0x05, 0x20, 0x02, 0x20, 0x04, 0x36, 0x02, 0x00, + 0x20, 0x00, 0x41, 0x08, 0x6a, 0x21, 0x02, 0x20, 0x01, 0x28, 0x02, 0x00, + 0x04, 0x40, 0x00, 0x0b, 0x20, 0x01, 0x41, 0x7f, 0x36, 0x02, 0x00, 0x20, + 0x02, 0x20, 0x01, 0x36, 0x02, 0x04, 0x20, 0x02, 0x20, 0x01, 0x41, 0x04, + 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x28, 0x02, 0x0c, 0x21, 0x01, 0x20, + 0x00, 0x28, 0x02, 0x08, 0x22, 0x02, 0x2d, 0x00, 0x04, 0x41, 0x02, 0x47, + 0x04, 0x40, 0x20, 0x02, 0x41, 0x01, 0x3a, 0x00, 0x04, 0x0b, 0x20, 0x01, + 0x28, 0x02, 0x00, 0x22, 0x02, 0x41, 0x01, 0x6a, 0x22, 0x03, 0x20, 0x02, + 0x48, 0x0d, 0x05, 0x20, 0x01, 0x20, 0x03, 0x36, 0x02, 0x00, 0x0c, 0x01, + 0x0b, 0x20, 0x00, 0x41, 0x80, 0x01, 0x6a, 0x10, 0x07, 0x0b, 0x20, 0x00, + 0x41, 0x18, 0x6a, 0x10, 0x27, 0x20, 0x00, 0x41, 0xd8, 0x00, 0x6a, 0x10, + 0x07, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x41, 0xc8, 0x00, 0x6a, 0x41, 0xb5, + 0x82, 0xc0, 0x00, 0x10, 0x28, 0x20, 0x00, 0x41, 0xc8, 0x00, 0x6a, 0x10, + 0x1b, 0x20, 0x00, 0x41, 0xc8, 0x00, 0x6a, 0x10, 0x07, 0x0b, 0x20, 0x00, + 0x41, 0x18, 0x6a, 0x10, 0x07, 0x20, 0x00, 0x41, 0x2c, 0x6a, 0x2d, 0x00, + 0x00, 0x41, 0x02, 0x47, 0x04, 0x40, 0x20, 0x00, 0x28, 0x02, 0x28, 0x22, + 0x01, 0x28, 0x02, 0x00, 0x04, 0x7f, 0x20, 0x01, 0x10, 0x07, 0x20, 0x00, + 0x28, 0x02, 0x28, 0x05, 0x20, 0x01, 0x0b, 0x41, 0x0c, 0x10, 0x08, 0x0b, + 0x20, 0x00, 0x41, 0xa0, 0x01, 0x6a, 0x24, 0x00, 0x0f, 0x0b, 0x20, 0x00, + 0x41, 0xd8, 0x00, 0x6a, 0x10, 0x29, 0x00, 0x0b, 0x00, 0x0b, 0x97, 0x02, + 0x01, 0x02, 0x7f, 0x23, 0x00, 0x41, 0x30, 0x6b, 0x22, 0x02, 0x24, 0x00, + 0x02, 0x40, 0x02, 0x40, 0x10, 0x04, 0x22, 0x03, 0x41, 0x03, 0x4b, 0x0d, + 0x00, 0x20, 0x01, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x81, 0x08, 0x3b, + 0x01, 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x02, 0x20, 0x03, 0x10, 0x30, 0x20, + 0x02, 0x28, 0x02, 0x00, 0x22, 0x03, 0x10, 0x05, 0x20, 0x00, 0x02, 0x7f, + 0x02, 0x40, 0x20, 0x01, 0x04, 0x40, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, + 0x08, 0x22, 0x01, 0x36, 0x02, 0x14, 0x20, 0x02, 0x20, 0x03, 0x36, 0x02, + 0x10, 0x02, 0x40, 0x20, 0x01, 0x41, 0x04, 0x49, 0x0d, 0x00, 0x20, 0x02, + 0x41, 0x00, 0x36, 0x02, 0x1c, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x20, 0x02, + 0x41, 0x1c, 0x6a, 0x41, 0x04, 0x10, 0x31, 0x0d, 0x00, 0x20, 0x02, 0x41, + 0x20, 0x6a, 0x20, 0x02, 0x28, 0x02, 0x14, 0x10, 0x30, 0x20, 0x02, 0x41, + 0x10, 0x6a, 0x20, 0x02, 0x28, 0x02, 0x20, 0x22, 0x01, 0x20, 0x02, 0x28, + 0x02, 0x28, 0x10, 0x31, 0x45, 0x0d, 0x02, 0x20, 0x02, 0x41, 0x20, 0x6a, + 0x10, 0x07, 0x0b, 0x20, 0x00, 0x41, 0x00, 0x3a, 0x00, 0x01, 0x41, 0x01, + 0x0c, 0x02, 0x0b, 0x20, 0x00, 0x41, 0x00, 0x3a, 0x00, 0x00, 0x20, 0x00, + 0x41, 0x04, 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x08, + 0x6a, 0x20, 0x02, 0x29, 0x03, 0x00, 0x37, 0x02, 0x00, 0x20, 0x00, 0x41, + 0x10, 0x6a, 0x20, 0x02, 0x41, 0x08, 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, + 0x00, 0x0c, 0x02, 0x0b, 0x20, 0x02, 0x28, 0x02, 0x1c, 0x21, 0x03, 0x20, + 0x00, 0x41, 0x0c, 0x6a, 0x20, 0x02, 0x29, 0x02, 0x24, 0x37, 0x02, 0x00, + 0x20, 0x00, 0x41, 0x08, 0x6a, 0x20, 0x01, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x41, 0x04, 0x6a, 0x20, 0x03, 0x36, 0x02, 0x00, 0x41, 0x00, 0x0b, 0x3a, + 0x00, 0x00, 0x20, 0x02, 0x10, 0x07, 0x0b, 0x20, 0x02, 0x41, 0x30, 0x6a, + 0x24, 0x00, 0x0b, 0xdb, 0x03, 0x01, 0x06, 0x7f, 0x23, 0x00, 0x41, 0x30, + 0x6b, 0x22, 0x02, 0x24, 0x00, 0x20, 0x02, 0x41, 0x20, 0x6a, 0x20, 0x00, + 0x41, 0x0c, 0x6a, 0x22, 0x04, 0x10, 0x1f, 0x20, 0x02, 0x28, 0x02, 0x24, + 0x22, 0x05, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x01, 0x6b, 0x22, 0x03, + 0x20, 0x01, 0x4e, 0x21, 0x06, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x20, 0x02, 0x28, 0x02, 0x20, 0x2d, 0x00, 0x04, 0x22, + 0x01, 0x41, 0x02, 0x47, 0x04, 0x40, 0x20, 0x06, 0x0d, 0x05, 0x20, 0x05, + 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x01, 0x45, 0x0d, 0x04, 0x20, 0x02, + 0x41, 0x18, 0x6a, 0x21, 0x01, 0x20, 0x04, 0x28, 0x02, 0x00, 0x04, 0x40, + 0x00, 0x0b, 0x20, 0x04, 0x41, 0x7f, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, + 0x04, 0x36, 0x02, 0x04, 0x20, 0x01, 0x20, 0x04, 0x41, 0x04, 0x6a, 0x36, + 0x02, 0x00, 0x20, 0x02, 0x28, 0x02, 0x1c, 0x21, 0x05, 0x20, 0x02, 0x28, + 0x02, 0x18, 0x22, 0x01, 0x2d, 0x00, 0x04, 0x41, 0x02, 0x46, 0x04, 0x40, + 0x00, 0x0b, 0x20, 0x01, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x00, 0x20, + 0x01, 0x28, 0x02, 0x00, 0x1b, 0x22, 0x03, 0x45, 0x0d, 0x03, 0x41, 0xe8, + 0x82, 0xc0, 0x00, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x28, 0x02, + 0x08, 0x21, 0x06, 0x20, 0x00, 0x28, 0x02, 0x00, 0x21, 0x01, 0x20, 0x03, + 0x28, 0x02, 0x00, 0x21, 0x00, 0x20, 0x03, 0x28, 0x02, 0x08, 0x22, 0x03, + 0x41, 0x3f, 0x4d, 0x04, 0x40, 0x20, 0x03, 0x41, 0x02, 0x74, 0x10, 0x2f, + 0x0c, 0x03, 0x0b, 0x20, 0x03, 0x41, 0xff, 0xff, 0x00, 0x4d, 0x04, 0x40, + 0x20, 0x02, 0x20, 0x03, 0x41, 0x02, 0x74, 0x41, 0x01, 0x72, 0x3b, 0x01, + 0x2e, 0x20, 0x02, 0x41, 0x2e, 0x6a, 0x41, 0x02, 0x10, 0x2e, 0x0c, 0x03, + 0x0b, 0x20, 0x03, 0x41, 0xff, 0xff, 0xff, 0xff, 0x03, 0x4b, 0x0d, 0x01, + 0x20, 0x03, 0x41, 0x02, 0x74, 0x41, 0x02, 0x72, 0x10, 0x2d, 0x0c, 0x02, + 0x0b, 0x20, 0x06, 0x0d, 0x04, 0x20, 0x05, 0x20, 0x03, 0x36, 0x02, 0x00, + 0x0c, 0x03, 0x0b, 0x41, 0x03, 0x10, 0x2f, 0x20, 0x03, 0x10, 0x2d, 0x0b, + 0x20, 0x00, 0x20, 0x03, 0x10, 0x2e, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x41, + 0xe8, 0x82, 0xc0, 0x00, 0x28, 0x02, 0x00, 0x10, 0x21, 0x20, 0x01, 0x20, + 0x06, 0x20, 0x02, 0x28, 0x02, 0x10, 0x20, 0x02, 0x28, 0x02, 0x14, 0x10, + 0x03, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x01, 0x6a, + 0x22, 0x00, 0x20, 0x01, 0x48, 0x0d, 0x01, 0x20, 0x05, 0x20, 0x00, 0x36, + 0x02, 0x00, 0x20, 0x02, 0x41, 0x08, 0x6a, 0x21, 0x00, 0x20, 0x04, 0x28, + 0x02, 0x00, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x04, 0x41, 0x7f, 0x36, 0x02, + 0x00, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x04, + 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x02, 0x28, 0x02, 0x0c, 0x21, + 0x01, 0x20, 0x02, 0x28, 0x02, 0x08, 0x22, 0x00, 0x2d, 0x00, 0x04, 0x41, + 0x02, 0x47, 0x04, 0x40, 0x20, 0x00, 0x41, 0x00, 0x3a, 0x00, 0x04, 0x0b, + 0x20, 0x01, 0x28, 0x02, 0x00, 0x22, 0x04, 0x41, 0x01, 0x6a, 0x22, 0x00, + 0x20, 0x04, 0x48, 0x0d, 0x01, 0x20, 0x01, 0x20, 0x00, 0x36, 0x02, 0x00, + 0x0b, 0x20, 0x02, 0x41, 0x30, 0x6a, 0x24, 0x00, 0x0f, 0x0b, 0x00, 0x0b, + 0x0a, 0x00, 0x20, 0x00, 0x20, 0x01, 0x41, 0x14, 0x10, 0x0a, 0x0b, 0x57, + 0x01, 0x01, 0x7f, 0x23, 0x00, 0x41, 0x20, 0x6b, 0x22, 0x01, 0x24, 0x00, + 0x20, 0x01, 0x41, 0x9e, 0x82, 0xc0, 0x00, 0x10, 0x28, 0x20, 0x01, 0x41, + 0x88, 0x82, 0xc0, 0x00, 0x41, 0x04, 0x10, 0x2a, 0x20, 0x01, 0x41, 0xb2, + 0x82, 0xc0, 0x00, 0x41, 0x03, 0x10, 0x2a, 0x20, 0x01, 0x41, 0x10, 0x6a, + 0x20, 0x00, 0x28, 0x02, 0x00, 0x20, 0x00, 0x28, 0x02, 0x08, 0x10, 0x2b, + 0x20, 0x01, 0x20, 0x01, 0x28, 0x02, 0x10, 0x20, 0x01, 0x28, 0x02, 0x18, + 0x10, 0x2a, 0x20, 0x01, 0x41, 0x10, 0x6a, 0x10, 0x07, 0x20, 0x01, 0x10, + 0x1b, 0x00, 0x0b, 0x0a, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x10, + 0x13, 0x0b, 0x89, 0x01, 0x02, 0x02, 0x7f, 0x01, 0x7e, 0x23, 0x00, 0x41, + 0x10, 0x6b, 0x22, 0x03, 0x24, 0x00, 0x02, 0x40, 0x20, 0x02, 0x20, 0x02, + 0x20, 0x02, 0x6a, 0x22, 0x04, 0x4d, 0x04, 0x40, 0x20, 0x03, 0x41, 0x08, + 0x6a, 0x20, 0x04, 0x41, 0x00, 0x10, 0x0b, 0x20, 0x03, 0x29, 0x03, 0x08, + 0x21, 0x05, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x08, 0x20, 0x00, 0x20, + 0x05, 0x37, 0x02, 0x00, 0x03, 0x40, 0x20, 0x02, 0x45, 0x0d, 0x02, 0x20, + 0x00, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x22, 0x04, 0x41, 0x04, 0x76, 0x41, + 0xd4, 0x82, 0xc0, 0x00, 0x6a, 0x2d, 0x00, 0x00, 0x10, 0x32, 0x20, 0x00, + 0x20, 0x04, 0x41, 0x0f, 0x71, 0x41, 0xd4, 0x82, 0xc0, 0x00, 0x6a, 0x2d, + 0x00, 0x00, 0x10, 0x32, 0x20, 0x02, 0x41, 0x01, 0x6b, 0x21, 0x02, 0x20, + 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, 0x00, 0x0b, 0x00, 0x0b, 0x00, + 0x0b, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0xa3, 0x11, 0x02, + 0x08, 0x7f, 0x02, 0x7e, 0x23, 0x00, 0x41, 0xb0, 0x02, 0x6b, 0x22, 0x00, + 0x24, 0x00, 0x20, 0x00, 0x41, 0xd0, 0x00, 0x6a, 0x10, 0x0c, 0x20, 0x00, + 0x41, 0xe0, 0x01, 0x6a, 0x41, 0x01, 0x10, 0x26, 0x02, 0x40, 0x02, 0x7f, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x00, 0x2d, 0x00, 0xe0, 0x01, 0x41, 0x01, 0x47, 0x04, 0x40, 0x20, + 0x00, 0x41, 0xf0, 0x00, 0x6a, 0x20, 0x00, 0x41, 0xf0, 0x01, 0x6a, 0x28, + 0x02, 0x00, 0x22, 0x02, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x41, + 0xe8, 0x01, 0x6a, 0x29, 0x03, 0x00, 0x22, 0x08, 0x37, 0x03, 0x68, 0x20, + 0x00, 0x28, 0x02, 0xe4, 0x01, 0x21, 0x01, 0x20, 0x00, 0x41, 0x80, 0x01, + 0x6a, 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x08, 0x37, 0x03, + 0x78, 0x20, 0x01, 0x41, 0xc4, 0xa5, 0x8a, 0x98, 0x7e, 0x46, 0x0d, 0x03, + 0x20, 0x01, 0x41, 0xed, 0x98, 0x99, 0xe7, 0x03, 0x46, 0x0d, 0x02, 0x20, + 0x01, 0x41, 0xce, 0xa6, 0xa3, 0xf4, 0x05, 0x46, 0x0d, 0x01, 0x20, 0x00, + 0x20, 0x01, 0x36, 0x02, 0xe0, 0x01, 0x20, 0x00, 0x41, 0x30, 0x6a, 0x41, + 0x04, 0x72, 0x20, 0x00, 0x41, 0xe0, 0x01, 0x6a, 0x41, 0x04, 0x10, 0x0a, + 0x41, 0x00, 0x21, 0x02, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x30, 0x20, + 0x00, 0x41, 0xf8, 0x00, 0x6a, 0x10, 0x07, 0x0c, 0x08, 0x0b, 0x20, 0x00, + 0x41, 0x02, 0x36, 0x02, 0x30, 0x41, 0x02, 0x0c, 0x06, 0x0b, 0x20, 0x00, + 0x20, 0x00, 0x28, 0x02, 0x80, 0x01, 0x36, 0x02, 0x94, 0x01, 0x20, 0x00, + 0x20, 0x00, 0x28, 0x02, 0x78, 0x36, 0x02, 0x90, 0x01, 0x20, 0x00, 0x41, + 0xa0, 0x02, 0x6a, 0x20, 0x00, 0x41, 0x90, 0x01, 0x6a, 0x10, 0x22, 0x20, + 0x00, 0x28, 0x02, 0xa0, 0x02, 0x04, 0x40, 0x20, 0x00, 0x41, 0xe8, 0x01, + 0x6a, 0x20, 0x00, 0x41, 0xa8, 0x02, 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, + 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0xa0, 0x02, 0x37, 0x03, 0xe0, + 0x01, 0x20, 0x00, 0x41, 0xd0, 0x00, 0x6a, 0x10, 0x1e, 0x20, 0x00, 0x41, + 0xe4, 0x00, 0x6a, 0x2d, 0x00, 0x00, 0x41, 0x02, 0x46, 0x0d, 0x03, 0x20, + 0x00, 0x41, 0x01, 0x3a, 0x00, 0x64, 0x20, 0x00, 0x41, 0xe0, 0x00, 0x6a, + 0x28, 0x02, 0x00, 0x22, 0x01, 0x28, 0x02, 0x00, 0x45, 0x0d, 0x03, 0x20, + 0x01, 0x10, 0x07, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x20, 0x00, 0x41, 0xe8, + 0x01, 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, 0x00, + 0x29, 0x03, 0xe0, 0x01, 0x37, 0x02, 0x00, 0x20, 0x00, 0x41, 0xd0, 0x00, + 0x6a, 0x10, 0x27, 0x20, 0x00, 0x41, 0x03, 0x36, 0x02, 0x30, 0x0c, 0x05, + 0x0b, 0x20, 0x00, 0x41, 0xe0, 0x01, 0x6a, 0x41, 0x04, 0x72, 0x41, 0x88, + 0x82, 0xc0, 0x00, 0x41, 0x04, 0x10, 0x0a, 0x20, 0x00, 0x41, 0xf0, 0x01, + 0x6a, 0x22, 0x01, 0x20, 0x00, 0x28, 0x02, 0x90, 0x01, 0x20, 0x00, 0x28, + 0x02, 0x94, 0x01, 0x10, 0x0a, 0x20, 0x00, 0x41, 0xc8, 0x01, 0x6a, 0x20, + 0x00, 0x41, 0xe8, 0x01, 0x6a, 0x29, 0x03, 0x00, 0x22, 0x08, 0x37, 0x03, + 0x00, 0x20, 0x00, 0x41, 0xd0, 0x01, 0x6a, 0x20, 0x01, 0x29, 0x03, 0x00, + 0x22, 0x09, 0x37, 0x03, 0x00, 0x20, 0x00, 0x41, 0xa8, 0x01, 0x6a, 0x22, + 0x01, 0x20, 0x08, 0x37, 0x03, 0x00, 0x20, 0x00, 0x41, 0xb0, 0x01, 0x6a, + 0x22, 0x02, 0x20, 0x09, 0x37, 0x03, 0x00, 0x20, 0x00, 0x41, 0xb8, 0x01, + 0x6a, 0x22, 0x05, 0x20, 0x00, 0x41, 0xf8, 0x01, 0x6a, 0x28, 0x02, 0x00, + 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x01, 0x36, 0x02, 0xe0, 0x01, 0x20, + 0x00, 0x20, 0x00, 0x29, 0x03, 0xe0, 0x01, 0x37, 0x03, 0xa0, 0x01, 0x20, + 0x00, 0x41, 0xc8, 0x00, 0x6a, 0x20, 0x05, 0x28, 0x02, 0x00, 0x36, 0x02, + 0x00, 0x20, 0x00, 0x41, 0x40, 0x6b, 0x20, 0x02, 0x29, 0x03, 0x00, 0x37, + 0x03, 0x00, 0x20, 0x00, 0x41, 0x38, 0x6a, 0x20, 0x01, 0x29, 0x03, 0x00, + 0x37, 0x03, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0xa0, 0x01, 0x37, + 0x03, 0x30, 0x0c, 0x04, 0x0b, 0x20, 0x00, 0x41, 0xd0, 0x00, 0x6a, 0x10, + 0x1d, 0x22, 0x01, 0x45, 0x0d, 0x01, 0x20, 0x00, 0x41, 0xc0, 0x01, 0x6a, + 0x20, 0x01, 0x10, 0x11, 0x20, 0x00, 0x41, 0xe0, 0x01, 0x6a, 0x20, 0x00, + 0x41, 0xc0, 0x01, 0x6a, 0x10, 0x1c, 0x20, 0x00, 0x28, 0x02, 0xe0, 0x01, + 0x20, 0x00, 0x28, 0x02, 0xe8, 0x01, 0x10, 0x02, 0x20, 0x00, 0x41, 0xe0, + 0x01, 0x6a, 0x10, 0x07, 0x20, 0x00, 0x41, 0x03, 0x36, 0x02, 0x30, 0x20, + 0x00, 0x41, 0xc0, 0x01, 0x6a, 0x10, 0x07, 0x0c, 0x03, 0x0b, 0x20, 0x00, + 0x20, 0x00, 0x28, 0x02, 0x80, 0x01, 0x36, 0x02, 0x8c, 0x01, 0x20, 0x00, + 0x20, 0x00, 0x28, 0x02, 0x78, 0x36, 0x02, 0x88, 0x01, 0x20, 0x00, 0x41, + 0xa0, 0x02, 0x6a, 0x20, 0x00, 0x41, 0x88, 0x01, 0x6a, 0x10, 0x22, 0x20, + 0x00, 0x28, 0x02, 0xa0, 0x02, 0x04, 0x40, 0x20, 0x00, 0x41, 0x98, 0x01, + 0x6a, 0x20, 0x00, 0x41, 0xa8, 0x02, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x03, + 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0xa0, 0x02, 0x22, + 0x08, 0x37, 0x03, 0x90, 0x01, 0x20, 0x00, 0x41, 0xa8, 0x01, 0x6a, 0x20, + 0x03, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x08, 0x37, 0x03, 0xa0, 0x01, + 0x20, 0x08, 0xa7, 0x21, 0x06, 0x02, 0x7f, 0x20, 0x03, 0x04, 0x40, 0x20, + 0x00, 0x41, 0x28, 0x6a, 0x20, 0x06, 0x20, 0x03, 0x41, 0x00, 0x10, 0x16, + 0x20, 0x00, 0x28, 0x02, 0x2c, 0x20, 0x00, 0x28, 0x02, 0x28, 0x21, 0x01, + 0x20, 0x00, 0x41, 0x20, 0x6a, 0x20, 0x06, 0x20, 0x03, 0x41, 0x01, 0x10, + 0x16, 0x20, 0x00, 0x28, 0x02, 0x24, 0x20, 0x00, 0x41, 0x18, 0x6a, 0x20, + 0x06, 0x20, 0x03, 0x20, 0x01, 0x20, 0x00, 0x28, 0x02, 0x20, 0x22, 0x04, + 0x20, 0x01, 0x20, 0x04, 0x4b, 0x22, 0x04, 0x1b, 0x22, 0x01, 0x10, 0x17, + 0x20, 0x04, 0x1b, 0x22, 0x04, 0x20, 0x01, 0x6a, 0x22, 0x07, 0x20, 0x04, + 0x49, 0x0d, 0x03, 0x20, 0x00, 0x28, 0x02, 0x1c, 0x21, 0x02, 0x20, 0x00, + 0x28, 0x02, 0x18, 0x21, 0x05, 0x20, 0x00, 0x41, 0x10, 0x6a, 0x20, 0x04, + 0x20, 0x07, 0x20, 0x06, 0x20, 0x03, 0x10, 0x18, 0x02, 0x7f, 0x20, 0x00, + 0x28, 0x02, 0x10, 0x21, 0x07, 0x20, 0x00, 0x28, 0x02, 0x14, 0x20, 0x02, + 0x46, 0x04, 0x7f, 0x20, 0x05, 0x20, 0x07, 0x20, 0x02, 0x10, 0x3b, 0x45, + 0x05, 0x41, 0x00, 0x0b, 0x45, 0x04, 0x40, 0x20, 0x03, 0x20, 0x01, 0x6b, + 0x22, 0x02, 0x20, 0x03, 0x4b, 0x0d, 0x05, 0x20, 0x01, 0x20, 0x02, 0x20, + 0x01, 0x20, 0x02, 0x4b, 0x1b, 0x22, 0x02, 0x41, 0x01, 0x6a, 0x22, 0x04, + 0x20, 0x02, 0x49, 0x0d, 0x05, 0x41, 0x7f, 0x21, 0x05, 0x20, 0x06, 0x20, + 0x03, 0x10, 0x1a, 0x21, 0x08, 0x20, 0x01, 0x21, 0x02, 0x41, 0x7f, 0x0c, + 0x01, 0x0b, 0x41, 0x00, 0x21, 0x05, 0x20, 0x03, 0x20, 0x06, 0x20, 0x03, + 0x20, 0x04, 0x41, 0x00, 0x10, 0x19, 0x22, 0x02, 0x20, 0x06, 0x20, 0x03, + 0x20, 0x04, 0x41, 0x01, 0x10, 0x19, 0x22, 0x07, 0x20, 0x02, 0x20, 0x07, + 0x4b, 0x1b, 0x6b, 0x22, 0x02, 0x20, 0x03, 0x4b, 0x0d, 0x04, 0x20, 0x00, + 0x41, 0x08, 0x6a, 0x20, 0x06, 0x20, 0x03, 0x20, 0x04, 0x10, 0x17, 0x20, + 0x00, 0x28, 0x02, 0x08, 0x20, 0x00, 0x28, 0x02, 0x0c, 0x10, 0x1a, 0x21, + 0x08, 0x20, 0x03, 0x0b, 0x21, 0x07, 0x20, 0x00, 0x41, 0x9c, 0x02, 0x6a, + 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x94, 0x02, 0x6a, 0x41, + 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x88, 0x02, 0x6a, 0x20, 0x07, + 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x84, 0x02, 0x6a, 0x20, 0x05, 0x36, + 0x02, 0x00, 0x20, 0x00, 0x41, 0xfc, 0x01, 0x6a, 0x42, 0x00, 0x37, 0x02, + 0x00, 0x20, 0x00, 0x41, 0xf8, 0x01, 0x6a, 0x20, 0x04, 0x36, 0x02, 0x00, + 0x20, 0x00, 0x41, 0xf4, 0x01, 0x6a, 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, + 0x00, 0x41, 0xf0, 0x01, 0x6a, 0x20, 0x01, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x41, 0xe8, 0x01, 0x6a, 0x20, 0x08, 0x37, 0x03, 0x00, 0x20, 0x00, 0x20, + 0x06, 0x36, 0x02, 0x98, 0x02, 0x20, 0x00, 0x41, 0x88, 0x80, 0xc0, 0x00, + 0x36, 0x02, 0x90, 0x02, 0x20, 0x00, 0x41, 0x01, 0x36, 0x02, 0xe0, 0x01, + 0x20, 0x08, 0x42, 0x20, 0x88, 0xa7, 0x21, 0x02, 0x20, 0x03, 0x0c, 0x01, + 0x0b, 0x20, 0x00, 0x41, 0x9c, 0x02, 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, + 0x20, 0x00, 0x41, 0x94, 0x02, 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, + 0x00, 0x41, 0xec, 0x01, 0x6a, 0x41, 0x81, 0x02, 0x3b, 0x01, 0x00, 0x20, + 0x00, 0x41, 0xe8, 0x01, 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x20, 0x06, 0x36, 0x02, 0x98, 0x02, 0x20, 0x00, 0x41, 0x88, 0x80, 0xc0, + 0x00, 0x36, 0x02, 0x90, 0x02, 0x20, 0x00, 0x42, 0x00, 0x37, 0x03, 0xe0, + 0x01, 0x41, 0x01, 0x21, 0x02, 0x41, 0x00, 0x0b, 0x21, 0x01, 0x02, 0x40, + 0x20, 0x03, 0x04, 0x40, 0x20, 0x00, 0x41, 0xe8, 0x01, 0x6a, 0x21, 0x02, + 0x20, 0x00, 0x41, 0x84, 0x02, 0x6a, 0x28, 0x02, 0x00, 0x41, 0x7f, 0x47, + 0x04, 0x40, 0x20, 0x00, 0x41, 0xc0, 0x01, 0x6a, 0x20, 0x02, 0x20, 0x01, + 0x10, 0x09, 0x0c, 0x02, 0x0b, 0x20, 0x00, 0x41, 0xc0, 0x01, 0x6a, 0x20, + 0x02, 0x20, 0x01, 0x10, 0x09, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x28, 0x02, + 0xe4, 0x01, 0x22, 0x01, 0x0d, 0x03, 0x20, 0x02, 0x41, 0xff, 0x01, 0x71, + 0x04, 0x40, 0x20, 0x00, 0x41, 0xc8, 0x01, 0x6a, 0x20, 0x01, 0x36, 0x02, + 0x00, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0xc4, 0x01, 0x20, 0x00, 0x41, + 0x01, 0x36, 0x02, 0xc0, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x41, 0x00, + 0x36, 0x02, 0xc0, 0x01, 0x0b, 0x20, 0x00, 0x28, 0x02, 0xc0, 0x01, 0x21, + 0x01, 0x20, 0x00, 0x41, 0xa0, 0x01, 0x6a, 0x10, 0x07, 0x20, 0x00, 0x41, + 0x01, 0x41, 0x00, 0x10, 0x0b, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0xe8, + 0x01, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0x00, 0x37, 0x03, 0xe0, 0x01, + 0x20, 0x00, 0x20, 0x01, 0x3a, 0x00, 0xc0, 0x01, 0x20, 0x00, 0x41, 0xe0, + 0x01, 0x6a, 0x20, 0x00, 0x41, 0xc0, 0x01, 0x6a, 0x41, 0x01, 0x10, 0x13, + 0x20, 0x00, 0x28, 0x02, 0xe0, 0x01, 0x20, 0x00, 0x28, 0x02, 0xe8, 0x01, + 0x10, 0x02, 0x20, 0x00, 0x41, 0xe0, 0x01, 0x6a, 0x10, 0x07, 0x20, 0x00, + 0x41, 0x03, 0x36, 0x02, 0x30, 0x0c, 0x03, 0x0b, 0x20, 0x00, 0x41, 0xe0, + 0x01, 0x6a, 0x41, 0x04, 0x72, 0x41, 0x80, 0x80, 0xc0, 0x00, 0x41, 0x05, + 0x10, 0x0a, 0x20, 0x00, 0x41, 0xf0, 0x01, 0x6a, 0x22, 0x01, 0x20, 0x00, + 0x28, 0x02, 0x88, 0x01, 0x20, 0x00, 0x28, 0x02, 0x8c, 0x01, 0x10, 0x0a, + 0x20, 0x00, 0x41, 0xc8, 0x01, 0x6a, 0x20, 0x00, 0x41, 0xe8, 0x01, 0x6a, + 0x29, 0x03, 0x00, 0x22, 0x08, 0x37, 0x03, 0x00, 0x20, 0x00, 0x41, 0xd0, + 0x01, 0x6a, 0x20, 0x01, 0x29, 0x03, 0x00, 0x22, 0x09, 0x37, 0x03, 0x00, + 0x20, 0x00, 0x41, 0xa8, 0x01, 0x6a, 0x22, 0x01, 0x20, 0x08, 0x37, 0x03, + 0x00, 0x20, 0x00, 0x41, 0xb0, 0x01, 0x6a, 0x22, 0x02, 0x20, 0x09, 0x37, + 0x03, 0x00, 0x20, 0x00, 0x41, 0xb8, 0x01, 0x6a, 0x22, 0x05, 0x20, 0x00, + 0x41, 0xf8, 0x01, 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x41, 0x01, 0x36, 0x02, 0xe0, 0x01, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, + 0xe0, 0x01, 0x37, 0x03, 0xa0, 0x01, 0x20, 0x00, 0x41, 0xc8, 0x00, 0x6a, + 0x20, 0x05, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x40, + 0x6b, 0x20, 0x02, 0x29, 0x03, 0x00, 0x37, 0x03, 0x00, 0x20, 0x00, 0x41, + 0x38, 0x6a, 0x20, 0x01, 0x29, 0x03, 0x00, 0x37, 0x03, 0x00, 0x20, 0x00, + 0x20, 0x00, 0x29, 0x03, 0xa0, 0x01, 0x37, 0x03, 0x30, 0x0c, 0x02, 0x0b, + 0x00, 0x0b, 0x20, 0x01, 0x10, 0x15, 0x00, 0x0b, 0x20, 0x00, 0x41, 0xf8, + 0x00, 0x6a, 0x10, 0x07, 0x20, 0x00, 0x28, 0x02, 0x30, 0x0b, 0x21, 0x02, + 0x0b, 0x20, 0x00, 0x41, 0xd0, 0x00, 0x6a, 0x10, 0x06, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x20, 0x02, 0x41, 0x03, 0x47, 0x04, 0x40, 0x20, 0x00, + 0x41, 0x30, 0x6a, 0x41, 0x04, 0x72, 0x21, 0x01, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x02, 0x41, 0x01, 0x6b, 0x0e, 0x02, 0x00, 0x01, 0x03, 0x0b, 0x20, + 0x00, 0x41, 0xa8, 0x02, 0x6a, 0x22, 0x02, 0x20, 0x01, 0x41, 0x08, 0x6a, + 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x29, 0x02, + 0x00, 0x37, 0x03, 0xa0, 0x02, 0x20, 0x00, 0x41, 0xd8, 0x00, 0x6a, 0x22, + 0x01, 0x20, 0x00, 0x41, 0xc8, 0x00, 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, + 0x00, 0x20, 0x00, 0x20, 0x00, 0x41, 0x40, 0x6b, 0x29, 0x03, 0x00, 0x37, + 0x03, 0x50, 0x20, 0x00, 0x41, 0xa0, 0x01, 0x6a, 0x41, 0x9e, 0x82, 0xc0, + 0x00, 0x41, 0x14, 0x10, 0x0a, 0x20, 0x00, 0x41, 0xa0, 0x01, 0x6a, 0x20, + 0x00, 0x28, 0x02, 0xa0, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x10, 0x13, + 0x20, 0x00, 0x41, 0xa0, 0x01, 0x6a, 0x41, 0xb2, 0x82, 0xc0, 0x00, 0x41, + 0x03, 0x10, 0x13, 0x20, 0x00, 0x41, 0xc0, 0x01, 0x6a, 0x20, 0x00, 0x28, + 0x02, 0x50, 0x20, 0x01, 0x28, 0x02, 0x00, 0x10, 0x2b, 0x20, 0x00, 0x41, + 0xa0, 0x01, 0x6a, 0x20, 0x00, 0x28, 0x02, 0xc0, 0x01, 0x20, 0x00, 0x28, + 0x02, 0xc8, 0x01, 0x10, 0x13, 0x20, 0x00, 0x41, 0xc0, 0x01, 0x6a, 0x10, + 0x07, 0x20, 0x00, 0x41, 0xe8, 0x01, 0x6a, 0x20, 0x00, 0x41, 0xa8, 0x01, + 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, + 0x03, 0xa0, 0x01, 0x37, 0x03, 0xe0, 0x01, 0x20, 0x00, 0x41, 0x00, 0x3a, + 0x00, 0xec, 0x01, 0x20, 0x00, 0x41, 0xd0, 0x00, 0x6a, 0x10, 0x07, 0x20, + 0x00, 0x41, 0xa0, 0x02, 0x6a, 0x10, 0x07, 0x0c, 0x03, 0x0b, 0x20, 0x00, + 0x41, 0xe0, 0x01, 0x6a, 0x41, 0xb5, 0x82, 0xc0, 0x00, 0x41, 0x14, 0x10, + 0x0a, 0x20, 0x00, 0x41, 0x00, 0x3a, 0x00, 0xec, 0x01, 0x0c, 0x02, 0x0b, + 0x20, 0x00, 0x42, 0x00, 0x37, 0x02, 0xe4, 0x01, 0x20, 0x00, 0x41, 0x01, + 0x3a, 0x00, 0xec, 0x01, 0x20, 0x00, 0x41, 0xcc, 0x82, 0xc0, 0x00, 0x28, + 0x02, 0x00, 0x36, 0x02, 0xe0, 0x01, 0x0c, 0x02, 0x0b, 0x20, 0x00, 0x41, + 0xd8, 0x00, 0x6a, 0x22, 0x02, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x28, 0x02, + 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x29, 0x02, 0x00, 0x37, + 0x03, 0x50, 0x20, 0x00, 0x41, 0xa0, 0x01, 0x6a, 0x41, 0x8c, 0x82, 0xc0, + 0x00, 0x41, 0x12, 0x10, 0x0a, 0x20, 0x00, 0x41, 0xc0, 0x01, 0x6a, 0x20, + 0x00, 0x28, 0x02, 0x50, 0x20, 0x02, 0x28, 0x02, 0x00, 0x10, 0x2b, 0x20, + 0x00, 0x41, 0xa0, 0x01, 0x6a, 0x20, 0x00, 0x28, 0x02, 0xc0, 0x01, 0x20, + 0x00, 0x28, 0x02, 0xc8, 0x01, 0x10, 0x13, 0x20, 0x00, 0x41, 0xc0, 0x01, + 0x6a, 0x10, 0x07, 0x20, 0x00, 0x41, 0xe8, 0x01, 0x6a, 0x20, 0x00, 0x41, + 0xa8, 0x01, 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, + 0x00, 0x29, 0x03, 0xa0, 0x01, 0x37, 0x03, 0xe0, 0x01, 0x20, 0x00, 0x41, + 0x00, 0x3a, 0x00, 0xec, 0x01, 0x20, 0x00, 0x41, 0xd0, 0x00, 0x6a, 0x10, + 0x07, 0x0b, 0x20, 0x00, 0x41, 0xc0, 0x01, 0x6a, 0x20, 0x00, 0x41, 0xe0, + 0x01, 0x6a, 0x10, 0x11, 0x20, 0x00, 0x41, 0xc0, 0x01, 0x6a, 0x10, 0x1b, + 0x20, 0x00, 0x41, 0xc0, 0x01, 0x6a, 0x10, 0x07, 0x0b, 0x20, 0x00, 0x41, + 0xe0, 0x01, 0x6a, 0x10, 0x07, 0x20, 0x00, 0x41, 0xb0, 0x02, 0x6a, 0x24, + 0x00, 0x0b, 0x24, 0x01, 0x01, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, + 0x01, 0x24, 0x00, 0x20, 0x01, 0x20, 0x00, 0x36, 0x02, 0x0c, 0x20, 0x01, + 0x41, 0x0c, 0x6a, 0x41, 0x04, 0x10, 0x2e, 0x20, 0x01, 0x41, 0x10, 0x6a, + 0x24, 0x00, 0x0b, 0x71, 0x01, 0x03, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, + 0x22, 0x02, 0x24, 0x00, 0x02, 0x40, 0x02, 0x40, 0x41, 0xe8, 0x82, 0xc0, + 0x00, 0x28, 0x02, 0x00, 0x22, 0x03, 0x20, 0x01, 0x6a, 0x22, 0x04, 0x20, + 0x03, 0x49, 0x0d, 0x00, 0x20, 0x04, 0x41, 0x80, 0x80, 0x01, 0x4b, 0x0d, + 0x00, 0x20, 0x02, 0x41, 0x08, 0x6a, 0x20, 0x03, 0x20, 0x04, 0x10, 0x20, + 0x20, 0x02, 0x28, 0x02, 0x08, 0x20, 0x02, 0x28, 0x02, 0x0c, 0x20, 0x00, + 0x20, 0x01, 0x10, 0x14, 0x41, 0xe8, 0x82, 0xc0, 0x00, 0x28, 0x02, 0x00, + 0x22, 0x00, 0x20, 0x01, 0x6a, 0x22, 0x01, 0x20, 0x00, 0x4f, 0x0d, 0x01, + 0x0b, 0x00, 0x0b, 0x41, 0xe8, 0x82, 0xc0, 0x00, 0x20, 0x01, 0x36, 0x02, + 0x00, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x24, 0x01, 0x01, + 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x01, 0x24, 0x00, 0x20, 0x01, + 0x20, 0x00, 0x3a, 0x00, 0x0f, 0x20, 0x01, 0x41, 0x0f, 0x6a, 0x41, 0x01, + 0x10, 0x2e, 0x20, 0x01, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x36, 0x02, + 0x01, 0x7f, 0x01, 0x7e, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, + 0x00, 0x20, 0x02, 0x41, 0x08, 0x6a, 0x20, 0x01, 0x41, 0x01, 0x10, 0x0b, + 0x20, 0x02, 0x29, 0x03, 0x08, 0x21, 0x03, 0x20, 0x00, 0x20, 0x01, 0x36, + 0x02, 0x08, 0x20, 0x00, 0x20, 0x03, 0x37, 0x02, 0x00, 0x20, 0x02, 0x41, + 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x39, 0x01, 0x02, 0x7f, 0x20, 0x00, 0x28, + 0x02, 0x04, 0x22, 0x03, 0x20, 0x02, 0x49, 0x22, 0x04, 0x45, 0x04, 0x40, + 0x20, 0x01, 0x20, 0x02, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x01, 0x20, + 0x02, 0x10, 0x14, 0x20, 0x00, 0x20, 0x03, 0x20, 0x02, 0x6b, 0x36, 0x02, + 0x04, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x6a, 0x36, 0x02, 0x00, 0x0b, + 0x20, 0x04, 0x0b, 0x9c, 0x02, 0x01, 0x02, 0x7f, 0x23, 0x00, 0x41, 0x10, + 0x6b, 0x22, 0x02, 0x24, 0x00, 0x02, 0x40, 0x20, 0x00, 0x20, 0x02, 0x41, + 0x0c, 0x6a, 0x02, 0x7f, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x41, 0xff, + 0x00, 0x4d, 0x04, 0x40, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x03, 0x20, + 0x00, 0x28, 0x02, 0x04, 0x46, 0x04, 0x40, 0x20, 0x00, 0x41, 0x01, 0x10, + 0x12, 0x20, 0x00, 0x28, 0x02, 0x08, 0x21, 0x03, 0x0b, 0x20, 0x00, 0x28, + 0x02, 0x00, 0x20, 0x03, 0x6a, 0x20, 0x01, 0x3a, 0x00, 0x00, 0x20, 0x03, + 0x41, 0x01, 0x6a, 0x22, 0x01, 0x20, 0x03, 0x49, 0x0d, 0x01, 0x20, 0x00, + 0x20, 0x01, 0x36, 0x02, 0x08, 0x0c, 0x04, 0x0b, 0x20, 0x02, 0x41, 0x00, + 0x36, 0x02, 0x0c, 0x20, 0x01, 0x41, 0x80, 0x10, 0x49, 0x0d, 0x01, 0x20, + 0x01, 0x41, 0x80, 0x80, 0x04, 0x49, 0x04, 0x40, 0x20, 0x02, 0x20, 0x01, + 0x41, 0x3f, 0x71, 0x41, 0x80, 0x01, 0x72, 0x3a, 0x00, 0x0e, 0x20, 0x02, + 0x20, 0x01, 0x41, 0x0c, 0x76, 0x41, 0xe0, 0x01, 0x72, 0x3a, 0x00, 0x0c, + 0x20, 0x02, 0x20, 0x01, 0x41, 0x06, 0x76, 0x41, 0x3f, 0x71, 0x41, 0x80, + 0x01, 0x72, 0x3a, 0x00, 0x0d, 0x41, 0x03, 0x0c, 0x03, 0x0b, 0x20, 0x02, + 0x20, 0x01, 0x41, 0x3f, 0x71, 0x41, 0x80, 0x01, 0x72, 0x3a, 0x00, 0x0f, + 0x20, 0x02, 0x20, 0x01, 0x41, 0x12, 0x76, 0x41, 0xf0, 0x01, 0x72, 0x3a, + 0x00, 0x0c, 0x20, 0x02, 0x20, 0x01, 0x41, 0x06, 0x76, 0x41, 0x3f, 0x71, + 0x41, 0x80, 0x01, 0x72, 0x3a, 0x00, 0x0e, 0x20, 0x02, 0x20, 0x01, 0x41, + 0x0c, 0x76, 0x41, 0x3f, 0x71, 0x41, 0x80, 0x01, 0x72, 0x3a, 0x00, 0x0d, + 0x41, 0x04, 0x0c, 0x02, 0x0b, 0x00, 0x0b, 0x20, 0x02, 0x20, 0x01, 0x41, + 0x3f, 0x71, 0x41, 0x80, 0x01, 0x72, 0x3a, 0x00, 0x0d, 0x20, 0x02, 0x20, + 0x01, 0x41, 0x06, 0x76, 0x41, 0xc0, 0x01, 0x72, 0x3a, 0x00, 0x0c, 0x41, + 0x02, 0x0b, 0x10, 0x13, 0x0b, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, + 0x0b, 0x42, 0x01, 0x01, 0x7f, 0x20, 0x00, 0x2f, 0x01, 0x04, 0x21, 0x03, + 0x20, 0x00, 0x41, 0x00, 0x3a, 0x00, 0x04, 0x20, 0x03, 0x41, 0x01, 0x71, + 0x45, 0x04, 0x40, 0x20, 0x00, 0x28, 0x02, 0x00, 0x20, 0x01, 0x20, 0x02, + 0x10, 0x31, 0x0f, 0x0b, 0x20, 0x01, 0x20, 0x03, 0x41, 0x08, 0x76, 0x3a, + 0x00, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x20, 0x01, 0x41, 0x01, 0x6a, + 0x20, 0x02, 0x41, 0x01, 0x6b, 0x10, 0x31, 0x0b, 0x26, 0x01, 0x01, 0x7f, + 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x20, 0x02, 0x20, + 0x01, 0x3a, 0x00, 0x0f, 0x20, 0x00, 0x20, 0x02, 0x41, 0x0f, 0x6a, 0x41, + 0x01, 0x10, 0x13, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x26, + 0x01, 0x01, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, 0x00, + 0x20, 0x02, 0x20, 0x00, 0x36, 0x02, 0x0c, 0x20, 0x01, 0x20, 0x02, 0x41, + 0x0c, 0x6a, 0x41, 0x04, 0x10, 0x13, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, + 0x00, 0x0b, 0xd2, 0x01, 0x01, 0x01, 0x7f, 0x02, 0x40, 0x20, 0x01, 0x41, + 0xff, 0xff, 0xff, 0xff, 0x03, 0x71, 0x20, 0x01, 0x47, 0x0d, 0x00, 0x20, + 0x02, 0x20, 0x02, 0x41, 0x40, 0x6b, 0x22, 0x03, 0x4b, 0x0d, 0x00, 0x20, + 0x03, 0x41, 0xff, 0xff, 0xff, 0xff, 0x01, 0x71, 0x20, 0x03, 0x47, 0x0d, + 0x00, 0x20, 0x01, 0x41, 0x02, 0x74, 0x22, 0x01, 0x20, 0x03, 0x41, 0x03, + 0x74, 0x22, 0x02, 0x20, 0x01, 0x20, 0x02, 0x4b, 0x1b, 0x22, 0x02, 0x41, + 0x08, 0x6a, 0x22, 0x01, 0x20, 0x02, 0x49, 0x0d, 0x00, 0x02, 0x7f, 0x02, + 0x40, 0x20, 0x01, 0x20, 0x01, 0x41, 0x80, 0x80, 0x04, 0x6a, 0x22, 0x02, + 0x4d, 0x04, 0x40, 0x20, 0x02, 0x41, 0x01, 0x6b, 0x22, 0x01, 0x20, 0x02, + 0x4d, 0x0d, 0x01, 0x0b, 0x00, 0x0b, 0x20, 0x01, 0x41, 0x10, 0x76, 0x22, + 0x02, 0x40, 0x00, 0x22, 0x01, 0x41, 0x7f, 0x46, 0x04, 0x40, 0x41, 0x00, + 0x21, 0x01, 0x41, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x01, 0x41, 0xff, 0xff, + 0x03, 0x71, 0x20, 0x01, 0x47, 0x0d, 0x01, 0x20, 0x02, 0x41, 0x10, 0x74, + 0x22, 0x02, 0x41, 0x08, 0x6b, 0x20, 0x02, 0x4b, 0x0d, 0x01, 0x20, 0x01, + 0x41, 0x10, 0x74, 0x22, 0x01, 0x42, 0x00, 0x37, 0x02, 0x04, 0x20, 0x01, + 0x20, 0x01, 0x20, 0x02, 0x6a, 0x41, 0x02, 0x72, 0x36, 0x02, 0x00, 0x41, + 0x00, 0x0b, 0x21, 0x02, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x04, 0x20, + 0x00, 0x20, 0x02, 0x36, 0x02, 0x00, 0x0f, 0x0b, 0x00, 0x0b, 0xaf, 0x04, + 0x01, 0x0a, 0x7f, 0x20, 0x00, 0x41, 0x02, 0x74, 0x21, 0x06, 0x41, 0x00, + 0x20, 0x01, 0x6b, 0x21, 0x08, 0x20, 0x00, 0x41, 0xff, 0xff, 0xff, 0xff, + 0x03, 0x71, 0x20, 0x00, 0x47, 0x21, 0x09, 0x20, 0x01, 0x41, 0x01, 0x6b, + 0x22, 0x0a, 0x20, 0x01, 0x4b, 0x21, 0x0b, 0x20, 0x02, 0x28, 0x02, 0x00, + 0x21, 0x00, 0x02, 0x40, 0x03, 0x40, 0x20, 0x00, 0x45, 0x0d, 0x01, 0x20, + 0x00, 0x21, 0x01, 0x02, 0x40, 0x03, 0x40, 0x02, 0x40, 0x20, 0x01, 0x28, + 0x02, 0x08, 0x22, 0x00, 0x41, 0x01, 0x71, 0x45, 0x04, 0x40, 0x20, 0x09, + 0x0d, 0x03, 0x20, 0x01, 0x28, 0x02, 0x00, 0x41, 0x7c, 0x71, 0x22, 0x03, + 0x20, 0x01, 0x41, 0x08, 0x6a, 0x22, 0x05, 0x6b, 0x22, 0x04, 0x20, 0x03, + 0x4b, 0x0d, 0x03, 0x20, 0x04, 0x20, 0x06, 0x49, 0x0d, 0x01, 0x20, 0x03, + 0x20, 0x06, 0x6b, 0x22, 0x0c, 0x20, 0x03, 0x4b, 0x0d, 0x03, 0x20, 0x0b, + 0x0d, 0x03, 0x20, 0x05, 0x41, 0x08, 0x6a, 0x22, 0x04, 0x20, 0x05, 0x49, + 0x0d, 0x03, 0x20, 0x04, 0x20, 0x04, 0x41, 0x40, 0x6b, 0x22, 0x04, 0x4b, + 0x0d, 0x03, 0x02, 0x40, 0x20, 0x04, 0x20, 0x08, 0x20, 0x0c, 0x71, 0x22, + 0x04, 0x4b, 0x04, 0x40, 0x20, 0x05, 0x20, 0x0a, 0x71, 0x0d, 0x03, 0x20, + 0x02, 0x20, 0x00, 0x41, 0x7c, 0x71, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, + 0x01, 0x28, 0x02, 0x00, 0x41, 0x01, 0x72, 0x36, 0x02, 0x00, 0x20, 0x01, + 0x21, 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x04, 0x41, 0x08, 0x6b, 0x22, 0x00, + 0x20, 0x04, 0x4b, 0x0d, 0x04, 0x20, 0x03, 0x20, 0x00, 0x6b, 0x22, 0x02, + 0x20, 0x03, 0x4b, 0x0d, 0x04, 0x20, 0x02, 0x41, 0x08, 0x6b, 0x20, 0x02, + 0x4b, 0x0d, 0x04, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x08, 0x20, 0x00, + 0x42, 0x00, 0x37, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x28, 0x02, 0x00, + 0x41, 0x7c, 0x71, 0x36, 0x02, 0x00, 0x02, 0x40, 0x20, 0x01, 0x28, 0x02, + 0x00, 0x22, 0x02, 0x41, 0x7c, 0x71, 0x22, 0x03, 0x45, 0x0d, 0x00, 0x41, + 0x00, 0x20, 0x03, 0x20, 0x02, 0x41, 0x02, 0x71, 0x1b, 0x22, 0x02, 0x45, + 0x0d, 0x00, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x04, 0x41, 0x03, 0x71, + 0x20, 0x00, 0x72, 0x36, 0x02, 0x04, 0x0b, 0x20, 0x00, 0x20, 0x00, 0x28, + 0x02, 0x04, 0x41, 0x03, 0x71, 0x20, 0x01, 0x72, 0x36, 0x02, 0x04, 0x20, + 0x01, 0x20, 0x01, 0x28, 0x02, 0x08, 0x41, 0x7e, 0x71, 0x36, 0x02, 0x08, + 0x20, 0x01, 0x20, 0x01, 0x28, 0x02, 0x00, 0x22, 0x02, 0x41, 0x03, 0x71, + 0x20, 0x00, 0x72, 0x22, 0x03, 0x36, 0x02, 0x00, 0x02, 0x40, 0x20, 0x02, + 0x41, 0x02, 0x71, 0x45, 0x04, 0x40, 0x20, 0x00, 0x28, 0x02, 0x00, 0x21, + 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x01, 0x20, 0x03, 0x41, 0x7d, 0x71, 0x36, + 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x02, 0x72, + 0x22, 0x01, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x41, 0x01, + 0x72, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x21, 0x07, + 0x0c, 0x05, 0x0b, 0x20, 0x01, 0x20, 0x00, 0x41, 0x7e, 0x71, 0x36, 0x02, + 0x08, 0x02, 0x7f, 0x41, 0x00, 0x20, 0x01, 0x28, 0x02, 0x04, 0x41, 0x7c, + 0x71, 0x22, 0x00, 0x45, 0x0d, 0x00, 0x1a, 0x41, 0x00, 0x20, 0x00, 0x20, + 0x00, 0x2d, 0x00, 0x00, 0x41, 0x01, 0x71, 0x1b, 0x0b, 0x21, 0x00, 0x20, + 0x01, 0x10, 0x38, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x41, 0x02, 0x71, 0x04, + 0x40, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x02, 0x72, 0x36, + 0x02, 0x00, 0x0b, 0x20, 0x02, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x28, 0x02, 0x00, 0x41, 0x7c, 0x71, 0x22, 0x01, 0x20, 0x00, 0x6b, 0x41, + 0x08, 0x6b, 0x20, 0x01, 0x4b, 0x0d, 0x02, 0x20, 0x00, 0x21, 0x01, 0x0c, + 0x01, 0x0b, 0x0b, 0x20, 0x02, 0x20, 0x00, 0x36, 0x02, 0x00, 0x0c, 0x01, + 0x0b, 0x0b, 0x00, 0x0b, 0x20, 0x07, 0x0b, 0x7d, 0x01, 0x02, 0x7f, 0x02, + 0x40, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x7c, 0x71, 0x22, + 0x02, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x02, 0x20, 0x01, 0x41, 0x02, + 0x71, 0x1b, 0x22, 0x01, 0x45, 0x0d, 0x00, 0x20, 0x01, 0x20, 0x01, 0x28, + 0x02, 0x04, 0x41, 0x03, 0x71, 0x20, 0x00, 0x28, 0x02, 0x04, 0x41, 0x7c, + 0x71, 0x72, 0x36, 0x02, 0x04, 0x0b, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, + 0x04, 0x22, 0x01, 0x41, 0x7c, 0x71, 0x22, 0x02, 0x04, 0x7f, 0x20, 0x02, + 0x20, 0x02, 0x28, 0x02, 0x00, 0x41, 0x03, 0x71, 0x20, 0x00, 0x28, 0x02, + 0x00, 0x41, 0x7c, 0x71, 0x72, 0x36, 0x02, 0x00, 0x20, 0x00, 0x28, 0x02, + 0x04, 0x05, 0x20, 0x01, 0x0b, 0x41, 0x03, 0x71, 0x36, 0x02, 0x04, 0x20, + 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x03, 0x71, 0x36, 0x02, 0x00, + 0x0b, 0x28, 0x01, 0x01, 0x7f, 0x03, 0x40, 0x20, 0x02, 0x20, 0x03, 0x47, + 0x04, 0x40, 0x20, 0x00, 0x20, 0x03, 0x6a, 0x20, 0x01, 0x20, 0x03, 0x6a, + 0x2d, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x20, 0x03, 0x41, 0x01, 0x6a, 0x21, + 0x03, 0x0c, 0x01, 0x0b, 0x0b, 0x0b, 0x22, 0x01, 0x01, 0x7f, 0x03, 0x40, + 0x20, 0x01, 0x20, 0x02, 0x47, 0x04, 0x40, 0x20, 0x00, 0x20, 0x02, 0x6a, + 0x41, 0x00, 0x3a, 0x00, 0x00, 0x20, 0x02, 0x41, 0x01, 0x6a, 0x21, 0x02, + 0x0c, 0x01, 0x0b, 0x0b, 0x0b, 0x3f, 0x01, 0x02, 0x7f, 0x03, 0x40, 0x20, + 0x02, 0x45, 0x04, 0x40, 0x41, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x41, 0x01, + 0x6b, 0x21, 0x02, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x21, 0x03, 0x20, 0x00, + 0x2d, 0x00, 0x00, 0x21, 0x04, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x21, 0x00, + 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x20, 0x03, 0x20, 0x04, 0x46, + 0x0d, 0x00, 0x0b, 0x20, 0x04, 0x20, 0x03, 0x6b, 0x0b, 0x0b, 0xb1, 0x02, + 0x03, 0x00, 0x41, 0x80, 0x80, 0xc0, 0x00, 0x0b, 0x88, 0x01, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x41, 0xca, 0x81, 0xc0, 0x00, 0x0b, 0x33, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x00, 0x41, 0x88, 0x82, 0xc0, 0x00, 0x0b, 0x5c, 0x6e, 0x61, 0x6d, + 0x65, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x73, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x3a, 0x20, 0x69, 0x6e, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x60, 0x60, 0x3a, 0x20, 0x63, 0x6f, 0x75, 0x6c, 0x64, 0x20, + 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, 0x69, 0x6e, 0x70, + 0x75, 0x74, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, + 0x62, 0x63, 0x64, 0x65, 0x66 +}; +constexpr unsigned int hello_world_wasm_len = 10661; diff --git "a/BFPL\345\243\271/bcos-executor/test/liquid/hello_world.rs" "b/BFPL\345\243\271/bcos-executor/test/liquid/hello_world.rs" new file mode 100644 index 00000000..be2989f4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/liquid/hello_world.rs" @@ -0,0 +1,29 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use liquid::{storage}; +use liquid_lang as liquid; + +#[liquid::contract] +mod hello_world { + use super::*; + + #[liquid(storage)] + struct HelloWorld { + name: storage::Value, + } + + #[liquid(methods)] + impl HelloWorld { + pub fn new(&mut self, name: String) { + self.name.initialize(name); + } + + pub fn set(&mut self, name: String) { + *self.name = name; + } + + pub fn get(&self) -> String { + self.name.clone() + } + } +} diff --git "a/BFPL\345\243\271/bcos-executor/test/liquid/hello_world.wasm" "b/BFPL\345\243\271/bcos-executor/test/liquid/hello_world.wasm" new file mode 100644 index 0000000000000000000000000000000000000000..856862402cfa2fdca0ebc43c328a861e350e3130 GIT binary patch literal 10661 zcmd6tUyL2sUB}NkGxyKlx$B)I3%GXLo;x+}rnM70Np}+m$c`aS+^yq^K9mO}vW+*5 z@Abc3J0?NY>r??5T8TVVB1jQhse)8Oh=5eV8!J>ngisY#eL&)6A0m;5w8}#t`hYN> z@9)gqU7OG+Bv>bRX3n4A`ThO=e&@`%)vFi1bIzX{J{LAO-E*G*vrXOU*KaLDvef^=`Ag@o zzS#C(-R;ktx>D)1C2~zP@>L+ZDaTdH3Sl#mgJ71UK{I>eUy2>)I<< z*4&})xwR`7F2CY_F~7Ka{*pT~*nW2L5$`vnD|^w^^W&q=)#2%UIl6^VR(>J4d^37= z^4y8OsE1|0C=N3x*dSLs_2rd2Sox=BdC-rCAj5ty_zc>R^U>G&xXR8jJgfaAW@ipQ zl`p%Ze(&qs%y*-V_4|{Gfjm`tePewwxUkGBdHuWVRfuj9!x?c_J9@a}6GmF!Q?ot> zvF_Ko^W|ePh$MWNJnPJ2ejPR{O)X+JfvDX^6)(WgluW=lwR<*S4x2Gxl0?>*vqozKH_(<~nItXu={Zl$uBiNM;?E>a%j8h7 zWxkB@(=r}zJ>1s0aVycGz-4G=pC8xZghR0>^Cc8%8k7j|%uoH}fB5cSbQkIOiLM+r z(GAcI(gG5|84pm_B)c<3gf!>#%&Gk-3rLHQwM)j5M03^}B)R+uG0PLffV_ytxJ)L+ zj;381;Y~la0_#JN<&tlk@~|%goN4q@hwHduXuW(f3~d5dsfXiosmkP8TA*CiTIRQE zQbgGkLiFVl%+#=E9$X~{V<026f>a)};$1)0IzAGX^-t=-z(%~v8yU^MIZlH;-3Y$q zW%5@pSW5n;HshqT>ldbId0Co?9pxLQN8-dZYc0f1?BIF&?MFY#AukVu4Y);}wM)?= z)z?&H#Yn7CGpz?fWi=K5&{+gtb-yG$+sFM zMPkuR=o5xPKqT|Y3T*laVDWR>6qb!ERHb{xl$yc28p-PZ%E6IG$(b}WDGt2>gd}TlJLpCeDmOhre4?C#Y&k50dP00Ri&{H10q#)I zEV%U)Kd!!YbK8w8cf`3Wt2aNdwiwXNNCJ7w8%Lc#z{oHj2{Y>3e!brDrx{6#hzuc} z)P77*2%ELUfo9Z)HVVvY$hZSVk$t)XR#qbV zVkh>;)lBTCrkOL1)L2eQj=?_8*xk%Z%+{Hg$CYPtVFH^Vnlf$~x$c&tJ zZFN(3cS3tJwA)(9VK`YxiJ)tD-Nr&)=rtDN77H1(vPy3>7=kc&T16TbgR;tCf-jg= zz_h(q>BJfNTW^%hUOjw5lg=7^%MgmznWuRy;-L7f_cyZT=$XLUlaW8^qdV^0@5k^O z2@U+ju?Z(86`z?J&kMFFi0(J=(oesng`%oSXn*TNw&`!|Ny%tk!>XXf0@|I`ib6i+ zr`!-Pqh>&ICrpv5IzR_O_@tFc+=Of8qY^39w{JT&f)+GVtUTeBDzkbYX}7$u9drA3 zhSR1oVFPwQNAy|gk(H041R4xuaK8JPY#IJAXi$>8gT+5l zuqOGIK~!l2fMeDW*~(8QSr1hpw@?Ky?ZoU^xza+25M}KSUTU-r1%>mB#X2g!09;qU zpNhybDm)g#{?b>fI$izhO@DSaSbtPXGnK>ih@1X66(OZ14yMf~AVpTihaor@5U7n0?zxFbN=u_-eNbU+4J9$%oaWt@-a>OpG z{YM71is7gud?))d7yLde7UkpQ&T!HJ+`~c_h??uewJ_KC9fduVKE&$md!0)q(%+Le zhNPjnvDFwsPn2I058-BqR=n09y3~CPs_;4;LG3c&;g29P5Jwa-aI|OXdYi)Krhj_2 z4|5tp0caywB{A2GIE56ZfgLv-+${-f+$jkf*|9pQTmQ()zFz&w#Nkl!80uE6|77_e zv;18`8KCdbw?&`TN3G0_L|fq9;4VpL8oD!L?`KOZPiuLessIfIr8bo`!XpwW|KoVU z97doYgZ0v$kEsr8_YBg=qBN_zxZ|E~0Atu~TxVM;v+<&0*NxN&yG;GdIEck-uZ?6W zj3i)>!zIHl?Q+F(76(gVo(t^?f7%uPiX9MG%Q42v>SAkm|JLk~;xu>FLxOcq8-B2{ z2BUVjp@Mvua;$AQ`wESAb;X0^Z5!dhhG)Pc!34SUL|s@8Ls zF5b%9q`9RnC0Wyv>&hXQW2C(Da0(6aeoWglSHH`~z}3Gr7N1%;x(C9eOW89t?pW6k zop`Q*#-{fRRc9&t4Mx&*flKFC!i$W4-HXusPKgfoU*}v$>iDSmYY5-(+JNWPyCh2Ag(?HbXc;t+zsCvi zJ1LF_j<8}7b&$F-z5^z3GYl(PKh5P|(VPX6tKR{CzDs5;7ij09JlI^@@jaf8sLGul zyDi*x?6$yS>DY~-Ti$$xJ1z2nLhFx1%GAf2&1_gMIXkGS=CG@FGgLwSC%lCTd5bKQ zRK;-~J92EKau(>3;OInqRzxe+QCmqLQWjM%M<13&$)$dqi_{!RF^S){=oFWSs*Z(&N>1YS*-Qpz|U0PKV)@JTi#=}w!D83_8wGm|6n_!Dei4V*eH)mv0cPSA~CAY%7^fcqXti< z2$bV#RXxJnyOP1qE9Og*P^&rW{^(V#;!ILbP_)_B^bWmDx14+QqG*F=zu7t1uHPWH zi8d>RnJDVssJE6CMh@^H9CW@M6kkp|ElNvody79!@#gDW+<@nwVcg&7HnUqiOo}xn zm!E|+wkY+pzvnXbyDjVAy)7e4ri@}|9u%|r z_4seoG8HUd8ia9GJM? z{ihtUL;NKbQxXw$A0`;r4g&^2dp{-yD>gc0fQ9g$AE9$0n{7fELeI-6@_T>whi}|a z(dg?Rz40gio>BgA`~5%v_D?$L6pOIj(NvOD&Nh;AM`ibbs?<{IVmL-4NDe~wg_&@0 ztPT_dVq+oPWd669ultu5$vq6&3gTEWZ~15|Sh|B_TE7$S$O&6vDYNCJl26e8o@trQ zBP=_C`8h76LyLTV2t2Hf#brWM1C18uq)$jCxDmBa(+vO|)A3Iu)<)}M16t51=m*>4 zc>y6xwZ3Cq?rK-MewQ{K`ByWRr|a+7Ocbonx2+0pdj5`w4vjxGYniQdyeJ79_OdPR zm02t#(}?9s0#u{-SX!&C2pGSV9n)cn6%6Ia_0~otw#8I=4|iBBJ#%7#AUpIfg!g?Z z`_X3=l@!qo>Ir1bUb5Xc8Ljcy?v}_V+ugd$b}64H+f|0DCeK!o1DegzNrGGRye%L5 zZ-LL7cFO9s5$}R`uKanebwoc4BP~ZtQio3+hiKVvEs;*8r<|L7rOG+HEq@_$pI4(V{jH?Z{=V^mTFXPYTd98 zCw=`4jb2uWx%y3lC26$&R+#44TeNb>h?eWuxYdCQxBubCC!0zX4{PhCpKzOSK|bA7 zbOH!r0MlZ3F}xvX(B6&^)opUhzV@bQFjGlxtTg#-FDoAa2(JbT5=KC@8SxQBVT}N+ zr6*U6d|)%&2EJhlf)K0qW`Z^OFDfr*QkAj$C{_e$ysM&i(`M1AyfYO|5`GW=kPzGH z9iEulP7}8i5^gn)PtN89-)X@6%uy5?o7@5}+aj|AWs4`=@II}*L+eLIkVTHh1C{pT zw(b~cOrb;}v_MS#e}(zVpGC{(;VJ? zYcz;(=ROLp+ID73PDaTqgcaH~AdUVx95WMqkz0&ZEBmV~NRMKLxzrg>wy+UREuhD3 znQ9??(aJE20UMx}ZBzU^W-fxQTy(aHe8WZ<_Q5u=Xh|5^c2Mbk=w919$rsv!Q`NX_<8(Hb+mWN~ zH-J7cl0#mVjrti%8|D0T9#7^W&GEfXg%f;*wBmnb>x7$w8;+3tE?bL%&nTM1_&qO} zy`<_H>miA&>yn#Fm>pF|P@HZ_pgLQal2}932;L zHXArz>$fkI^>#SbaIlw-@1aBaZ!6f#Jy=dJ1?>$ri|{f>5X4mphi!x2d?Z$t?t>sp zMz~q3LnpcUX?T=#J*lIvjT3I4Jq@p8W}bA?cM$)ESmn6syQuahZ0YQ~gc zyTWXpvgpSUf{kk0~NNTC}_#6mCbN{;S zEv*?P7`Bwh?Q50va>eW^EzO~skPLrcyVBvQ)23lbU-X*WPKW)#GcI6Uc3$kO)_JKZ z?_}IE`$+A&dF#oaUyqr3*g;;CIqK}1BDtvvbbuOl_?x!ijKUs-x}DGiiRx`WA|PC@ zS78}1;?97-`V6j1$m?4cPC`9hJbJ;4DjvGo4)T=>CvTlTBQ1HlwVX}i4nA^fm&vvkwnpnk9g-0 z1*NEX*!>zvO5oIeJv!D@HEofp1~FkuM;G=LlimY}J*e4uD|lH!W57kO$_OqCfGf|Gyje{bh3W17}3i|QF`Of zno7aqR^*K$dX(O*2?CI{kD@pmr^h%`ylMJ9sj& zladl|dsT%uYZ_&AO3$X7fhvgYmY}pI_Z}#Up%5_%r^pyZ&Cl_}p_R;`5hZx^OODx_m8e ktgW77)RmV^4X8qY$BsYz$fKWITzc&B)vr8%ZtaEt0{fWKGynhq literal 0 HcmV?d00001 diff --git "a/BFPL\345\243\271/bcos-executor/test/liquid/hello_world_caller.h" "b/BFPL\345\243\271/bcos-executor/test/liquid/hello_world_caller.h" new file mode 100644 index 00000000..e109b222 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/liquid/hello_world_caller.h" @@ -0,0 +1,820 @@ +#pragma once + +constexpr unsigned char hello_world_caller_wasm[] = { + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x46, 0x0c, 0x60, + 0x02, 0x7f, 0x7f, 0x00, 0x60, 0x01, 0x7f, 0x00, 0x60, 0x03, 0x7f, 0x7f, + 0x7f, 0x00, 0x60, 0x03, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x04, 0x7f, + 0x7f, 0x7f, 0x7f, 0x00, 0x60, 0x00, 0x01, 0x7f, 0x60, 0x00, 0x00, 0x60, + 0x04, 0x7f, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x05, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x00, 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x60, 0x02, 0x7f, 0x7f, + 0x01, 0x7f, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7e, 0x02, 0xa5, 0x01, 0x09, + 0x04, 0x62, 0x63, 0x6f, 0x73, 0x04, 0x63, 0x61, 0x6c, 0x6c, 0x00, 0x07, + 0x04, 0x62, 0x63, 0x6f, 0x73, 0x11, 0x67, 0x65, 0x74, 0x52, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x69, 0x7a, 0x65, 0x00, + 0x05, 0x04, 0x62, 0x63, 0x6f, 0x73, 0x0d, 0x67, 0x65, 0x74, 0x52, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x00, 0x01, 0x04, 0x62, + 0x63, 0x6f, 0x73, 0x06, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x00, 0x00, + 0x04, 0x62, 0x63, 0x6f, 0x73, 0x0a, 0x73, 0x65, 0x74, 0x53, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x00, 0x04, 0x04, 0x62, 0x63, 0x6f, 0x73, 0x06, + 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x00, 0x00, 0x04, 0x62, 0x63, 0x6f, + 0x73, 0x0a, 0x67, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x00, 0x03, 0x04, 0x62, 0x63, 0x6f, 0x73, 0x0f, 0x67, 0x65, 0x74, 0x43, + 0x61, 0x6c, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x53, 0x69, 0x7a, 0x65, 0x00, + 0x05, 0x04, 0x62, 0x63, 0x6f, 0x73, 0x0b, 0x67, 0x65, 0x74, 0x43, 0x61, + 0x6c, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x00, 0x01, 0x03, 0x32, 0x31, 0x01, + 0x01, 0x00, 0x02, 0x01, 0x02, 0x00, 0x02, 0x0a, 0x02, 0x00, 0x02, 0x02, + 0x04, 0x01, 0x04, 0x04, 0x08, 0x07, 0x0b, 0x05, 0x06, 0x00, 0x00, 0x09, + 0x00, 0x01, 0x01, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x02, 0x00, 0x03, 0x03, 0x00, 0x00, 0x02, 0x03, 0x01, 0x02, 0x00, 0x03, + 0x05, 0x03, 0x01, 0x00, 0x11, 0x06, 0x09, 0x01, 0x7f, 0x01, 0x41, 0x80, + 0x80, 0xc0, 0x00, 0x0b, 0x07, 0x26, 0x04, 0x06, 0x6d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x02, 0x00, 0x09, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x00, 0x1d, 0x06, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x00, + 0x1e, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x25, 0x0a, 0xb5, 0x47, 0x31, + 0x33, 0x01, 0x01, 0x7f, 0x20, 0x00, 0x10, 0x0a, 0x20, 0x00, 0x41, 0x14, + 0x6a, 0x2d, 0x00, 0x00, 0x41, 0x02, 0x47, 0x04, 0x40, 0x20, 0x00, 0x28, + 0x02, 0x10, 0x22, 0x01, 0x28, 0x02, 0x00, 0x04, 0x7f, 0x20, 0x01, 0x10, + 0x0a, 0x20, 0x00, 0x28, 0x02, 0x10, 0x05, 0x20, 0x01, 0x0b, 0x41, 0x0c, + 0x10, 0x0b, 0x0b, 0x0b, 0x26, 0x01, 0x01, 0x7f, 0x02, 0x40, 0x20, 0x00, + 0x28, 0x02, 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x04, 0x22, 0x00, + 0x1b, 0x22, 0x01, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x45, 0x0d, 0x00, 0x20, + 0x01, 0x20, 0x00, 0x10, 0x0b, 0x0b, 0x0b, 0x85, 0x02, 0x01, 0x03, 0x7f, + 0x20, 0x00, 0x04, 0x40, 0x20, 0x01, 0x20, 0x01, 0x41, 0x04, 0x6a, 0x22, + 0x03, 0x4d, 0x41, 0x00, 0x20, 0x03, 0x41, 0x01, 0x6b, 0x20, 0x03, 0x4d, + 0x1b, 0x45, 0x04, 0x40, 0x00, 0x0b, 0x41, 0xc0, 0x82, 0xc0, 0x00, 0x28, + 0x02, 0x00, 0x21, 0x03, 0x20, 0x00, 0x41, 0x08, 0x6b, 0x22, 0x01, 0x20, + 0x01, 0x28, 0x02, 0x00, 0x22, 0x04, 0x41, 0x7e, 0x71, 0x36, 0x02, 0x00, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, 0x41, 0x7c, 0x71, 0x22, 0x02, 0x20, + 0x00, 0x6b, 0x20, 0x02, 0x4d, 0x04, 0x40, 0x20, 0x00, 0x41, 0x00, 0x36, + 0x02, 0x00, 0x20, 0x00, 0x41, 0x04, 0x6b, 0x28, 0x02, 0x00, 0x41, 0x7c, + 0x71, 0x22, 0x02, 0x45, 0x0d, 0x01, 0x20, 0x02, 0x2d, 0x00, 0x00, 0x41, + 0x01, 0x71, 0x0d, 0x01, 0x20, 0x01, 0x10, 0x36, 0x20, 0x02, 0x28, 0x02, + 0x00, 0x21, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x41, 0x02, 0x71, 0x04, + 0x40, 0x20, 0x02, 0x20, 0x00, 0x41, 0x02, 0x72, 0x22, 0x00, 0x36, 0x02, + 0x00, 0x0b, 0x20, 0x03, 0x21, 0x01, 0x20, 0x00, 0x41, 0x7c, 0x71, 0x22, + 0x00, 0x20, 0x02, 0x6b, 0x41, 0x08, 0x6b, 0x20, 0x00, 0x4d, 0x0d, 0x02, + 0x0b, 0x00, 0x0b, 0x02, 0x40, 0x20, 0x04, 0x41, 0x7c, 0x71, 0x22, 0x02, + 0x45, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x02, 0x20, 0x04, 0x41, 0x02, 0x71, + 0x1b, 0x22, 0x02, 0x45, 0x0d, 0x00, 0x20, 0x02, 0x2d, 0x00, 0x00, 0x41, + 0x01, 0x71, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x02, 0x28, 0x02, 0x08, 0x41, + 0x7c, 0x71, 0x36, 0x02, 0x00, 0x20, 0x02, 0x20, 0x01, 0x41, 0x01, 0x72, + 0x36, 0x02, 0x08, 0x20, 0x03, 0x21, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x00, + 0x20, 0x03, 0x36, 0x02, 0x00, 0x0b, 0x41, 0xc0, 0x82, 0xc0, 0x00, 0x20, + 0x01, 0x36, 0x02, 0x00, 0x0b, 0x0b, 0x48, 0x01, 0x01, 0x7f, 0x02, 0x40, + 0x02, 0x40, 0x20, 0x02, 0x20, 0x02, 0x41, 0x01, 0x6b, 0x22, 0x02, 0x49, + 0x0d, 0x00, 0x20, 0x01, 0x28, 0x02, 0x08, 0x1a, 0x20, 0x01, 0x28, 0x02, + 0x10, 0x1a, 0x20, 0x01, 0x29, 0x03, 0x00, 0x1a, 0x20, 0x01, 0x28, 0x02, + 0x14, 0x22, 0x03, 0x20, 0x02, 0x20, 0x03, 0x6a, 0x4b, 0x0d, 0x00, 0x20, + 0x01, 0x41, 0x00, 0x36, 0x02, 0x14, 0x0c, 0x01, 0x0b, 0x00, 0x0b, 0x20, + 0x00, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0b, 0x67, 0x01, 0x03, 0x7f, 0x23, + 0x00, 0x41, 0x10, 0x6b, 0x22, 0x01, 0x24, 0x00, 0x20, 0x01, 0x41, 0x08, + 0x6a, 0x41, 0x0b, 0x41, 0x00, 0x10, 0x0e, 0x20, 0x01, 0x28, 0x02, 0x0c, + 0x21, 0x03, 0x20, 0x01, 0x28, 0x02, 0x08, 0x22, 0x02, 0x41, 0x80, 0x80, + 0xc0, 0x00, 0x29, 0x00, 0x00, 0x37, 0x00, 0x00, 0x20, 0x02, 0x41, 0x07, + 0x6a, 0x41, 0x87, 0x80, 0xc0, 0x00, 0x28, 0x00, 0x00, 0x36, 0x00, 0x00, + 0x20, 0x00, 0x41, 0x14, 0x6a, 0x41, 0x02, 0x3a, 0x00, 0x00, 0x20, 0x00, + 0x42, 0x0b, 0x37, 0x02, 0x08, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x04, + 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, 0x01, 0x41, 0x10, 0x6a, + 0x24, 0x00, 0x0b, 0x66, 0x01, 0x01, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, + 0x22, 0x03, 0x24, 0x00, 0x02, 0x40, 0x20, 0x01, 0x41, 0x00, 0x4e, 0x04, + 0x40, 0x02, 0x7f, 0x20, 0x02, 0x45, 0x04, 0x40, 0x20, 0x03, 0x41, 0x08, + 0x6a, 0x20, 0x01, 0x10, 0x0f, 0x20, 0x03, 0x28, 0x02, 0x0c, 0x21, 0x02, + 0x20, 0x03, 0x28, 0x02, 0x08, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x20, 0x01, + 0x41, 0x01, 0x10, 0x10, 0x20, 0x03, 0x28, 0x02, 0x04, 0x21, 0x02, 0x20, + 0x03, 0x28, 0x02, 0x00, 0x0b, 0x22, 0x01, 0x0d, 0x01, 0x0b, 0x00, 0x0b, + 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x02, 0x36, + 0x02, 0x04, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x33, 0x01, + 0x01, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x20, + 0x02, 0x41, 0x08, 0x6a, 0x20, 0x01, 0x41, 0x00, 0x10, 0x10, 0x20, 0x00, + 0x20, 0x02, 0x28, 0x02, 0x08, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x02, + 0x28, 0x02, 0x0c, 0x36, 0x02, 0x04, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, + 0x00, 0x0b, 0x4a, 0x01, 0x01, 0x7f, 0x41, 0x01, 0x21, 0x03, 0x02, 0x40, + 0x20, 0x01, 0x45, 0x04, 0x40, 0x41, 0x00, 0x21, 0x01, 0x0c, 0x01, 0x0b, + 0x20, 0x01, 0x41, 0x01, 0x10, 0x11, 0x21, 0x03, 0x02, 0x40, 0x20, 0x02, + 0x04, 0x40, 0x20, 0x03, 0x45, 0x0d, 0x01, 0x20, 0x03, 0x20, 0x01, 0x10, + 0x38, 0x0c, 0x02, 0x0b, 0x20, 0x03, 0x0d, 0x01, 0x0b, 0x41, 0x00, 0x21, + 0x03, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, + 0x03, 0x36, 0x02, 0x00, 0x0b, 0x9c, 0x01, 0x01, 0x02, 0x7f, 0x23, 0x00, + 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x02, 0x40, 0x20, 0x00, 0x20, + 0x00, 0x41, 0x04, 0x6a, 0x22, 0x03, 0x4d, 0x04, 0x40, 0x20, 0x03, 0x41, + 0x01, 0x6b, 0x22, 0x00, 0x20, 0x03, 0x4d, 0x0d, 0x01, 0x0b, 0x00, 0x0b, + 0x20, 0x00, 0x41, 0x02, 0x76, 0x21, 0x00, 0x20, 0x02, 0x41, 0xc0, 0x82, + 0xc0, 0x00, 0x28, 0x02, 0x00, 0x36, 0x02, 0x0c, 0x02, 0x40, 0x20, 0x00, + 0x20, 0x01, 0x20, 0x02, 0x41, 0x0c, 0x6a, 0x10, 0x35, 0x22, 0x03, 0x0d, + 0x00, 0x20, 0x02, 0x20, 0x00, 0x20, 0x01, 0x10, 0x34, 0x41, 0x00, 0x21, + 0x03, 0x20, 0x02, 0x28, 0x02, 0x00, 0x0d, 0x00, 0x20, 0x02, 0x28, 0x02, + 0x04, 0x22, 0x03, 0x20, 0x02, 0x28, 0x02, 0x0c, 0x36, 0x02, 0x08, 0x20, + 0x02, 0x20, 0x03, 0x36, 0x02, 0x0c, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, + 0x41, 0x0c, 0x6a, 0x10, 0x35, 0x21, 0x03, 0x0b, 0x41, 0xc0, 0x82, 0xc0, + 0x00, 0x20, 0x02, 0x28, 0x02, 0x0c, 0x36, 0x02, 0x00, 0x20, 0x02, 0x41, + 0x10, 0x6a, 0x24, 0x00, 0x20, 0x03, 0x0b, 0xd0, 0x01, 0x01, 0x03, 0x7f, + 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, 0x00, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x20, 0x01, 0x41, 0x00, 0x4e, 0x04, 0x40, 0x20, 0x02, + 0x28, 0x02, 0x00, 0x22, 0x04, 0x0d, 0x01, 0x20, 0x03, 0x20, 0x01, 0x10, + 0x0f, 0x20, 0x03, 0x28, 0x02, 0x04, 0x21, 0x04, 0x20, 0x03, 0x28, 0x02, + 0x00, 0x21, 0x02, 0x0c, 0x02, 0x0b, 0x20, 0x00, 0x41, 0x01, 0x36, 0x02, + 0x00, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0c, + 0x02, 0x0b, 0x20, 0x02, 0x28, 0x02, 0x04, 0x22, 0x05, 0x45, 0x04, 0x40, + 0x20, 0x03, 0x41, 0x08, 0x6a, 0x20, 0x01, 0x41, 0x00, 0x10, 0x10, 0x20, + 0x03, 0x28, 0x02, 0x0c, 0x21, 0x04, 0x20, 0x03, 0x28, 0x02, 0x08, 0x21, + 0x02, 0x0c, 0x01, 0x0b, 0x20, 0x01, 0x41, 0x01, 0x10, 0x11, 0x22, 0x02, + 0x45, 0x04, 0x40, 0x41, 0x00, 0x21, 0x02, 0x0c, 0x01, 0x0b, 0x20, 0x02, + 0x20, 0x04, 0x20, 0x05, 0x10, 0x37, 0x20, 0x04, 0x20, 0x05, 0x10, 0x0b, + 0x20, 0x01, 0x21, 0x04, 0x0b, 0x20, 0x00, 0x02, 0x7f, 0x20, 0x02, 0x04, + 0x40, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x04, 0x41, 0x00, 0x0c, 0x01, + 0x0b, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x04, 0x41, 0x01, 0x21, 0x04, + 0x41, 0x01, 0x0b, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x20, + 0x04, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x00, + 0x0b, 0xa7, 0x01, 0x01, 0x03, 0x7f, 0x23, 0x00, 0x41, 0x20, 0x6b, 0x22, + 0x02, 0x24, 0x00, 0x02, 0x40, 0x20, 0x01, 0x20, 0x00, 0x28, 0x02, 0x04, + 0x22, 0x03, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x04, 0x6b, 0x4b, 0x04, + 0x40, 0x20, 0x01, 0x20, 0x04, 0x6a, 0x22, 0x01, 0x20, 0x04, 0x49, 0x0d, + 0x01, 0x20, 0x03, 0x20, 0x03, 0x6a, 0x22, 0x04, 0x20, 0x03, 0x49, 0x0d, + 0x01, 0x20, 0x04, 0x20, 0x01, 0x20, 0x01, 0x20, 0x04, 0x49, 0x1b, 0x22, + 0x01, 0x41, 0x08, 0x20, 0x01, 0x41, 0x08, 0x4b, 0x1b, 0x21, 0x01, 0x02, + 0x40, 0x20, 0x03, 0x04, 0x40, 0x20, 0x02, 0x41, 0x18, 0x6a, 0x41, 0x01, + 0x36, 0x02, 0x00, 0x20, 0x02, 0x20, 0x03, 0x36, 0x02, 0x14, 0x20, 0x02, + 0x20, 0x00, 0x28, 0x02, 0x00, 0x36, 0x02, 0x10, 0x0c, 0x01, 0x0b, 0x20, + 0x02, 0x41, 0x00, 0x36, 0x02, 0x10, 0x0b, 0x20, 0x02, 0x20, 0x01, 0x20, + 0x02, 0x41, 0x10, 0x6a, 0x10, 0x12, 0x20, 0x02, 0x28, 0x02, 0x00, 0x41, + 0x01, 0x46, 0x0d, 0x01, 0x20, 0x00, 0x20, 0x02, 0x29, 0x02, 0x04, 0x37, + 0x02, 0x00, 0x0b, 0x20, 0x02, 0x41, 0x20, 0x6a, 0x24, 0x00, 0x0f, 0x0b, + 0x00, 0x0b, 0x32, 0x01, 0x01, 0x7f, 0x20, 0x00, 0x20, 0x02, 0x10, 0x13, + 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x03, 0x20, 0x00, 0x28, 0x02, 0x00, + 0x6a, 0x20, 0x01, 0x20, 0x02, 0x10, 0x37, 0x20, 0x03, 0x20, 0x02, 0x20, + 0x03, 0x6a, 0x22, 0x01, 0x4b, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x00, 0x20, + 0x01, 0x36, 0x02, 0x08, 0x0b, 0x46, 0x01, 0x02, 0x7f, 0x23, 0x00, 0x41, + 0x10, 0x6b, 0x22, 0x03, 0x24, 0x00, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x20, + 0x02, 0x41, 0x00, 0x10, 0x0e, 0x20, 0x03, 0x28, 0x02, 0x08, 0x21, 0x04, + 0x20, 0x00, 0x20, 0x03, 0x28, 0x02, 0x0c, 0x36, 0x02, 0x04, 0x20, 0x00, + 0x20, 0x04, 0x36, 0x02, 0x00, 0x20, 0x04, 0x20, 0x01, 0x20, 0x02, 0x10, + 0x37, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x08, 0x20, 0x03, 0x41, 0x10, + 0x6a, 0x24, 0x00, 0x0b, 0x14, 0x00, 0x20, 0x01, 0x20, 0x03, 0x46, 0x04, + 0x40, 0x20, 0x00, 0x20, 0x02, 0x20, 0x01, 0x10, 0x37, 0x0f, 0x0b, 0x00, + 0x0b, 0xc6, 0x01, 0x01, 0x04, 0x7f, 0x02, 0x40, 0x20, 0x00, 0x0d, 0x00, + 0x20, 0x00, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x45, 0x0d, + 0x00, 0x20, 0x00, 0x45, 0x0d, 0x00, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x21, + 0x00, 0x0b, 0x03, 0x40, 0x02, 0x40, 0x41, 0x00, 0x20, 0x00, 0x04, 0x7f, + 0x20, 0x00, 0x0d, 0x01, 0x41, 0x00, 0x05, 0x41, 0x00, 0x0b, 0x22, 0x00, + 0x6b, 0x22, 0x02, 0x0d, 0x02, 0x20, 0x02, 0x45, 0x0d, 0x02, 0x20, 0x00, + 0x41, 0xc0, 0x82, 0xc0, 0x00, 0x6a, 0x22, 0x00, 0x2c, 0x00, 0x00, 0x22, + 0x03, 0x41, 0x7f, 0x4a, 0x0d, 0x02, 0x20, 0x00, 0x20, 0x02, 0x6a, 0x22, + 0x04, 0x21, 0x01, 0x20, 0x02, 0x41, 0x01, 0x47, 0x04, 0x40, 0x20, 0x00, + 0x2d, 0x00, 0x01, 0x1a, 0x20, 0x00, 0x41, 0x02, 0x6a, 0x21, 0x01, 0x0b, + 0x20, 0x03, 0x41, 0xff, 0x01, 0x71, 0x41, 0xe0, 0x01, 0x49, 0x0d, 0x02, + 0x20, 0x04, 0x22, 0x00, 0x20, 0x01, 0x47, 0x04, 0x7f, 0x20, 0x01, 0x41, + 0x01, 0x6a, 0x21, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x05, 0x41, 0x00, + 0x0b, 0x1a, 0x20, 0x03, 0x41, 0xff, 0x01, 0x71, 0x41, 0xf0, 0x01, 0x49, + 0x0d, 0x02, 0x20, 0x00, 0x20, 0x04, 0x47, 0x04, 0x40, 0x20, 0x00, 0x2d, + 0x00, 0x00, 0x1a, 0x0b, 0x0c, 0x02, 0x0b, 0x20, 0x00, 0x41, 0x01, 0x6b, + 0x21, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0xec, 0x01, 0x01, + 0x06, 0x7f, 0x41, 0x01, 0x21, 0x07, 0x41, 0x01, 0x21, 0x04, 0x03, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, 0x22, 0x06, 0x20, 0x05, + 0x6a, 0x22, 0x04, 0x20, 0x06, 0x49, 0x0d, 0x00, 0x20, 0x02, 0x20, 0x04, + 0x4d, 0x0d, 0x01, 0x20, 0x05, 0x20, 0x08, 0x6a, 0x22, 0x09, 0x20, 0x08, + 0x49, 0x0d, 0x00, 0x20, 0x02, 0x20, 0x09, 0x4d, 0x0d, 0x00, 0x02, 0x40, + 0x02, 0x40, 0x20, 0x01, 0x20, 0x04, 0x6a, 0x2d, 0x00, 0x00, 0x22, 0x04, + 0x20, 0x01, 0x20, 0x09, 0x6a, 0x2d, 0x00, 0x00, 0x22, 0x09, 0x4b, 0x20, + 0x03, 0x71, 0x0d, 0x00, 0x20, 0x04, 0x20, 0x09, 0x4f, 0x20, 0x03, 0x72, + 0x45, 0x0d, 0x00, 0x20, 0x04, 0x20, 0x09, 0x46, 0x0d, 0x01, 0x20, 0x06, + 0x41, 0x01, 0x6a, 0x22, 0x04, 0x20, 0x06, 0x49, 0x0d, 0x02, 0x41, 0x01, + 0x21, 0x07, 0x41, 0x00, 0x21, 0x05, 0x20, 0x06, 0x21, 0x08, 0x0c, 0x05, + 0x0b, 0x20, 0x05, 0x41, 0x01, 0x6a, 0x22, 0x04, 0x20, 0x05, 0x49, 0x0d, + 0x01, 0x20, 0x04, 0x20, 0x06, 0x6a, 0x22, 0x04, 0x20, 0x06, 0x49, 0x0d, + 0x01, 0x20, 0x04, 0x20, 0x08, 0x6b, 0x22, 0x07, 0x20, 0x04, 0x4b, 0x0d, + 0x01, 0x0c, 0x03, 0x0b, 0x20, 0x05, 0x20, 0x05, 0x41, 0x01, 0x6a, 0x22, + 0x05, 0x4b, 0x0d, 0x00, 0x20, 0x06, 0x21, 0x04, 0x20, 0x05, 0x20, 0x07, + 0x47, 0x0d, 0x03, 0x20, 0x04, 0x20, 0x04, 0x20, 0x07, 0x6a, 0x22, 0x04, + 0x4d, 0x0d, 0x02, 0x0b, 0x00, 0x0b, 0x20, 0x00, 0x20, 0x07, 0x36, 0x02, + 0x04, 0x20, 0x00, 0x20, 0x08, 0x36, 0x02, 0x00, 0x0f, 0x0b, 0x41, 0x00, + 0x21, 0x05, 0x0c, 0x00, 0x0b, 0x00, 0x0b, 0x37, 0x01, 0x01, 0x7f, 0x23, + 0x00, 0x41, 0x10, 0x6b, 0x22, 0x04, 0x24, 0x00, 0x20, 0x04, 0x41, 0x08, + 0x6a, 0x41, 0x00, 0x20, 0x03, 0x20, 0x01, 0x20, 0x02, 0x10, 0x1a, 0x20, + 0x00, 0x20, 0x04, 0x28, 0x02, 0x08, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, + 0x04, 0x28, 0x02, 0x0c, 0x36, 0x02, 0x04, 0x20, 0x04, 0x41, 0x10, 0x6a, + 0x24, 0x00, 0x0b, 0x36, 0x00, 0x02, 0x40, 0x20, 0x01, 0x20, 0x02, 0x4d, + 0x04, 0x40, 0x20, 0x02, 0x20, 0x04, 0x4d, 0x04, 0x40, 0x20, 0x02, 0x20, + 0x02, 0x20, 0x01, 0x6b, 0x22, 0x04, 0x49, 0x0d, 0x02, 0x20, 0x00, 0x20, + 0x04, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x01, 0x20, 0x03, 0x6a, 0x36, + 0x02, 0x00, 0x0f, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0xa0, 0x02, + 0x01, 0x07, 0x7f, 0x41, 0x01, 0x21, 0x09, 0x41, 0x01, 0x21, 0x04, 0x03, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, 0x22, + 0x07, 0x20, 0x05, 0x6a, 0x22, 0x04, 0x20, 0x07, 0x49, 0x0d, 0x00, 0x20, + 0x01, 0x20, 0x04, 0x4d, 0x0d, 0x03, 0x20, 0x07, 0x41, 0x01, 0x6a, 0x22, + 0x04, 0x20, 0x07, 0x49, 0x0d, 0x00, 0x20, 0x04, 0x20, 0x05, 0x6a, 0x22, + 0x08, 0x20, 0x04, 0x49, 0x0d, 0x00, 0x20, 0x01, 0x20, 0x08, 0x6b, 0x22, + 0x08, 0x20, 0x01, 0x4b, 0x0d, 0x00, 0x20, 0x01, 0x20, 0x08, 0x4d, 0x0d, + 0x00, 0x20, 0x0a, 0x41, 0x01, 0x6a, 0x22, 0x06, 0x20, 0x0a, 0x49, 0x0d, + 0x00, 0x20, 0x06, 0x20, 0x05, 0x20, 0x06, 0x6a, 0x22, 0x06, 0x4b, 0x0d, + 0x00, 0x20, 0x01, 0x20, 0x06, 0x6b, 0x22, 0x06, 0x20, 0x01, 0x4b, 0x0d, + 0x00, 0x20, 0x01, 0x20, 0x06, 0x4d, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x00, 0x20, 0x08, 0x6a, 0x2d, 0x00, 0x00, 0x22, 0x08, 0x20, 0x00, + 0x20, 0x06, 0x6a, 0x2d, 0x00, 0x00, 0x22, 0x06, 0x4b, 0x20, 0x03, 0x71, + 0x0d, 0x00, 0x20, 0x06, 0x20, 0x08, 0x4d, 0x20, 0x03, 0x72, 0x45, 0x0d, + 0x00, 0x20, 0x06, 0x20, 0x08, 0x46, 0x0d, 0x01, 0x41, 0x01, 0x21, 0x09, + 0x41, 0x00, 0x21, 0x05, 0x20, 0x07, 0x21, 0x0a, 0x0c, 0x04, 0x0b, 0x20, + 0x05, 0x41, 0x01, 0x6a, 0x22, 0x04, 0x20, 0x05, 0x49, 0x0d, 0x01, 0x20, + 0x04, 0x20, 0x07, 0x6a, 0x22, 0x04, 0x20, 0x07, 0x49, 0x0d, 0x01, 0x20, + 0x04, 0x20, 0x0a, 0x6b, 0x22, 0x09, 0x20, 0x04, 0x4b, 0x0d, 0x01, 0x0c, + 0x02, 0x0b, 0x20, 0x05, 0x20, 0x05, 0x41, 0x01, 0x6a, 0x22, 0x05, 0x4b, + 0x0d, 0x00, 0x20, 0x05, 0x20, 0x09, 0x47, 0x04, 0x40, 0x20, 0x07, 0x21, + 0x04, 0x0c, 0x03, 0x0b, 0x20, 0x07, 0x20, 0x09, 0x6a, 0x22, 0x04, 0x20, + 0x07, 0x4f, 0x0d, 0x01, 0x0b, 0x00, 0x0b, 0x41, 0x00, 0x21, 0x05, 0x0b, + 0x20, 0x02, 0x20, 0x09, 0x47, 0x0d, 0x01, 0x0b, 0x0b, 0x20, 0x0a, 0x0b, + 0x2b, 0x01, 0x01, 0x7e, 0x03, 0x40, 0x20, 0x01, 0x04, 0x40, 0x20, 0x01, + 0x41, 0x01, 0x6b, 0x21, 0x01, 0x42, 0x01, 0x20, 0x00, 0x31, 0x00, 0x00, + 0x86, 0x20, 0x02, 0x84, 0x21, 0x02, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x21, + 0x00, 0x0c, 0x01, 0x0b, 0x0b, 0x20, 0x02, 0x0b, 0x04, 0x00, 0x41, 0x00, + 0x0b, 0xbd, 0x04, 0x02, 0x05, 0x7f, 0x01, 0x7e, 0x23, 0x00, 0x41, 0xa0, + 0x01, 0x6b, 0x22, 0x00, 0x24, 0x00, 0x20, 0x00, 0x41, 0x18, 0x6a, 0x10, + 0x0d, 0x20, 0x00, 0x41, 0x30, 0x6a, 0x41, 0x00, 0x10, 0x1f, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x30, 0x45, 0x04, 0x40, + 0x20, 0x00, 0x41, 0xd0, 0x00, 0x6a, 0x20, 0x00, 0x41, 0x3c, 0x6a, 0x29, + 0x02, 0x00, 0x37, 0x03, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x02, 0x34, + 0x37, 0x03, 0x48, 0x20, 0x00, 0x41, 0xe0, 0x00, 0x6a, 0x20, 0x00, 0x41, + 0xd4, 0x00, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x01, 0x36, 0x02, 0x00, 0x20, + 0x00, 0x20, 0x00, 0x29, 0x02, 0x4c, 0x22, 0x05, 0x37, 0x03, 0x58, 0x20, + 0x00, 0x20, 0x01, 0x36, 0x02, 0x6c, 0x20, 0x00, 0x20, 0x05, 0x3e, 0x02, + 0x68, 0x20, 0x00, 0x41, 0x90, 0x01, 0x6a, 0x20, 0x00, 0x41, 0xe8, 0x00, + 0x6a, 0x10, 0x20, 0x20, 0x00, 0x28, 0x02, 0x90, 0x01, 0x45, 0x0d, 0x02, + 0x20, 0x00, 0x41, 0xf8, 0x00, 0x6a, 0x20, 0x00, 0x41, 0x98, 0x01, 0x6a, + 0x28, 0x02, 0x00, 0x22, 0x01, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x29, 0x03, 0x90, 0x01, 0x22, 0x05, 0x37, 0x03, 0x70, 0x20, 0x00, 0x41, + 0x88, 0x01, 0x6a, 0x20, 0x01, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x05, + 0x37, 0x03, 0x80, 0x01, 0x02, 0x40, 0x20, 0x00, 0x41, 0x18, 0x6a, 0x10, + 0x21, 0x45, 0x04, 0x40, 0x20, 0x00, 0x41, 0x10, 0x6a, 0x21, 0x03, 0x20, + 0x00, 0x41, 0x24, 0x6a, 0x22, 0x01, 0x22, 0x02, 0x28, 0x02, 0x00, 0x04, + 0x40, 0x00, 0x0b, 0x20, 0x02, 0x41, 0x7f, 0x36, 0x02, 0x00, 0x20, 0x03, + 0x20, 0x02, 0x36, 0x02, 0x04, 0x20, 0x03, 0x20, 0x02, 0x41, 0x04, 0x6a, + 0x36, 0x02, 0x00, 0x20, 0x00, 0x28, 0x02, 0x14, 0x21, 0x02, 0x20, 0x00, + 0x28, 0x02, 0x10, 0x20, 0x00, 0x41, 0x98, 0x01, 0x6a, 0x20, 0x00, 0x41, + 0xf8, 0x00, 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, + 0x00, 0x29, 0x03, 0x70, 0x37, 0x03, 0x90, 0x01, 0x20, 0x00, 0x41, 0x90, + 0x01, 0x6a, 0x10, 0x22, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x03, 0x41, + 0x01, 0x6a, 0x22, 0x04, 0x20, 0x03, 0x48, 0x0d, 0x05, 0x20, 0x02, 0x20, + 0x04, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x21, 0x02, 0x20, + 0x01, 0x28, 0x02, 0x00, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x01, 0x41, 0x7f, + 0x36, 0x02, 0x00, 0x20, 0x02, 0x20, 0x01, 0x36, 0x02, 0x04, 0x20, 0x02, + 0x20, 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x28, 0x02, + 0x0c, 0x21, 0x01, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x02, 0x2d, 0x00, + 0x04, 0x41, 0x02, 0x47, 0x04, 0x40, 0x20, 0x02, 0x41, 0x01, 0x3a, 0x00, + 0x04, 0x0b, 0x20, 0x01, 0x28, 0x02, 0x00, 0x22, 0x02, 0x41, 0x01, 0x6a, + 0x22, 0x03, 0x20, 0x02, 0x48, 0x0d, 0x05, 0x20, 0x01, 0x20, 0x03, 0x36, + 0x02, 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x41, 0x80, 0x01, 0x6a, 0x10, + 0x0a, 0x0b, 0x20, 0x00, 0x41, 0x18, 0x6a, 0x10, 0x23, 0x20, 0x00, 0x41, + 0xd8, 0x00, 0x6a, 0x10, 0x0a, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x41, 0xc8, + 0x00, 0x6a, 0x41, 0xa9, 0x82, 0xc0, 0x00, 0x41, 0x14, 0x10, 0x15, 0x20, + 0x00, 0x41, 0xc8, 0x00, 0x6a, 0x10, 0x24, 0x20, 0x00, 0x41, 0xc8, 0x00, + 0x6a, 0x10, 0x0a, 0x0b, 0x20, 0x00, 0x41, 0x18, 0x6a, 0x10, 0x0a, 0x20, + 0x00, 0x41, 0x2c, 0x6a, 0x2d, 0x00, 0x00, 0x41, 0x02, 0x47, 0x04, 0x40, + 0x20, 0x00, 0x28, 0x02, 0x28, 0x22, 0x01, 0x28, 0x02, 0x00, 0x04, 0x7f, + 0x20, 0x01, 0x10, 0x0a, 0x20, 0x00, 0x28, 0x02, 0x28, 0x05, 0x20, 0x01, + 0x0b, 0x41, 0x0c, 0x10, 0x0b, 0x0b, 0x20, 0x00, 0x41, 0xa0, 0x01, 0x6a, + 0x24, 0x00, 0x0f, 0x0b, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x01, 0x24, + 0x00, 0x20, 0x01, 0x41, 0x9b, 0x82, 0xc0, 0x00, 0x41, 0x0e, 0x10, 0x15, + 0x20, 0x01, 0x10, 0x24, 0x20, 0x01, 0x10, 0x0a, 0x00, 0x0b, 0x00, 0x0b, + 0x97, 0x02, 0x01, 0x02, 0x7f, 0x23, 0x00, 0x41, 0x30, 0x6b, 0x22, 0x02, + 0x24, 0x00, 0x02, 0x40, 0x02, 0x40, 0x10, 0x07, 0x22, 0x03, 0x41, 0x03, + 0x4b, 0x0d, 0x00, 0x20, 0x01, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x81, + 0x08, 0x3b, 0x01, 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x02, 0x20, 0x03, 0x10, + 0x2b, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x03, 0x10, 0x08, 0x20, 0x00, + 0x02, 0x7f, 0x02, 0x40, 0x20, 0x01, 0x04, 0x40, 0x20, 0x02, 0x20, 0x02, + 0x28, 0x02, 0x08, 0x22, 0x01, 0x36, 0x02, 0x14, 0x20, 0x02, 0x20, 0x03, + 0x36, 0x02, 0x10, 0x02, 0x40, 0x20, 0x01, 0x41, 0x04, 0x49, 0x0d, 0x00, + 0x20, 0x02, 0x41, 0x00, 0x36, 0x02, 0x1c, 0x20, 0x02, 0x41, 0x10, 0x6a, + 0x20, 0x02, 0x41, 0x1c, 0x6a, 0x41, 0x04, 0x10, 0x30, 0x0d, 0x00, 0x20, + 0x02, 0x41, 0x20, 0x6a, 0x20, 0x02, 0x28, 0x02, 0x14, 0x10, 0x2b, 0x20, + 0x02, 0x41, 0x10, 0x6a, 0x20, 0x02, 0x28, 0x02, 0x20, 0x22, 0x01, 0x20, + 0x02, 0x28, 0x02, 0x28, 0x10, 0x30, 0x45, 0x0d, 0x02, 0x20, 0x02, 0x41, + 0x20, 0x6a, 0x10, 0x0a, 0x0b, 0x20, 0x00, 0x41, 0x00, 0x3a, 0x00, 0x01, + 0x41, 0x01, 0x0c, 0x02, 0x0b, 0x20, 0x00, 0x41, 0x00, 0x3a, 0x00, 0x00, + 0x20, 0x00, 0x41, 0x04, 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x41, 0x08, 0x6a, 0x20, 0x02, 0x29, 0x03, 0x00, 0x37, 0x02, 0x00, 0x20, + 0x00, 0x41, 0x10, 0x6a, 0x20, 0x02, 0x41, 0x08, 0x6a, 0x28, 0x02, 0x00, + 0x36, 0x02, 0x00, 0x0c, 0x02, 0x0b, 0x20, 0x02, 0x28, 0x02, 0x1c, 0x21, + 0x03, 0x20, 0x00, 0x41, 0x0c, 0x6a, 0x20, 0x02, 0x29, 0x02, 0x24, 0x37, + 0x02, 0x00, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x20, 0x01, 0x36, 0x02, 0x00, + 0x20, 0x00, 0x41, 0x04, 0x6a, 0x20, 0x03, 0x36, 0x02, 0x00, 0x41, 0x00, + 0x0b, 0x3a, 0x00, 0x00, 0x20, 0x02, 0x10, 0x0a, 0x0b, 0x20, 0x02, 0x41, + 0x30, 0x6a, 0x24, 0x00, 0x0b, 0x62, 0x02, 0x01, 0x7f, 0x01, 0x7e, 0x23, + 0x00, 0x41, 0x20, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x20, 0x02, 0x41, 0x10, + 0x6a, 0x20, 0x01, 0x10, 0x26, 0x02, 0x40, 0x20, 0x02, 0x28, 0x02, 0x10, + 0x04, 0x40, 0x20, 0x02, 0x41, 0x08, 0x6a, 0x20, 0x02, 0x41, 0x18, 0x6a, + 0x28, 0x02, 0x00, 0x22, 0x01, 0x36, 0x02, 0x00, 0x20, 0x02, 0x20, 0x02, + 0x29, 0x03, 0x10, 0x22, 0x03, 0x37, 0x03, 0x00, 0x20, 0x00, 0x41, 0x08, + 0x6a, 0x20, 0x01, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x03, 0x37, 0x02, + 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0b, + 0x20, 0x02, 0x41, 0x20, 0x6a, 0x24, 0x00, 0x0b, 0x26, 0x00, 0x20, 0x00, + 0x10, 0x27, 0x20, 0x00, 0x41, 0x10, 0x6a, 0x22, 0x00, 0x2d, 0x00, 0x04, + 0x41, 0x02, 0x46, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x00, 0x28, 0x02, 0x00, + 0x22, 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x1b, 0x0b, 0xa0, + 0x01, 0x01, 0x02, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, + 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x04, 0x41, 0x02, + 0x46, 0x04, 0x40, 0x41, 0x0c, 0x41, 0x04, 0x10, 0x11, 0x22, 0x02, 0x45, + 0x0d, 0x02, 0x20, 0x00, 0x41, 0x00, 0x3a, 0x00, 0x04, 0x20, 0x00, 0x20, + 0x02, 0x36, 0x02, 0x00, 0x20, 0x02, 0x20, 0x01, 0x29, 0x02, 0x00, 0x37, + 0x02, 0x00, 0x20, 0x02, 0x41, 0x08, 0x6a, 0x20, 0x01, 0x41, 0x08, 0x6a, + 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x41, + 0x08, 0x6a, 0x22, 0x02, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x28, 0x02, 0x00, + 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x01, 0x29, 0x02, 0x00, 0x37, 0x03, + 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x00, 0x28, 0x02, 0x00, 0x04, + 0x40, 0x20, 0x00, 0x10, 0x0a, 0x0b, 0x20, 0x00, 0x20, 0x03, 0x29, 0x03, + 0x00, 0x37, 0x02, 0x00, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x20, 0x02, 0x28, + 0x02, 0x00, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, + 0x00, 0x0f, 0x0b, 0x00, 0x0b, 0xdc, 0x03, 0x01, 0x08, 0x7f, 0x23, 0x00, + 0x41, 0x40, 0x6a, 0x22, 0x01, 0x24, 0x00, 0x20, 0x01, 0x41, 0x28, 0x6a, + 0x20, 0x00, 0x41, 0x0c, 0x6a, 0x22, 0x03, 0x10, 0x2d, 0x20, 0x01, 0x28, + 0x02, 0x2c, 0x22, 0x04, 0x28, 0x02, 0x00, 0x22, 0x05, 0x41, 0x01, 0x6b, + 0x22, 0x02, 0x20, 0x05, 0x4e, 0x21, 0x05, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x01, 0x28, 0x02, 0x28, 0x2d, 0x00, 0x04, 0x22, 0x06, 0x41, 0x02, 0x47, + 0x04, 0x40, 0x20, 0x05, 0x0d, 0x02, 0x20, 0x04, 0x20, 0x02, 0x36, 0x02, + 0x00, 0x20, 0x06, 0x45, 0x0d, 0x01, 0x20, 0x01, 0x41, 0x20, 0x6a, 0x21, + 0x04, 0x20, 0x03, 0x28, 0x02, 0x00, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x03, + 0x41, 0x7f, 0x36, 0x02, 0x00, 0x20, 0x04, 0x20, 0x03, 0x36, 0x02, 0x04, + 0x20, 0x04, 0x20, 0x03, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x01, + 0x28, 0x02, 0x24, 0x21, 0x04, 0x20, 0x01, 0x28, 0x02, 0x20, 0x22, 0x02, + 0x2d, 0x00, 0x04, 0x41, 0x02, 0x46, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x02, + 0x28, 0x02, 0x00, 0x22, 0x02, 0x41, 0x00, 0x20, 0x02, 0x28, 0x02, 0x00, + 0x1b, 0x22, 0x02, 0x04, 0x40, 0x20, 0x00, 0x28, 0x02, 0x08, 0x21, 0x06, + 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0xc4, 0x82, 0xc0, 0x00, 0x41, 0x00, + 0x36, 0x02, 0x00, 0x20, 0x01, 0x41, 0x30, 0x6a, 0x20, 0x02, 0x10, 0x29, + 0x41, 0xc4, 0x82, 0xc0, 0x00, 0x28, 0x02, 0x00, 0x22, 0x00, 0x20, 0x01, + 0x28, 0x02, 0x38, 0x22, 0x02, 0x6a, 0x22, 0x05, 0x20, 0x00, 0x49, 0x0d, + 0x03, 0x20, 0x05, 0x41, 0x80, 0x80, 0x01, 0x4b, 0x0d, 0x03, 0x20, 0x01, + 0x28, 0x02, 0x30, 0x21, 0x08, 0x20, 0x01, 0x41, 0x18, 0x6a, 0x20, 0x00, + 0x20, 0x05, 0x10, 0x2e, 0x20, 0x01, 0x28, 0x02, 0x18, 0x20, 0x01, 0x28, + 0x02, 0x1c, 0x20, 0x08, 0x20, 0x02, 0x10, 0x16, 0x41, 0xc4, 0x82, 0xc0, + 0x00, 0x28, 0x02, 0x00, 0x22, 0x00, 0x20, 0x02, 0x6a, 0x22, 0x02, 0x20, + 0x00, 0x49, 0x0d, 0x03, 0x41, 0xc4, 0x82, 0xc0, 0x00, 0x20, 0x02, 0x36, + 0x02, 0x00, 0x20, 0x01, 0x41, 0x30, 0x6a, 0x10, 0x0a, 0x20, 0x01, 0x41, + 0x10, 0x6a, 0x41, 0xc4, 0x82, 0xc0, 0x00, 0x28, 0x02, 0x00, 0x10, 0x2f, + 0x20, 0x06, 0x20, 0x01, 0x28, 0x02, 0x10, 0x20, 0x01, 0x28, 0x02, 0x14, + 0x10, 0x04, 0x0b, 0x20, 0x04, 0x28, 0x02, 0x00, 0x22, 0x00, 0x41, 0x01, + 0x6a, 0x22, 0x02, 0x20, 0x00, 0x48, 0x0d, 0x02, 0x20, 0x04, 0x20, 0x02, + 0x36, 0x02, 0x00, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x21, 0x00, 0x20, 0x03, + 0x28, 0x02, 0x00, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x03, 0x41, 0x7f, 0x36, + 0x02, 0x00, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, + 0x03, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x01, 0x28, 0x02, 0x0c, + 0x21, 0x00, 0x20, 0x01, 0x28, 0x02, 0x08, 0x22, 0x03, 0x2d, 0x00, 0x04, + 0x41, 0x02, 0x47, 0x04, 0x40, 0x20, 0x03, 0x41, 0x00, 0x3a, 0x00, 0x04, + 0x0b, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x03, 0x41, 0x01, 0x6a, 0x22, + 0x04, 0x20, 0x03, 0x48, 0x0d, 0x02, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, + 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x0d, 0x01, 0x20, 0x04, 0x20, 0x02, + 0x36, 0x02, 0x00, 0x0b, 0x20, 0x01, 0x41, 0x40, 0x6b, 0x24, 0x00, 0x0f, + 0x0b, 0x00, 0x0b, 0x2a, 0x01, 0x01, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, + 0x22, 0x01, 0x24, 0x00, 0x20, 0x01, 0x20, 0x00, 0x10, 0x29, 0x20, 0x01, + 0x28, 0x02, 0x00, 0x20, 0x01, 0x28, 0x02, 0x08, 0x10, 0x05, 0x20, 0x01, + 0x10, 0x0a, 0x20, 0x01, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x9e, 0x0e, + 0x02, 0x08, 0x7f, 0x01, 0x7e, 0x23, 0x00, 0x41, 0x80, 0x02, 0x6b, 0x22, + 0x00, 0x24, 0x00, 0x20, 0x00, 0x41, 0xc8, 0x00, 0x6a, 0x10, 0x0d, 0x20, + 0x00, 0x41, 0xa8, 0x01, 0x6a, 0x41, 0x01, 0x10, 0x1f, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0xa8, + 0x01, 0x41, 0x01, 0x47, 0x04, 0x40, 0x20, 0x00, 0x41, 0xe8, 0x00, 0x6a, + 0x20, 0x00, 0x41, 0xb8, 0x01, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x01, 0x36, + 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x41, 0xb0, 0x01, 0x6a, 0x29, 0x03, + 0x00, 0x22, 0x08, 0x37, 0x03, 0x60, 0x20, 0x00, 0x28, 0x02, 0xac, 0x01, + 0x21, 0x02, 0x20, 0x00, 0x41, 0xf8, 0x00, 0x6a, 0x20, 0x01, 0x36, 0x02, + 0x00, 0x20, 0x00, 0x20, 0x08, 0x37, 0x03, 0x70, 0x02, 0x7f, 0x02, 0x40, + 0x20, 0x02, 0x41, 0xc4, 0xa5, 0x8a, 0x98, 0x7e, 0x47, 0x04, 0x40, 0x02, + 0x40, 0x20, 0x02, 0x41, 0xed, 0x98, 0x99, 0xe7, 0x03, 0x47, 0x04, 0x40, + 0x20, 0x02, 0x41, 0xce, 0xa6, 0xa3, 0xf4, 0x05, 0x47, 0x0d, 0x01, 0x20, + 0x00, 0x20, 0x00, 0x28, 0x02, 0x78, 0x36, 0x02, 0x84, 0x01, 0x20, 0x00, + 0x20, 0x00, 0x28, 0x02, 0x70, 0x36, 0x02, 0x80, 0x01, 0x20, 0x00, 0x41, + 0xf0, 0x01, 0x6a, 0x20, 0x00, 0x41, 0x80, 0x01, 0x6a, 0x10, 0x26, 0x20, + 0x00, 0x28, 0x02, 0xf0, 0x01, 0x45, 0x0d, 0x03, 0x20, 0x00, 0x41, 0x90, + 0x01, 0x6a, 0x20, 0x00, 0x41, 0xf8, 0x01, 0x6a, 0x28, 0x02, 0x00, 0x36, + 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0xf0, 0x01, 0x37, 0x03, + 0x88, 0x01, 0x20, 0x00, 0x41, 0xc8, 0x00, 0x6a, 0x10, 0x27, 0x20, 0x00, + 0x41, 0xdc, 0x00, 0x6a, 0x2d, 0x00, 0x00, 0x41, 0x02, 0x46, 0x0d, 0x09, + 0x20, 0x00, 0x41, 0x01, 0x3a, 0x00, 0x5c, 0x20, 0x00, 0x41, 0xd8, 0x00, + 0x6a, 0x28, 0x02, 0x00, 0x22, 0x02, 0x28, 0x02, 0x00, 0x45, 0x0d, 0x09, + 0x20, 0x00, 0x41, 0xf8, 0x01, 0x6a, 0x20, 0x00, 0x41, 0x90, 0x01, 0x6a, + 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, + 0x88, 0x01, 0x37, 0x03, 0xf0, 0x01, 0x20, 0x00, 0x41, 0xce, 0xa6, 0xa3, + 0xf4, 0x05, 0x36, 0x02, 0xa8, 0x01, 0x20, 0x00, 0x41, 0x98, 0x01, 0x6a, + 0x20, 0x00, 0x41, 0xa8, 0x01, 0x6a, 0x10, 0x28, 0x20, 0x00, 0x41, 0xa8, + 0x01, 0x6a, 0x20, 0x00, 0x41, 0xf0, 0x01, 0x6a, 0x10, 0x29, 0x20, 0x00, + 0x28, 0x02, 0xac, 0x01, 0x21, 0x06, 0x20, 0x00, 0x28, 0x02, 0xa8, 0x01, + 0x21, 0x01, 0x20, 0x00, 0x41, 0x98, 0x01, 0x6a, 0x20, 0x00, 0x28, 0x02, + 0xb0, 0x01, 0x22, 0x04, 0x10, 0x13, 0x20, 0x00, 0x28, 0x02, 0x98, 0x01, + 0x22, 0x05, 0x20, 0x00, 0x28, 0x02, 0xa0, 0x01, 0x22, 0x03, 0x6a, 0x20, + 0x01, 0x20, 0x04, 0x10, 0x37, 0x20, 0x03, 0x20, 0x04, 0x6a, 0x22, 0x04, + 0x20, 0x03, 0x49, 0x0d, 0x09, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, 0xa0, + 0x01, 0x20, 0x00, 0x20, 0x06, 0x36, 0x02, 0xec, 0x01, 0x20, 0x00, 0x20, + 0x01, 0x36, 0x02, 0xe8, 0x01, 0x20, 0x00, 0x41, 0xe8, 0x01, 0x6a, 0x10, + 0x0a, 0x20, 0x02, 0x28, 0x02, 0x00, 0x20, 0x02, 0x41, 0x08, 0x6a, 0x28, + 0x02, 0x00, 0x20, 0x05, 0x20, 0x04, 0x10, 0x00, 0x45, 0x04, 0x40, 0x41, + 0xc4, 0x82, 0xc0, 0x00, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, + 0x08, 0x6a, 0x41, 0x00, 0x10, 0x2a, 0x0b, 0x20, 0x00, 0x41, 0x98, 0x01, + 0x6a, 0x10, 0x0a, 0x20, 0x00, 0x41, 0xf0, 0x01, 0x6a, 0x10, 0x0a, 0x20, + 0x00, 0x41, 0xc8, 0x00, 0x6a, 0x10, 0x23, 0x41, 0x03, 0x0c, 0x04, 0x0b, + 0x20, 0x00, 0x41, 0xc8, 0x00, 0x6a, 0x10, 0x21, 0x22, 0x01, 0x45, 0x0d, + 0x08, 0x20, 0x00, 0x41, 0xed, 0x98, 0x99, 0xe7, 0x03, 0x36, 0x02, 0xa8, + 0x01, 0x20, 0x00, 0x41, 0xf0, 0x01, 0x6a, 0x20, 0x00, 0x41, 0xa8, 0x01, + 0x6a, 0x10, 0x28, 0x02, 0x7f, 0x41, 0x01, 0x20, 0x01, 0x28, 0x02, 0x00, + 0x20, 0x01, 0x28, 0x02, 0x08, 0x20, 0x00, 0x28, 0x02, 0xf0, 0x01, 0x20, + 0x00, 0x28, 0x02, 0xf8, 0x01, 0x10, 0x00, 0x0d, 0x00, 0x1a, 0x02, 0x40, + 0x10, 0x01, 0x22, 0x01, 0x41, 0x81, 0x80, 0x01, 0x4f, 0x04, 0x40, 0x20, + 0x00, 0x41, 0x98, 0x01, 0x6a, 0x20, 0x01, 0x10, 0x2b, 0x20, 0x00, 0x28, + 0x02, 0x98, 0x01, 0x22, 0x02, 0x10, 0x02, 0x20, 0x00, 0x20, 0x00, 0x28, + 0x02, 0xa0, 0x01, 0x36, 0x02, 0x8c, 0x01, 0x20, 0x00, 0x20, 0x02, 0x36, + 0x02, 0x88, 0x01, 0x20, 0x00, 0x41, 0xa8, 0x01, 0x6a, 0x20, 0x00, 0x41, + 0x88, 0x01, 0x6a, 0x10, 0x26, 0x20, 0x00, 0x29, 0x02, 0xac, 0x01, 0x21, + 0x08, 0x20, 0x00, 0x28, 0x02, 0xa8, 0x01, 0x21, 0x01, 0x20, 0x00, 0x41, + 0x98, 0x01, 0x6a, 0x10, 0x0a, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x21, 0x02, + 0x20, 0x01, 0x04, 0x40, 0x10, 0x2c, 0x10, 0x02, 0x20, 0x01, 0x21, 0x02, + 0x0b, 0x41, 0xc4, 0x82, 0xc0, 0x00, 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, + 0x00, 0x41, 0x10, 0x6a, 0x20, 0x02, 0x10, 0x2a, 0x20, 0x00, 0x20, 0x00, + 0x29, 0x03, 0x10, 0x37, 0x03, 0x98, 0x01, 0x20, 0x00, 0x41, 0xa8, 0x01, + 0x6a, 0x20, 0x00, 0x41, 0x98, 0x01, 0x6a, 0x10, 0x26, 0x20, 0x00, 0x29, + 0x02, 0xac, 0x01, 0x21, 0x08, 0x20, 0x00, 0x28, 0x02, 0xa8, 0x01, 0x21, + 0x01, 0x0b, 0x20, 0x01, 0x45, 0x0b, 0x20, 0x00, 0x41, 0xf0, 0x01, 0x6a, + 0x10, 0x0a, 0x0d, 0x08, 0x20, 0x01, 0x45, 0x0d, 0x08, 0x20, 0x00, 0x20, + 0x08, 0x37, 0x02, 0x9c, 0x01, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x98, + 0x01, 0x20, 0x00, 0x41, 0xa8, 0x01, 0x6a, 0x20, 0x00, 0x41, 0x98, 0x01, + 0x6a, 0x10, 0x29, 0x20, 0x00, 0x28, 0x02, 0xa8, 0x01, 0x20, 0x00, 0x28, + 0x02, 0xb0, 0x01, 0x10, 0x03, 0x20, 0x00, 0x41, 0xa8, 0x01, 0x6a, 0x10, + 0x0a, 0x20, 0x00, 0x41, 0x98, 0x01, 0x6a, 0x10, 0x0a, 0x41, 0x03, 0x0c, + 0x03, 0x0b, 0x20, 0x00, 0x41, 0xf0, 0x00, 0x6a, 0x10, 0x0a, 0x20, 0x00, + 0x41, 0xc8, 0x00, 0x6a, 0x10, 0x09, 0x41, 0x8b, 0x82, 0xc0, 0x00, 0x21, + 0x01, 0x41, 0x10, 0x21, 0x02, 0x0c, 0x05, 0x0b, 0x20, 0x00, 0x20, 0x00, + 0x28, 0x02, 0x78, 0x36, 0x02, 0xec, 0x01, 0x20, 0x00, 0x20, 0x00, 0x28, + 0x02, 0x70, 0x36, 0x02, 0xe8, 0x01, 0x20, 0x00, 0x41, 0xf0, 0x01, 0x6a, + 0x20, 0x00, 0x41, 0xe8, 0x01, 0x6a, 0x10, 0x26, 0x20, 0x00, 0x28, 0x02, + 0xf0, 0x01, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x90, 0x01, 0x6a, 0x20, + 0x00, 0x41, 0xf8, 0x01, 0x6a, 0x22, 0x02, 0x28, 0x02, 0x00, 0x22, 0x03, + 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0xf0, 0x01, 0x22, + 0x08, 0x37, 0x03, 0x88, 0x01, 0x20, 0x02, 0x20, 0x03, 0x36, 0x02, 0x00, + 0x20, 0x00, 0x20, 0x08, 0x37, 0x03, 0xf0, 0x01, 0x20, 0x08, 0xa7, 0x21, + 0x06, 0x02, 0x7f, 0x20, 0x03, 0x04, 0x40, 0x20, 0x00, 0x41, 0x40, 0x6b, + 0x20, 0x06, 0x20, 0x03, 0x41, 0x00, 0x10, 0x18, 0x20, 0x00, 0x28, 0x02, + 0x44, 0x20, 0x00, 0x28, 0x02, 0x40, 0x21, 0x02, 0x20, 0x00, 0x41, 0x38, + 0x6a, 0x20, 0x06, 0x20, 0x03, 0x41, 0x01, 0x10, 0x18, 0x20, 0x00, 0x28, + 0x02, 0x3c, 0x20, 0x00, 0x41, 0x30, 0x6a, 0x20, 0x06, 0x20, 0x03, 0x20, + 0x02, 0x20, 0x00, 0x28, 0x02, 0x38, 0x22, 0x05, 0x20, 0x02, 0x20, 0x05, + 0x4b, 0x22, 0x05, 0x1b, 0x22, 0x02, 0x10, 0x19, 0x20, 0x05, 0x1b, 0x22, + 0x05, 0x20, 0x02, 0x6a, 0x22, 0x07, 0x20, 0x05, 0x49, 0x0d, 0x08, 0x20, + 0x00, 0x28, 0x02, 0x34, 0x21, 0x01, 0x20, 0x00, 0x28, 0x02, 0x30, 0x21, + 0x04, 0x20, 0x00, 0x41, 0x28, 0x6a, 0x20, 0x05, 0x20, 0x07, 0x20, 0x06, + 0x20, 0x03, 0x10, 0x1a, 0x02, 0x7f, 0x20, 0x00, 0x28, 0x02, 0x28, 0x21, + 0x07, 0x20, 0x00, 0x28, 0x02, 0x2c, 0x20, 0x01, 0x46, 0x04, 0x7f, 0x20, + 0x04, 0x20, 0x07, 0x20, 0x01, 0x10, 0x39, 0x45, 0x05, 0x41, 0x00, 0x0b, + 0x45, 0x04, 0x40, 0x20, 0x03, 0x20, 0x02, 0x6b, 0x22, 0x01, 0x20, 0x03, + 0x4b, 0x0d, 0x0a, 0x20, 0x02, 0x20, 0x01, 0x20, 0x01, 0x20, 0x02, 0x49, + 0x1b, 0x22, 0x01, 0x41, 0x01, 0x6a, 0x22, 0x05, 0x20, 0x01, 0x49, 0x0d, + 0x0a, 0x41, 0x7f, 0x21, 0x04, 0x20, 0x06, 0x20, 0x03, 0x10, 0x1c, 0x21, + 0x08, 0x20, 0x02, 0x21, 0x01, 0x41, 0x7f, 0x0c, 0x01, 0x0b, 0x41, 0x00, + 0x21, 0x04, 0x20, 0x03, 0x20, 0x06, 0x20, 0x03, 0x20, 0x05, 0x41, 0x00, + 0x10, 0x1b, 0x22, 0x01, 0x20, 0x06, 0x20, 0x03, 0x20, 0x05, 0x41, 0x01, + 0x10, 0x1b, 0x22, 0x07, 0x20, 0x01, 0x20, 0x07, 0x4b, 0x1b, 0x6b, 0x22, + 0x01, 0x20, 0x03, 0x4b, 0x0d, 0x09, 0x20, 0x00, 0x41, 0x20, 0x6a, 0x20, + 0x06, 0x20, 0x03, 0x20, 0x05, 0x10, 0x19, 0x20, 0x00, 0x28, 0x02, 0x20, + 0x20, 0x00, 0x28, 0x02, 0x24, 0x10, 0x1c, 0x21, 0x08, 0x20, 0x03, 0x0b, + 0x21, 0x07, 0x20, 0x00, 0x41, 0xe4, 0x01, 0x6a, 0x20, 0x03, 0x36, 0x02, + 0x00, 0x20, 0x00, 0x41, 0xdc, 0x01, 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, + 0x20, 0x00, 0x41, 0xd0, 0x01, 0x6a, 0x20, 0x07, 0x36, 0x02, 0x00, 0x20, + 0x00, 0x41, 0xcc, 0x01, 0x6a, 0x20, 0x04, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x41, 0xc4, 0x01, 0x6a, 0x42, 0x00, 0x37, 0x02, 0x00, 0x20, 0x00, 0x41, + 0xc0, 0x01, 0x6a, 0x20, 0x05, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0xbc, + 0x01, 0x6a, 0x20, 0x01, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0xb8, 0x01, + 0x6a, 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0xb0, 0x01, 0x6a, + 0x20, 0x08, 0x37, 0x03, 0x00, 0x20, 0x00, 0x20, 0x06, 0x36, 0x02, 0xe0, + 0x01, 0x20, 0x00, 0x41, 0xc0, 0x82, 0xc0, 0x00, 0x36, 0x02, 0xd8, 0x01, + 0x20, 0x00, 0x41, 0x01, 0x36, 0x02, 0xa8, 0x01, 0x20, 0x08, 0x42, 0x20, + 0x88, 0xa7, 0x21, 0x01, 0x20, 0x03, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x41, + 0xe4, 0x01, 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0xdc, + 0x01, 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0xb4, 0x01, + 0x6a, 0x41, 0x81, 0x02, 0x3b, 0x01, 0x00, 0x20, 0x00, 0x41, 0xb0, 0x01, + 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x06, 0x36, 0x02, + 0xe0, 0x01, 0x20, 0x00, 0x41, 0xc0, 0x82, 0xc0, 0x00, 0x36, 0x02, 0xd8, + 0x01, 0x20, 0x00, 0x42, 0x00, 0x37, 0x03, 0xa8, 0x01, 0x41, 0x01, 0x21, + 0x01, 0x41, 0x00, 0x0b, 0x21, 0x02, 0x02, 0x40, 0x20, 0x03, 0x04, 0x40, + 0x20, 0x00, 0x41, 0xb0, 0x01, 0x6a, 0x21, 0x01, 0x20, 0x00, 0x41, 0xcc, + 0x01, 0x6a, 0x28, 0x02, 0x00, 0x41, 0x7f, 0x47, 0x04, 0x40, 0x20, 0x00, + 0x41, 0x98, 0x01, 0x6a, 0x20, 0x01, 0x20, 0x02, 0x10, 0x0c, 0x0c, 0x02, + 0x0b, 0x20, 0x00, 0x41, 0x98, 0x01, 0x6a, 0x20, 0x01, 0x20, 0x02, 0x10, + 0x0c, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x28, 0x02, 0xac, 0x01, 0x22, 0x02, + 0x0d, 0x08, 0x20, 0x01, 0x41, 0xff, 0x01, 0x71, 0x04, 0x40, 0x20, 0x00, + 0x41, 0xa0, 0x01, 0x6a, 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, + 0x02, 0x36, 0x02, 0x9c, 0x01, 0x20, 0x00, 0x41, 0x01, 0x36, 0x02, 0x98, + 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x98, 0x01, + 0x0b, 0x20, 0x00, 0x28, 0x02, 0x98, 0x01, 0x21, 0x02, 0x20, 0x00, 0x41, + 0xf0, 0x01, 0x6a, 0x10, 0x0a, 0x20, 0x00, 0x41, 0x18, 0x6a, 0x41, 0x01, + 0x41, 0x00, 0x10, 0x0e, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0xb0, 0x01, + 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0x18, 0x37, 0x03, 0xa8, 0x01, 0x20, + 0x00, 0x20, 0x02, 0x3a, 0x00, 0x98, 0x01, 0x20, 0x00, 0x41, 0xa8, 0x01, + 0x6a, 0x20, 0x00, 0x41, 0x98, 0x01, 0x6a, 0x41, 0x01, 0x10, 0x14, 0x20, + 0x00, 0x28, 0x02, 0xa8, 0x01, 0x20, 0x00, 0x28, 0x02, 0xb0, 0x01, 0x10, + 0x03, 0x20, 0x00, 0x41, 0xa8, 0x01, 0x6a, 0x10, 0x0a, 0x41, 0x03, 0x0c, + 0x01, 0x0b, 0x41, 0x01, 0x0b, 0x21, 0x04, 0x20, 0x00, 0x41, 0xf0, 0x00, + 0x6a, 0x10, 0x0a, 0x20, 0x00, 0x41, 0xc8, 0x00, 0x6a, 0x10, 0x09, 0x41, + 0x8b, 0x82, 0xc0, 0x00, 0x21, 0x01, 0x41, 0x10, 0x21, 0x02, 0x02, 0x40, + 0x20, 0x04, 0x41, 0x01, 0x6b, 0x0e, 0x03, 0x00, 0x02, 0x04, 0x03, 0x0b, + 0x41, 0x9b, 0x82, 0xc0, 0x00, 0x21, 0x01, 0x41, 0x0e, 0x21, 0x02, 0x0c, + 0x02, 0x0b, 0x20, 0x00, 0x41, 0xc8, 0x00, 0x6a, 0x10, 0x09, 0x0b, 0x41, + 0xa9, 0x82, 0xc0, 0x00, 0x21, 0x01, 0x41, 0x14, 0x21, 0x02, 0x0b, 0x20, + 0x00, 0x41, 0xa8, 0x01, 0x6a, 0x20, 0x01, 0x20, 0x02, 0x10, 0x15, 0x20, + 0x00, 0x41, 0xa8, 0x01, 0x6a, 0x10, 0x24, 0x20, 0x00, 0x41, 0xa8, 0x01, + 0x6a, 0x10, 0x0a, 0x0b, 0x20, 0x00, 0x41, 0x80, 0x02, 0x6a, 0x24, 0x00, + 0x0f, 0x0b, 0x00, 0x0b, 0x20, 0x02, 0x10, 0x17, 0x00, 0x0b, 0x8b, 0x09, + 0x02, 0x08, 0x7f, 0x03, 0x7e, 0x23, 0x00, 0x41, 0x20, 0x6b, 0x22, 0x03, + 0x24, 0x00, 0x20, 0x03, 0x41, 0x00, 0x3a, 0x00, 0x08, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x7e, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x01, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x41, 0x01, 0x10, 0x30, + 0x0d, 0x00, 0x02, 0x40, 0x20, 0x03, 0x2d, 0x00, 0x08, 0x22, 0x02, 0x41, + 0x03, 0x71, 0x22, 0x04, 0x41, 0x03, 0x47, 0x04, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x20, 0x04, 0x41, 0x01, 0x6b, 0x0e, 0x02, 0x02, 0x01, + 0x00, 0x0b, 0x20, 0x02, 0x41, 0x02, 0x76, 0x21, 0x02, 0x0c, 0x03, 0x0b, + 0x20, 0x03, 0x20, 0x02, 0x3a, 0x00, 0x0d, 0x20, 0x03, 0x41, 0x01, 0x3a, + 0x00, 0x0c, 0x20, 0x03, 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, 0x03, 0x41, + 0x00, 0x36, 0x02, 0x1c, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x20, 0x03, 0x41, + 0x1c, 0x6a, 0x41, 0x04, 0x10, 0x31, 0x0d, 0x03, 0x20, 0x03, 0x28, 0x02, + 0x1c, 0x22, 0x02, 0x41, 0xff, 0xff, 0x03, 0x4d, 0x0d, 0x03, 0x20, 0x02, + 0x41, 0x02, 0x76, 0x21, 0x02, 0x0c, 0x02, 0x0b, 0x20, 0x03, 0x20, 0x02, + 0x3a, 0x00, 0x0d, 0x20, 0x03, 0x41, 0x01, 0x3a, 0x00, 0x0c, 0x20, 0x03, + 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, 0x03, 0x41, 0x00, 0x3b, 0x01, 0x1c, + 0x20, 0x03, 0x41, 0x08, 0x6a, 0x20, 0x03, 0x41, 0x1c, 0x6a, 0x41, 0x02, + 0x10, 0x31, 0x0d, 0x02, 0x20, 0x03, 0x2f, 0x01, 0x1c, 0x22, 0x02, 0x41, + 0xff, 0x01, 0x4d, 0x0d, 0x02, 0x20, 0x02, 0x41, 0x02, 0x76, 0x21, 0x02, + 0x0c, 0x01, 0x0b, 0x20, 0x02, 0x41, 0x04, 0x4f, 0x0d, 0x01, 0x20, 0x03, + 0x41, 0x00, 0x36, 0x02, 0x08, 0x20, 0x01, 0x20, 0x03, 0x41, 0x08, 0x6a, + 0x41, 0x04, 0x10, 0x30, 0x0d, 0x01, 0x20, 0x03, 0x28, 0x02, 0x08, 0x22, + 0x02, 0x41, 0xff, 0xff, 0xff, 0xff, 0x03, 0x4d, 0x0d, 0x01, 0x0b, 0x20, + 0x01, 0x28, 0x02, 0x04, 0x20, 0x02, 0x49, 0x0d, 0x00, 0x20, 0x03, 0x41, + 0x08, 0x6a, 0x20, 0x02, 0x10, 0x2b, 0x20, 0x01, 0x20, 0x03, 0x28, 0x02, + 0x08, 0x22, 0x04, 0x20, 0x03, 0x28, 0x02, 0x10, 0x10, 0x30, 0x04, 0x40, + 0x20, 0x03, 0x41, 0x08, 0x6a, 0x10, 0x0a, 0x0c, 0x01, 0x0b, 0x41, 0x00, + 0x20, 0x03, 0x29, 0x02, 0x0c, 0x22, 0x0c, 0x42, 0x20, 0x88, 0xa7, 0x22, + 0x05, 0x41, 0x07, 0x6b, 0x22, 0x01, 0x20, 0x01, 0x20, 0x05, 0x4b, 0x1b, + 0x21, 0x09, 0x20, 0x04, 0x41, 0x03, 0x6a, 0x41, 0x7c, 0x71, 0x20, 0x04, + 0x6b, 0x21, 0x08, 0x41, 0x00, 0x21, 0x01, 0x03, 0x40, 0x20, 0x01, 0x20, + 0x05, 0x4f, 0x0d, 0x07, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, + 0x20, 0x04, 0x6a, 0x2d, 0x00, 0x00, 0x22, 0x06, 0x41, 0x18, 0x74, 0x41, + 0x18, 0x75, 0x22, 0x07, 0x41, 0x00, 0x4e, 0x04, 0x40, 0x20, 0x08, 0x41, + 0x7f, 0x46, 0x0d, 0x03, 0x20, 0x08, 0x20, 0x01, 0x6b, 0x41, 0x03, 0x71, + 0x0d, 0x03, 0x03, 0x40, 0x20, 0x01, 0x20, 0x09, 0x4f, 0x0d, 0x03, 0x20, + 0x01, 0x20, 0x04, 0x6a, 0x22, 0x02, 0x41, 0x04, 0x6a, 0x28, 0x02, 0x00, + 0x20, 0x02, 0x28, 0x02, 0x00, 0x72, 0x41, 0x80, 0x81, 0x82, 0x84, 0x78, + 0x71, 0x0d, 0x03, 0x20, 0x01, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x22, 0x01, + 0x4d, 0x0d, 0x00, 0x0b, 0x0c, 0x01, 0x0b, 0x42, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x20, 0x21, 0x0a, 0x42, 0x80, 0x80, 0x80, 0x80, 0x10, 0x21, 0x0b, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x06, + 0x41, 0x8b, 0x80, 0xc0, 0x00, 0x6a, 0x2d, 0x00, 0x00, 0x41, 0x02, 0x6b, + 0x0e, 0x03, 0x00, 0x02, 0x01, 0x0e, 0x0b, 0x20, 0x01, 0x41, 0x01, 0x6a, + 0x22, 0x02, 0x20, 0x05, 0x49, 0x0d, 0x02, 0x42, 0x00, 0x21, 0x0a, 0x0c, + 0x0c, 0x0b, 0x42, 0x00, 0x21, 0x0a, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x22, + 0x02, 0x20, 0x05, 0x4f, 0x0d, 0x0b, 0x20, 0x02, 0x20, 0x04, 0x6a, 0x2d, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x06, 0x41, 0xf0, 0x01, 0x6b, 0x0e, 0x05, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x0b, 0x20, 0x02, 0x41, 0xbf, 0x01, 0x4b, 0x0d, 0x0c, 0x20, + 0x07, 0x41, 0x0f, 0x6a, 0x41, 0xff, 0x01, 0x71, 0x41, 0x02, 0x4b, 0x0d, + 0x0c, 0x20, 0x02, 0x41, 0x18, 0x74, 0x41, 0x18, 0x75, 0x41, 0x00, 0x4e, + 0x0d, 0x0c, 0x0c, 0x02, 0x0b, 0x20, 0x02, 0x41, 0xf0, 0x00, 0x6a, 0x41, + 0xff, 0x01, 0x71, 0x41, 0x30, 0x4f, 0x0d, 0x0b, 0x0c, 0x01, 0x0b, 0x20, + 0x02, 0x41, 0x18, 0x74, 0x41, 0x18, 0x75, 0x41, 0x7f, 0x4a, 0x0d, 0x0a, + 0x20, 0x02, 0x41, 0x8f, 0x01, 0x4b, 0x0d, 0x0a, 0x0b, 0x20, 0x01, 0x41, + 0x02, 0x6a, 0x22, 0x02, 0x20, 0x05, 0x4f, 0x0d, 0x0b, 0x20, 0x02, 0x20, + 0x04, 0x6a, 0x2d, 0x00, 0x00, 0x41, 0xc0, 0x01, 0x71, 0x41, 0x80, 0x01, + 0x47, 0x0d, 0x08, 0x42, 0x00, 0x21, 0x0b, 0x20, 0x01, 0x41, 0x03, 0x6a, + 0x22, 0x02, 0x20, 0x05, 0x4f, 0x0d, 0x0c, 0x20, 0x02, 0x20, 0x04, 0x6a, + 0x2d, 0x00, 0x00, 0x41, 0xc0, 0x01, 0x71, 0x41, 0x80, 0x01, 0x46, 0x0d, + 0x02, 0x42, 0x80, 0x80, 0x80, 0x80, 0x80, 0xe0, 0x00, 0x0c, 0x0a, 0x0b, + 0x42, 0x00, 0x21, 0x0a, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x22, 0x02, 0x20, + 0x05, 0x4f, 0x0d, 0x0a, 0x20, 0x02, 0x20, 0x04, 0x6a, 0x2d, 0x00, 0x00, + 0x21, 0x02, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x06, 0x41, 0xe0, + 0x01, 0x47, 0x04, 0x40, 0x20, 0x06, 0x41, 0xed, 0x01, 0x46, 0x0d, 0x01, + 0x20, 0x07, 0x41, 0x1f, 0x6a, 0x41, 0xff, 0x01, 0x71, 0x41, 0x0c, 0x49, + 0x0d, 0x02, 0x20, 0x02, 0x41, 0xbf, 0x01, 0x4b, 0x0d, 0x0c, 0x20, 0x07, + 0x41, 0xfe, 0x01, 0x71, 0x41, 0xee, 0x01, 0x47, 0x0d, 0x0c, 0x20, 0x02, + 0x41, 0x18, 0x74, 0x41, 0x18, 0x75, 0x41, 0x00, 0x4e, 0x0d, 0x0c, 0x0c, + 0x03, 0x0b, 0x20, 0x02, 0x41, 0xe0, 0x01, 0x71, 0x41, 0xa0, 0x01, 0x47, + 0x0d, 0x0b, 0x0c, 0x02, 0x0b, 0x20, 0x02, 0x41, 0x18, 0x74, 0x41, 0x18, + 0x75, 0x41, 0x7f, 0x4a, 0x0d, 0x0a, 0x20, 0x02, 0x41, 0xa0, 0x01, 0x4f, + 0x0d, 0x0a, 0x0c, 0x01, 0x0b, 0x20, 0x02, 0x41, 0x18, 0x74, 0x41, 0x18, + 0x75, 0x41, 0x7f, 0x4a, 0x0d, 0x09, 0x20, 0x02, 0x41, 0xbf, 0x01, 0x4b, + 0x0d, 0x09, 0x0b, 0x42, 0x00, 0x21, 0x0b, 0x20, 0x01, 0x41, 0x02, 0x6a, + 0x22, 0x02, 0x20, 0x05, 0x4f, 0x0d, 0x0b, 0x20, 0x02, 0x20, 0x04, 0x6a, + 0x2d, 0x00, 0x00, 0x41, 0xc0, 0x01, 0x71, 0x41, 0x80, 0x01, 0x47, 0x0d, + 0x07, 0x0c, 0x01, 0x0b, 0x20, 0x02, 0x20, 0x04, 0x6a, 0x2d, 0x00, 0x00, + 0x41, 0xc0, 0x01, 0x71, 0x41, 0x80, 0x01, 0x47, 0x0d, 0x0a, 0x0b, 0x20, + 0x02, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, 0x03, 0x0b, 0x00, 0x0b, 0x20, + 0x01, 0x20, 0x05, 0x20, 0x01, 0x20, 0x05, 0x4b, 0x1b, 0x21, 0x02, 0x03, + 0x40, 0x20, 0x01, 0x20, 0x02, 0x46, 0x04, 0x40, 0x20, 0x02, 0x21, 0x01, + 0x0c, 0x03, 0x0b, 0x20, 0x01, 0x20, 0x04, 0x6a, 0x2c, 0x00, 0x00, 0x41, + 0x00, 0x48, 0x0d, 0x02, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, + 0x00, 0x0b, 0x00, 0x0b, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, + 0x00, 0x0b, 0x00, 0x0b, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0c, + 0x06, 0x0b, 0x42, 0x80, 0x80, 0x80, 0x80, 0x80, 0xc0, 0x00, 0x0c, 0x01, + 0x0b, 0x42, 0x80, 0x80, 0x80, 0x80, 0x80, 0x20, 0x0b, 0x21, 0x0a, 0x42, + 0x80, 0x80, 0x80, 0x80, 0x10, 0x21, 0x0b, 0x0c, 0x01, 0x0b, 0x42, 0x00, + 0x21, 0x0b, 0x0b, 0x20, 0x01, 0xad, 0x20, 0x0a, 0x20, 0x0b, 0x84, 0x84, + 0x21, 0x0a, 0x0b, 0x20, 0x01, 0x20, 0x05, 0x4f, 0x04, 0x40, 0x20, 0x00, + 0x20, 0x0c, 0x37, 0x02, 0x04, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, 0x00, + 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x20, 0x0a, 0x37, 0x02, 0x14, 0x20, 0x03, + 0x20, 0x0c, 0x37, 0x02, 0x0c, 0x20, 0x03, 0x20, 0x04, 0x36, 0x02, 0x08, + 0x20, 0x03, 0x41, 0x08, 0x6a, 0x10, 0x0a, 0x20, 0x00, 0x41, 0x00, 0x36, + 0x02, 0x00, 0x0b, 0x20, 0x03, 0x41, 0x20, 0x6a, 0x24, 0x00, 0x0b, 0x90, + 0x02, 0x02, 0x05, 0x7f, 0x01, 0x7e, 0x23, 0x00, 0x41, 0x30, 0x6b, 0x22, + 0x01, 0x24, 0x00, 0x20, 0x01, 0x41, 0x10, 0x6a, 0x20, 0x00, 0x41, 0x0c, + 0x6a, 0x22, 0x02, 0x10, 0x2d, 0x02, 0x40, 0x20, 0x01, 0x28, 0x02, 0x14, + 0x22, 0x03, 0x28, 0x02, 0x00, 0x22, 0x04, 0x41, 0x01, 0x6b, 0x22, 0x05, + 0x20, 0x04, 0x4e, 0x0d, 0x00, 0x20, 0x01, 0x28, 0x02, 0x10, 0x2d, 0x00, + 0x04, 0x20, 0x03, 0x20, 0x05, 0x36, 0x02, 0x00, 0x41, 0x02, 0x46, 0x04, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x00, 0x20, 0x00, + 0x28, 0x02, 0x08, 0x10, 0x2c, 0x10, 0x06, 0x22, 0x00, 0x45, 0x0d, 0x00, + 0x20, 0x00, 0x41, 0x81, 0x80, 0x01, 0x4f, 0x0d, 0x03, 0x41, 0xc4, 0x82, + 0xc0, 0x00, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x01, 0x41, 0x08, 0x6a, + 0x20, 0x00, 0x10, 0x2a, 0x20, 0x01, 0x20, 0x01, 0x29, 0x03, 0x08, 0x37, + 0x03, 0x18, 0x20, 0x01, 0x41, 0x20, 0x6a, 0x20, 0x01, 0x41, 0x18, 0x6a, + 0x10, 0x20, 0x20, 0x01, 0x28, 0x02, 0x20, 0x22, 0x00, 0x45, 0x0d, 0x00, + 0x20, 0x01, 0x29, 0x02, 0x24, 0x21, 0x06, 0x0c, 0x01, 0x0b, 0x41, 0x00, + 0x21, 0x00, 0x0b, 0x20, 0x02, 0x28, 0x02, 0x00, 0x04, 0x40, 0x00, 0x0b, + 0x20, 0x02, 0x41, 0x7f, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, 0x02, 0x36, + 0x02, 0x04, 0x20, 0x01, 0x20, 0x02, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, + 0x20, 0x01, 0x28, 0x02, 0x04, 0x21, 0x02, 0x20, 0x01, 0x28, 0x02, 0x00, + 0x20, 0x01, 0x20, 0x06, 0x37, 0x02, 0x24, 0x20, 0x01, 0x20, 0x00, 0x36, + 0x02, 0x20, 0x20, 0x01, 0x41, 0x20, 0x6a, 0x10, 0x22, 0x20, 0x02, 0x28, + 0x02, 0x00, 0x22, 0x00, 0x41, 0x01, 0x6a, 0x22, 0x03, 0x20, 0x00, 0x48, + 0x0d, 0x01, 0x20, 0x02, 0x20, 0x03, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x01, + 0x41, 0x30, 0x6a, 0x24, 0x00, 0x0f, 0x0b, 0x00, 0x0b, 0x48, 0x01, 0x02, + 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x20, 0x02, + 0x41, 0x08, 0x6a, 0x41, 0x04, 0x41, 0x00, 0x10, 0x0e, 0x20, 0x02, 0x28, + 0x02, 0x08, 0x21, 0x03, 0x20, 0x00, 0x20, 0x02, 0x28, 0x02, 0x0c, 0x36, + 0x02, 0x04, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, + 0x01, 0x28, 0x00, 0x00, 0x36, 0x00, 0x00, 0x20, 0x00, 0x41, 0x04, 0x36, + 0x02, 0x08, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0xb7, 0x01, + 0x02, 0x03, 0x7f, 0x01, 0x7e, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x03, + 0x24, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x28, 0x02, 0x08, 0x22, + 0x02, 0x41, 0x04, 0x6a, 0x22, 0x04, 0x20, 0x02, 0x4f, 0x04, 0x40, 0x20, + 0x01, 0x28, 0x02, 0x00, 0x21, 0x01, 0x20, 0x03, 0x20, 0x04, 0x41, 0x00, + 0x10, 0x0e, 0x20, 0x03, 0x29, 0x03, 0x00, 0x21, 0x05, 0x20, 0x00, 0x41, + 0x00, 0x36, 0x02, 0x08, 0x20, 0x00, 0x20, 0x05, 0x37, 0x02, 0x00, 0x20, + 0x02, 0x41, 0x3f, 0x4d, 0x04, 0x40, 0x20, 0x00, 0x20, 0x02, 0x41, 0x02, + 0x74, 0x10, 0x32, 0x0c, 0x03, 0x0b, 0x20, 0x02, 0x41, 0xff, 0xff, 0x00, + 0x4d, 0x04, 0x40, 0x20, 0x03, 0x20, 0x02, 0x41, 0x02, 0x74, 0x41, 0x01, + 0x72, 0x3b, 0x01, 0x0e, 0x20, 0x00, 0x20, 0x03, 0x41, 0x0e, 0x6a, 0x41, + 0x02, 0x10, 0x14, 0x0c, 0x03, 0x0b, 0x20, 0x02, 0x41, 0xff, 0xff, 0xff, + 0xff, 0x03, 0x4b, 0x0d, 0x01, 0x20, 0x02, 0x41, 0x02, 0x74, 0x41, 0x02, + 0x72, 0x20, 0x00, 0x10, 0x33, 0x0c, 0x02, 0x0b, 0x00, 0x0b, 0x20, 0x00, + 0x41, 0x03, 0x10, 0x32, 0x20, 0x02, 0x20, 0x00, 0x10, 0x33, 0x0b, 0x20, + 0x00, 0x20, 0x01, 0x20, 0x02, 0x10, 0x14, 0x20, 0x03, 0x41, 0x10, 0x6a, + 0x24, 0x00, 0x0b, 0x58, 0x01, 0x02, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, + 0x22, 0x02, 0x24, 0x00, 0x20, 0x02, 0x41, 0x08, 0x6a, 0x41, 0xc4, 0x82, + 0xc0, 0x00, 0x28, 0x02, 0x00, 0x10, 0x2f, 0x20, 0x02, 0x28, 0x02, 0x08, + 0x21, 0x03, 0x20, 0x01, 0x20, 0x02, 0x28, 0x02, 0x0c, 0x4b, 0x04, 0x40, + 0x00, 0x0b, 0x20, 0x02, 0x20, 0x01, 0x36, 0x02, 0x04, 0x20, 0x02, 0x20, + 0x03, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x02, 0x28, 0x02, 0x00, 0x36, + 0x02, 0x00, 0x20, 0x00, 0x20, 0x02, 0x28, 0x02, 0x04, 0x36, 0x02, 0x04, + 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x36, 0x02, 0x01, 0x7f, + 0x01, 0x7e, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x20, + 0x02, 0x41, 0x08, 0x6a, 0x20, 0x01, 0x41, 0x01, 0x10, 0x0e, 0x20, 0x02, + 0x29, 0x03, 0x08, 0x21, 0x03, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x08, + 0x20, 0x00, 0x20, 0x03, 0x37, 0x02, 0x00, 0x20, 0x02, 0x41, 0x10, 0x6a, + 0x24, 0x00, 0x0b, 0x2a, 0x01, 0x02, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, + 0x22, 0x00, 0x24, 0x00, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x41, 0x00, 0x41, + 0xc4, 0x82, 0xc0, 0x00, 0x28, 0x02, 0x00, 0x10, 0x2e, 0x20, 0x00, 0x28, + 0x02, 0x08, 0x20, 0x00, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x2d, 0x01, + 0x01, 0x7f, 0x20, 0x01, 0x28, 0x02, 0x00, 0x41, 0x01, 0x6a, 0x22, 0x02, + 0x41, 0x00, 0x4c, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x01, 0x20, 0x02, 0x36, + 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, + 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x0b, 0x3d, 0x01, 0x01, 0x7f, + 0x02, 0x40, 0x20, 0x01, 0x20, 0x02, 0x4d, 0x04, 0x40, 0x20, 0x02, 0x41, + 0x80, 0x80, 0x01, 0x4d, 0x04, 0x40, 0x20, 0x02, 0x20, 0x02, 0x20, 0x01, + 0x6b, 0x22, 0x03, 0x49, 0x0d, 0x02, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, + 0x04, 0x20, 0x00, 0x20, 0x01, 0x41, 0xc8, 0x82, 0xc0, 0x00, 0x6a, 0x36, + 0x02, 0x00, 0x0f, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x4b, 0x01, + 0x02, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x20, + 0x02, 0x41, 0x08, 0x6a, 0x21, 0x03, 0x20, 0x01, 0x41, 0x80, 0x80, 0x01, + 0x4b, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x03, 0x20, 0x01, 0x36, 0x02, 0x04, + 0x20, 0x03, 0x41, 0xc8, 0x82, 0xc0, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x20, 0x02, 0x28, 0x02, 0x08, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x02, + 0x28, 0x02, 0x0c, 0x36, 0x02, 0x04, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, + 0x00, 0x0b, 0x39, 0x01, 0x02, 0x7f, 0x20, 0x00, 0x28, 0x02, 0x04, 0x22, + 0x03, 0x20, 0x02, 0x49, 0x22, 0x04, 0x45, 0x04, 0x40, 0x20, 0x01, 0x20, + 0x02, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x01, 0x20, 0x02, 0x10, 0x16, + 0x20, 0x00, 0x20, 0x03, 0x20, 0x02, 0x6b, 0x36, 0x02, 0x04, 0x20, 0x00, + 0x20, 0x01, 0x20, 0x02, 0x6a, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x04, 0x0b, + 0x42, 0x01, 0x01, 0x7f, 0x20, 0x00, 0x2f, 0x01, 0x04, 0x21, 0x03, 0x20, + 0x00, 0x41, 0x00, 0x3a, 0x00, 0x04, 0x20, 0x03, 0x41, 0x01, 0x71, 0x45, + 0x04, 0x40, 0x20, 0x00, 0x28, 0x02, 0x00, 0x20, 0x01, 0x20, 0x02, 0x10, + 0x30, 0x0f, 0x0b, 0x20, 0x01, 0x20, 0x03, 0x41, 0x08, 0x76, 0x3a, 0x00, + 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x20, + 0x02, 0x41, 0x01, 0x6b, 0x10, 0x30, 0x0b, 0x26, 0x01, 0x01, 0x7f, 0x23, + 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x20, 0x02, 0x20, 0x01, + 0x3a, 0x00, 0x0f, 0x20, 0x00, 0x20, 0x02, 0x41, 0x0f, 0x6a, 0x41, 0x01, + 0x10, 0x14, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x26, 0x01, + 0x01, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x20, + 0x02, 0x20, 0x00, 0x36, 0x02, 0x0c, 0x20, 0x01, 0x20, 0x02, 0x41, 0x0c, + 0x6a, 0x41, 0x04, 0x10, 0x14, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, + 0x0b, 0xd2, 0x01, 0x01, 0x01, 0x7f, 0x02, 0x40, 0x20, 0x01, 0x41, 0xff, + 0xff, 0xff, 0xff, 0x03, 0x71, 0x20, 0x01, 0x47, 0x0d, 0x00, 0x20, 0x02, + 0x20, 0x02, 0x41, 0x40, 0x6b, 0x22, 0x03, 0x4b, 0x0d, 0x00, 0x20, 0x03, + 0x41, 0xff, 0xff, 0xff, 0xff, 0x01, 0x71, 0x20, 0x03, 0x47, 0x0d, 0x00, + 0x20, 0x01, 0x41, 0x02, 0x74, 0x22, 0x01, 0x20, 0x03, 0x41, 0x03, 0x74, + 0x22, 0x02, 0x20, 0x01, 0x20, 0x02, 0x4b, 0x1b, 0x22, 0x02, 0x41, 0x08, + 0x6a, 0x22, 0x01, 0x20, 0x02, 0x49, 0x0d, 0x00, 0x02, 0x7f, 0x02, 0x40, + 0x20, 0x01, 0x20, 0x01, 0x41, 0x80, 0x80, 0x04, 0x6a, 0x22, 0x02, 0x4d, + 0x04, 0x40, 0x20, 0x02, 0x41, 0x01, 0x6b, 0x22, 0x01, 0x20, 0x02, 0x4d, + 0x0d, 0x01, 0x0b, 0x00, 0x0b, 0x20, 0x01, 0x41, 0x10, 0x76, 0x22, 0x02, + 0x40, 0x00, 0x22, 0x01, 0x41, 0x7f, 0x46, 0x04, 0x40, 0x41, 0x00, 0x21, + 0x01, 0x41, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x01, 0x41, 0xff, 0xff, 0x03, + 0x71, 0x20, 0x01, 0x47, 0x0d, 0x01, 0x20, 0x02, 0x41, 0x10, 0x74, 0x22, + 0x02, 0x41, 0x08, 0x6b, 0x20, 0x02, 0x4b, 0x0d, 0x01, 0x20, 0x01, 0x41, + 0x10, 0x74, 0x22, 0x01, 0x42, 0x00, 0x37, 0x02, 0x04, 0x20, 0x01, 0x20, + 0x01, 0x20, 0x02, 0x6a, 0x41, 0x02, 0x72, 0x36, 0x02, 0x00, 0x41, 0x00, + 0x0b, 0x21, 0x02, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x04, 0x20, 0x00, + 0x20, 0x02, 0x36, 0x02, 0x00, 0x0f, 0x0b, 0x00, 0x0b, 0xaf, 0x04, 0x01, + 0x0a, 0x7f, 0x20, 0x00, 0x41, 0x02, 0x74, 0x21, 0x06, 0x41, 0x00, 0x20, + 0x01, 0x6b, 0x21, 0x08, 0x20, 0x00, 0x41, 0xff, 0xff, 0xff, 0xff, 0x03, + 0x71, 0x20, 0x00, 0x47, 0x21, 0x09, 0x20, 0x01, 0x41, 0x01, 0x6b, 0x22, + 0x0a, 0x20, 0x01, 0x4b, 0x21, 0x0b, 0x20, 0x02, 0x28, 0x02, 0x00, 0x21, + 0x00, 0x02, 0x40, 0x03, 0x40, 0x20, 0x00, 0x45, 0x0d, 0x01, 0x20, 0x00, + 0x21, 0x01, 0x02, 0x40, 0x03, 0x40, 0x02, 0x40, 0x20, 0x01, 0x28, 0x02, + 0x08, 0x22, 0x00, 0x41, 0x01, 0x71, 0x45, 0x04, 0x40, 0x20, 0x09, 0x0d, + 0x03, 0x20, 0x01, 0x28, 0x02, 0x00, 0x41, 0x7c, 0x71, 0x22, 0x03, 0x20, + 0x01, 0x41, 0x08, 0x6a, 0x22, 0x05, 0x6b, 0x22, 0x04, 0x20, 0x03, 0x4b, + 0x0d, 0x03, 0x20, 0x04, 0x20, 0x06, 0x49, 0x0d, 0x01, 0x20, 0x03, 0x20, + 0x06, 0x6b, 0x22, 0x0c, 0x20, 0x03, 0x4b, 0x0d, 0x03, 0x20, 0x0b, 0x0d, + 0x03, 0x20, 0x05, 0x41, 0x08, 0x6a, 0x22, 0x04, 0x20, 0x05, 0x49, 0x0d, + 0x03, 0x20, 0x04, 0x20, 0x04, 0x41, 0x40, 0x6b, 0x22, 0x04, 0x4b, 0x0d, + 0x03, 0x02, 0x40, 0x20, 0x04, 0x20, 0x08, 0x20, 0x0c, 0x71, 0x22, 0x04, + 0x4b, 0x04, 0x40, 0x20, 0x05, 0x20, 0x0a, 0x71, 0x0d, 0x03, 0x20, 0x02, + 0x20, 0x00, 0x41, 0x7c, 0x71, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, 0x01, + 0x28, 0x02, 0x00, 0x41, 0x01, 0x72, 0x36, 0x02, 0x00, 0x20, 0x01, 0x21, + 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x04, 0x41, 0x08, 0x6b, 0x22, 0x00, 0x20, + 0x04, 0x4b, 0x0d, 0x04, 0x20, 0x03, 0x20, 0x00, 0x6b, 0x22, 0x02, 0x20, + 0x03, 0x4b, 0x0d, 0x04, 0x20, 0x02, 0x41, 0x08, 0x6b, 0x20, 0x02, 0x4b, + 0x0d, 0x04, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x08, 0x20, 0x00, 0x42, + 0x00, 0x37, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x28, 0x02, 0x00, 0x41, + 0x7c, 0x71, 0x36, 0x02, 0x00, 0x02, 0x40, 0x20, 0x01, 0x28, 0x02, 0x00, + 0x22, 0x02, 0x41, 0x7c, 0x71, 0x22, 0x03, 0x45, 0x0d, 0x00, 0x41, 0x00, + 0x20, 0x03, 0x20, 0x02, 0x41, 0x02, 0x71, 0x1b, 0x22, 0x02, 0x45, 0x0d, + 0x00, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x04, 0x41, 0x03, 0x71, 0x20, + 0x00, 0x72, 0x36, 0x02, 0x04, 0x0b, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, + 0x04, 0x41, 0x03, 0x71, 0x20, 0x01, 0x72, 0x36, 0x02, 0x04, 0x20, 0x01, + 0x20, 0x01, 0x28, 0x02, 0x08, 0x41, 0x7e, 0x71, 0x36, 0x02, 0x08, 0x20, + 0x01, 0x20, 0x01, 0x28, 0x02, 0x00, 0x22, 0x02, 0x41, 0x03, 0x71, 0x20, + 0x00, 0x72, 0x22, 0x03, 0x36, 0x02, 0x00, 0x02, 0x40, 0x20, 0x02, 0x41, + 0x02, 0x71, 0x45, 0x04, 0x40, 0x20, 0x00, 0x28, 0x02, 0x00, 0x21, 0x01, + 0x0c, 0x01, 0x0b, 0x20, 0x01, 0x20, 0x03, 0x41, 0x7d, 0x71, 0x36, 0x02, + 0x00, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x02, 0x72, 0x22, + 0x01, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x41, 0x01, 0x72, + 0x36, 0x02, 0x00, 0x0b, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x21, 0x07, 0x0c, + 0x05, 0x0b, 0x20, 0x01, 0x20, 0x00, 0x41, 0x7e, 0x71, 0x36, 0x02, 0x08, + 0x02, 0x7f, 0x41, 0x00, 0x20, 0x01, 0x28, 0x02, 0x04, 0x41, 0x7c, 0x71, + 0x22, 0x00, 0x45, 0x0d, 0x00, 0x1a, 0x41, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x2d, 0x00, 0x00, 0x41, 0x01, 0x71, 0x1b, 0x0b, 0x21, 0x00, 0x20, 0x01, + 0x10, 0x36, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x41, 0x02, 0x71, 0x04, 0x40, + 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x02, 0x72, 0x36, 0x02, + 0x00, 0x0b, 0x20, 0x02, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x28, + 0x02, 0x00, 0x41, 0x7c, 0x71, 0x22, 0x01, 0x20, 0x00, 0x6b, 0x41, 0x08, + 0x6b, 0x20, 0x01, 0x4b, 0x0d, 0x02, 0x20, 0x00, 0x21, 0x01, 0x0c, 0x01, + 0x0b, 0x0b, 0x20, 0x02, 0x20, 0x00, 0x36, 0x02, 0x00, 0x0c, 0x01, 0x0b, + 0x0b, 0x00, 0x0b, 0x20, 0x07, 0x0b, 0x7d, 0x01, 0x02, 0x7f, 0x02, 0x40, + 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x7c, 0x71, 0x22, 0x02, + 0x45, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x02, 0x20, 0x01, 0x41, 0x02, 0x71, + 0x1b, 0x22, 0x01, 0x45, 0x0d, 0x00, 0x20, 0x01, 0x20, 0x01, 0x28, 0x02, + 0x04, 0x41, 0x03, 0x71, 0x20, 0x00, 0x28, 0x02, 0x04, 0x41, 0x7c, 0x71, + 0x72, 0x36, 0x02, 0x04, 0x0b, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x04, + 0x22, 0x01, 0x41, 0x7c, 0x71, 0x22, 0x02, 0x04, 0x7f, 0x20, 0x02, 0x20, + 0x02, 0x28, 0x02, 0x00, 0x41, 0x03, 0x71, 0x20, 0x00, 0x28, 0x02, 0x00, + 0x41, 0x7c, 0x71, 0x72, 0x36, 0x02, 0x00, 0x20, 0x00, 0x28, 0x02, 0x04, + 0x05, 0x20, 0x01, 0x0b, 0x41, 0x03, 0x71, 0x36, 0x02, 0x04, 0x20, 0x00, + 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x03, 0x71, 0x36, 0x02, 0x00, 0x0b, + 0x28, 0x01, 0x01, 0x7f, 0x03, 0x40, 0x20, 0x02, 0x20, 0x03, 0x47, 0x04, + 0x40, 0x20, 0x00, 0x20, 0x03, 0x6a, 0x20, 0x01, 0x20, 0x03, 0x6a, 0x2d, + 0x00, 0x00, 0x3a, 0x00, 0x00, 0x20, 0x03, 0x41, 0x01, 0x6a, 0x21, 0x03, + 0x0c, 0x01, 0x0b, 0x0b, 0x0b, 0x22, 0x01, 0x01, 0x7f, 0x03, 0x40, 0x20, + 0x01, 0x20, 0x02, 0x47, 0x04, 0x40, 0x20, 0x00, 0x20, 0x02, 0x6a, 0x41, + 0x00, 0x3a, 0x00, 0x00, 0x20, 0x02, 0x41, 0x01, 0x6a, 0x21, 0x02, 0x0c, + 0x01, 0x0b, 0x0b, 0x0b, 0x3f, 0x01, 0x02, 0x7f, 0x03, 0x40, 0x20, 0x02, + 0x45, 0x04, 0x40, 0x41, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x41, 0x01, 0x6b, + 0x21, 0x02, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x21, 0x03, 0x20, 0x00, 0x2d, + 0x00, 0x00, 0x21, 0x04, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x21, 0x00, 0x20, + 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x20, 0x03, 0x20, 0x04, 0x46, 0x0d, + 0x00, 0x0b, 0x20, 0x04, 0x20, 0x03, 0x6b, 0x0b, 0x0b, 0x8a, 0x02, 0x03, + 0x00, 0x41, 0x80, 0x80, 0xc0, 0x00, 0x0b, 0x8b, 0x01, 0x68, 0x65, 0x6c, + 0x6c, 0x6f, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x41, 0xcd, 0x81, 0xc0, 0x00, 0x0b, 0x33, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x00, 0x41, 0x8b, 0x82, 0xc0, 0x00, 0x0b, 0x32, 0x75, + 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x70, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x63, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6e, + 0x6f, 0x74, 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, 0x69, 0x6e, 0x70, 0x75, + 0x74 +}; +constexpr unsigned int hello_world_caller_wasm_len = 9769; diff --git "a/BFPL\345\243\271/bcos-executor/test/liquid/hello_world_caller.rs" "b/BFPL\345\243\271/bcos-executor/test/liquid/hello_world_caller.rs" new file mode 100644 index 00000000..066b23de --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/liquid/hello_world_caller.rs" @@ -0,0 +1,38 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use liquid::storage; +use liquid_lang as liquid; + +#[liquid::interface(name = auto)] +mod hello_world { + extern "liquid" { + fn set(&mut self, name: String); + fn get(&self) -> String; + } +} + +#[liquid::contract] +mod hello_world_caller { + use super::*; + use hello_world::HelloWorld; + + #[liquid(storage)] + struct HelloWorldCaller { + hello_world: storage::Value, + } + + #[liquid(methods)] + impl HelloWorldCaller { + pub fn new(&mut self, addr: Address) { + self.hello_world.initialize(HelloWorld::at(addr)); + } + + pub fn set(&mut self, name: String) { + (*self.hello_world).set(name); + } + + pub fn get(&self) -> String { + (*self.hello_world).get().unwrap() + } + } +} diff --git "a/BFPL\345\243\271/bcos-executor/test/liquid/hello_world_caller.wasm" "b/BFPL\345\243\271/bcos-executor/test/liquid/hello_world_caller.wasm" new file mode 100644 index 0000000000000000000000000000000000000000..9c6fe981dc2b9e237ec85472892333d19192cc91 GIT binary patch literal 9769 zcmc(lON?FjUB~~Ab00J3j(q|{F}9)qdoq}DYB!SyGig%jJ*Iwj>R6(iE|8jOGEIEP zu|4BS+92wgQ~|68iR>_nU%PzS_3Zhf7uUCcbA9`jtt+2j+g|(9rQcq69h;cDZ^C&S z?0)Uil}p!Oa?ajnudi=^Y5VHd+KcP1$n#t4udZ)xPv!wJnU6Mqkdfa2`2)DheZ$Vj z#~<_F1s`0P<$oxAQS^Idr|aB|^S$Tu!URt~U(pR=$ySDCrvHQaMwaZsubH7x)ymsk|`{micIs4cX-tQz=%_i5( zk54++gtNscxy4Y`elfUWCwWcs-bub}231*>$Ji5Wk!zghirO8i{pmSgbnyUW+3yA) zfekpHd{d0;c!A}y@f#^#IC8oexw5(Yt-I`Z!wCNVM$JOr>Y}-^xeWIsuoTVjY}O&U zjTA13vc@sPt=M3t&7Gd}DTs8x$&;^^QxHh#Fn%`K#r`I2)!JI7ID|m$bSZ3rHMt9O zp>*XG89YE}0Cm_rQU|d#fe&Uu@3UQG1Q_#M%b_oz179*5g~RwtUXSHDlNP{33h-Ws z^AZs-4(nQRJszDs&)1PfalEPp;&UF55DYbf^djPX*fkYmTF`?2xG5V~9T1oS5){lj zm#x6HeA+=Qp~bgEo|Y9qJc7Q~Hjs_}v=zXrh>Ph3C;BIVg) z6n0WTXXvj#M#u+K6jsOut2hULjMSJ!Q7pXB(%*zvv8OOG*X1y<4cJ{X7+1eqN0|r^eD$PonE`4@u3>L5(U)%7 zsb%dxsLGbYLRJWa*i5l}&rg+YN-$T*iZI}H(eh||D27N?HV<7g7a1Nm3`B;bF{7lj zr#}<4Y0!4|*@+HKju^a2)<$rG)TvhN%KWeZyet73K`r8JTsC`&wqd^UD*t4D?&6-4%g=Fm8E zZ1SC#B9O&$s6<(i2rBL-^lDuFjLEO@X8;Qe$3w9qDnpZy{6XC9acoR8)z4H>$@n=9iej0jl@*KQ6o=`=W>L(+2uozq;L+1a>uC!DKe zv-1UwrGS8k@YQ$=`X`+~%*rqx%AcE${buulKhH|ydtwQ>pz&iIL4j2$4!5iB*(%s- z!MMX^Yls^|H*j#_${&)vIRy>SVj>w##H=B%od@ zR_FYHXwk_FI|{~Kbl6F~vvZMEMe4C9rwnA+%P~dq>Y$X-F@9I3!L7g|3UfupBy0!~ zr(zo5c(MRjWD{6+VWd?`b&`hNu-%OspODHhA6XH0GB%7^xVMAvNw_oop9&X91q_*Z zdzN%0-k8vUn6@GK8rOX}oM0jVyvx#0>Ld0vtEJsEt7#OKkXAQOcQ+Vsmv$!_awtw3 zQex@aQ@7Pn7kI6Pctk@+thCY_&JKtw_iIHG7J<^r>;|@AS|N_@*GebN$liLxLi*}q z6WVmqLbenkr=K~?Ynf)tPkDbMj*@2sxHpFWluzz4=YB7RH%M2+Obi=$VpCZ-Z2KRI z(An`f$WXVH3(%q?&{T8WmWZp>CtC}4C*6|bs%zdONVw*+<>Ah-PeK;m0o zP_D7yCi`08jO_X_Vi1~#nZ9alW!3`Gt0o^%m|5K0Q9vTB$*{PQ12q8NdC7$e1t1{J z1Xy5Y7V6`*6>u{QxmBIBQp~0m&&_pYz6J=}2N1G`kQmtGoxV4|DqglAFy_-*@BYFQ_q_`ReDG;k8<5 z?~@&~yq}P}hD1tbXt7$7A!$VxmwVdb3prBrESid3GVnx|7br|gj=UI-z6?)7 z&-KJnv!;QTg@wFKm0{L-F*g0+M}ixKFW@&+mtPH>Q{+~;5XeOO>KL3sV`VxQZj>pV z8$5_U9;-TOf<#`B4LD!08ELIMVSb|Z)r4@@MO){5kdNQb(KZys1oeL~-;8O7rrveto z8O)?$Qtp30`o3z^5iV<%wkS6o-~`o4xx|T@^xwH^tW1=ev(tw&}ticowJ0r0^2Nc^bI>6%=dcjXPz z2Z)wnt2%B&+QiUec&euC=p=WZRMg?LLJ~npi(zS~%I**xQgMD-BZqW3nkbyBPwa!i zc9g<)8_SFd73Wl)U?wZ8M@d-#sC1dH2qGpdMSAT&wgKy^nQUbl9TN|fecs& zxPBd{Au5`AwAQs306}2^Q1;oeL?#WW#vX0tLi3SnjbO^fD2@wuMxf(%%Mcd*rw2km zd(Cc`BqPN#WurTu@^_MsO#fK%9UR+=F{Kd}{oVapqj|@VvAw##5}%XSzUM6mD#Ns{ z#mqH}L!o*1yWhTb17HCUZGLp?kN-2~$o#K={%8N)A-}>XAil92-e@1MEr(qmn|qcu z6rGQ0#Xa&^WH_g{kBlOj3IC4AdwwO})F!bd-+kZZn0#h#hBvanZ*e2C%Z_Atr)?Z4 z3x9kx52vGzPApVg{Il*AxZ z9Ty``cM0U-CaOrvFGICjNE2BXEqhtk#$KQj+%u#sd0jv96$gmJVN`jJQLpd%i}G9( zj3-;L0yar8Qixbx4u2p{Fx#8rB#Z1O)S=DeG8t%CM7R@?SKBK{Sl>I8tWs&b!` z<~&k8A_TUx50Kt@bhdC*)lCeo5lA=i! zDXI_9+(V=j(atn)60n9u4$i>TjK)01Y+3{k#%zkr0{$?QnT?sHd&X==S*s|??A`P7 zE+l7(TZ6n}mHoGeT~5$Cxf0knh*1QP>X1nOye=d2#HYqK;q%02SxP8nDbQH+6xK+* zU#&ZY-|80T66T*@XpAA`qI$PVT!=Pq*aoE%CMDk}i)loGQY(8^Z zM>>^?U{NcMS_R8Z*is61@+NQ+mCn!2Haox-xTDxWKz2JsIW(^VQbYo%;;7y}d!9j% zdaFlTurm-vnPN*JA4-xg3kENiBCh!lJTyzQd7lWEy}NmjQBGWlv-xKp+Y0ktKR(q~ zDt8&}qP$MLuZ)2=dhrJdo6cTn<0bBkS*=qf_bT+ek3x;sR18`1g_W z*Nna%DB;)!(00ZD0Tj}r;xTe^p^XF4g@T@!3TC8nVA8Krh?^;`P-mEPhn!ULMuJe? zE-zka?^d_(2CKqV^Ho3Z$~R}okFl*PBc+lh<$f5o|8C?v@YKG98A@g8;iOZ?G;Y#y zR#IzM8L1(7t0?LG1w{><;%PU>ep1nZH-xOl4Z=HWK)F~E{unuqO5!MRKmM2oBKsPH z0aHH-#xs6Dj8HvBZ4w{#2E)^7n1Dc&oX{%!)$p>GVuHF)XLoDb z27-LM3P+_ds+Q#J2&9eN)T3cg4`lq*ls!2s%@|s3oWGkmrstiCjbR^0_i?1Di*voH zU~_M#+Z@_%4!u&-aiM3N`pwQ6BmkisNO>;Wwwa3zshNX_3MFn!k6X>|>)(3g8!Tc@ z6nODMx!@Q0g`vzBq>% zb1zBW*mBS%%WNFzc;{R`O8g^*>40X*!LfD1g5Eflh&`B_Gye=qB{sbTR7H!)3OZ~A z&*l#Sg2DR6idFH4!4={zL2Ow;6&OHl9I_kANw;bK}7w7D7 zy>EP`g*zkiemb{36ViPQo|S|GI-{8%IrX4_(u^5iFoV(YEsivnaWxHAaGt#qKBn0fPT^7G za(^<9++V&!ToO7R-0b-IR+w~-IQ)A%w{d>9b6coRVzfGs)Ht9%ba8T{9()!bWwhc@ z0c_>3Moh~z6>ps`eM+isom;JAsw}q=fD|Ba2%eLV|AjUjRf$N_i`Lv z7E!Z9&P5so5ljM5bTFmM85L1TqmWlJ|Ukpl6bWYpDu8GT$c1N2c`T&Za&UO%KkW zHgkg?TY8dXs)^L1YU93W<^r)&6l~@*a@)o$e~r}v6|^mEo$vQGvH6&Td?x2=-+Uh# z?C)c*PRW|^f_3PtcKf}B7om%`G2@r;rcY)=t*v|pSYpJ!z@4uPRko0&A=*kP=M$oY zQnzJ*0vb4U3CdIA*gfhCnPzVOL_RKC7nRMX*`pqo*Bbu( zW{R32-E6}m?Z$0#3h-7rCs<0gav=8?O|DA9rXK|&7PcQeISsv9hY@ljf#>h6GzwKx zfF~M$QCR_AAJEB-o+)0v>Trb*rg0iRjUzycUU+W}MP{Rz@K=SOMaob*>^96&Sny`W z%Vu(CsX4IXjheK*N=lLa4_z3M4~T$1;oo`*BUxs@Cj6LsW>h_}RA2Xb63ZPvo#-e- zG|)#Tq>}h{K?v7S$^VX4@f}3{DB52VDPJ2Rp!p({2GoZ7GTBM9DotRd{qzruLZq+% zP;cK3*vuCca>nYM!nnS_35|R;3rUADR1Bl-ab-d*FfuFjHjH*U#0bR#(ex(>5(0G? z(gCNs0WxJ95D_MHWMMy@P-Q6cfM)CMTnYw)-PDXpoKh%O@=g0KgMmXITh?>62$s21 zWTQdW)9KjrtlwtzX)?gJ@au;Zkb9VS{+)Ev#gTHzEWgCimqKtNz$timMW0F`?GOL} zYrn*xT4Kg1o2-FI1=n8rP5c=avil0FewYDLl>z#l2F(F*W}3uwrft8dux4%J#Ea`l zd3l*MYFgA5Dvy%!9211gUR=3|2Sx;lgBdf{28wD?$^Qutd|VN!Wt|sd|9K&9U`v9Q z=$m?3ehy9qOFbZb0j*&DumLbd9NlCotcM}pWVFQ@s$onrZj|M>Lv#eiyBvJ{OY4^} zU;WD0uWns_fhh6+`sbQ|c^#Ub2tV~NKJZuQ&si6yeEgLw8&|G={YtvNetG?QeqD9x o%ByRaFTIeit!=HneEs>WuUvj1UAelQw$|5PNSCf$du9870TxC`NB{r; literal 0 HcmV?d00001 diff --git "a/BFPL\345\243\271/bcos-executor/test/liquid/transfer.h" "b/BFPL\345\243\271/bcos-executor/test/liquid/transfer.h" new file mode 100644 index 00000000..8341b58f --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/liquid/transfer.h" @@ -0,0 +1,1071 @@ +#pragma once + +constexpr unsigned char transfer_wasm[] = {0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x4f, 0x0d, + 0x60, 0x02, 0x7f, 0x7f, 0x00, 0x60, 0x03, 0x7f, 0x7f, 0x7f, 0x00, 0x60, 0x01, 0x7f, 0x00, 0x60, + 0x03, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x04, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x60, 0x00, 0x01, + 0x7f, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x05, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x60, 0x04, 0x7f, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, 0x60, + 0x02, 0x7f, 0x7f, 0x01, 0x7e, 0x60, 0x06, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x02, 0x6b, + 0x06, 0x04, 0x62, 0x63, 0x6f, 0x73, 0x0f, 0x67, 0x65, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x44, 0x61, + 0x74, 0x61, 0x53, 0x69, 0x7a, 0x65, 0x00, 0x05, 0x04, 0x62, 0x63, 0x6f, 0x73, 0x0b, 0x67, 0x65, + 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x00, 0x02, 0x04, 0x62, 0x63, 0x6f, 0x73, + 0x06, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x00, 0x00, 0x04, 0x62, 0x63, 0x6f, 0x73, 0x0a, 0x67, + 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x00, 0x03, 0x04, 0x62, 0x63, 0x6f, 0x73, + 0x0a, 0x73, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x00, 0x04, 0x04, 0x62, 0x63, + 0x6f, 0x73, 0x06, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x00, 0x00, 0x03, 0x4b, 0x4a, 0x00, 0x01, + 0x02, 0x01, 0x00, 0x04, 0x07, 0x00, 0x04, 0x07, 0x05, 0x09, 0x05, 0x01, 0x00, 0x01, 0x06, 0x01, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x04, 0x02, 0x04, 0x04, 0x07, 0x0a, 0x0b, 0x00, 0x00, 0x03, 0x01, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x01, 0x0a, 0x05, 0x08, + 0x09, 0x01, 0x01, 0x02, 0x02, 0x08, 0x06, 0x06, 0x02, 0x0b, 0x01, 0x06, 0x03, 0x01, 0x00, 0x0c, + 0x02, 0x01, 0x03, 0x02, 0x03, 0x01, 0x00, 0x03, 0x05, 0x03, 0x01, 0x00, 0x11, 0x06, 0x09, 0x01, + 0x7f, 0x01, 0x41, 0x80, 0x80, 0xc0, 0x00, 0x0b, 0x07, 0x26, 0x04, 0x06, 0x6d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x02, 0x00, 0x09, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x00, 0x36, + 0x06, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x00, 0x37, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x3d, + 0x0a, 0x88, 0x80, 0x01, 0x4a, 0x51, 0x01, 0x03, 0x7f, 0x02, 0x40, 0x20, 0x01, 0x28, 0x02, 0x00, + 0x22, 0x02, 0x20, 0x01, 0x28, 0x02, 0x04, 0x46, 0x04, 0x40, 0x41, 0x00, 0x21, 0x02, 0x0c, 0x01, + 0x0b, 0x20, 0x01, 0x20, 0x02, 0x41, 0x0c, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x01, 0x28, 0x02, 0x08, + 0x22, 0x03, 0x41, 0x01, 0x6a, 0x22, 0x04, 0x20, 0x03, 0x4f, 0x04, 0x40, 0x20, 0x01, 0x20, 0x04, + 0x36, 0x02, 0x08, 0x0c, 0x01, 0x0b, 0x00, 0x0b, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x04, 0x20, + 0x00, 0x20, 0x03, 0x36, 0x02, 0x00, 0x0b, 0xb3, 0x01, 0x01, 0x04, 0x7f, 0x23, 0x00, 0x41, 0x20, + 0x6b, 0x22, 0x03, 0x24, 0x00, 0x20, 0x01, 0x2f, 0x01, 0xe2, 0x01, 0x21, 0x04, 0x20, 0x03, 0x41, + 0x00, 0x3a, 0x00, 0x18, 0x20, 0x03, 0x20, 0x04, 0x36, 0x02, 0x14, 0x20, 0x03, 0x41, 0x00, 0x36, + 0x02, 0x10, 0x20, 0x03, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x22, 0x04, 0x29, 0x02, 0x00, 0x37, 0x02, + 0x00, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x20, 0x04, 0x41, 0x08, 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, + 0x00, 0x20, 0x03, 0x2d, 0x00, 0x08, 0x21, 0x06, 0x20, 0x03, 0x28, 0x02, 0x04, 0x21, 0x05, 0x20, + 0x03, 0x28, 0x02, 0x00, 0x21, 0x04, 0x03, 0x40, 0x02, 0x40, 0x20, 0x06, 0x0d, 0x00, 0x20, 0x04, + 0x20, 0x05, 0x4b, 0x0d, 0x00, 0x20, 0x03, 0x20, 0x01, 0x36, 0x02, 0x14, 0x20, 0x03, 0x20, 0x02, + 0x36, 0x02, 0x10, 0x20, 0x03, 0x20, 0x04, 0x36, 0x02, 0x18, 0x20, 0x04, 0x20, 0x05, 0x4f, 0x21, + 0x06, 0x20, 0x04, 0x20, 0x04, 0x20, 0x05, 0x49, 0x6a, 0x21, 0x04, 0x20, 0x03, 0x41, 0x10, 0x6a, + 0x10, 0x08, 0x0c, 0x01, 0x0b, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, + 0x02, 0x36, 0x02, 0x00, 0x20, 0x03, 0x41, 0x20, 0x6a, 0x24, 0x00, 0x0b, 0x5e, 0x01, 0x04, 0x7f, + 0x23, 0x00, 0x41, 0x20, 0x6b, 0x22, 0x01, 0x24, 0x00, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x22, 0x02, + 0x28, 0x02, 0x00, 0x21, 0x03, 0x20, 0x00, 0x28, 0x02, 0x04, 0x21, 0x04, 0x20, 0x01, 0x41, 0x18, + 0x6a, 0x20, 0x02, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, 0x00, 0x29, 0x02, 0x00, + 0x37, 0x03, 0x10, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x20, 0x01, 0x41, 0x10, 0x6a, 0x10, 0x0d, 0x20, + 0x01, 0x28, 0x02, 0x0c, 0x22, 0x00, 0x20, 0x03, 0x3b, 0x01, 0xe0, 0x01, 0x20, 0x00, 0x20, 0x04, + 0x36, 0x02, 0x00, 0x20, 0x01, 0x41, 0x20, 0x6a, 0x24, 0x00, 0x0b, 0x58, 0x01, 0x02, 0x7f, 0x02, + 0x40, 0x02, 0x40, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x03, 0x04, 0x40, 0x20, 0x01, 0x41, 0x01, + 0x6a, 0x22, 0x04, 0x20, 0x01, 0x49, 0x0d, 0x02, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x04, 0x20, + 0x00, 0x20, 0x04, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x02, 0x2f, 0x01, 0xe0, 0x01, 0x36, 0x02, + 0x08, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x04, 0x0b, 0x41, 0x94, 0x02, 0x41, + 0xe4, 0x01, 0x20, 0x01, 0x1b, 0x22, 0x00, 0x04, 0x40, 0x20, 0x02, 0x20, 0x00, 0x10, 0x0a, 0x0b, + 0x0f, 0x0b, 0x00, 0x0b, 0x85, 0x02, 0x01, 0x03, 0x7f, 0x20, 0x00, 0x04, 0x40, 0x20, 0x01, 0x20, + 0x01, 0x41, 0x04, 0x6a, 0x22, 0x03, 0x4d, 0x41, 0x00, 0x20, 0x03, 0x41, 0x01, 0x6b, 0x20, 0x03, + 0x4d, 0x1b, 0x45, 0x04, 0x40, 0x00, 0x0b, 0x41, 0x8c, 0x83, 0xc0, 0x00, 0x28, 0x02, 0x00, 0x21, + 0x03, 0x20, 0x00, 0x41, 0x08, 0x6b, 0x22, 0x01, 0x20, 0x01, 0x28, 0x02, 0x00, 0x22, 0x04, 0x41, + 0x7e, 0x71, 0x36, 0x02, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, 0x41, 0x7c, 0x71, 0x22, 0x02, + 0x20, 0x00, 0x6b, 0x20, 0x02, 0x4d, 0x04, 0x40, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, + 0x00, 0x41, 0x04, 0x6b, 0x28, 0x02, 0x00, 0x41, 0x7c, 0x71, 0x22, 0x02, 0x45, 0x0d, 0x01, 0x20, + 0x02, 0x2d, 0x00, 0x00, 0x41, 0x01, 0x71, 0x0d, 0x01, 0x20, 0x01, 0x10, 0x4b, 0x20, 0x02, 0x28, + 0x02, 0x00, 0x21, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x41, 0x02, 0x71, 0x04, 0x40, 0x20, 0x02, + 0x20, 0x00, 0x41, 0x02, 0x72, 0x22, 0x00, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x03, 0x21, 0x01, 0x20, + 0x00, 0x41, 0x7c, 0x71, 0x22, 0x00, 0x20, 0x02, 0x6b, 0x41, 0x08, 0x6b, 0x20, 0x00, 0x4d, 0x0d, + 0x02, 0x0b, 0x00, 0x0b, 0x02, 0x40, 0x20, 0x04, 0x41, 0x7c, 0x71, 0x22, 0x02, 0x45, 0x0d, 0x00, + 0x41, 0x00, 0x20, 0x02, 0x20, 0x04, 0x41, 0x02, 0x71, 0x1b, 0x22, 0x02, 0x45, 0x0d, 0x00, 0x20, + 0x02, 0x2d, 0x00, 0x00, 0x41, 0x01, 0x71, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x02, 0x28, 0x02, 0x08, + 0x41, 0x7c, 0x71, 0x36, 0x02, 0x00, 0x20, 0x02, 0x20, 0x01, 0x41, 0x01, 0x72, 0x36, 0x02, 0x08, + 0x20, 0x03, 0x21, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x00, 0x0b, 0x41, + 0x8c, 0x83, 0xc0, 0x00, 0x20, 0x01, 0x36, 0x02, 0x00, 0x0b, 0x0b, 0x6e, 0x01, 0x01, 0x7f, 0x02, + 0x40, 0x20, 0x02, 0x41, 0x01, 0x6a, 0x22, 0x04, 0x20, 0x02, 0x49, 0x0d, 0x00, 0x20, 0x01, 0x20, + 0x04, 0x4b, 0x04, 0x40, 0x20, 0x01, 0x20, 0x01, 0x20, 0x02, 0x6b, 0x22, 0x01, 0x49, 0x0d, 0x01, + 0x20, 0x01, 0x20, 0x01, 0x41, 0x01, 0x6b, 0x22, 0x01, 0x49, 0x0d, 0x01, 0x20, 0x00, 0x20, 0x04, + 0x41, 0x0c, 0x6c, 0x6a, 0x20, 0x00, 0x20, 0x02, 0x41, 0x0c, 0x6c, 0x6a, 0x20, 0x01, 0x41, 0x0c, + 0x6c, 0x10, 0x4d, 0x0b, 0x20, 0x00, 0x20, 0x02, 0x41, 0x0c, 0x6c, 0x6a, 0x22, 0x00, 0x20, 0x03, + 0x29, 0x02, 0x00, 0x37, 0x02, 0x00, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x20, 0x03, 0x41, 0x08, 0x6a, + 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x0f, 0x0b, 0x00, 0x0b, 0x62, 0x01, 0x01, 0x7f, 0x02, 0x40, + 0x20, 0x02, 0x41, 0x01, 0x6a, 0x22, 0x05, 0x20, 0x02, 0x49, 0x0d, 0x00, 0x20, 0x01, 0x20, 0x05, + 0x4b, 0x04, 0x40, 0x20, 0x01, 0x20, 0x01, 0x20, 0x02, 0x6b, 0x22, 0x01, 0x49, 0x0d, 0x01, 0x20, + 0x01, 0x20, 0x01, 0x41, 0x01, 0x6b, 0x22, 0x01, 0x49, 0x0d, 0x01, 0x20, 0x00, 0x20, 0x05, 0x41, + 0x03, 0x74, 0x6a, 0x20, 0x00, 0x20, 0x02, 0x41, 0x03, 0x74, 0x6a, 0x20, 0x01, 0x41, 0x03, 0x74, + 0x10, 0x4d, 0x0b, 0x20, 0x00, 0x20, 0x02, 0x41, 0x03, 0x74, 0x6a, 0x22, 0x00, 0x20, 0x04, 0x3a, + 0x00, 0x04, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x00, 0x0f, 0x0b, 0x00, 0x0b, 0x39, 0x01, 0x02, + 0x7f, 0x20, 0x01, 0x28, 0x02, 0x00, 0x22, 0x02, 0x41, 0x01, 0x6b, 0x22, 0x03, 0x20, 0x02, 0x4d, + 0x04, 0x40, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x28, 0x02, 0x04, + 0x20, 0x01, 0x28, 0x02, 0x08, 0x41, 0x02, 0x74, 0x6a, 0x41, 0xe4, 0x01, 0x6a, 0x28, 0x02, 0x00, + 0x36, 0x02, 0x04, 0x0f, 0x0b, 0x00, 0x0b, 0x6c, 0x01, 0x03, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, + 0x22, 0x04, 0x24, 0x00, 0x20, 0x00, 0x28, 0x02, 0x08, 0x21, 0x05, 0x20, 0x00, 0x28, 0x02, 0x04, + 0x22, 0x00, 0x2f, 0x01, 0xe2, 0x01, 0x21, 0x06, 0x20, 0x04, 0x41, 0x08, 0x6a, 0x20, 0x01, 0x41, + 0x08, 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x04, 0x20, 0x01, 0x29, 0x02, 0x00, 0x37, + 0x03, 0x00, 0x20, 0x00, 0x41, 0x04, 0x6a, 0x20, 0x06, 0x41, 0x01, 0x6a, 0x22, 0x01, 0x20, 0x05, + 0x20, 0x04, 0x10, 0x0b, 0x20, 0x00, 0x41, 0x88, 0x01, 0x6a, 0x20, 0x01, 0x20, 0x05, 0x20, 0x02, + 0x20, 0x03, 0x10, 0x0c, 0x20, 0x00, 0x20, 0x01, 0x3b, 0x01, 0xe2, 0x01, 0x20, 0x04, 0x41, 0x10, + 0x6a, 0x24, 0x00, 0x0b, 0x9c, 0x02, 0x01, 0x05, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x06, + 0x24, 0x00, 0x20, 0x00, 0x28, 0x02, 0x08, 0x21, 0x05, 0x20, 0x00, 0x28, 0x02, 0x04, 0x22, 0x07, + 0x2f, 0x01, 0xe2, 0x01, 0x21, 0x08, 0x20, 0x06, 0x41, 0x08, 0x6a, 0x20, 0x01, 0x41, 0x08, 0x6a, + 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x06, 0x20, 0x01, 0x29, 0x02, 0x00, 0x37, 0x03, 0x00, + 0x20, 0x07, 0x41, 0x04, 0x6a, 0x20, 0x08, 0x41, 0x01, 0x6a, 0x22, 0x09, 0x20, 0x05, 0x20, 0x06, + 0x10, 0x0b, 0x20, 0x07, 0x41, 0x88, 0x01, 0x6a, 0x20, 0x09, 0x20, 0x05, 0x20, 0x02, 0x20, 0x03, + 0x10, 0x0c, 0x02, 0x40, 0x20, 0x05, 0x41, 0x01, 0x6a, 0x22, 0x01, 0x20, 0x05, 0x49, 0x0d, 0x00, + 0x20, 0x01, 0x41, 0x01, 0x6a, 0x22, 0x03, 0x20, 0x01, 0x49, 0x0d, 0x00, 0x20, 0x08, 0x41, 0x02, + 0x6a, 0x22, 0x02, 0x20, 0x03, 0x4b, 0x04, 0x40, 0x20, 0x02, 0x20, 0x01, 0x6b, 0x22, 0x05, 0x20, + 0x02, 0x4b, 0x0d, 0x01, 0x20, 0x05, 0x20, 0x05, 0x41, 0x01, 0x6b, 0x22, 0x05, 0x49, 0x0d, 0x01, + 0x20, 0x07, 0x41, 0xe4, 0x01, 0x6a, 0x22, 0x08, 0x20, 0x03, 0x41, 0x02, 0x74, 0x6a, 0x20, 0x08, + 0x20, 0x01, 0x41, 0x02, 0x74, 0x6a, 0x20, 0x05, 0x41, 0x02, 0x74, 0x10, 0x4d, 0x0b, 0x20, 0x07, + 0x20, 0x01, 0x41, 0x02, 0x74, 0x6a, 0x41, 0xe4, 0x01, 0x6a, 0x20, 0x04, 0x36, 0x02, 0x00, 0x20, + 0x07, 0x20, 0x09, 0x3b, 0x01, 0xe2, 0x01, 0x20, 0x01, 0x20, 0x02, 0x20, 0x01, 0x20, 0x02, 0x4b, + 0x1b, 0x21, 0x02, 0x20, 0x00, 0x28, 0x02, 0x00, 0x21, 0x00, 0x03, 0x40, 0x20, 0x01, 0x20, 0x02, + 0x47, 0x04, 0x40, 0x20, 0x06, 0x20, 0x07, 0x36, 0x02, 0x04, 0x20, 0x06, 0x20, 0x00, 0x36, 0x02, + 0x00, 0x20, 0x06, 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x20, + 0x06, 0x10, 0x08, 0x0c, 0x01, 0x0b, 0x0b, 0x20, 0x06, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0f, 0x0b, + 0x00, 0x0b, 0x21, 0x01, 0x01, 0x7f, 0x41, 0xe4, 0x01, 0x10, 0x11, 0x22, 0x00, 0x45, 0x04, 0x40, + 0x00, 0x0b, 0x20, 0x00, 0x41, 0x00, 0x3b, 0x01, 0xe2, 0x01, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, + 0x00, 0x20, 0x00, 0x0b, 0x08, 0x00, 0x20, 0x00, 0x41, 0x04, 0x10, 0x16, 0x0b, 0x21, 0x01, 0x01, + 0x7f, 0x41, 0x94, 0x02, 0x10, 0x11, 0x22, 0x00, 0x45, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x00, 0x41, + 0x00, 0x3b, 0x01, 0xe2, 0x01, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x0b, 0x66, + 0x01, 0x01, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, 0x00, 0x02, 0x40, 0x20, 0x01, + 0x41, 0x00, 0x4e, 0x04, 0x40, 0x02, 0x7f, 0x20, 0x02, 0x45, 0x04, 0x40, 0x20, 0x03, 0x41, 0x08, + 0x6a, 0x20, 0x01, 0x10, 0x14, 0x20, 0x03, 0x28, 0x02, 0x0c, 0x21, 0x02, 0x20, 0x03, 0x28, 0x02, + 0x08, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x20, 0x01, 0x41, 0x01, 0x10, 0x15, 0x20, 0x03, 0x28, 0x02, + 0x04, 0x21, 0x02, 0x20, 0x03, 0x28, 0x02, 0x00, 0x0b, 0x22, 0x01, 0x0d, 0x01, 0x0b, 0x00, 0x0b, + 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x04, 0x20, 0x03, + 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x33, 0x01, 0x01, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, + 0x02, 0x24, 0x00, 0x20, 0x02, 0x41, 0x08, 0x6a, 0x20, 0x01, 0x41, 0x00, 0x10, 0x15, 0x20, 0x00, + 0x20, 0x02, 0x28, 0x02, 0x08, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x02, 0x28, 0x02, 0x0c, 0x36, + 0x02, 0x04, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x4a, 0x01, 0x01, 0x7f, 0x41, 0x01, + 0x21, 0x03, 0x02, 0x40, 0x20, 0x01, 0x45, 0x04, 0x40, 0x41, 0x00, 0x21, 0x01, 0x0c, 0x01, 0x0b, + 0x20, 0x01, 0x41, 0x01, 0x10, 0x16, 0x21, 0x03, 0x02, 0x40, 0x20, 0x02, 0x04, 0x40, 0x20, 0x03, + 0x45, 0x0d, 0x01, 0x20, 0x03, 0x20, 0x01, 0x10, 0x4e, 0x0c, 0x02, 0x0b, 0x20, 0x03, 0x0d, 0x01, + 0x0b, 0x41, 0x00, 0x21, 0x03, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, + 0x03, 0x36, 0x02, 0x00, 0x0b, 0x9c, 0x01, 0x01, 0x02, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, + 0x02, 0x24, 0x00, 0x02, 0x40, 0x20, 0x00, 0x20, 0x00, 0x41, 0x04, 0x6a, 0x22, 0x03, 0x4d, 0x04, + 0x40, 0x20, 0x03, 0x41, 0x01, 0x6b, 0x22, 0x00, 0x20, 0x03, 0x4d, 0x0d, 0x01, 0x0b, 0x00, 0x0b, + 0x20, 0x00, 0x41, 0x02, 0x76, 0x21, 0x00, 0x20, 0x02, 0x41, 0x8c, 0x83, 0xc0, 0x00, 0x28, 0x02, + 0x00, 0x36, 0x02, 0x0c, 0x02, 0x40, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x41, 0x0c, 0x6a, 0x10, + 0x4a, 0x22, 0x03, 0x0d, 0x00, 0x20, 0x02, 0x20, 0x00, 0x20, 0x01, 0x10, 0x49, 0x41, 0x00, 0x21, + 0x03, 0x20, 0x02, 0x28, 0x02, 0x00, 0x0d, 0x00, 0x20, 0x02, 0x28, 0x02, 0x04, 0x22, 0x03, 0x20, + 0x02, 0x28, 0x02, 0x0c, 0x36, 0x02, 0x08, 0x20, 0x02, 0x20, 0x03, 0x36, 0x02, 0x0c, 0x20, 0x00, + 0x20, 0x01, 0x20, 0x02, 0x41, 0x0c, 0x6a, 0x10, 0x4a, 0x21, 0x03, 0x0b, 0x41, 0x8c, 0x83, 0xc0, + 0x00, 0x20, 0x02, 0x28, 0x02, 0x0c, 0x36, 0x02, 0x00, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, + 0x20, 0x03, 0x0b, 0xd1, 0x01, 0x01, 0x03, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, + 0x00, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x41, 0x00, 0x4e, 0x04, 0x40, 0x20, 0x02, + 0x28, 0x02, 0x00, 0x22, 0x04, 0x0d, 0x01, 0x20, 0x03, 0x20, 0x01, 0x10, 0x14, 0x20, 0x03, 0x28, + 0x02, 0x04, 0x21, 0x04, 0x20, 0x03, 0x28, 0x02, 0x00, 0x21, 0x02, 0x0c, 0x02, 0x0b, 0x20, 0x00, + 0x41, 0x01, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0c, + 0x02, 0x0b, 0x20, 0x02, 0x28, 0x02, 0x04, 0x22, 0x05, 0x45, 0x04, 0x40, 0x20, 0x03, 0x41, 0x08, + 0x6a, 0x20, 0x01, 0x41, 0x00, 0x10, 0x15, 0x20, 0x03, 0x28, 0x02, 0x0c, 0x21, 0x04, 0x20, 0x03, + 0x28, 0x02, 0x08, 0x21, 0x02, 0x0c, 0x01, 0x0b, 0x20, 0x01, 0x41, 0x01, 0x10, 0x16, 0x22, 0x02, + 0x45, 0x04, 0x40, 0x41, 0x00, 0x21, 0x02, 0x0c, 0x01, 0x0b, 0x20, 0x02, 0x20, 0x04, 0x20, 0x05, + 0x10, 0x4c, 0x1a, 0x20, 0x04, 0x20, 0x05, 0x10, 0x0a, 0x20, 0x01, 0x21, 0x04, 0x0b, 0x20, 0x00, + 0x02, 0x7f, 0x20, 0x02, 0x04, 0x40, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x04, 0x41, 0x00, 0x0c, + 0x01, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x04, 0x41, 0x01, 0x21, 0x04, 0x41, 0x01, 0x0b, + 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x20, 0x04, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x03, + 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x26, 0x01, 0x01, 0x7f, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, + 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x04, 0x22, 0x00, 0x1b, 0x22, 0x01, 0x45, 0x0d, 0x00, + 0x20, 0x00, 0x45, 0x0d, 0x00, 0x20, 0x01, 0x20, 0x00, 0x10, 0x0a, 0x0b, 0x0b, 0x6f, 0x00, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x41, 0x05, 0x4f, 0x04, 0x40, 0x20, 0x01, 0x41, 0x05, + 0x6b, 0x0e, 0x02, 0x01, 0x02, 0x03, 0x0b, 0x20, 0x00, 0x42, 0x04, 0x37, 0x02, 0x00, 0x20, 0x00, + 0x41, 0x08, 0x6a, 0x20, 0x01, 0x36, 0x02, 0x00, 0x0f, 0x0b, 0x20, 0x00, 0x42, 0x05, 0x37, 0x02, + 0x00, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x41, 0x05, 0x36, 0x02, 0x00, 0x0f, 0x0b, 0x20, 0x00, 0x42, + 0x85, 0x80, 0x80, 0x80, 0x10, 0x37, 0x02, 0x00, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x41, 0x00, 0x36, + 0x02, 0x00, 0x0f, 0x0b, 0x20, 0x00, 0x42, 0x86, 0x80, 0x80, 0x80, 0x10, 0x37, 0x02, 0x00, 0x20, + 0x00, 0x41, 0x08, 0x6a, 0x20, 0x01, 0x41, 0x07, 0x6b, 0x36, 0x02, 0x00, 0x0b, 0xa7, 0x01, 0x01, + 0x03, 0x7f, 0x23, 0x00, 0x41, 0x20, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x02, 0x40, 0x20, 0x01, 0x20, + 0x00, 0x28, 0x02, 0x04, 0x22, 0x03, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x04, 0x6b, 0x4b, 0x04, + 0x40, 0x20, 0x01, 0x20, 0x04, 0x6a, 0x22, 0x01, 0x20, 0x04, 0x49, 0x0d, 0x01, 0x20, 0x03, 0x20, + 0x03, 0x6a, 0x22, 0x04, 0x20, 0x03, 0x49, 0x0d, 0x01, 0x20, 0x04, 0x20, 0x01, 0x20, 0x01, 0x20, + 0x04, 0x49, 0x1b, 0x22, 0x01, 0x41, 0x08, 0x20, 0x01, 0x41, 0x08, 0x4b, 0x1b, 0x21, 0x01, 0x02, + 0x40, 0x20, 0x03, 0x04, 0x40, 0x20, 0x02, 0x41, 0x18, 0x6a, 0x41, 0x01, 0x36, 0x02, 0x00, 0x20, + 0x02, 0x20, 0x03, 0x36, 0x02, 0x14, 0x20, 0x02, 0x20, 0x00, 0x28, 0x02, 0x00, 0x36, 0x02, 0x10, + 0x0c, 0x01, 0x0b, 0x20, 0x02, 0x41, 0x00, 0x36, 0x02, 0x10, 0x0b, 0x20, 0x02, 0x20, 0x01, 0x20, + 0x02, 0x41, 0x10, 0x6a, 0x10, 0x17, 0x20, 0x02, 0x28, 0x02, 0x00, 0x41, 0x01, 0x46, 0x0d, 0x01, + 0x20, 0x00, 0x20, 0x02, 0x29, 0x02, 0x04, 0x37, 0x02, 0x00, 0x0b, 0x20, 0x02, 0x41, 0x20, 0x6a, + 0x24, 0x00, 0x0f, 0x0b, 0x00, 0x0b, 0x33, 0x01, 0x01, 0x7f, 0x20, 0x00, 0x20, 0x02, 0x10, 0x1a, + 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x03, 0x20, 0x00, 0x28, 0x02, 0x00, 0x6a, 0x20, 0x01, 0x20, + 0x02, 0x10, 0x4c, 0x1a, 0x20, 0x03, 0x20, 0x02, 0x20, 0x03, 0x6a, 0x22, 0x01, 0x4b, 0x04, 0x40, + 0x00, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x08, 0x0b, 0x47, 0x01, 0x02, 0x7f, 0x23, 0x00, + 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, 0x00, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x20, 0x02, 0x41, 0x00, + 0x10, 0x13, 0x20, 0x03, 0x28, 0x02, 0x08, 0x21, 0x04, 0x20, 0x00, 0x20, 0x03, 0x28, 0x02, 0x0c, + 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, 0x00, 0x20, 0x04, 0x20, 0x01, 0x20, 0x02, + 0x10, 0x4c, 0x1a, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x08, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, + 0x00, 0x0b, 0x15, 0x00, 0x20, 0x01, 0x20, 0x03, 0x46, 0x04, 0x40, 0x20, 0x00, 0x20, 0x02, 0x20, + 0x01, 0x10, 0x4c, 0x1a, 0x0f, 0x0b, 0x00, 0x0b, 0xc6, 0x01, 0x01, 0x04, 0x7f, 0x02, 0x40, 0x20, + 0x00, 0x0d, 0x00, 0x20, 0x00, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x45, 0x0d, 0x00, + 0x20, 0x00, 0x45, 0x0d, 0x00, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x21, 0x00, 0x0b, 0x03, 0x40, 0x02, + 0x40, 0x41, 0x00, 0x20, 0x00, 0x04, 0x7f, 0x20, 0x00, 0x0d, 0x01, 0x41, 0x00, 0x05, 0x41, 0x00, + 0x0b, 0x22, 0x00, 0x6b, 0x22, 0x02, 0x0d, 0x02, 0x20, 0x02, 0x45, 0x0d, 0x02, 0x20, 0x00, 0x41, + 0xf0, 0x82, 0xc0, 0x00, 0x6a, 0x22, 0x00, 0x2c, 0x00, 0x00, 0x22, 0x03, 0x41, 0x7f, 0x4a, 0x0d, + 0x02, 0x20, 0x00, 0x20, 0x02, 0x6a, 0x22, 0x04, 0x21, 0x01, 0x20, 0x02, 0x41, 0x01, 0x47, 0x04, + 0x40, 0x20, 0x00, 0x2d, 0x00, 0x01, 0x1a, 0x20, 0x00, 0x41, 0x02, 0x6a, 0x21, 0x01, 0x0b, 0x20, + 0x03, 0x41, 0xff, 0x01, 0x71, 0x41, 0xe0, 0x01, 0x49, 0x0d, 0x02, 0x20, 0x04, 0x22, 0x00, 0x20, + 0x01, 0x47, 0x04, 0x7f, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x00, + 0x05, 0x41, 0x00, 0x0b, 0x1a, 0x20, 0x03, 0x41, 0xff, 0x01, 0x71, 0x41, 0xf0, 0x01, 0x49, 0x0d, + 0x02, 0x20, 0x00, 0x20, 0x04, 0x47, 0x04, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x1a, 0x0b, 0x0c, + 0x02, 0x0b, 0x20, 0x00, 0x41, 0x01, 0x6b, 0x21, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0b, + 0xec, 0x01, 0x01, 0x06, 0x7f, 0x41, 0x01, 0x21, 0x07, 0x41, 0x01, 0x21, 0x04, 0x03, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, 0x22, 0x06, 0x20, 0x05, 0x6a, 0x22, 0x04, 0x20, 0x06, + 0x49, 0x0d, 0x00, 0x20, 0x02, 0x20, 0x04, 0x4d, 0x0d, 0x01, 0x20, 0x05, 0x20, 0x08, 0x6a, 0x22, + 0x09, 0x20, 0x08, 0x49, 0x0d, 0x00, 0x20, 0x02, 0x20, 0x09, 0x4d, 0x0d, 0x00, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x01, 0x20, 0x04, 0x6a, 0x2d, 0x00, 0x00, 0x22, 0x04, 0x20, 0x01, 0x20, 0x09, 0x6a, + 0x2d, 0x00, 0x00, 0x22, 0x09, 0x4b, 0x20, 0x03, 0x71, 0x0d, 0x00, 0x20, 0x04, 0x20, 0x09, 0x4f, + 0x20, 0x03, 0x72, 0x45, 0x0d, 0x00, 0x20, 0x04, 0x20, 0x09, 0x46, 0x0d, 0x01, 0x20, 0x06, 0x41, + 0x01, 0x6a, 0x22, 0x04, 0x20, 0x06, 0x49, 0x0d, 0x02, 0x41, 0x01, 0x21, 0x07, 0x41, 0x00, 0x21, + 0x05, 0x20, 0x06, 0x21, 0x08, 0x0c, 0x05, 0x0b, 0x20, 0x05, 0x41, 0x01, 0x6a, 0x22, 0x04, 0x20, + 0x05, 0x49, 0x0d, 0x01, 0x20, 0x04, 0x20, 0x06, 0x6a, 0x22, 0x04, 0x20, 0x06, 0x49, 0x0d, 0x01, + 0x20, 0x04, 0x20, 0x08, 0x6b, 0x22, 0x07, 0x20, 0x04, 0x4b, 0x0d, 0x01, 0x0c, 0x03, 0x0b, 0x20, + 0x05, 0x20, 0x05, 0x41, 0x01, 0x6a, 0x22, 0x05, 0x4b, 0x0d, 0x00, 0x20, 0x06, 0x21, 0x04, 0x20, + 0x05, 0x20, 0x07, 0x47, 0x0d, 0x03, 0x20, 0x04, 0x20, 0x04, 0x20, 0x07, 0x6a, 0x22, 0x04, 0x4d, + 0x0d, 0x02, 0x0b, 0x00, 0x0b, 0x20, 0x00, 0x20, 0x07, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x08, + 0x36, 0x02, 0x00, 0x0f, 0x0b, 0x41, 0x00, 0x21, 0x05, 0x0c, 0x00, 0x0b, 0x00, 0x0b, 0x37, 0x01, + 0x01, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x04, 0x24, 0x00, 0x20, 0x04, 0x41, 0x08, 0x6a, + 0x41, 0x00, 0x20, 0x03, 0x20, 0x01, 0x20, 0x02, 0x10, 0x21, 0x20, 0x00, 0x20, 0x04, 0x28, 0x02, + 0x08, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x04, 0x28, 0x02, 0x0c, 0x36, 0x02, 0x04, 0x20, 0x04, + 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x36, 0x00, 0x02, 0x40, 0x20, 0x01, 0x20, 0x02, 0x4d, 0x04, + 0x40, 0x20, 0x02, 0x20, 0x04, 0x4d, 0x04, 0x40, 0x20, 0x02, 0x20, 0x02, 0x20, 0x01, 0x6b, 0x22, + 0x04, 0x49, 0x0d, 0x02, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x01, 0x20, + 0x03, 0x6a, 0x36, 0x02, 0x00, 0x0f, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0xa0, 0x02, 0x01, + 0x07, 0x7f, 0x41, 0x01, 0x21, 0x09, 0x41, 0x01, 0x21, 0x04, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, 0x22, 0x07, 0x20, 0x05, 0x6a, 0x22, 0x04, 0x20, 0x07, 0x49, + 0x0d, 0x00, 0x20, 0x01, 0x20, 0x04, 0x4d, 0x0d, 0x03, 0x20, 0x07, 0x41, 0x01, 0x6a, 0x22, 0x04, + 0x20, 0x07, 0x49, 0x0d, 0x00, 0x20, 0x04, 0x20, 0x05, 0x6a, 0x22, 0x08, 0x20, 0x04, 0x49, 0x0d, + 0x00, 0x20, 0x01, 0x20, 0x08, 0x6b, 0x22, 0x08, 0x20, 0x01, 0x4b, 0x0d, 0x00, 0x20, 0x01, 0x20, + 0x08, 0x4d, 0x0d, 0x00, 0x20, 0x0a, 0x41, 0x01, 0x6a, 0x22, 0x06, 0x20, 0x0a, 0x49, 0x0d, 0x00, + 0x20, 0x06, 0x20, 0x05, 0x20, 0x06, 0x6a, 0x22, 0x06, 0x4b, 0x0d, 0x00, 0x20, 0x01, 0x20, 0x06, + 0x6b, 0x22, 0x06, 0x20, 0x01, 0x4b, 0x0d, 0x00, 0x20, 0x01, 0x20, 0x06, 0x4d, 0x0d, 0x00, 0x02, + 0x40, 0x02, 0x40, 0x20, 0x00, 0x20, 0x08, 0x6a, 0x2d, 0x00, 0x00, 0x22, 0x08, 0x20, 0x00, 0x20, + 0x06, 0x6a, 0x2d, 0x00, 0x00, 0x22, 0x06, 0x4b, 0x20, 0x03, 0x71, 0x0d, 0x00, 0x20, 0x06, 0x20, + 0x08, 0x4d, 0x20, 0x03, 0x72, 0x45, 0x0d, 0x00, 0x20, 0x06, 0x20, 0x08, 0x46, 0x0d, 0x01, 0x41, + 0x01, 0x21, 0x09, 0x41, 0x00, 0x21, 0x05, 0x20, 0x07, 0x21, 0x0a, 0x0c, 0x04, 0x0b, 0x20, 0x05, + 0x41, 0x01, 0x6a, 0x22, 0x04, 0x20, 0x05, 0x49, 0x0d, 0x01, 0x20, 0x04, 0x20, 0x07, 0x6a, 0x22, + 0x04, 0x20, 0x07, 0x49, 0x0d, 0x01, 0x20, 0x04, 0x20, 0x0a, 0x6b, 0x22, 0x09, 0x20, 0x04, 0x4b, + 0x0d, 0x01, 0x0c, 0x02, 0x0b, 0x20, 0x05, 0x20, 0x05, 0x41, 0x01, 0x6a, 0x22, 0x05, 0x4b, 0x0d, + 0x00, 0x20, 0x05, 0x20, 0x09, 0x47, 0x04, 0x40, 0x20, 0x07, 0x21, 0x04, 0x0c, 0x03, 0x0b, 0x20, + 0x07, 0x20, 0x09, 0x6a, 0x22, 0x04, 0x20, 0x07, 0x4f, 0x0d, 0x01, 0x0b, 0x00, 0x0b, 0x41, 0x00, + 0x21, 0x05, 0x0b, 0x20, 0x02, 0x20, 0x09, 0x47, 0x0d, 0x01, 0x0b, 0x0b, 0x20, 0x0a, 0x0b, 0x2b, + 0x01, 0x01, 0x7e, 0x03, 0x40, 0x20, 0x01, 0x04, 0x40, 0x20, 0x01, 0x41, 0x01, 0x6b, 0x21, 0x01, + 0x42, 0x01, 0x20, 0x00, 0x31, 0x00, 0x00, 0x86, 0x20, 0x02, 0x84, 0x21, 0x02, 0x20, 0x00, 0x41, + 0x01, 0x6a, 0x21, 0x00, 0x0c, 0x01, 0x0b, 0x0b, 0x20, 0x02, 0x0b, 0x97, 0x02, 0x01, 0x02, 0x7f, + 0x23, 0x00, 0x41, 0x30, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x02, 0x40, 0x02, 0x40, 0x10, 0x00, 0x22, + 0x03, 0x41, 0x03, 0x4b, 0x0d, 0x00, 0x20, 0x01, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x81, 0x08, + 0x3b, 0x01, 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x02, 0x20, 0x03, 0x10, 0x25, 0x20, 0x02, 0x28, 0x02, + 0x00, 0x22, 0x03, 0x10, 0x01, 0x20, 0x00, 0x02, 0x7f, 0x02, 0x40, 0x20, 0x01, 0x04, 0x40, 0x20, + 0x02, 0x20, 0x02, 0x28, 0x02, 0x08, 0x22, 0x01, 0x36, 0x02, 0x14, 0x20, 0x02, 0x20, 0x03, 0x36, + 0x02, 0x10, 0x02, 0x40, 0x20, 0x01, 0x41, 0x04, 0x49, 0x0d, 0x00, 0x20, 0x02, 0x41, 0x00, 0x36, + 0x02, 0x1c, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x20, 0x02, 0x41, 0x1c, 0x6a, 0x41, 0x04, 0x10, 0x26, + 0x0d, 0x00, 0x20, 0x02, 0x41, 0x20, 0x6a, 0x20, 0x02, 0x28, 0x02, 0x14, 0x10, 0x25, 0x20, 0x02, + 0x41, 0x10, 0x6a, 0x20, 0x02, 0x28, 0x02, 0x20, 0x22, 0x01, 0x20, 0x02, 0x28, 0x02, 0x28, 0x10, + 0x26, 0x45, 0x0d, 0x02, 0x20, 0x02, 0x41, 0x20, 0x6a, 0x10, 0x18, 0x0b, 0x20, 0x00, 0x41, 0x00, + 0x3a, 0x00, 0x01, 0x41, 0x01, 0x0c, 0x02, 0x0b, 0x20, 0x00, 0x41, 0x00, 0x3a, 0x00, 0x00, 0x20, + 0x00, 0x41, 0x04, 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x20, 0x02, + 0x29, 0x03, 0x00, 0x37, 0x02, 0x00, 0x20, 0x00, 0x41, 0x10, 0x6a, 0x20, 0x02, 0x41, 0x08, 0x6a, + 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x0c, 0x02, 0x0b, 0x20, 0x02, 0x28, 0x02, 0x1c, 0x21, 0x03, + 0x20, 0x00, 0x41, 0x0c, 0x6a, 0x20, 0x02, 0x29, 0x02, 0x24, 0x37, 0x02, 0x00, 0x20, 0x00, 0x41, + 0x08, 0x6a, 0x20, 0x01, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x04, 0x6a, 0x20, 0x03, 0x36, 0x02, + 0x00, 0x41, 0x00, 0x0b, 0x3a, 0x00, 0x00, 0x20, 0x02, 0x10, 0x18, 0x0b, 0x20, 0x02, 0x41, 0x30, + 0x6a, 0x24, 0x00, 0x0b, 0x36, 0x02, 0x01, 0x7f, 0x01, 0x7e, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, + 0x02, 0x24, 0x00, 0x20, 0x02, 0x41, 0x08, 0x6a, 0x20, 0x01, 0x41, 0x01, 0x10, 0x13, 0x20, 0x02, + 0x29, 0x03, 0x08, 0x21, 0x03, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, 0x00, 0x20, 0x03, + 0x37, 0x02, 0x00, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x39, 0x01, 0x02, 0x7f, 0x20, + 0x00, 0x28, 0x02, 0x04, 0x22, 0x03, 0x20, 0x02, 0x49, 0x22, 0x04, 0x45, 0x04, 0x40, 0x20, 0x01, + 0x20, 0x02, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x01, 0x20, 0x02, 0x10, 0x1d, 0x20, 0x00, 0x20, + 0x03, 0x20, 0x02, 0x6b, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x6a, 0x36, 0x02, + 0x00, 0x0b, 0x20, 0x04, 0x0b, 0x89, 0x01, 0x02, 0x02, 0x7f, 0x01, 0x7e, 0x23, 0x00, 0x41, 0x10, + 0x6b, 0x22, 0x03, 0x24, 0x00, 0x02, 0x40, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0x6a, 0x22, 0x04, + 0x4d, 0x04, 0x40, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x20, 0x04, 0x41, 0x00, 0x10, 0x13, 0x20, 0x03, + 0x29, 0x03, 0x08, 0x21, 0x05, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x08, 0x20, 0x00, 0x20, 0x05, + 0x37, 0x02, 0x00, 0x03, 0x40, 0x20, 0x02, 0x45, 0x0d, 0x02, 0x20, 0x00, 0x20, 0x01, 0x2d, 0x00, + 0x00, 0x22, 0x04, 0x41, 0x04, 0x76, 0x41, 0xb4, 0x82, 0xc0, 0x00, 0x6a, 0x2d, 0x00, 0x00, 0x10, + 0x28, 0x20, 0x00, 0x20, 0x04, 0x41, 0x0f, 0x71, 0x41, 0xb4, 0x82, 0xc0, 0x00, 0x6a, 0x2d, 0x00, + 0x00, 0x10, 0x28, 0x20, 0x02, 0x41, 0x01, 0x6b, 0x21, 0x02, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, + 0x01, 0x0c, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, + 0x9c, 0x02, 0x01, 0x02, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x02, 0x40, + 0x20, 0x00, 0x20, 0x02, 0x41, 0x0c, 0x6a, 0x02, 0x7f, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x41, + 0xff, 0x00, 0x4d, 0x04, 0x40, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x03, 0x20, 0x00, 0x28, 0x02, + 0x04, 0x46, 0x04, 0x40, 0x20, 0x00, 0x41, 0x01, 0x10, 0x1a, 0x20, 0x00, 0x28, 0x02, 0x08, 0x21, + 0x03, 0x0b, 0x20, 0x00, 0x28, 0x02, 0x00, 0x20, 0x03, 0x6a, 0x20, 0x01, 0x3a, 0x00, 0x00, 0x20, + 0x03, 0x41, 0x01, 0x6a, 0x22, 0x01, 0x20, 0x03, 0x49, 0x0d, 0x01, 0x20, 0x00, 0x20, 0x01, 0x36, + 0x02, 0x08, 0x0c, 0x04, 0x0b, 0x20, 0x02, 0x41, 0x00, 0x36, 0x02, 0x0c, 0x20, 0x01, 0x41, 0x80, + 0x10, 0x49, 0x0d, 0x01, 0x20, 0x01, 0x41, 0x80, 0x80, 0x04, 0x49, 0x04, 0x40, 0x20, 0x02, 0x20, + 0x01, 0x41, 0x3f, 0x71, 0x41, 0x80, 0x01, 0x72, 0x3a, 0x00, 0x0e, 0x20, 0x02, 0x20, 0x01, 0x41, + 0x0c, 0x76, 0x41, 0xe0, 0x01, 0x72, 0x3a, 0x00, 0x0c, 0x20, 0x02, 0x20, 0x01, 0x41, 0x06, 0x76, + 0x41, 0x3f, 0x71, 0x41, 0x80, 0x01, 0x72, 0x3a, 0x00, 0x0d, 0x41, 0x03, 0x0c, 0x03, 0x0b, 0x20, + 0x02, 0x20, 0x01, 0x41, 0x3f, 0x71, 0x41, 0x80, 0x01, 0x72, 0x3a, 0x00, 0x0f, 0x20, 0x02, 0x20, + 0x01, 0x41, 0x12, 0x76, 0x41, 0xf0, 0x01, 0x72, 0x3a, 0x00, 0x0c, 0x20, 0x02, 0x20, 0x01, 0x41, + 0x06, 0x76, 0x41, 0x3f, 0x71, 0x41, 0x80, 0x01, 0x72, 0x3a, 0x00, 0x0e, 0x20, 0x02, 0x20, 0x01, + 0x41, 0x0c, 0x76, 0x41, 0x3f, 0x71, 0x41, 0x80, 0x01, 0x72, 0x3a, 0x00, 0x0d, 0x41, 0x04, 0x0c, + 0x02, 0x0b, 0x00, 0x0b, 0x20, 0x02, 0x20, 0x01, 0x41, 0x3f, 0x71, 0x41, 0x80, 0x01, 0x72, 0x3a, + 0x00, 0x0d, 0x20, 0x02, 0x20, 0x01, 0x41, 0x06, 0x76, 0x41, 0xc0, 0x01, 0x72, 0x3a, 0x00, 0x0c, + 0x41, 0x02, 0x0b, 0x10, 0x1b, 0x0b, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x83, 0x09, + 0x02, 0x08, 0x7f, 0x03, 0x7e, 0x23, 0x00, 0x41, 0x20, 0x6b, 0x22, 0x03, 0x24, 0x00, 0x20, 0x03, + 0x41, 0x00, 0x3a, 0x00, 0x08, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x7e, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x41, 0x01, 0x10, 0x26, + 0x0d, 0x00, 0x02, 0x40, 0x20, 0x03, 0x2d, 0x00, 0x08, 0x22, 0x02, 0x41, 0x03, 0x71, 0x22, 0x04, + 0x41, 0x03, 0x47, 0x04, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, 0x41, 0x01, 0x6b, + 0x0e, 0x02, 0x02, 0x01, 0x00, 0x0b, 0x20, 0x02, 0x41, 0x02, 0x76, 0x21, 0x02, 0x0c, 0x03, 0x0b, + 0x20, 0x03, 0x20, 0x02, 0x3a, 0x00, 0x0d, 0x20, 0x03, 0x41, 0x01, 0x3a, 0x00, 0x0c, 0x20, 0x03, + 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, 0x03, 0x41, 0x00, 0x36, 0x02, 0x1c, 0x20, 0x03, 0x41, 0x08, + 0x6a, 0x20, 0x03, 0x41, 0x1c, 0x6a, 0x41, 0x04, 0x10, 0x2a, 0x0d, 0x03, 0x20, 0x03, 0x28, 0x02, + 0x1c, 0x22, 0x02, 0x41, 0xff, 0xff, 0x03, 0x4d, 0x0d, 0x03, 0x20, 0x02, 0x41, 0x02, 0x76, 0x21, + 0x02, 0x0c, 0x02, 0x0b, 0x20, 0x03, 0x20, 0x02, 0x3a, 0x00, 0x0d, 0x20, 0x03, 0x41, 0x01, 0x3a, + 0x00, 0x0c, 0x20, 0x03, 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, 0x03, 0x41, 0x00, 0x3b, 0x01, 0x1c, + 0x20, 0x03, 0x41, 0x08, 0x6a, 0x20, 0x03, 0x41, 0x1c, 0x6a, 0x41, 0x02, 0x10, 0x2a, 0x0d, 0x02, + 0x20, 0x03, 0x2f, 0x01, 0x1c, 0x22, 0x02, 0x41, 0xff, 0x01, 0x4d, 0x0d, 0x02, 0x20, 0x02, 0x41, + 0x02, 0x76, 0x21, 0x02, 0x0c, 0x01, 0x0b, 0x20, 0x02, 0x41, 0x04, 0x4f, 0x0d, 0x01, 0x20, 0x03, + 0x20, 0x01, 0x10, 0x2b, 0x20, 0x03, 0x28, 0x02, 0x00, 0x0d, 0x01, 0x20, 0x03, 0x28, 0x02, 0x04, + 0x22, 0x02, 0x41, 0xff, 0xff, 0xff, 0xff, 0x03, 0x4d, 0x0d, 0x01, 0x0b, 0x20, 0x01, 0x28, 0x02, + 0x04, 0x20, 0x02, 0x49, 0x0d, 0x00, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x20, 0x02, 0x10, 0x25, 0x20, + 0x01, 0x20, 0x03, 0x28, 0x02, 0x08, 0x22, 0x04, 0x20, 0x03, 0x28, 0x02, 0x10, 0x10, 0x26, 0x04, + 0x40, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x10, 0x18, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x20, 0x03, 0x29, + 0x02, 0x0c, 0x22, 0x0c, 0x42, 0x20, 0x88, 0xa7, 0x22, 0x05, 0x41, 0x07, 0x6b, 0x22, 0x01, 0x20, + 0x01, 0x20, 0x05, 0x4b, 0x1b, 0x21, 0x09, 0x20, 0x04, 0x41, 0x03, 0x6a, 0x41, 0x7c, 0x71, 0x20, + 0x04, 0x6b, 0x21, 0x08, 0x41, 0x00, 0x21, 0x01, 0x03, 0x40, 0x20, 0x01, 0x20, 0x05, 0x4f, 0x0d, + 0x07, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x20, 0x04, 0x6a, 0x2d, 0x00, 0x00, 0x22, + 0x06, 0x41, 0x18, 0x74, 0x41, 0x18, 0x75, 0x22, 0x07, 0x41, 0x00, 0x4e, 0x04, 0x40, 0x20, 0x08, + 0x41, 0x7f, 0x46, 0x0d, 0x03, 0x20, 0x08, 0x20, 0x01, 0x6b, 0x41, 0x03, 0x71, 0x0d, 0x03, 0x03, + 0x40, 0x20, 0x01, 0x20, 0x09, 0x4f, 0x0d, 0x03, 0x20, 0x01, 0x20, 0x04, 0x6a, 0x22, 0x02, 0x41, + 0x04, 0x6a, 0x28, 0x02, 0x00, 0x20, 0x02, 0x28, 0x02, 0x00, 0x72, 0x41, 0x80, 0x81, 0x82, 0x84, + 0x78, 0x71, 0x0d, 0x03, 0x20, 0x01, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x22, 0x01, 0x4d, 0x0d, 0x00, + 0x0b, 0x0c, 0x01, 0x0b, 0x42, 0x80, 0x80, 0x80, 0x80, 0x80, 0x20, 0x21, 0x0a, 0x42, 0x80, 0x80, + 0x80, 0x80, 0x10, 0x21, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x06, 0x41, 0x80, 0x80, 0x40, 0x6b, 0x2d, 0x00, 0x00, 0x41, 0x02, 0x6b, 0x0e, 0x03, 0x00, 0x02, + 0x01, 0x0e, 0x0b, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x22, 0x02, 0x20, 0x05, 0x49, 0x0d, 0x02, 0x42, + 0x00, 0x21, 0x0a, 0x0c, 0x0c, 0x0b, 0x42, 0x00, 0x21, 0x0a, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x22, + 0x02, 0x20, 0x05, 0x4f, 0x0d, 0x0b, 0x20, 0x02, 0x20, 0x04, 0x6a, 0x2d, 0x00, 0x00, 0x21, 0x02, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x06, 0x41, 0xf0, 0x01, 0x6b, 0x0e, 0x05, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0b, 0x20, 0x02, 0x41, 0xbf, 0x01, 0x4b, 0x0d, 0x0c, 0x20, + 0x07, 0x41, 0x0f, 0x6a, 0x41, 0xff, 0x01, 0x71, 0x41, 0x02, 0x4b, 0x0d, 0x0c, 0x20, 0x02, 0x41, + 0x18, 0x74, 0x41, 0x18, 0x75, 0x41, 0x00, 0x4e, 0x0d, 0x0c, 0x0c, 0x02, 0x0b, 0x20, 0x02, 0x41, + 0xf0, 0x00, 0x6a, 0x41, 0xff, 0x01, 0x71, 0x41, 0x30, 0x4f, 0x0d, 0x0b, 0x0c, 0x01, 0x0b, 0x20, + 0x02, 0x41, 0x18, 0x74, 0x41, 0x18, 0x75, 0x41, 0x7f, 0x4a, 0x0d, 0x0a, 0x20, 0x02, 0x41, 0x8f, + 0x01, 0x4b, 0x0d, 0x0a, 0x0b, 0x20, 0x01, 0x41, 0x02, 0x6a, 0x22, 0x02, 0x20, 0x05, 0x4f, 0x0d, + 0x0b, 0x20, 0x02, 0x20, 0x04, 0x6a, 0x2d, 0x00, 0x00, 0x41, 0xc0, 0x01, 0x71, 0x41, 0x80, 0x01, + 0x47, 0x0d, 0x08, 0x42, 0x00, 0x21, 0x0b, 0x20, 0x01, 0x41, 0x03, 0x6a, 0x22, 0x02, 0x20, 0x05, + 0x4f, 0x0d, 0x0c, 0x20, 0x02, 0x20, 0x04, 0x6a, 0x2d, 0x00, 0x00, 0x41, 0xc0, 0x01, 0x71, 0x41, + 0x80, 0x01, 0x46, 0x0d, 0x02, 0x42, 0x80, 0x80, 0x80, 0x80, 0x80, 0xe0, 0x00, 0x0c, 0x0a, 0x0b, + 0x42, 0x00, 0x21, 0x0a, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x22, 0x02, 0x20, 0x05, 0x4f, 0x0d, 0x0a, + 0x20, 0x02, 0x20, 0x04, 0x6a, 0x2d, 0x00, 0x00, 0x21, 0x02, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x06, 0x41, 0xe0, 0x01, 0x47, 0x04, 0x40, 0x20, 0x06, 0x41, 0xed, 0x01, 0x46, 0x0d, 0x01, + 0x20, 0x07, 0x41, 0x1f, 0x6a, 0x41, 0xff, 0x01, 0x71, 0x41, 0x0c, 0x49, 0x0d, 0x02, 0x20, 0x02, + 0x41, 0xbf, 0x01, 0x4b, 0x0d, 0x0c, 0x20, 0x07, 0x41, 0xfe, 0x01, 0x71, 0x41, 0xee, 0x01, 0x47, + 0x0d, 0x0c, 0x20, 0x02, 0x41, 0x18, 0x74, 0x41, 0x18, 0x75, 0x41, 0x00, 0x4e, 0x0d, 0x0c, 0x0c, + 0x03, 0x0b, 0x20, 0x02, 0x41, 0xe0, 0x01, 0x71, 0x41, 0xa0, 0x01, 0x47, 0x0d, 0x0b, 0x0c, 0x02, + 0x0b, 0x20, 0x02, 0x41, 0x18, 0x74, 0x41, 0x18, 0x75, 0x41, 0x7f, 0x4a, 0x0d, 0x0a, 0x20, 0x02, + 0x41, 0xa0, 0x01, 0x4f, 0x0d, 0x0a, 0x0c, 0x01, 0x0b, 0x20, 0x02, 0x41, 0x18, 0x74, 0x41, 0x18, + 0x75, 0x41, 0x7f, 0x4a, 0x0d, 0x09, 0x20, 0x02, 0x41, 0xbf, 0x01, 0x4b, 0x0d, 0x09, 0x0b, 0x42, + 0x00, 0x21, 0x0b, 0x20, 0x01, 0x41, 0x02, 0x6a, 0x22, 0x02, 0x20, 0x05, 0x4f, 0x0d, 0x0b, 0x20, + 0x02, 0x20, 0x04, 0x6a, 0x2d, 0x00, 0x00, 0x41, 0xc0, 0x01, 0x71, 0x41, 0x80, 0x01, 0x47, 0x0d, + 0x07, 0x0c, 0x01, 0x0b, 0x20, 0x02, 0x20, 0x04, 0x6a, 0x2d, 0x00, 0x00, 0x41, 0xc0, 0x01, 0x71, + 0x41, 0x80, 0x01, 0x47, 0x0d, 0x0a, 0x0b, 0x20, 0x02, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, 0x03, + 0x0b, 0x00, 0x0b, 0x20, 0x01, 0x20, 0x05, 0x20, 0x01, 0x20, 0x05, 0x4b, 0x1b, 0x21, 0x02, 0x03, + 0x40, 0x20, 0x01, 0x20, 0x02, 0x46, 0x04, 0x40, 0x20, 0x02, 0x21, 0x01, 0x0c, 0x03, 0x0b, 0x20, + 0x01, 0x20, 0x04, 0x6a, 0x2c, 0x00, 0x00, 0x41, 0x00, 0x48, 0x0d, 0x02, 0x20, 0x01, 0x41, 0x01, + 0x6a, 0x21, 0x01, 0x0c, 0x00, 0x0b, 0x00, 0x0b, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, + 0x00, 0x0b, 0x00, 0x0b, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0c, 0x06, 0x0b, 0x42, 0x80, + 0x80, 0x80, 0x80, 0x80, 0xc0, 0x00, 0x0c, 0x01, 0x0b, 0x42, 0x80, 0x80, 0x80, 0x80, 0x80, 0x20, + 0x0b, 0x21, 0x0a, 0x42, 0x80, 0x80, 0x80, 0x80, 0x10, 0x21, 0x0b, 0x0c, 0x01, 0x0b, 0x42, 0x00, + 0x21, 0x0b, 0x0b, 0x20, 0x01, 0xad, 0x20, 0x0a, 0x20, 0x0b, 0x84, 0x84, 0x21, 0x0a, 0x0b, 0x20, + 0x01, 0x20, 0x05, 0x4f, 0x04, 0x40, 0x20, 0x00, 0x20, 0x0c, 0x37, 0x02, 0x04, 0x20, 0x00, 0x20, + 0x04, 0x36, 0x02, 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x20, 0x0a, 0x37, 0x02, 0x14, 0x20, 0x03, + 0x20, 0x0c, 0x37, 0x02, 0x0c, 0x20, 0x03, 0x20, 0x04, 0x36, 0x02, 0x08, 0x20, 0x03, 0x41, 0x08, + 0x6a, 0x10, 0x18, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x03, 0x41, 0x20, 0x6a, + 0x24, 0x00, 0x0b, 0x42, 0x01, 0x01, 0x7f, 0x20, 0x00, 0x2f, 0x01, 0x04, 0x21, 0x03, 0x20, 0x00, + 0x41, 0x00, 0x3a, 0x00, 0x04, 0x20, 0x03, 0x41, 0x01, 0x71, 0x45, 0x04, 0x40, 0x20, 0x00, 0x28, + 0x02, 0x00, 0x20, 0x01, 0x20, 0x02, 0x10, 0x26, 0x0f, 0x0b, 0x20, 0x01, 0x20, 0x03, 0x41, 0x08, + 0x76, 0x3a, 0x00, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x20, 0x02, + 0x41, 0x01, 0x6b, 0x10, 0x26, 0x0b, 0x48, 0x01, 0x02, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, + 0x02, 0x24, 0x00, 0x20, 0x02, 0x41, 0x00, 0x36, 0x02, 0x0c, 0x02, 0x40, 0x20, 0x01, 0x20, 0x02, + 0x41, 0x0c, 0x6a, 0x41, 0x04, 0x10, 0x26, 0x45, 0x04, 0x40, 0x20, 0x02, 0x28, 0x02, 0x0c, 0x21, + 0x01, 0x0c, 0x01, 0x0b, 0x41, 0x01, 0x21, 0x03, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x04, + 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x4b, + 0x01, 0x02, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x20, 0x02, 0x41, 0x08, + 0x6a, 0x21, 0x03, 0x20, 0x01, 0x41, 0x80, 0x80, 0x01, 0x4b, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x03, + 0x20, 0x01, 0x36, 0x02, 0x04, 0x20, 0x03, 0x41, 0x94, 0x83, 0xc0, 0x00, 0x36, 0x02, 0x00, 0x20, + 0x00, 0x20, 0x02, 0x28, 0x02, 0x08, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x02, 0x28, 0x02, 0x0c, + 0x36, 0x02, 0x04, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x9e, 0x01, 0x01, 0x03, 0x7f, + 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x28, + 0x02, 0x08, 0x22, 0x02, 0x41, 0x04, 0x6a, 0x22, 0x04, 0x20, 0x02, 0x4f, 0x04, 0x40, 0x20, 0x01, + 0x28, 0x02, 0x00, 0x21, 0x01, 0x20, 0x00, 0x20, 0x04, 0x10, 0x2e, 0x20, 0x02, 0x41, 0x3f, 0x4d, + 0x04, 0x40, 0x20, 0x00, 0x20, 0x02, 0x41, 0x02, 0x74, 0x10, 0x2f, 0x0c, 0x03, 0x0b, 0x20, 0x02, + 0x41, 0xff, 0xff, 0x00, 0x4d, 0x04, 0x40, 0x20, 0x03, 0x20, 0x02, 0x41, 0x02, 0x74, 0x41, 0x01, + 0x72, 0x3b, 0x01, 0x0e, 0x20, 0x00, 0x20, 0x03, 0x41, 0x0e, 0x6a, 0x41, 0x02, 0x10, 0x1b, 0x0c, + 0x03, 0x0b, 0x20, 0x02, 0x41, 0xff, 0xff, 0xff, 0xff, 0x03, 0x4b, 0x0d, 0x01, 0x20, 0x02, 0x41, + 0x02, 0x74, 0x41, 0x02, 0x72, 0x20, 0x00, 0x10, 0x30, 0x0c, 0x02, 0x0b, 0x00, 0x0b, 0x20, 0x00, + 0x41, 0x03, 0x10, 0x2f, 0x20, 0x02, 0x20, 0x00, 0x10, 0x30, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x20, + 0x02, 0x10, 0x1b, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x36, 0x02, 0x01, 0x7f, 0x01, + 0x7e, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x20, 0x02, 0x41, 0x08, 0x6a, 0x20, + 0x01, 0x41, 0x00, 0x10, 0x13, 0x20, 0x02, 0x29, 0x03, 0x08, 0x21, 0x03, 0x20, 0x00, 0x41, 0x00, + 0x36, 0x02, 0x08, 0x20, 0x00, 0x20, 0x03, 0x37, 0x02, 0x00, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, + 0x00, 0x0b, 0x26, 0x01, 0x01, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x20, + 0x02, 0x20, 0x01, 0x3a, 0x00, 0x0f, 0x20, 0x00, 0x20, 0x02, 0x41, 0x0f, 0x6a, 0x41, 0x01, 0x10, + 0x1b, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, 0x26, 0x01, 0x01, 0x7f, 0x23, 0x00, 0x41, + 0x10, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x20, 0x02, 0x20, 0x00, 0x36, 0x02, 0x0c, 0x20, 0x01, 0x20, + 0x02, 0x41, 0x0c, 0x6a, 0x41, 0x04, 0x10, 0x1b, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0b, + 0x8f, 0x02, 0x02, 0x03, 0x7f, 0x01, 0x7e, 0x23, 0x00, 0x41, 0x30, 0x6b, 0x22, 0x01, 0x24, 0x00, + 0x20, 0x01, 0x41, 0x08, 0x6a, 0x41, 0x08, 0x41, 0x00, 0x10, 0x13, 0x20, 0x01, 0x28, 0x02, 0x08, + 0x21, 0x02, 0x20, 0x01, 0x28, 0x02, 0x0c, 0x21, 0x03, 0x20, 0x00, 0x41, 0x14, 0x6a, 0x41, 0x02, + 0x3a, 0x00, 0x00, 0x20, 0x00, 0x42, 0x08, 0x37, 0x02, 0x08, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, + 0x04, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, 0x02, 0x42, 0xe1, 0xc6, 0x8d, 0xfb, 0xd6, + 0xce, 0x9b, 0xba, 0xf3, 0x00, 0x37, 0x00, 0x00, 0x20, 0x01, 0x41, 0x09, 0x41, 0x00, 0x10, 0x13, + 0x20, 0x01, 0x41, 0x00, 0x36, 0x02, 0x28, 0x20, 0x01, 0x20, 0x01, 0x29, 0x03, 0x00, 0x37, 0x03, + 0x20, 0x20, 0x01, 0x41, 0x20, 0x6a, 0x41, 0xc4, 0x82, 0xc0, 0x00, 0x41, 0x08, 0x10, 0x1b, 0x20, + 0x01, 0x28, 0x02, 0x28, 0x22, 0x02, 0x20, 0x01, 0x28, 0x02, 0x24, 0x46, 0x04, 0x40, 0x20, 0x01, + 0x41, 0x20, 0x6a, 0x41, 0x01, 0x10, 0x1a, 0x20, 0x01, 0x28, 0x02, 0x28, 0x21, 0x02, 0x0b, 0x20, + 0x01, 0x28, 0x02, 0x20, 0x20, 0x02, 0x6a, 0x41, 0x24, 0x3a, 0x00, 0x00, 0x20, 0x02, 0x20, 0x02, + 0x41, 0x01, 0x6a, 0x22, 0x03, 0x4b, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x01, 0x41, 0x18, 0x6a, 0x20, + 0x03, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, 0x01, 0x29, 0x03, 0x20, 0x22, 0x04, 0x37, 0x03, 0x10, + 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x18, 0x20, 0x00, 0x41, 0x1c, 0x6a, 0x20, 0x04, 0x37, 0x02, + 0x00, 0x20, 0x00, 0x41, 0x24, 0x6a, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x28, 0x6a, + 0x41, 0x09, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x34, 0x6a, 0x42, 0x00, 0x37, 0x02, 0x00, 0x20, + 0x00, 0x41, 0x2c, 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, 0x01, 0x41, 0x30, 0x6a, 0x24, 0x00, + 0x0b, 0x2e, 0x00, 0x20, 0x00, 0x10, 0x18, 0x20, 0x00, 0x41, 0x14, 0x6a, 0x2d, 0x00, 0x00, 0x41, + 0x02, 0x47, 0x04, 0x40, 0x20, 0x00, 0x41, 0x10, 0x6a, 0x28, 0x02, 0x00, 0x41, 0x08, 0x10, 0x0a, + 0x0b, 0x20, 0x00, 0x41, 0x1c, 0x6a, 0x10, 0x18, 0x20, 0x00, 0x41, 0x30, 0x6a, 0x10, 0x33, 0x0b, + 0x9b, 0x03, 0x01, 0x06, 0x7f, 0x23, 0x00, 0x41, 0x30, 0x6b, 0x22, 0x01, 0x24, 0x00, 0x20, 0x00, + 0x28, 0x02, 0x04, 0x21, 0x03, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x04, 0x02, 0x40, 0x20, 0x03, + 0x45, 0x0d, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x21, 0x02, 0x03, 0x40, 0x20, 0x02, 0x04, 0x40, + 0x20, 0x01, 0x41, 0x00, 0x36, 0x02, 0x28, 0x20, 0x01, 0x20, 0x03, 0x36, 0x02, 0x24, 0x20, 0x01, + 0x20, 0x02, 0x36, 0x02, 0x20, 0x20, 0x01, 0x41, 0x18, 0x6a, 0x20, 0x01, 0x41, 0x20, 0x6a, 0x10, + 0x0d, 0x20, 0x01, 0x28, 0x02, 0x1c, 0x21, 0x03, 0x20, 0x01, 0x28, 0x02, 0x18, 0x21, 0x02, 0x0c, + 0x01, 0x0b, 0x0b, 0x20, 0x00, 0x28, 0x02, 0x08, 0x21, 0x06, 0x03, 0x40, 0x20, 0x06, 0x45, 0x04, + 0x40, 0x41, 0x00, 0x21, 0x02, 0x03, 0x40, 0x20, 0x01, 0x41, 0x20, 0x6a, 0x20, 0x02, 0x20, 0x03, + 0x10, 0x09, 0x20, 0x01, 0x28, 0x02, 0x24, 0x22, 0x03, 0x45, 0x0d, 0x03, 0x20, 0x01, 0x28, 0x02, + 0x20, 0x21, 0x02, 0x0c, 0x00, 0x0b, 0x00, 0x0b, 0x20, 0x06, 0x41, 0x01, 0x6b, 0x21, 0x06, 0x41, + 0x00, 0x21, 0x04, 0x20, 0x05, 0x21, 0x00, 0x20, 0x03, 0x21, 0x02, 0x03, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x02, 0x2f, 0x01, 0xe2, 0x01, 0x20, 0x00, 0x4b, 0x04, 0x40, 0x20, 0x00, 0x41, 0x01, + 0x6a, 0x21, 0x05, 0x20, 0x04, 0x45, 0x04, 0x40, 0x20, 0x02, 0x21, 0x03, 0x0c, 0x02, 0x0b, 0x20, + 0x01, 0x20, 0x05, 0x36, 0x02, 0x28, 0x20, 0x01, 0x20, 0x02, 0x36, 0x02, 0x24, 0x20, 0x01, 0x20, + 0x04, 0x36, 0x02, 0x20, 0x20, 0x01, 0x41, 0x10, 0x6a, 0x20, 0x01, 0x41, 0x20, 0x6a, 0x10, 0x0d, + 0x20, 0x01, 0x28, 0x02, 0x14, 0x21, 0x03, 0x20, 0x01, 0x28, 0x02, 0x10, 0x21, 0x04, 0x03, 0x40, + 0x20, 0x04, 0x45, 0x04, 0x40, 0x41, 0x00, 0x21, 0x05, 0x0c, 0x03, 0x0b, 0x20, 0x01, 0x41, 0x00, + 0x36, 0x02, 0x28, 0x20, 0x01, 0x20, 0x03, 0x36, 0x02, 0x24, 0x20, 0x01, 0x20, 0x04, 0x36, 0x02, + 0x20, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x20, 0x01, 0x41, 0x20, 0x6a, 0x10, 0x0d, 0x20, 0x01, 0x28, + 0x02, 0x0c, 0x21, 0x03, 0x20, 0x01, 0x28, 0x02, 0x08, 0x21, 0x04, 0x0c, 0x00, 0x0b, 0x00, 0x0b, + 0x20, 0x01, 0x41, 0x20, 0x6a, 0x20, 0x04, 0x20, 0x02, 0x10, 0x09, 0x20, 0x01, 0x28, 0x02, 0x24, + 0x22, 0x03, 0x0d, 0x01, 0x41, 0x00, 0x21, 0x03, 0x0b, 0x20, 0x02, 0x20, 0x00, 0x41, 0x0c, 0x6c, + 0x6a, 0x41, 0x04, 0x6a, 0x10, 0x18, 0x20, 0x02, 0x20, 0x00, 0x41, 0x03, 0x74, 0x6a, 0x41, 0x88, + 0x01, 0x6a, 0x28, 0x02, 0x00, 0x41, 0x08, 0x10, 0x0a, 0x0c, 0x02, 0x0b, 0x20, 0x01, 0x28, 0x02, + 0x28, 0x21, 0x00, 0x20, 0x01, 0x28, 0x02, 0x20, 0x21, 0x04, 0x20, 0x03, 0x21, 0x02, 0x0c, 0x00, + 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x20, 0x01, 0x41, 0x30, 0x6a, 0x24, 0x00, 0x0b, 0x48, 0x01, 0x01, + 0x7f, 0x02, 0x40, 0x02, 0x40, 0x20, 0x02, 0x20, 0x02, 0x41, 0x01, 0x6b, 0x22, 0x02, 0x49, 0x0d, + 0x00, 0x20, 0x01, 0x28, 0x02, 0x08, 0x1a, 0x20, 0x01, 0x28, 0x02, 0x10, 0x1a, 0x20, 0x01, 0x29, + 0x03, 0x00, 0x1a, 0x20, 0x01, 0x28, 0x02, 0x14, 0x22, 0x03, 0x20, 0x02, 0x20, 0x03, 0x6a, 0x4b, + 0x0d, 0x00, 0x20, 0x01, 0x41, 0x00, 0x36, 0x02, 0x14, 0x0c, 0x01, 0x0b, 0x00, 0x0b, 0x20, 0x00, + 0x41, 0x00, 0x36, 0x02, 0x00, 0x0b, 0x30, 0x00, 0x20, 0x00, 0x20, 0x02, 0x20, 0x03, 0x20, 0x01, + 0x20, 0x01, 0x20, 0x03, 0x4b, 0x1b, 0x10, 0x4f, 0x22, 0x00, 0x45, 0x04, 0x40, 0x41, 0x7f, 0x20, + 0x01, 0x20, 0x03, 0x47, 0x20, 0x01, 0x20, 0x03, 0x49, 0x1b, 0x0f, 0x0b, 0x41, 0x7f, 0x41, 0x01, + 0x20, 0x00, 0x41, 0x00, 0x48, 0x1b, 0x0b, 0x04, 0x00, 0x41, 0x00, 0x0b, 0x8b, 0x04, 0x01, 0x05, + 0x7f, 0x23, 0x00, 0x41, 0xa0, 0x01, 0x6b, 0x22, 0x00, 0x24, 0x00, 0x20, 0x00, 0x41, 0x18, 0x6a, + 0x10, 0x31, 0x20, 0x00, 0x41, 0xd8, 0x00, 0x6a, 0x41, 0x00, 0x10, 0x24, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x00, 0x2d, 0x00, 0x58, 0x45, 0x04, 0x40, 0x20, 0x00, 0x41, 0xf8, 0x00, 0x6a, 0x20, 0x00, + 0x41, 0xe4, 0x00, 0x6a, 0x29, 0x02, 0x00, 0x37, 0x03, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x02, + 0x5c, 0x37, 0x03, 0x70, 0x20, 0x00, 0x41, 0x88, 0x01, 0x6a, 0x20, 0x00, 0x41, 0xfc, 0x00, 0x6a, + 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x02, 0x74, 0x37, 0x03, 0x80, + 0x01, 0x20, 0x00, 0x41, 0x18, 0x6a, 0x10, 0x38, 0x45, 0x04, 0x40, 0x20, 0x00, 0x41, 0x10, 0x6a, + 0x21, 0x03, 0x20, 0x00, 0x41, 0x24, 0x6a, 0x22, 0x02, 0x22, 0x01, 0x28, 0x02, 0x00, 0x04, 0x40, + 0x00, 0x0b, 0x20, 0x01, 0x41, 0x7f, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x01, 0x36, 0x02, 0x04, + 0x20, 0x03, 0x20, 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x28, 0x02, 0x14, 0x21, + 0x01, 0x20, 0x00, 0x28, 0x02, 0x10, 0x41, 0x01, 0x41, 0x00, 0x10, 0x39, 0x20, 0x01, 0x28, 0x02, + 0x00, 0x22, 0x03, 0x41, 0x01, 0x6a, 0x22, 0x04, 0x20, 0x03, 0x48, 0x0d, 0x03, 0x20, 0x01, 0x20, + 0x04, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x21, 0x01, 0x20, 0x02, 0x28, 0x02, 0x00, + 0x04, 0x40, 0x00, 0x0b, 0x20, 0x02, 0x41, 0x7f, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, 0x02, 0x36, + 0x02, 0x04, 0x20, 0x01, 0x20, 0x02, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x28, 0x02, + 0x0c, 0x21, 0x02, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x01, 0x2d, 0x00, 0x04, 0x41, 0x02, 0x47, + 0x04, 0x40, 0x20, 0x01, 0x41, 0x01, 0x3a, 0x00, 0x04, 0x0b, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, + 0x01, 0x41, 0x01, 0x6a, 0x22, 0x03, 0x20, 0x01, 0x48, 0x0d, 0x03, 0x20, 0x02, 0x20, 0x03, 0x36, + 0x02, 0x00, 0x0b, 0x20, 0x00, 0x41, 0x90, 0x01, 0x6a, 0x41, 0xcc, 0x82, 0xc0, 0x00, 0x41, 0x05, + 0x10, 0x1c, 0x20, 0x00, 0x41, 0x18, 0x6a, 0x20, 0x00, 0x41, 0x90, 0x01, 0x6a, 0x41, 0x7f, 0x10, + 0x3a, 0x20, 0x00, 0x41, 0x90, 0x01, 0x6a, 0x41, 0xd1, 0x82, 0xc0, 0x00, 0x41, 0x03, 0x10, 0x1c, + 0x20, 0x00, 0x41, 0x18, 0x6a, 0x20, 0x00, 0x41, 0x90, 0x01, 0x6a, 0x41, 0x00, 0x10, 0x3a, 0x20, + 0x00, 0x41, 0x90, 0x01, 0x6a, 0x41, 0xd4, 0x82, 0xc0, 0x00, 0x41, 0x07, 0x10, 0x1c, 0x20, 0x00, + 0x41, 0x18, 0x6a, 0x20, 0x00, 0x41, 0x90, 0x01, 0x6a, 0x41, 0x7f, 0x10, 0x3a, 0x20, 0x00, 0x41, + 0x90, 0x01, 0x6a, 0x41, 0xdb, 0x82, 0xc0, 0x00, 0x41, 0x05, 0x10, 0x1c, 0x20, 0x00, 0x41, 0x18, + 0x6a, 0x20, 0x00, 0x41, 0x90, 0x01, 0x6a, 0x41, 0x00, 0x10, 0x3a, 0x20, 0x00, 0x41, 0x18, 0x6a, + 0x10, 0x3b, 0x20, 0x00, 0x41, 0x80, 0x01, 0x6a, 0x10, 0x18, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x41, + 0xf0, 0x00, 0x6a, 0x41, 0xf5, 0x82, 0xc0, 0x00, 0x41, 0x14, 0x10, 0x1c, 0x20, 0x00, 0x41, 0xf0, + 0x00, 0x6a, 0x10, 0x3c, 0x20, 0x00, 0x41, 0xf0, 0x00, 0x6a, 0x10, 0x18, 0x0b, 0x20, 0x00, 0x41, + 0x18, 0x6a, 0x10, 0x18, 0x20, 0x00, 0x41, 0x2c, 0x6a, 0x2d, 0x00, 0x00, 0x41, 0x02, 0x47, 0x04, + 0x40, 0x20, 0x00, 0x41, 0x28, 0x6a, 0x28, 0x02, 0x00, 0x41, 0x08, 0x10, 0x0a, 0x0b, 0x20, 0x00, + 0x41, 0x34, 0x6a, 0x10, 0x18, 0x20, 0x00, 0x41, 0xc8, 0x00, 0x6a, 0x10, 0x33, 0x20, 0x00, 0x41, + 0xa0, 0x01, 0x6a, 0x24, 0x00, 0x0f, 0x0b, 0x00, 0x0b, 0x2c, 0x00, 0x20, 0x00, 0x10, 0x48, 0x20, + 0x00, 0x41, 0x10, 0x6a, 0x22, 0x00, 0x2d, 0x00, 0x04, 0x41, 0x02, 0x46, 0x04, 0x40, 0x00, 0x0b, + 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x00, 0x41, 0x04, 0x6a, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, + 0x00, 0x41, 0x01, 0x46, 0x1b, 0x0b, 0x37, 0x00, 0x20, 0x00, 0x2d, 0x00, 0x04, 0x41, 0x02, 0x46, + 0x04, 0x40, 0x20, 0x01, 0x20, 0x02, 0x10, 0x43, 0x21, 0x01, 0x20, 0x00, 0x41, 0x00, 0x3a, 0x00, + 0x04, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x00, 0x0f, 0x0b, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, + 0x00, 0x20, 0x02, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x00, 0x0b, 0xbe, 0x02, + 0x01, 0x06, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x06, 0x24, 0x00, 0x20, 0x00, 0x10, 0x38, + 0x22, 0x03, 0x45, 0x04, 0x40, 0x00, 0x0b, 0x02, 0x40, 0x20, 0x03, 0x28, 0x02, 0x00, 0x41, 0x7f, + 0x46, 0x0d, 0x00, 0x20, 0x06, 0x20, 0x01, 0x10, 0x2d, 0x02, 0x40, 0x20, 0x00, 0x41, 0x18, 0x6a, + 0x22, 0x03, 0x20, 0x06, 0x28, 0x02, 0x00, 0x22, 0x07, 0x20, 0x06, 0x28, 0x02, 0x08, 0x22, 0x08, + 0x10, 0x44, 0x22, 0x04, 0x04, 0x40, 0x20, 0x04, 0x41, 0x01, 0x3a, 0x00, 0x04, 0x20, 0x04, 0x28, + 0x02, 0x00, 0x22, 0x05, 0x28, 0x02, 0x00, 0x21, 0x04, 0x20, 0x05, 0x41, 0x00, 0x36, 0x02, 0x00, + 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x20, 0x07, 0x20, 0x08, 0x10, 0x45, 0x20, 0x03, 0x20, 0x07, 0x20, + 0x08, 0x10, 0x44, 0x22, 0x04, 0x45, 0x04, 0x40, 0x41, 0x00, 0x21, 0x04, 0x0c, 0x01, 0x0b, 0x20, + 0x04, 0x41, 0x01, 0x3a, 0x00, 0x04, 0x20, 0x04, 0x28, 0x02, 0x00, 0x22, 0x05, 0x28, 0x02, 0x00, + 0x21, 0x04, 0x20, 0x05, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0b, 0x02, 0x40, 0x20, 0x03, 0x20, 0x07, + 0x20, 0x08, 0x10, 0x44, 0x22, 0x05, 0x04, 0x40, 0x20, 0x05, 0x28, 0x02, 0x00, 0x22, 0x03, 0x20, + 0x02, 0x36, 0x02, 0x04, 0x20, 0x03, 0x41, 0x01, 0x36, 0x02, 0x00, 0x20, 0x05, 0x41, 0x01, 0x3a, + 0x00, 0x04, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x20, 0x07, 0x20, 0x08, 0x41, 0x01, 0x20, 0x02, 0x41, + 0x01, 0x10, 0x47, 0x0b, 0x20, 0x04, 0x41, 0x01, 0x47, 0x04, 0x40, 0x20, 0x00, 0x10, 0x48, 0x20, + 0x00, 0x41, 0x14, 0x6a, 0x2d, 0x00, 0x00, 0x41, 0x02, 0x46, 0x0d, 0x01, 0x20, 0x00, 0x41, 0x01, + 0x3a, 0x00, 0x14, 0x20, 0x00, 0x41, 0x10, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x00, 0x41, 0x04, 0x6a, + 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x01, 0x46, 0x1b, 0x22, 0x00, 0x45, 0x04, 0x40, + 0x00, 0x0b, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x02, 0x41, 0x01, 0x6a, 0x22, 0x03, 0x20, 0x02, + 0x49, 0x0d, 0x01, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x06, 0x10, 0x18, 0x20, + 0x01, 0x10, 0x18, 0x20, 0x06, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0f, 0x0b, 0x00, 0x0b, 0xa8, 0x09, + 0x01, 0x08, 0x7f, 0x23, 0x00, 0x41, 0x80, 0x01, 0x6b, 0x22, 0x01, 0x24, 0x00, 0x20, 0x01, 0x41, + 0xc8, 0x00, 0x6a, 0x20, 0x00, 0x41, 0x0c, 0x6a, 0x22, 0x02, 0x10, 0x46, 0x20, 0x01, 0x28, 0x02, + 0x4c, 0x22, 0x03, 0x28, 0x02, 0x00, 0x22, 0x05, 0x41, 0x01, 0x6b, 0x22, 0x04, 0x20, 0x05, 0x4e, + 0x21, 0x05, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x28, 0x02, 0x48, 0x2d, 0x00, 0x04, 0x22, 0x06, + 0x41, 0x02, 0x47, 0x04, 0x40, 0x20, 0x05, 0x0d, 0x02, 0x20, 0x03, 0x20, 0x04, 0x36, 0x02, 0x00, + 0x20, 0x06, 0x45, 0x0d, 0x01, 0x20, 0x01, 0x41, 0x40, 0x6b, 0x21, 0x04, 0x20, 0x02, 0x28, 0x02, + 0x00, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x02, 0x41, 0x7f, 0x36, 0x02, 0x00, 0x20, 0x04, 0x20, 0x02, + 0x36, 0x02, 0x04, 0x20, 0x04, 0x20, 0x02, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x01, 0x28, + 0x02, 0x44, 0x21, 0x04, 0x20, 0x01, 0x28, 0x02, 0x40, 0x22, 0x03, 0x2d, 0x00, 0x04, 0x41, 0x02, + 0x46, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x03, 0x28, 0x02, 0x00, 0x22, 0x03, 0x41, 0x04, 0x6a, 0x41, + 0x00, 0x20, 0x03, 0x28, 0x02, 0x00, 0x41, 0x01, 0x46, 0x1b, 0x22, 0x03, 0x04, 0x40, 0x20, 0x00, + 0x28, 0x02, 0x00, 0x20, 0x00, 0x28, 0x02, 0x08, 0x20, 0x03, 0x10, 0x42, 0x0b, 0x20, 0x04, 0x28, + 0x02, 0x00, 0x22, 0x03, 0x41, 0x01, 0x6a, 0x22, 0x05, 0x20, 0x03, 0x48, 0x0d, 0x02, 0x20, 0x04, + 0x20, 0x05, 0x36, 0x02, 0x00, 0x20, 0x01, 0x41, 0x38, 0x6a, 0x21, 0x04, 0x20, 0x02, 0x28, 0x02, + 0x00, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x02, 0x41, 0x7f, 0x36, 0x02, 0x00, 0x20, 0x04, 0x20, 0x02, + 0x36, 0x02, 0x04, 0x20, 0x04, 0x20, 0x02, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x01, 0x28, + 0x02, 0x3c, 0x21, 0x02, 0x20, 0x01, 0x28, 0x02, 0x38, 0x22, 0x04, 0x2d, 0x00, 0x04, 0x41, 0x02, + 0x47, 0x04, 0x40, 0x20, 0x04, 0x41, 0x00, 0x3a, 0x00, 0x04, 0x0b, 0x20, 0x02, 0x28, 0x02, 0x00, + 0x22, 0x03, 0x41, 0x01, 0x6a, 0x22, 0x04, 0x20, 0x03, 0x48, 0x0d, 0x02, 0x20, 0x02, 0x20, 0x04, + 0x36, 0x02, 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x0d, 0x01, 0x20, 0x03, 0x20, 0x04, 0x36, 0x02, + 0x00, 0x0b, 0x41, 0x00, 0x21, 0x05, 0x02, 0x40, 0x20, 0x00, 0x41, 0x2c, 0x6a, 0x28, 0x02, 0x00, + 0x41, 0x01, 0x6a, 0x22, 0x02, 0x41, 0x00, 0x4a, 0x04, 0x40, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, + 0x2c, 0x20, 0x00, 0x41, 0x34, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x03, 0x45, 0x04, 0x40, 0x41, 0x00, + 0x21, 0x03, 0x41, 0x00, 0x21, 0x02, 0x0c, 0x02, 0x0b, 0x20, 0x03, 0x21, 0x05, 0x20, 0x00, 0x41, + 0x30, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x02, 0x21, 0x04, 0x03, 0x40, 0x20, 0x03, 0x2f, 0x01, 0xe2, + 0x01, 0x21, 0x06, 0x20, 0x04, 0x45, 0x04, 0x40, 0x20, 0x02, 0x0d, 0x04, 0x20, 0x00, 0x41, 0x38, + 0x6a, 0x28, 0x02, 0x00, 0x21, 0x02, 0x0c, 0x03, 0x0b, 0x20, 0x02, 0x45, 0x0d, 0x03, 0x20, 0x01, + 0x41, 0x00, 0x36, 0x02, 0x58, 0x20, 0x01, 0x20, 0x05, 0x36, 0x02, 0x54, 0x20, 0x01, 0x20, 0x04, + 0x36, 0x02, 0x50, 0x20, 0x01, 0x41, 0x30, 0x6a, 0x20, 0x01, 0x41, 0xd0, 0x00, 0x6a, 0x10, 0x0d, + 0x20, 0x01, 0x28, 0x02, 0x34, 0x21, 0x05, 0x20, 0x01, 0x28, 0x02, 0x30, 0x21, 0x04, 0x20, 0x01, + 0x20, 0x06, 0x36, 0x02, 0x58, 0x20, 0x01, 0x20, 0x03, 0x36, 0x02, 0x54, 0x20, 0x01, 0x20, 0x02, + 0x36, 0x02, 0x50, 0x20, 0x01, 0x41, 0x28, 0x6a, 0x20, 0x01, 0x41, 0xd0, 0x00, 0x6a, 0x10, 0x0d, + 0x20, 0x01, 0x28, 0x02, 0x2c, 0x21, 0x03, 0x20, 0x01, 0x28, 0x02, 0x28, 0x21, 0x02, 0x0c, 0x00, + 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x20, 0x00, 0x41, 0x18, 0x6a, 0x21, 0x08, 0x20, 0x01, 0x41, 0xe4, + 0x00, 0x6a, 0x20, 0x06, 0x36, 0x02, 0x00, 0x20, 0x01, 0x41, 0xe0, 0x00, 0x6a, 0x20, 0x03, 0x36, + 0x02, 0x00, 0x20, 0x01, 0x42, 0x00, 0x37, 0x03, 0x58, 0x20, 0x01, 0x20, 0x05, 0x36, 0x02, 0x54, + 0x20, 0x01, 0x20, 0x04, 0x36, 0x02, 0x50, 0x03, 0x40, 0x02, 0x40, 0x20, 0x02, 0x04, 0x40, 0x20, + 0x01, 0x20, 0x02, 0x41, 0x01, 0x6b, 0x36, 0x02, 0x68, 0x20, 0x01, 0x41, 0xd0, 0x00, 0x6a, 0x41, + 0x00, 0x20, 0x01, 0x28, 0x02, 0x54, 0x1b, 0x22, 0x07, 0x28, 0x02, 0x00, 0x21, 0x03, 0x20, 0x07, + 0x28, 0x02, 0x08, 0x21, 0x05, 0x20, 0x07, 0x28, 0x02, 0x04, 0x21, 0x02, 0x03, 0x40, 0x02, 0x40, + 0x20, 0x05, 0x20, 0x02, 0x2f, 0x01, 0xe2, 0x01, 0x49, 0x0d, 0x00, 0x20, 0x02, 0x28, 0x02, 0x00, + 0x22, 0x04, 0x45, 0x0d, 0x00, 0x20, 0x03, 0x20, 0x03, 0x41, 0x01, 0x6a, 0x22, 0x03, 0x4b, 0x0d, + 0x05, 0x20, 0x02, 0x2f, 0x01, 0xe0, 0x01, 0x21, 0x05, 0x20, 0x04, 0x21, 0x02, 0x0c, 0x01, 0x0b, + 0x0b, 0x20, 0x05, 0x41, 0x01, 0x6a, 0x21, 0x06, 0x20, 0x03, 0x45, 0x04, 0x40, 0x20, 0x02, 0x21, + 0x04, 0x0c, 0x02, 0x0b, 0x20, 0x01, 0x20, 0x06, 0x36, 0x02, 0x78, 0x20, 0x01, 0x20, 0x02, 0x36, + 0x02, 0x74, 0x20, 0x01, 0x20, 0x03, 0x36, 0x02, 0x70, 0x20, 0x01, 0x41, 0x20, 0x6a, 0x20, 0x01, + 0x41, 0xf0, 0x00, 0x6a, 0x10, 0x0d, 0x20, 0x01, 0x28, 0x02, 0x24, 0x21, 0x04, 0x20, 0x01, 0x28, + 0x02, 0x20, 0x21, 0x03, 0x03, 0x40, 0x20, 0x03, 0x45, 0x04, 0x40, 0x41, 0x00, 0x21, 0x06, 0x0c, + 0x03, 0x0b, 0x20, 0x01, 0x41, 0x00, 0x36, 0x02, 0x78, 0x20, 0x01, 0x20, 0x04, 0x36, 0x02, 0x74, + 0x20, 0x01, 0x20, 0x03, 0x36, 0x02, 0x70, 0x20, 0x01, 0x41, 0x18, 0x6a, 0x20, 0x01, 0x41, 0xf0, + 0x00, 0x6a, 0x10, 0x0d, 0x20, 0x01, 0x28, 0x02, 0x1c, 0x21, 0x04, 0x20, 0x01, 0x28, 0x02, 0x18, + 0x21, 0x03, 0x0c, 0x00, 0x0b, 0x00, 0x0b, 0x20, 0x00, 0x28, 0x02, 0x2c, 0x22, 0x02, 0x41, 0x01, + 0x6b, 0x22, 0x04, 0x20, 0x02, 0x4e, 0x0d, 0x02, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, 0x2c, 0x20, + 0x01, 0x41, 0x80, 0x01, 0x6a, 0x24, 0x00, 0x0f, 0x0b, 0x20, 0x07, 0x20, 0x04, 0x36, 0x02, 0x04, + 0x20, 0x07, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, 0x07, 0x20, 0x06, 0x36, 0x02, 0x08, 0x02, 0x40, + 0x20, 0x02, 0x20, 0x05, 0x41, 0x03, 0x74, 0x6a, 0x22, 0x04, 0x41, 0x8c, 0x01, 0x6a, 0x2d, 0x00, + 0x00, 0x45, 0x0d, 0x00, 0x20, 0x04, 0x41, 0x88, 0x01, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x06, 0x28, + 0x02, 0x00, 0x41, 0x01, 0x47, 0x0d, 0x00, 0x20, 0x01, 0x41, 0x10, 0x6a, 0x21, 0x03, 0x20, 0x08, + 0x22, 0x04, 0x28, 0x02, 0x00, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x04, 0x41, 0x7f, 0x36, 0x02, 0x00, + 0x20, 0x03, 0x20, 0x04, 0x36, 0x02, 0x04, 0x20, 0x03, 0x20, 0x04, 0x41, 0x04, 0x6a, 0x36, 0x02, + 0x00, 0x20, 0x01, 0x28, 0x02, 0x14, 0x21, 0x03, 0x20, 0x01, 0x28, 0x02, 0x10, 0x20, 0x02, 0x20, + 0x05, 0x41, 0x0c, 0x6c, 0x6a, 0x22, 0x02, 0x41, 0x04, 0x6a, 0x28, 0x02, 0x00, 0x20, 0x02, 0x41, + 0x0c, 0x6a, 0x28, 0x02, 0x00, 0x10, 0x1b, 0x20, 0x03, 0x28, 0x02, 0x00, 0x22, 0x02, 0x41, 0x01, + 0x6a, 0x22, 0x05, 0x20, 0x02, 0x48, 0x0d, 0x02, 0x20, 0x03, 0x20, 0x05, 0x36, 0x02, 0x00, 0x20, + 0x01, 0x41, 0x08, 0x6a, 0x20, 0x04, 0x10, 0x46, 0x20, 0x01, 0x28, 0x02, 0x0c, 0x21, 0x02, 0x20, + 0x01, 0x28, 0x02, 0x08, 0x22, 0x03, 0x28, 0x02, 0x00, 0x20, 0x03, 0x28, 0x02, 0x08, 0x20, 0x06, + 0x41, 0x04, 0x6a, 0x10, 0x42, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x03, 0x41, 0x01, 0x6b, 0x22, + 0x05, 0x20, 0x03, 0x4e, 0x0d, 0x02, 0x20, 0x02, 0x20, 0x05, 0x36, 0x02, 0x00, 0x20, 0x04, 0x28, + 0x02, 0x00, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x08, 0x41, 0x7f, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, + 0x08, 0x36, 0x02, 0x04, 0x20, 0x01, 0x20, 0x08, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x01, + 0x28, 0x02, 0x04, 0x21, 0x02, 0x20, 0x00, 0x28, 0x02, 0x28, 0x22, 0x04, 0x20, 0x01, 0x28, 0x02, + 0x00, 0x22, 0x03, 0x28, 0x02, 0x08, 0x4d, 0x04, 0x40, 0x20, 0x03, 0x20, 0x04, 0x36, 0x02, 0x08, + 0x0b, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x04, 0x41, 0x01, 0x6a, 0x22, 0x03, 0x20, 0x04, 0x48, + 0x0d, 0x02, 0x20, 0x02, 0x20, 0x03, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x01, 0x28, 0x02, 0x68, 0x21, + 0x02, 0x0c, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x2a, 0x01, 0x01, 0x7f, 0x23, 0x00, 0x41, 0x10, + 0x6b, 0x22, 0x01, 0x24, 0x00, 0x20, 0x01, 0x20, 0x00, 0x10, 0x2d, 0x20, 0x01, 0x28, 0x02, 0x00, + 0x20, 0x01, 0x28, 0x02, 0x08, 0x10, 0x05, 0x20, 0x01, 0x10, 0x18, 0x20, 0x01, 0x41, 0x10, 0x6a, + 0x24, 0x00, 0x0b, 0xd0, 0x17, 0x02, 0x08, 0x7f, 0x02, 0x7e, 0x23, 0x00, 0x41, 0xf0, 0x02, 0x6b, + 0x22, 0x00, 0x24, 0x00, 0x20, 0x00, 0x41, 0xd8, 0x00, 0x6a, 0x10, 0x31, 0x20, 0x00, 0x41, 0xa0, + 0x02, 0x6a, 0x41, 0x01, 0x10, 0x24, 0x02, 0x40, 0x02, 0x7f, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, + 0xa0, 0x02, 0x41, 0x01, 0x47, 0x04, 0x40, 0x20, 0x00, 0x41, 0xa0, 0x01, 0x6a, 0x20, 0x00, 0x41, + 0xb0, 0x02, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x02, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x41, + 0xa8, 0x02, 0x6a, 0x29, 0x03, 0x00, 0x22, 0x08, 0x37, 0x03, 0x98, 0x01, 0x20, 0x00, 0x28, 0x02, + 0xa4, 0x02, 0x21, 0x01, 0x20, 0x00, 0x41, 0xb0, 0x01, 0x6a, 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, + 0x00, 0x20, 0x08, 0x37, 0x03, 0xa8, 0x01, 0x20, 0x01, 0x41, 0xa8, 0x88, 0xdb, 0xb6, 0x7a, 0x46, + 0x0d, 0x01, 0x20, 0x01, 0x41, 0xc4, 0xa5, 0x8a, 0x98, 0x7e, 0x46, 0x0d, 0x03, 0x20, 0x01, 0x41, + 0xfc, 0xcc, 0xe4, 0xc8, 0x02, 0x46, 0x0d, 0x02, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0xa0, 0x02, + 0x20, 0x00, 0x41, 0x38, 0x6a, 0x41, 0x04, 0x72, 0x20, 0x00, 0x41, 0xa0, 0x02, 0x6a, 0x41, 0x04, + 0x10, 0x1c, 0x41, 0x00, 0x21, 0x02, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x38, 0x20, 0x00, 0x41, + 0xa8, 0x01, 0x6a, 0x10, 0x18, 0x0c, 0x0b, 0x0b, 0x20, 0x00, 0x41, 0x02, 0x36, 0x02, 0x38, 0x41, + 0x02, 0x0c, 0x09, 0x0b, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0xb0, 0x01, 0x36, 0x02, 0xbc, 0x01, + 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0xa8, 0x01, 0x36, 0x02, 0xb8, 0x01, 0x20, 0x00, 0x41, 0xe0, + 0x02, 0x6a, 0x20, 0x00, 0x41, 0xb8, 0x01, 0x6a, 0x10, 0x29, 0x20, 0x00, 0x28, 0x02, 0xe0, 0x02, + 0x45, 0x0d, 0x02, 0x20, 0x00, 0x41, 0xc8, 0x01, 0x6a, 0x20, 0x00, 0x41, 0xe8, 0x02, 0x6a, 0x22, + 0x01, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0xe0, 0x02, 0x37, + 0x03, 0xc0, 0x01, 0x20, 0x00, 0x41, 0xe0, 0x02, 0x6a, 0x20, 0x00, 0x41, 0xb8, 0x01, 0x6a, 0x10, + 0x29, 0x20, 0x00, 0x28, 0x02, 0xe0, 0x02, 0x45, 0x0d, 0x03, 0x20, 0x00, 0x41, 0xd8, 0x01, 0x6a, + 0x20, 0x01, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0xe0, 0x02, + 0x37, 0x03, 0xd0, 0x01, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x20, 0x00, 0x41, 0xb8, 0x01, 0x6a, 0x10, + 0x2b, 0x20, 0x00, 0x28, 0x02, 0x08, 0x45, 0x04, 0x40, 0x20, 0x00, 0x28, 0x02, 0x0c, 0x21, 0x01, + 0x20, 0x00, 0x41, 0x88, 0x02, 0x6a, 0x20, 0x00, 0x41, 0xc8, 0x01, 0x6a, 0x28, 0x02, 0x00, 0x36, + 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0xc0, 0x01, 0x37, 0x03, 0x80, 0x02, 0x20, 0x00, + 0x41, 0xa8, 0x02, 0x6a, 0x20, 0x00, 0x41, 0xd8, 0x01, 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0xd0, 0x01, 0x37, 0x03, 0xa0, 0x02, 0x20, 0x01, 0x20, 0x00, + 0x41, 0xd8, 0x00, 0x6a, 0x20, 0x00, 0x41, 0x80, 0x02, 0x6a, 0x10, 0x3e, 0x28, 0x02, 0x00, 0x22, + 0x02, 0x4d, 0x04, 0x40, 0x20, 0x00, 0x41, 0xd8, 0x00, 0x6a, 0x20, 0x00, 0x41, 0x80, 0x02, 0x6a, + 0x10, 0x3f, 0x22, 0x04, 0x28, 0x02, 0x00, 0x22, 0x03, 0x20, 0x01, 0x6b, 0x22, 0x05, 0x20, 0x03, + 0x4b, 0x0d, 0x06, 0x20, 0x04, 0x20, 0x05, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0xd8, 0x00, 0x6a, + 0x20, 0x00, 0x41, 0xa0, 0x02, 0x6a, 0x10, 0x3f, 0x22, 0x04, 0x28, 0x02, 0x00, 0x22, 0x03, 0x20, + 0x01, 0x6a, 0x22, 0x05, 0x20, 0x03, 0x49, 0x0d, 0x06, 0x20, 0x04, 0x20, 0x05, 0x36, 0x02, 0x00, + 0x0b, 0x20, 0x00, 0x41, 0xa0, 0x02, 0x6a, 0x10, 0x18, 0x20, 0x00, 0x41, 0x80, 0x02, 0x6a, 0x10, + 0x18, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x4d, 0x3a, 0x00, 0xe0, 0x01, 0x20, 0x00, 0x41, 0xd8, + 0x00, 0x6a, 0x10, 0x3b, 0x20, 0x00, 0x41, 0xe0, 0x01, 0x6a, 0x10, 0x40, 0x20, 0x00, 0x41, 0x03, + 0x36, 0x02, 0x38, 0x0c, 0x08, 0x0b, 0x20, 0x00, 0x41, 0xa0, 0x02, 0x6a, 0x41, 0x04, 0x72, 0x41, + 0xe6, 0x82, 0xc0, 0x00, 0x41, 0x06, 0x10, 0x1c, 0x20, 0x00, 0x41, 0xb0, 0x02, 0x6a, 0x20, 0x00, + 0x28, 0x02, 0xb8, 0x01, 0x20, 0x00, 0x28, 0x02, 0xbc, 0x01, 0x10, 0x1c, 0x20, 0x00, 0x41, 0xc4, + 0x00, 0x6a, 0x20, 0x00, 0x41, 0xac, 0x02, 0x6a, 0x29, 0x02, 0x00, 0x37, 0x02, 0x00, 0x20, 0x00, + 0x41, 0xcc, 0x00, 0x6a, 0x20, 0x00, 0x41, 0xb4, 0x02, 0x6a, 0x29, 0x02, 0x00, 0x37, 0x02, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x29, 0x02, 0xa4, 0x02, 0x22, 0x08, 0x37, 0x03, 0x80, 0x02, 0x20, 0x00, + 0x41, 0x01, 0x36, 0x02, 0x38, 0x20, 0x00, 0x20, 0x08, 0x37, 0x02, 0x3c, 0x20, 0x00, 0x41, 0xd0, + 0x01, 0x6a, 0x10, 0x18, 0x0c, 0x06, 0x0b, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0xb0, 0x01, 0x36, + 0x02, 0xc4, 0x01, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0xa8, 0x01, 0x36, 0x02, 0xc0, 0x01, 0x20, + 0x00, 0x41, 0xe0, 0x02, 0x6a, 0x20, 0x00, 0x41, 0xc0, 0x01, 0x6a, 0x10, 0x29, 0x20, 0x00, 0x28, + 0x02, 0xe0, 0x02, 0x04, 0x40, 0x20, 0x00, 0x41, 0xd8, 0x01, 0x6a, 0x20, 0x00, 0x41, 0xe8, 0x02, + 0x6a, 0x28, 0x02, 0x00, 0x22, 0x01, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0xe0, + 0x02, 0x22, 0x08, 0x37, 0x03, 0xd0, 0x01, 0x20, 0x00, 0x41, 0xa8, 0x02, 0x6a, 0x20, 0x01, 0x36, + 0x02, 0x00, 0x20, 0x00, 0x20, 0x08, 0x37, 0x03, 0xa0, 0x02, 0x20, 0x00, 0x41, 0xd8, 0x00, 0x6a, + 0x20, 0x00, 0x41, 0xa0, 0x02, 0x6a, 0x10, 0x3e, 0x28, 0x02, 0x00, 0x20, 0x00, 0x41, 0xa0, 0x02, + 0x6a, 0x10, 0x18, 0x20, 0x00, 0x41, 0xa0, 0x02, 0x6a, 0x41, 0x04, 0x10, 0x2e, 0x20, 0x00, 0x41, + 0xa0, 0x02, 0x6a, 0x10, 0x30, 0x20, 0x00, 0x28, 0x02, 0xa0, 0x02, 0x20, 0x00, 0x28, 0x02, 0xa8, + 0x02, 0x10, 0x02, 0x20, 0x00, 0x41, 0xa0, 0x02, 0x6a, 0x10, 0x18, 0x20, 0x00, 0x41, 0x03, 0x36, + 0x02, 0x38, 0x0c, 0x07, 0x0b, 0x20, 0x00, 0x41, 0xa0, 0x02, 0x6a, 0x41, 0x04, 0x72, 0x41, 0xec, + 0x82, 0xc0, 0x00, 0x41, 0x04, 0x10, 0x1c, 0x20, 0x00, 0x41, 0xb0, 0x02, 0x6a, 0x22, 0x01, 0x20, + 0x00, 0x28, 0x02, 0xc0, 0x01, 0x20, 0x00, 0x28, 0x02, 0xc4, 0x01, 0x10, 0x1c, 0x20, 0x00, 0x41, + 0x88, 0x02, 0x6a, 0x20, 0x00, 0x41, 0xa8, 0x02, 0x6a, 0x29, 0x03, 0x00, 0x22, 0x08, 0x37, 0x03, + 0x00, 0x20, 0x00, 0x41, 0x90, 0x02, 0x6a, 0x20, 0x01, 0x29, 0x03, 0x00, 0x22, 0x09, 0x37, 0x03, + 0x00, 0x20, 0x00, 0x41, 0xe8, 0x01, 0x6a, 0x22, 0x01, 0x20, 0x08, 0x37, 0x03, 0x00, 0x20, 0x00, + 0x41, 0xf0, 0x01, 0x6a, 0x22, 0x02, 0x20, 0x09, 0x37, 0x03, 0x00, 0x20, 0x00, 0x41, 0xf8, 0x01, + 0x6a, 0x22, 0x04, 0x20, 0x00, 0x41, 0xb8, 0x02, 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, + 0x00, 0x41, 0x01, 0x36, 0x02, 0xa0, 0x02, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0xa0, 0x02, 0x37, + 0x03, 0xe0, 0x01, 0x20, 0x00, 0x41, 0xd0, 0x00, 0x6a, 0x20, 0x04, 0x28, 0x02, 0x00, 0x36, 0x02, + 0x00, 0x20, 0x00, 0x41, 0xc8, 0x00, 0x6a, 0x20, 0x02, 0x29, 0x03, 0x00, 0x37, 0x03, 0x00, 0x20, + 0x00, 0x41, 0x40, 0x6b, 0x20, 0x01, 0x29, 0x03, 0x00, 0x37, 0x03, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x29, 0x03, 0xe0, 0x01, 0x37, 0x03, 0x38, 0x0c, 0x06, 0x0b, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, + 0xb0, 0x01, 0x36, 0x02, 0xc4, 0x01, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0xa8, 0x01, 0x36, 0x02, + 0xc0, 0x01, 0x20, 0x00, 0x41, 0xe0, 0x02, 0x6a, 0x20, 0x00, 0x41, 0xc0, 0x01, 0x6a, 0x10, 0x29, + 0x20, 0x00, 0x28, 0x02, 0xe0, 0x02, 0x04, 0x40, 0x20, 0x00, 0x41, 0xd8, 0x01, 0x6a, 0x20, 0x00, + 0x41, 0xe8, 0x02, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x03, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x29, 0x03, 0xe0, 0x02, 0x22, 0x08, 0x37, 0x03, 0xd0, 0x01, 0x20, 0x00, 0x41, 0xe8, 0x01, 0x6a, + 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x08, 0x37, 0x03, 0xe0, 0x01, 0x20, 0x08, 0xa7, + 0x21, 0x05, 0x02, 0x7f, 0x20, 0x03, 0x04, 0x40, 0x20, 0x00, 0x41, 0x30, 0x6a, 0x20, 0x05, 0x20, + 0x03, 0x41, 0x00, 0x10, 0x1f, 0x20, 0x00, 0x28, 0x02, 0x34, 0x20, 0x00, 0x28, 0x02, 0x30, 0x21, + 0x01, 0x20, 0x00, 0x41, 0x28, 0x6a, 0x20, 0x05, 0x20, 0x03, 0x41, 0x01, 0x10, 0x1f, 0x20, 0x00, + 0x28, 0x02, 0x2c, 0x20, 0x00, 0x41, 0x20, 0x6a, 0x20, 0x05, 0x20, 0x03, 0x20, 0x01, 0x20, 0x00, + 0x28, 0x02, 0x28, 0x22, 0x06, 0x20, 0x01, 0x20, 0x06, 0x4b, 0x22, 0x06, 0x1b, 0x22, 0x01, 0x10, + 0x20, 0x20, 0x06, 0x1b, 0x22, 0x06, 0x20, 0x01, 0x6a, 0x22, 0x07, 0x20, 0x06, 0x49, 0x0d, 0x05, + 0x20, 0x00, 0x28, 0x02, 0x24, 0x21, 0x02, 0x20, 0x00, 0x28, 0x02, 0x20, 0x21, 0x04, 0x20, 0x00, + 0x41, 0x18, 0x6a, 0x20, 0x06, 0x20, 0x07, 0x20, 0x05, 0x20, 0x03, 0x10, 0x21, 0x02, 0x7f, 0x20, + 0x00, 0x28, 0x02, 0x18, 0x21, 0x07, 0x20, 0x00, 0x28, 0x02, 0x1c, 0x20, 0x02, 0x46, 0x04, 0x7f, + 0x20, 0x04, 0x20, 0x07, 0x20, 0x02, 0x10, 0x4f, 0x45, 0x05, 0x41, 0x00, 0x0b, 0x45, 0x04, 0x40, + 0x20, 0x03, 0x20, 0x01, 0x6b, 0x22, 0x02, 0x20, 0x03, 0x4b, 0x0d, 0x07, 0x20, 0x01, 0x20, 0x02, + 0x20, 0x01, 0x20, 0x02, 0x4b, 0x1b, 0x22, 0x02, 0x41, 0x01, 0x6a, 0x22, 0x06, 0x20, 0x02, 0x49, + 0x0d, 0x07, 0x41, 0x7f, 0x21, 0x04, 0x20, 0x05, 0x20, 0x03, 0x10, 0x23, 0x21, 0x08, 0x20, 0x01, + 0x21, 0x02, 0x41, 0x7f, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x21, 0x04, 0x20, 0x03, 0x20, 0x05, 0x20, + 0x03, 0x20, 0x06, 0x41, 0x00, 0x10, 0x22, 0x22, 0x02, 0x20, 0x05, 0x20, 0x03, 0x20, 0x06, 0x41, + 0x01, 0x10, 0x22, 0x22, 0x07, 0x20, 0x02, 0x20, 0x07, 0x4b, 0x1b, 0x6b, 0x22, 0x02, 0x20, 0x03, + 0x4b, 0x0d, 0x06, 0x20, 0x00, 0x41, 0x10, 0x6a, 0x20, 0x05, 0x20, 0x03, 0x20, 0x06, 0x10, 0x20, + 0x20, 0x00, 0x28, 0x02, 0x10, 0x20, 0x00, 0x28, 0x02, 0x14, 0x10, 0x23, 0x21, 0x08, 0x20, 0x03, + 0x0b, 0x21, 0x07, 0x20, 0x00, 0x41, 0xdc, 0x02, 0x6a, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x41, 0xd4, 0x02, 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0xc8, 0x02, 0x6a, 0x20, + 0x07, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0xc4, 0x02, 0x6a, 0x20, 0x04, 0x36, 0x02, 0x00, 0x20, + 0x00, 0x41, 0xbc, 0x02, 0x6a, 0x42, 0x00, 0x37, 0x02, 0x00, 0x20, 0x00, 0x41, 0xb8, 0x02, 0x6a, + 0x20, 0x06, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0xb4, 0x02, 0x6a, 0x20, 0x02, 0x36, 0x02, 0x00, + 0x20, 0x00, 0x41, 0xb0, 0x02, 0x6a, 0x20, 0x01, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0xa8, 0x02, + 0x6a, 0x20, 0x08, 0x37, 0x03, 0x00, 0x20, 0x00, 0x20, 0x05, 0x36, 0x02, 0xd8, 0x02, 0x20, 0x00, + 0x41, 0xf0, 0x82, 0xc0, 0x00, 0x36, 0x02, 0xd0, 0x02, 0x20, 0x00, 0x41, 0x01, 0x36, 0x02, 0xa0, + 0x02, 0x20, 0x08, 0x42, 0x20, 0x88, 0xa7, 0x21, 0x02, 0x20, 0x03, 0x0c, 0x01, 0x0b, 0x20, 0x00, + 0x41, 0xdc, 0x02, 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0xd4, 0x02, 0x6a, 0x41, + 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0xac, 0x02, 0x6a, 0x41, 0x81, 0x02, 0x3b, 0x01, 0x00, + 0x20, 0x00, 0x41, 0xa8, 0x02, 0x6a, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x05, 0x36, + 0x02, 0xd8, 0x02, 0x20, 0x00, 0x41, 0xf0, 0x82, 0xc0, 0x00, 0x36, 0x02, 0xd0, 0x02, 0x20, 0x00, + 0x42, 0x00, 0x37, 0x03, 0xa0, 0x02, 0x41, 0x01, 0x21, 0x02, 0x41, 0x00, 0x0b, 0x21, 0x01, 0x02, + 0x40, 0x20, 0x03, 0x04, 0x40, 0x20, 0x00, 0x41, 0xa8, 0x02, 0x6a, 0x21, 0x02, 0x20, 0x00, 0x41, + 0xc4, 0x02, 0x6a, 0x28, 0x02, 0x00, 0x41, 0x7f, 0x47, 0x04, 0x40, 0x20, 0x00, 0x41, 0x80, 0x02, + 0x6a, 0x20, 0x02, 0x20, 0x01, 0x10, 0x34, 0x0c, 0x02, 0x0b, 0x20, 0x00, 0x41, 0x80, 0x02, 0x6a, + 0x20, 0x02, 0x20, 0x01, 0x10, 0x34, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x28, 0x02, 0xa4, 0x02, 0x22, + 0x01, 0x0d, 0x05, 0x20, 0x02, 0x41, 0xff, 0x01, 0x71, 0x04, 0x40, 0x20, 0x00, 0x41, 0x88, 0x02, + 0x6a, 0x20, 0x01, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x84, 0x02, 0x20, 0x00, + 0x41, 0x01, 0x36, 0x02, 0x80, 0x02, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x80, + 0x02, 0x0b, 0x20, 0x00, 0x28, 0x02, 0x80, 0x02, 0x21, 0x01, 0x20, 0x00, 0x41, 0xe0, 0x01, 0x6a, + 0x10, 0x18, 0x20, 0x00, 0x20, 0x01, 0x3a, 0x00, 0xa0, 0x02, 0x20, 0x00, 0x41, 0xa0, 0x02, 0x6a, + 0x10, 0x40, 0x20, 0x00, 0x41, 0x03, 0x36, 0x02, 0x38, 0x0c, 0x06, 0x0b, 0x20, 0x00, 0x41, 0xa0, + 0x02, 0x6a, 0x41, 0x04, 0x72, 0x41, 0xf0, 0x82, 0xc0, 0x00, 0x41, 0x05, 0x10, 0x1c, 0x20, 0x00, + 0x41, 0xb0, 0x02, 0x6a, 0x22, 0x01, 0x20, 0x00, 0x28, 0x02, 0xc0, 0x01, 0x20, 0x00, 0x28, 0x02, + 0xc4, 0x01, 0x10, 0x1c, 0x20, 0x00, 0x41, 0x88, 0x02, 0x6a, 0x20, 0x00, 0x41, 0xa8, 0x02, 0x6a, + 0x29, 0x03, 0x00, 0x22, 0x08, 0x37, 0x03, 0x00, 0x20, 0x00, 0x41, 0x90, 0x02, 0x6a, 0x20, 0x01, + 0x29, 0x03, 0x00, 0x22, 0x09, 0x37, 0x03, 0x00, 0x20, 0x00, 0x41, 0xe8, 0x01, 0x6a, 0x22, 0x01, + 0x20, 0x08, 0x37, 0x03, 0x00, 0x20, 0x00, 0x41, 0xf0, 0x01, 0x6a, 0x22, 0x02, 0x20, 0x09, 0x37, + 0x03, 0x00, 0x20, 0x00, 0x41, 0xf8, 0x01, 0x6a, 0x22, 0x04, 0x20, 0x00, 0x41, 0xb8, 0x02, 0x6a, + 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x01, 0x36, 0x02, 0xa0, 0x02, 0x20, 0x00, + 0x20, 0x00, 0x29, 0x03, 0xa0, 0x02, 0x37, 0x03, 0xe0, 0x01, 0x20, 0x00, 0x41, 0xd0, 0x00, 0x6a, + 0x20, 0x04, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0xc8, 0x00, 0x6a, 0x20, 0x02, + 0x29, 0x03, 0x00, 0x37, 0x03, 0x00, 0x20, 0x00, 0x41, 0x40, 0x6b, 0x20, 0x01, 0x29, 0x03, 0x00, + 0x37, 0x03, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0xe0, 0x01, 0x37, 0x03, 0x38, 0x0c, 0x05, + 0x0b, 0x20, 0x00, 0x41, 0xa0, 0x02, 0x6a, 0x41, 0x04, 0x72, 0x41, 0xe0, 0x82, 0xc0, 0x00, 0x41, + 0x04, 0x10, 0x1c, 0x20, 0x00, 0x41, 0xb0, 0x02, 0x6a, 0x22, 0x01, 0x20, 0x00, 0x28, 0x02, 0xb8, + 0x01, 0x20, 0x00, 0x28, 0x02, 0xbc, 0x01, 0x10, 0x1c, 0x20, 0x00, 0x41, 0x88, 0x02, 0x6a, 0x20, + 0x00, 0x41, 0xa8, 0x02, 0x6a, 0x29, 0x03, 0x00, 0x22, 0x08, 0x37, 0x03, 0x00, 0x20, 0x00, 0x41, + 0x90, 0x02, 0x6a, 0x20, 0x01, 0x29, 0x03, 0x00, 0x22, 0x09, 0x37, 0x03, 0x00, 0x20, 0x00, 0x41, + 0xe8, 0x01, 0x6a, 0x22, 0x01, 0x20, 0x08, 0x37, 0x03, 0x00, 0x20, 0x00, 0x41, 0xf0, 0x01, 0x6a, + 0x22, 0x02, 0x20, 0x09, 0x37, 0x03, 0x00, 0x20, 0x00, 0x41, 0xf8, 0x01, 0x6a, 0x22, 0x04, 0x20, + 0x00, 0x41, 0xb8, 0x02, 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x01, 0x36, + 0x02, 0xa0, 0x02, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0xa0, 0x02, 0x37, 0x03, 0xe0, 0x01, 0x20, + 0x00, 0x41, 0xd0, 0x00, 0x6a, 0x20, 0x04, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, + 0xc8, 0x00, 0x6a, 0x20, 0x02, 0x29, 0x03, 0x00, 0x37, 0x03, 0x00, 0x20, 0x00, 0x41, 0x40, 0x6b, + 0x20, 0x01, 0x29, 0x03, 0x00, 0x37, 0x03, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0xe0, 0x01, + 0x37, 0x03, 0x38, 0x0c, 0x04, 0x0b, 0x20, 0x00, 0x41, 0xa0, 0x02, 0x6a, 0x41, 0x04, 0x72, 0x41, + 0xe4, 0x82, 0xc0, 0x00, 0x41, 0x02, 0x10, 0x1c, 0x20, 0x00, 0x41, 0xb0, 0x02, 0x6a, 0x22, 0x01, + 0x20, 0x00, 0x28, 0x02, 0xb8, 0x01, 0x20, 0x00, 0x28, 0x02, 0xbc, 0x01, 0x10, 0x1c, 0x20, 0x00, + 0x41, 0x88, 0x02, 0x6a, 0x20, 0x00, 0x41, 0xa8, 0x02, 0x6a, 0x29, 0x03, 0x00, 0x22, 0x08, 0x37, + 0x03, 0x00, 0x20, 0x00, 0x41, 0x90, 0x02, 0x6a, 0x20, 0x01, 0x29, 0x03, 0x00, 0x22, 0x09, 0x37, + 0x03, 0x00, 0x20, 0x00, 0x41, 0xe8, 0x01, 0x6a, 0x22, 0x01, 0x20, 0x08, 0x37, 0x03, 0x00, 0x20, + 0x00, 0x41, 0xf0, 0x01, 0x6a, 0x22, 0x02, 0x20, 0x09, 0x37, 0x03, 0x00, 0x20, 0x00, 0x41, 0xf8, + 0x01, 0x6a, 0x22, 0x04, 0x20, 0x00, 0x41, 0xb8, 0x02, 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, + 0x20, 0x00, 0x41, 0x01, 0x36, 0x02, 0xa0, 0x02, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, 0xa0, 0x02, + 0x37, 0x03, 0xe0, 0x01, 0x20, 0x00, 0x41, 0xd0, 0x00, 0x6a, 0x20, 0x04, 0x28, 0x02, 0x00, 0x36, + 0x02, 0x00, 0x20, 0x00, 0x41, 0xc8, 0x00, 0x6a, 0x20, 0x02, 0x29, 0x03, 0x00, 0x37, 0x03, 0x00, + 0x20, 0x00, 0x41, 0x40, 0x6b, 0x20, 0x01, 0x29, 0x03, 0x00, 0x37, 0x03, 0x00, 0x20, 0x00, 0x20, + 0x00, 0x29, 0x03, 0xe0, 0x01, 0x37, 0x03, 0x38, 0x0c, 0x02, 0x0b, 0x00, 0x0b, 0x20, 0x01, 0x10, + 0x1e, 0x00, 0x0b, 0x20, 0x00, 0x41, 0xc0, 0x01, 0x6a, 0x10, 0x18, 0x0b, 0x20, 0x00, 0x41, 0xa8, + 0x01, 0x6a, 0x10, 0x18, 0x20, 0x00, 0x28, 0x02, 0x38, 0x0b, 0x21, 0x02, 0x0b, 0x20, 0x00, 0x41, + 0xd8, 0x00, 0x6a, 0x10, 0x32, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x02, 0x41, 0x03, 0x47, + 0x04, 0x40, 0x20, 0x00, 0x41, 0x38, 0x6a, 0x41, 0x04, 0x72, 0x21, 0x01, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x02, 0x41, 0x01, 0x6b, 0x0e, 0x02, 0x00, 0x01, 0x03, 0x0b, 0x20, 0x00, 0x41, 0xe8, 0x02, + 0x6a, 0x22, 0x02, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x20, 0x01, 0x29, 0x02, 0x00, 0x37, 0x03, 0xe0, 0x02, 0x20, 0x00, 0x41, 0xe8, 0x01, 0x6a, 0x22, + 0x01, 0x20, 0x00, 0x41, 0xd0, 0x00, 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, + 0x00, 0x41, 0xc8, 0x00, 0x6a, 0x29, 0x03, 0x00, 0x37, 0x03, 0xe0, 0x01, 0x20, 0x00, 0x41, 0x80, + 0x02, 0x6a, 0x41, 0x92, 0x82, 0xc0, 0x00, 0x41, 0x14, 0x10, 0x1c, 0x20, 0x00, 0x41, 0x80, 0x02, + 0x6a, 0x20, 0x00, 0x28, 0x02, 0xe0, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x10, 0x1b, 0x20, 0x00, + 0x41, 0x80, 0x02, 0x6a, 0x41, 0xa6, 0x82, 0xc0, 0x00, 0x41, 0x03, 0x10, 0x1b, 0x20, 0x00, 0x41, + 0xd8, 0x00, 0x6a, 0x20, 0x00, 0x28, 0x02, 0xe0, 0x01, 0x20, 0x01, 0x28, 0x02, 0x00, 0x10, 0x27, + 0x20, 0x00, 0x41, 0x80, 0x02, 0x6a, 0x20, 0x00, 0x28, 0x02, 0x58, 0x20, 0x00, 0x28, 0x02, 0x60, + 0x10, 0x1b, 0x20, 0x00, 0x41, 0xd8, 0x00, 0x6a, 0x10, 0x18, 0x20, 0x00, 0x41, 0xa8, 0x02, 0x6a, + 0x20, 0x00, 0x41, 0x88, 0x02, 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x29, 0x03, 0x80, 0x02, 0x37, 0x03, 0xa0, 0x02, 0x20, 0x00, 0x41, 0x00, 0x3a, 0x00, 0xac, 0x02, + 0x20, 0x00, 0x41, 0xe0, 0x01, 0x6a, 0x10, 0x18, 0x20, 0x00, 0x41, 0xe0, 0x02, 0x6a, 0x10, 0x18, + 0x0c, 0x03, 0x0b, 0x20, 0x00, 0x41, 0xa0, 0x02, 0x6a, 0x41, 0xf5, 0x82, 0xc0, 0x00, 0x41, 0x14, + 0x10, 0x1c, 0x20, 0x00, 0x41, 0x00, 0x3a, 0x00, 0xac, 0x02, 0x0c, 0x02, 0x0b, 0x20, 0x00, 0x42, + 0x00, 0x37, 0x02, 0xa4, 0x02, 0x20, 0x00, 0x41, 0x01, 0x3a, 0x00, 0xac, 0x02, 0x20, 0x00, 0x41, + 0xac, 0x82, 0xc0, 0x00, 0x28, 0x02, 0x00, 0x36, 0x02, 0xa0, 0x02, 0x0c, 0x02, 0x0b, 0x20, 0x00, + 0x41, 0xe8, 0x01, 0x6a, 0x22, 0x02, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, + 0x00, 0x20, 0x00, 0x20, 0x01, 0x29, 0x02, 0x00, 0x37, 0x03, 0xe0, 0x01, 0x20, 0x00, 0x41, 0x80, + 0x02, 0x6a, 0x41, 0x80, 0x82, 0xc0, 0x00, 0x41, 0x12, 0x10, 0x1c, 0x20, 0x00, 0x41, 0xd8, 0x00, + 0x6a, 0x20, 0x00, 0x28, 0x02, 0xe0, 0x01, 0x20, 0x02, 0x28, 0x02, 0x00, 0x10, 0x27, 0x20, 0x00, + 0x41, 0x80, 0x02, 0x6a, 0x20, 0x00, 0x28, 0x02, 0x58, 0x20, 0x00, 0x28, 0x02, 0x60, 0x10, 0x1b, + 0x20, 0x00, 0x41, 0xd8, 0x00, 0x6a, 0x10, 0x18, 0x20, 0x00, 0x41, 0xa8, 0x02, 0x6a, 0x20, 0x00, + 0x41, 0x88, 0x02, 0x6a, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x29, 0x03, + 0x80, 0x02, 0x37, 0x03, 0xa0, 0x02, 0x20, 0x00, 0x41, 0x00, 0x3a, 0x00, 0xac, 0x02, 0x20, 0x00, + 0x41, 0xe0, 0x01, 0x6a, 0x10, 0x18, 0x0b, 0x20, 0x00, 0x28, 0x02, 0xa0, 0x02, 0x21, 0x02, 0x20, + 0x00, 0x20, 0x00, 0x28, 0x02, 0xa8, 0x02, 0x22, 0x01, 0x41, 0x00, 0x10, 0x13, 0x20, 0x00, 0x20, + 0x00, 0x28, 0x02, 0x04, 0x36, 0x02, 0x5c, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x04, + 0x36, 0x02, 0x58, 0x20, 0x04, 0x20, 0x02, 0x20, 0x01, 0x10, 0x4c, 0x1a, 0x20, 0x00, 0x20, 0x01, + 0x36, 0x02, 0x60, 0x20, 0x00, 0x41, 0xd8, 0x00, 0x6a, 0x10, 0x3c, 0x20, 0x00, 0x41, 0xd8, 0x00, + 0x6a, 0x10, 0x18, 0x0b, 0x20, 0x00, 0x41, 0xa0, 0x02, 0x6a, 0x10, 0x18, 0x20, 0x00, 0x41, 0xf0, + 0x02, 0x6a, 0x24, 0x00, 0x0b, 0x8c, 0x01, 0x01, 0x03, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, + 0x02, 0x24, 0x00, 0x20, 0x02, 0x20, 0x01, 0x10, 0x2d, 0x02, 0x7f, 0x20, 0x00, 0x41, 0x18, 0x6a, + 0x22, 0x00, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x01, 0x20, 0x02, 0x28, 0x02, 0x08, 0x22, 0x03, + 0x10, 0x44, 0x22, 0x04, 0x04, 0x40, 0x20, 0x04, 0x28, 0x02, 0x00, 0x22, 0x00, 0x41, 0x04, 0x6a, + 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x01, 0x46, 0x1b, 0x0c, 0x01, 0x0b, 0x20, 0x00, + 0x20, 0x01, 0x20, 0x03, 0x10, 0x45, 0x41, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x03, 0x10, 0x44, + 0x22, 0x00, 0x45, 0x0d, 0x00, 0x1a, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x00, 0x41, 0x04, 0x6a, + 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x01, 0x46, 0x1b, 0x0b, 0x21, 0x00, 0x20, 0x02, + 0x10, 0x18, 0x20, 0x00, 0x45, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, + 0x20, 0x00, 0x0b, 0x9a, 0x01, 0x01, 0x03, 0x7f, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, + 0x00, 0x20, 0x02, 0x20, 0x01, 0x10, 0x2d, 0x02, 0x7f, 0x20, 0x00, 0x41, 0x18, 0x6a, 0x22, 0x00, + 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x01, 0x20, 0x02, 0x28, 0x02, 0x08, 0x22, 0x03, 0x10, 0x44, + 0x22, 0x04, 0x04, 0x40, 0x20, 0x04, 0x41, 0x01, 0x3a, 0x00, 0x04, 0x20, 0x04, 0x28, 0x02, 0x00, + 0x22, 0x00, 0x41, 0x04, 0x6a, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x01, 0x46, 0x1b, + 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x20, 0x03, 0x10, 0x45, 0x41, 0x00, 0x20, 0x00, 0x20, + 0x01, 0x20, 0x03, 0x10, 0x44, 0x22, 0x00, 0x45, 0x0d, 0x00, 0x1a, 0x20, 0x00, 0x41, 0x01, 0x3a, + 0x00, 0x04, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x00, 0x41, 0x04, 0x6a, 0x41, 0x00, 0x20, 0x00, + 0x28, 0x02, 0x00, 0x41, 0x01, 0x46, 0x1b, 0x0b, 0x21, 0x00, 0x20, 0x02, 0x10, 0x18, 0x20, 0x00, + 0x45, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x20, 0x00, 0x0b, 0x5b, + 0x01, 0x01, 0x7f, 0x23, 0x00, 0x41, 0x20, 0x6b, 0x22, 0x01, 0x24, 0x00, 0x20, 0x01, 0x41, 0x08, + 0x6a, 0x41, 0x01, 0x41, 0x00, 0x10, 0x13, 0x20, 0x01, 0x41, 0x00, 0x36, 0x02, 0x18, 0x20, 0x01, + 0x20, 0x01, 0x29, 0x03, 0x08, 0x37, 0x03, 0x10, 0x20, 0x01, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x3a, + 0x00, 0x1f, 0x20, 0x01, 0x41, 0x10, 0x6a, 0x20, 0x01, 0x41, 0x1f, 0x6a, 0x41, 0x01, 0x10, 0x1b, + 0x20, 0x01, 0x28, 0x02, 0x10, 0x20, 0x01, 0x28, 0x02, 0x18, 0x10, 0x02, 0x20, 0x01, 0x41, 0x10, + 0x6a, 0x10, 0x18, 0x20, 0x01, 0x41, 0x20, 0x6a, 0x24, 0x00, 0x0b, 0xce, 0x01, 0x02, 0x03, 0x7f, + 0x01, 0x7e, 0x23, 0x00, 0x41, 0x30, 0x6b, 0x22, 0x02, 0x24, 0x00, 0x20, 0x02, 0x41, 0x20, 0x6a, + 0x21, 0x03, 0x02, 0x40, 0x41, 0x90, 0x83, 0xc0, 0x00, 0x28, 0x02, 0x00, 0x22, 0x04, 0x41, 0x80, + 0x80, 0x01, 0x4d, 0x04, 0x40, 0x20, 0x03, 0x20, 0x04, 0x36, 0x02, 0x04, 0x20, 0x03, 0x41, 0x94, + 0x83, 0xc0, 0x00, 0x36, 0x02, 0x00, 0x0c, 0x01, 0x0b, 0x00, 0x0b, 0x02, 0x40, 0x02, 0x7e, 0x42, + 0x81, 0x06, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x28, 0x02, 0x20, 0x10, 0x03, 0x22, 0x00, 0x45, + 0x0d, 0x00, 0x1a, 0x20, 0x00, 0x41, 0x81, 0x80, 0x01, 0x4f, 0x0d, 0x01, 0x41, 0x90, 0x83, 0xc0, + 0x00, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x02, 0x41, 0x18, 0x6a, 0x20, 0x00, 0x10, 0x2c, 0x20, + 0x02, 0x41, 0x10, 0x6a, 0x21, 0x01, 0x20, 0x02, 0x28, 0x02, 0x18, 0x21, 0x03, 0x20, 0x00, 0x20, + 0x02, 0x28, 0x02, 0x1c, 0x4b, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x01, 0x20, 0x00, 0x36, 0x02, 0x04, + 0x20, 0x01, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x02, 0x20, 0x02, 0x29, 0x03, 0x10, 0x37, 0x03, + 0x28, 0x20, 0x02, 0x41, 0x08, 0x6a, 0x20, 0x02, 0x41, 0x28, 0x6a, 0x10, 0x2b, 0x20, 0x02, 0x28, + 0x02, 0x08, 0x41, 0x00, 0x47, 0xad, 0x20, 0x02, 0x35, 0x02, 0x0c, 0x42, 0x20, 0x86, 0x84, 0x0b, + 0x20, 0x02, 0x41, 0x30, 0x6a, 0x24, 0x00, 0x0f, 0x0b, 0x00, 0x0b, 0x8a, 0x01, 0x01, 0x02, 0x7f, + 0x23, 0x00, 0x41, 0x20, 0x6b, 0x22, 0x03, 0x24, 0x00, 0x41, 0x90, 0x83, 0xc0, 0x00, 0x41, 0x00, + 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x02, 0x28, 0x02, 0x00, 0x36, 0x02, 0x1c, 0x20, 0x03, 0x41, + 0x10, 0x6a, 0x22, 0x02, 0x41, 0x04, 0x36, 0x02, 0x04, 0x20, 0x02, 0x41, 0x94, 0x83, 0xc0, 0x00, + 0x36, 0x02, 0x00, 0x20, 0x03, 0x28, 0x02, 0x10, 0x20, 0x03, 0x28, 0x02, 0x14, 0x20, 0x03, 0x41, + 0x1c, 0x6a, 0x41, 0x04, 0x10, 0x1d, 0x41, 0x90, 0x83, 0xc0, 0x00, 0x28, 0x02, 0x00, 0x22, 0x04, + 0x41, 0x04, 0x6a, 0x22, 0x02, 0x20, 0x04, 0x49, 0x04, 0x40, 0x00, 0x0b, 0x41, 0x90, 0x83, 0xc0, + 0x00, 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x20, 0x02, 0x10, 0x2c, 0x20, + 0x00, 0x20, 0x01, 0x20, 0x03, 0x28, 0x02, 0x08, 0x20, 0x03, 0x28, 0x02, 0x0c, 0x10, 0x04, 0x20, + 0x03, 0x41, 0x20, 0x6a, 0x24, 0x00, 0x0b, 0x21, 0x01, 0x01, 0x7f, 0x41, 0x08, 0x41, 0x04, 0x10, + 0x16, 0x22, 0x02, 0x45, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x02, 0x20, 0x01, 0x36, 0x02, 0x04, 0x20, + 0x02, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x02, 0x0b, 0xe7, 0x01, 0x01, 0x05, 0x7f, 0x23, 0x00, + 0x41, 0x20, 0x6b, 0x22, 0x03, 0x24, 0x00, 0x02, 0x7f, 0x02, 0x40, 0x20, 0x00, 0x41, 0x1c, 0x6a, + 0x28, 0x02, 0x00, 0x22, 0x04, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x18, 0x6a, 0x28, 0x02, 0x00, + 0x21, 0x06, 0x03, 0x40, 0x20, 0x04, 0x2f, 0x01, 0xe2, 0x01, 0x21, 0x00, 0x20, 0x03, 0x41, 0x00, + 0x36, 0x02, 0x18, 0x20, 0x03, 0x20, 0x04, 0x41, 0x04, 0x6a, 0x22, 0x05, 0x36, 0x02, 0x10, 0x20, + 0x03, 0x20, 0x05, 0x20, 0x00, 0x41, 0x0c, 0x6c, 0x6a, 0x36, 0x02, 0x14, 0x02, 0x40, 0x02, 0x40, + 0x03, 0x40, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x10, 0x06, 0x20, 0x03, + 0x28, 0x02, 0x0c, 0x22, 0x07, 0x45, 0x0d, 0x01, 0x20, 0x03, 0x28, 0x02, 0x08, 0x21, 0x05, 0x02, + 0x40, 0x20, 0x01, 0x20, 0x02, 0x20, 0x07, 0x28, 0x02, 0x00, 0x20, 0x07, 0x28, 0x02, 0x08, 0x10, + 0x35, 0x41, 0x18, 0x74, 0x41, 0x18, 0x75, 0x0e, 0x02, 0x03, 0x01, 0x00, 0x0b, 0x0b, 0x20, 0x05, + 0x21, 0x00, 0x0b, 0x20, 0x06, 0x45, 0x0d, 0x02, 0x20, 0x03, 0x20, 0x00, 0x36, 0x02, 0x18, 0x20, + 0x03, 0x20, 0x04, 0x36, 0x02, 0x14, 0x20, 0x03, 0x20, 0x06, 0x36, 0x02, 0x10, 0x20, 0x03, 0x20, + 0x03, 0x41, 0x10, 0x6a, 0x10, 0x0d, 0x20, 0x03, 0x28, 0x02, 0x04, 0x21, 0x04, 0x20, 0x03, 0x28, + 0x02, 0x00, 0x21, 0x06, 0x0c, 0x01, 0x0b, 0x0b, 0x20, 0x04, 0x20, 0x05, 0x41, 0x03, 0x74, 0x6a, + 0x41, 0x88, 0x01, 0x6a, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x0b, 0x20, 0x03, 0x41, 0x20, 0x6a, 0x24, + 0x00, 0x0b, 0xa4, 0x02, 0x02, 0x04, 0x7f, 0x01, 0x7e, 0x23, 0x00, 0x41, 0x20, 0x6b, 0x22, 0x04, + 0x24, 0x00, 0x20, 0x04, 0x41, 0x18, 0x6a, 0x21, 0x03, 0x20, 0x00, 0x28, 0x02, 0x00, 0x04, 0x40, + 0x00, 0x0b, 0x20, 0x00, 0x41, 0x7f, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x00, 0x36, 0x02, 0x04, + 0x20, 0x03, 0x20, 0x00, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x04, 0x28, 0x02, 0x1c, 0x21, + 0x03, 0x20, 0x04, 0x28, 0x02, 0x18, 0x20, 0x01, 0x20, 0x02, 0x10, 0x1b, 0x02, 0x40, 0x20, 0x03, + 0x28, 0x02, 0x00, 0x22, 0x05, 0x41, 0x01, 0x6a, 0x22, 0x06, 0x20, 0x05, 0x48, 0x0d, 0x00, 0x20, + 0x03, 0x20, 0x06, 0x36, 0x02, 0x00, 0x20, 0x04, 0x41, 0x10, 0x6a, 0x20, 0x00, 0x10, 0x46, 0x20, + 0x04, 0x28, 0x02, 0x14, 0x21, 0x03, 0x20, 0x04, 0x28, 0x02, 0x10, 0x22, 0x05, 0x28, 0x02, 0x00, + 0x20, 0x05, 0x28, 0x02, 0x08, 0x10, 0x41, 0x21, 0x07, 0x20, 0x03, 0x28, 0x02, 0x00, 0x22, 0x05, + 0x41, 0x01, 0x6b, 0x22, 0x06, 0x20, 0x05, 0x4e, 0x0d, 0x00, 0x20, 0x03, 0x20, 0x06, 0x36, 0x02, + 0x00, 0x20, 0x04, 0x41, 0x08, 0x6a, 0x21, 0x03, 0x20, 0x00, 0x28, 0x02, 0x00, 0x04, 0x40, 0x00, + 0x0b, 0x20, 0x00, 0x41, 0x7f, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x00, 0x36, 0x02, 0x04, 0x20, + 0x03, 0x20, 0x00, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x04, 0x28, 0x02, 0x0c, 0x21, 0x03, + 0x20, 0x00, 0x28, 0x02, 0x10, 0x22, 0x05, 0x20, 0x04, 0x28, 0x02, 0x08, 0x22, 0x06, 0x28, 0x02, + 0x08, 0x4d, 0x04, 0x40, 0x20, 0x06, 0x20, 0x05, 0x36, 0x02, 0x08, 0x0b, 0x20, 0x03, 0x28, 0x02, + 0x00, 0x22, 0x05, 0x41, 0x01, 0x6a, 0x22, 0x06, 0x20, 0x05, 0x48, 0x0d, 0x00, 0x20, 0x03, 0x20, + 0x06, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x07, 0xa7, 0x41, 0x7f, 0x73, + 0x41, 0x01, 0x71, 0x20, 0x07, 0x42, 0x20, 0x88, 0xa7, 0x41, 0x00, 0x10, 0x47, 0x20, 0x04, 0x41, + 0x20, 0x6a, 0x24, 0x00, 0x0f, 0x0b, 0x00, 0x0b, 0x2d, 0x01, 0x01, 0x7f, 0x20, 0x01, 0x28, 0x02, + 0x00, 0x41, 0x01, 0x6a, 0x22, 0x02, 0x41, 0x00, 0x4c, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x01, 0x20, + 0x02, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x01, 0x41, + 0x04, 0x6a, 0x36, 0x02, 0x00, 0x0b, 0xdb, 0x0d, 0x02, 0x0f, 0x7f, 0x02, 0x7e, 0x23, 0x00, 0x41, + 0x80, 0x01, 0x6b, 0x22, 0x06, 0x24, 0x00, 0x20, 0x03, 0x20, 0x04, 0x10, 0x43, 0x21, 0x0b, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x14, 0x45, 0x04, + 0x40, 0x20, 0x00, 0x41, 0x7f, 0x36, 0x02, 0x14, 0x20, 0x06, 0x41, 0xc8, 0x00, 0x6a, 0x20, 0x02, + 0x41, 0x00, 0x10, 0x13, 0x20, 0x06, 0x28, 0x02, 0x4c, 0x21, 0x0d, 0x20, 0x06, 0x28, 0x02, 0x48, + 0x20, 0x01, 0x20, 0x02, 0x10, 0x4c, 0x21, 0x09, 0x20, 0x06, 0x20, 0x02, 0x36, 0x02, 0x68, 0x20, + 0x06, 0x20, 0x0d, 0x36, 0x02, 0x64, 0x20, 0x06, 0x20, 0x09, 0x36, 0x02, 0x60, 0x02, 0x40, 0x20, + 0x00, 0x41, 0x1c, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x03, 0x04, 0x40, 0x20, 0x00, 0x28, 0x02, 0x18, + 0x21, 0x07, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x10, 0x10, 0x22, 0x03, 0x36, 0x02, 0x1c, 0x20, 0x00, + 0x41, 0x00, 0x36, 0x02, 0x18, 0x0b, 0x02, 0x40, 0x03, 0x40, 0x20, 0x03, 0x2f, 0x01, 0xe2, 0x01, + 0x21, 0x01, 0x20, 0x06, 0x41, 0x00, 0x36, 0x02, 0x78, 0x20, 0x06, 0x20, 0x03, 0x41, 0x04, 0x6a, + 0x22, 0x04, 0x36, 0x02, 0x70, 0x20, 0x06, 0x20, 0x04, 0x20, 0x01, 0x41, 0x0c, 0x6c, 0x6a, 0x36, + 0x02, 0x74, 0x02, 0x40, 0x03, 0x40, 0x20, 0x06, 0x41, 0x40, 0x6b, 0x20, 0x06, 0x41, 0xf0, 0x00, + 0x6a, 0x10, 0x06, 0x20, 0x06, 0x28, 0x02, 0x44, 0x22, 0x0a, 0x45, 0x0d, 0x01, 0x20, 0x06, 0x28, + 0x02, 0x40, 0x21, 0x04, 0x02, 0x40, 0x20, 0x09, 0x20, 0x02, 0x20, 0x0a, 0x28, 0x02, 0x00, 0x20, + 0x0a, 0x28, 0x02, 0x08, 0x10, 0x35, 0x41, 0x18, 0x74, 0x41, 0x18, 0x75, 0x0e, 0x02, 0x04, 0x01, + 0x00, 0x0b, 0x0b, 0x20, 0x04, 0x21, 0x01, 0x0b, 0x20, 0x07, 0x04, 0x40, 0x20, 0x06, 0x20, 0x01, + 0x36, 0x02, 0x78, 0x20, 0x06, 0x20, 0x03, 0x36, 0x02, 0x74, 0x20, 0x06, 0x20, 0x07, 0x36, 0x02, + 0x70, 0x20, 0x06, 0x41, 0x38, 0x6a, 0x20, 0x06, 0x41, 0xf0, 0x00, 0x6a, 0x10, 0x0d, 0x20, 0x06, + 0x28, 0x02, 0x3c, 0x21, 0x03, 0x20, 0x06, 0x28, 0x02, 0x38, 0x21, 0x07, 0x0c, 0x01, 0x0b, 0x0b, + 0x20, 0x06, 0x41, 0x00, 0x36, 0x02, 0x50, 0x20, 0x06, 0x20, 0x03, 0xad, 0x20, 0x01, 0xad, 0x42, + 0x20, 0x86, 0x84, 0x37, 0x02, 0x54, 0x02, 0x40, 0x20, 0x03, 0x2f, 0x01, 0xe2, 0x01, 0x41, 0x0b, + 0x4f, 0x04, 0x40, 0x20, 0x06, 0x41, 0xf0, 0x00, 0x6a, 0x20, 0x01, 0x10, 0x19, 0x20, 0x06, 0x41, + 0xf8, 0x00, 0x6a, 0x28, 0x02, 0x00, 0x21, 0x11, 0x20, 0x06, 0x28, 0x02, 0x74, 0x21, 0x12, 0x20, + 0x06, 0x28, 0x02, 0x70, 0x21, 0x04, 0x10, 0x10, 0x21, 0x0a, 0x20, 0x03, 0x2f, 0x01, 0xe2, 0x01, + 0x22, 0x0c, 0x20, 0x04, 0x6b, 0x22, 0x01, 0x20, 0x0c, 0x4b, 0x0d, 0x08, 0x20, 0x01, 0x20, 0x01, + 0x41, 0x01, 0x6b, 0x22, 0x01, 0x49, 0x0d, 0x08, 0x20, 0x0a, 0x20, 0x01, 0x3b, 0x01, 0xe2, 0x01, + 0x20, 0x04, 0x41, 0x01, 0x6a, 0x22, 0x07, 0x20, 0x04, 0x49, 0x0d, 0x08, 0x20, 0x0c, 0x20, 0x0c, + 0x20, 0x07, 0x6b, 0x22, 0x0e, 0x49, 0x0d, 0x08, 0x20, 0x03, 0x20, 0x04, 0x41, 0x03, 0x74, 0x6a, + 0x22, 0x0f, 0x41, 0x88, 0x01, 0x6a, 0x28, 0x02, 0x00, 0x21, 0x0c, 0x20, 0x0f, 0x41, 0x8c, 0x01, + 0x6a, 0x2d, 0x00, 0x00, 0x20, 0x03, 0x20, 0x04, 0x41, 0x0c, 0x6c, 0x6a, 0x22, 0x08, 0x41, 0x04, + 0x6a, 0x28, 0x02, 0x00, 0x21, 0x0f, 0x20, 0x08, 0x41, 0x08, 0x6a, 0x29, 0x02, 0x00, 0x21, 0x15, + 0x20, 0x06, 0x41, 0x30, 0x6a, 0x21, 0x08, 0x20, 0x0a, 0x41, 0x04, 0x6a, 0x21, 0x10, 0x20, 0x01, + 0x41, 0x0c, 0x4f, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x08, 0x20, 0x01, 0x36, 0x02, 0x04, 0x20, 0x08, + 0x20, 0x10, 0x36, 0x02, 0x00, 0x20, 0x0e, 0x20, 0x06, 0x28, 0x02, 0x34, 0x47, 0x0d, 0x08, 0x20, + 0x06, 0x28, 0x02, 0x30, 0x20, 0x03, 0x20, 0x07, 0x41, 0x0c, 0x6c, 0x6a, 0x41, 0x04, 0x6a, 0x20, + 0x0e, 0x41, 0x0c, 0x6c, 0x10, 0x4c, 0x1a, 0x20, 0x06, 0x41, 0x28, 0x6a, 0x21, 0x08, 0x20, 0x0a, + 0x41, 0x88, 0x01, 0x6a, 0x21, 0x10, 0x20, 0x01, 0x41, 0x0c, 0x4f, 0x04, 0x40, 0x00, 0x0b, 0x20, + 0x08, 0x20, 0x01, 0x36, 0x02, 0x04, 0x20, 0x08, 0x20, 0x10, 0x36, 0x02, 0x00, 0x20, 0x0e, 0x20, + 0x06, 0x28, 0x02, 0x2c, 0x47, 0x0d, 0x08, 0x20, 0x06, 0x28, 0x02, 0x28, 0x20, 0x03, 0x20, 0x07, + 0x41, 0x03, 0x74, 0x6a, 0x41, 0x88, 0x01, 0x6a, 0x20, 0x0e, 0x41, 0x03, 0x74, 0x10, 0x4c, 0x1a, + 0x20, 0x03, 0x20, 0x04, 0x3b, 0x01, 0xe2, 0x01, 0x20, 0x06, 0x20, 0x11, 0x36, 0x02, 0x68, 0x20, + 0x06, 0x20, 0x0a, 0x20, 0x03, 0x20, 0x12, 0x1b, 0x36, 0x02, 0x64, 0x41, 0x00, 0x21, 0x01, 0x20, + 0x06, 0x41, 0x00, 0x36, 0x02, 0x60, 0x20, 0x06, 0x20, 0x02, 0x36, 0x02, 0x78, 0x20, 0x06, 0x20, + 0x0d, 0x36, 0x02, 0x74, 0x20, 0x06, 0x20, 0x09, 0x36, 0x02, 0x70, 0x20, 0x06, 0x41, 0xe0, 0x00, + 0x6a, 0x20, 0x06, 0x41, 0xf0, 0x00, 0x6a, 0x20, 0x0b, 0x20, 0x05, 0x10, 0x0e, 0x41, 0x01, 0x71, + 0x21, 0x07, 0x41, 0x00, 0x21, 0x02, 0x03, 0x40, 0x20, 0x03, 0x28, 0x02, 0x00, 0x22, 0x04, 0x45, + 0x0d, 0x02, 0x20, 0x01, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x22, 0x01, 0x4b, 0x0d, 0x09, 0x20, 0x06, + 0x20, 0x03, 0x2f, 0x01, 0xe0, 0x01, 0x22, 0x03, 0x36, 0x02, 0x58, 0x20, 0x06, 0x20, 0x04, 0x36, + 0x02, 0x54, 0x20, 0x06, 0x20, 0x01, 0x36, 0x02, 0x50, 0x20, 0x01, 0x41, 0x01, 0x6b, 0x20, 0x02, + 0x47, 0x0d, 0x09, 0x20, 0x07, 0x41, 0x01, 0x71, 0x21, 0x0d, 0x20, 0x04, 0x2f, 0x01, 0xe2, 0x01, + 0x41, 0x0b, 0x49, 0x0d, 0x06, 0x20, 0x06, 0x41, 0xf0, 0x00, 0x6a, 0x20, 0x03, 0x10, 0x19, 0x20, + 0x06, 0x28, 0x02, 0x78, 0x21, 0x11, 0x20, 0x06, 0x28, 0x02, 0x74, 0x21, 0x12, 0x20, 0x06, 0x28, + 0x02, 0x70, 0x21, 0x03, 0x20, 0x04, 0x2f, 0x01, 0xe2, 0x01, 0x10, 0x12, 0x21, 0x05, 0x20, 0x04, + 0x2f, 0x01, 0xe2, 0x01, 0x22, 0x09, 0x20, 0x03, 0x6b, 0x22, 0x02, 0x20, 0x09, 0x4b, 0x0d, 0x09, + 0x20, 0x02, 0x20, 0x02, 0x41, 0x01, 0x6b, 0x22, 0x02, 0x49, 0x0d, 0x09, 0x20, 0x05, 0x20, 0x02, + 0x3b, 0x01, 0xe2, 0x01, 0x20, 0x03, 0x41, 0x01, 0x6a, 0x22, 0x07, 0x20, 0x03, 0x49, 0x0d, 0x09, + 0x20, 0x09, 0x20, 0x09, 0x20, 0x07, 0x6b, 0x22, 0x0b, 0x49, 0x0d, 0x09, 0x20, 0x04, 0x20, 0x03, + 0x41, 0x0c, 0x6c, 0x6a, 0x22, 0x09, 0x41, 0x08, 0x6a, 0x29, 0x02, 0x00, 0x21, 0x16, 0x20, 0x09, + 0x41, 0x04, 0x6a, 0x28, 0x02, 0x00, 0x21, 0x09, 0x20, 0x04, 0x20, 0x03, 0x41, 0x03, 0x74, 0x6a, + 0x22, 0x08, 0x41, 0x88, 0x01, 0x6a, 0x28, 0x02, 0x00, 0x21, 0x0e, 0x20, 0x08, 0x41, 0x8c, 0x01, + 0x6a, 0x2d, 0x00, 0x00, 0x21, 0x10, 0x20, 0x06, 0x41, 0x20, 0x6a, 0x21, 0x08, 0x20, 0x05, 0x41, + 0x04, 0x6a, 0x21, 0x14, 0x20, 0x02, 0x41, 0x0c, 0x4f, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x08, 0x20, + 0x02, 0x36, 0x02, 0x04, 0x20, 0x08, 0x20, 0x14, 0x36, 0x02, 0x00, 0x20, 0x0b, 0x20, 0x06, 0x28, + 0x02, 0x24, 0x47, 0x0d, 0x09, 0x20, 0x06, 0x28, 0x02, 0x20, 0x20, 0x04, 0x20, 0x07, 0x41, 0x0c, + 0x6c, 0x6a, 0x41, 0x04, 0x6a, 0x20, 0x0b, 0x41, 0x0c, 0x6c, 0x10, 0x4c, 0x1a, 0x20, 0x06, 0x41, + 0x18, 0x6a, 0x21, 0x08, 0x20, 0x05, 0x41, 0x88, 0x01, 0x6a, 0x21, 0x14, 0x20, 0x02, 0x41, 0x0c, + 0x4f, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x08, 0x20, 0x02, 0x36, 0x02, 0x04, 0x20, 0x08, 0x20, 0x14, + 0x36, 0x02, 0x00, 0x20, 0x0b, 0x20, 0x06, 0x28, 0x02, 0x1c, 0x47, 0x0d, 0x09, 0x20, 0x06, 0x28, + 0x02, 0x18, 0x20, 0x04, 0x20, 0x07, 0x41, 0x03, 0x74, 0x6a, 0x41, 0x88, 0x01, 0x6a, 0x20, 0x0b, + 0x41, 0x03, 0x74, 0x10, 0x4c, 0x1a, 0x20, 0x04, 0x20, 0x03, 0x3b, 0x01, 0xe2, 0x01, 0x41, 0x01, + 0x6a, 0x22, 0x03, 0x20, 0x07, 0x6b, 0x22, 0x02, 0x20, 0x03, 0x4b, 0x0d, 0x09, 0x20, 0x05, 0x2f, + 0x01, 0xe2, 0x01, 0x22, 0x03, 0x41, 0x01, 0x6a, 0x21, 0x0b, 0x20, 0x03, 0x41, 0x0c, 0x4f, 0x0d, + 0x05, 0x20, 0x02, 0x20, 0x0b, 0x47, 0x0d, 0x09, 0x20, 0x05, 0x41, 0xe4, 0x01, 0x6a, 0x20, 0x04, + 0x20, 0x07, 0x41, 0x02, 0x74, 0x6a, 0x41, 0xe4, 0x01, 0x6a, 0x20, 0x02, 0x41, 0x02, 0x74, 0x10, + 0x4c, 0x1a, 0x20, 0x06, 0x41, 0x10, 0x6a, 0x20, 0x05, 0x20, 0x01, 0x10, 0x07, 0x20, 0x06, 0x28, + 0x02, 0x14, 0x21, 0x05, 0x20, 0x06, 0x28, 0x02, 0x10, 0x21, 0x02, 0x20, 0x01, 0x21, 0x03, 0x20, + 0x10, 0x41, 0x01, 0x71, 0x21, 0x07, 0x20, 0x06, 0x20, 0x11, 0x36, 0x02, 0x68, 0x20, 0x06, 0x20, + 0x12, 0x04, 0x7f, 0x20, 0x02, 0x21, 0x03, 0x20, 0x05, 0x05, 0x20, 0x04, 0x0b, 0x36, 0x02, 0x64, + 0x20, 0x06, 0x20, 0x03, 0x36, 0x02, 0x60, 0x20, 0x06, 0x20, 0x15, 0x37, 0x02, 0x74, 0x20, 0x06, + 0x20, 0x0f, 0x36, 0x02, 0x70, 0x20, 0x06, 0x41, 0xe0, 0x00, 0x6a, 0x20, 0x06, 0x41, 0xf0, 0x00, + 0x6a, 0x20, 0x0c, 0x20, 0x0d, 0x20, 0x0a, 0x10, 0x0f, 0x20, 0x09, 0x21, 0x0f, 0x20, 0x16, 0x21, + 0x15, 0x20, 0x05, 0x21, 0x0a, 0x20, 0x0e, 0x21, 0x0c, 0x20, 0x04, 0x21, 0x03, 0x0c, 0x00, 0x0b, + 0x00, 0x0b, 0x20, 0x06, 0x20, 0x02, 0x36, 0x02, 0x78, 0x20, 0x06, 0x20, 0x0d, 0x36, 0x02, 0x74, + 0x20, 0x06, 0x20, 0x09, 0x36, 0x02, 0x70, 0x20, 0x06, 0x41, 0xd0, 0x00, 0x6a, 0x20, 0x06, 0x41, + 0xf0, 0x00, 0x6a, 0x20, 0x0b, 0x20, 0x05, 0x10, 0x0e, 0x0c, 0x05, 0x0b, 0x20, 0x00, 0x41, 0x1c, + 0x6a, 0x28, 0x02, 0x00, 0x22, 0x03, 0x45, 0x0d, 0x06, 0x20, 0x00, 0x28, 0x02, 0x18, 0x21, 0x01, + 0x10, 0x12, 0x22, 0x04, 0x20, 0x03, 0x36, 0x02, 0xe4, 0x01, 0x20, 0x01, 0x20, 0x01, 0x41, 0x01, + 0x6a, 0x22, 0x03, 0x4b, 0x0d, 0x06, 0x20, 0x06, 0x41, 0x08, 0x6a, 0x20, 0x04, 0x20, 0x03, 0x10, + 0x07, 0x20, 0x06, 0x28, 0x02, 0x08, 0x21, 0x03, 0x20, 0x00, 0x20, 0x06, 0x28, 0x02, 0x0c, 0x22, + 0x01, 0x36, 0x02, 0x1c, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x18, 0x20, 0x03, 0x41, 0x01, 0x6b, + 0x22, 0x04, 0x20, 0x03, 0x4b, 0x0d, 0x06, 0x20, 0x02, 0x20, 0x04, 0x47, 0x0d, 0x06, 0x20, 0x01, + 0x2f, 0x01, 0xe2, 0x01, 0x22, 0x02, 0x41, 0x0a, 0x4b, 0x0d, 0x06, 0x20, 0x01, 0x20, 0x02, 0x41, + 0x01, 0x6a, 0x22, 0x04, 0x3b, 0x01, 0xe2, 0x01, 0x20, 0x01, 0x20, 0x02, 0x41, 0x0c, 0x6c, 0x6a, + 0x22, 0x05, 0x41, 0x08, 0x6a, 0x20, 0x15, 0x37, 0x02, 0x00, 0x20, 0x05, 0x41, 0x04, 0x6a, 0x20, + 0x0f, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, 0x02, 0x41, 0x03, 0x74, 0x6a, 0x22, 0x02, 0x41, 0x8c, + 0x01, 0x6a, 0x20, 0x07, 0x41, 0x01, 0x71, 0x3a, 0x00, 0x00, 0x20, 0x02, 0x41, 0x88, 0x01, 0x6a, + 0x20, 0x0c, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, 0x04, 0x41, 0x02, 0x74, 0x6a, 0x41, 0xe4, 0x01, + 0x6a, 0x20, 0x0a, 0x36, 0x02, 0x00, 0x20, 0x06, 0x20, 0x04, 0x36, 0x02, 0x78, 0x20, 0x06, 0x20, + 0x01, 0x36, 0x02, 0x74, 0x20, 0x06, 0x20, 0x03, 0x36, 0x02, 0x70, 0x20, 0x06, 0x41, 0xf0, 0x00, + 0x6a, 0x10, 0x08, 0x0c, 0x04, 0x0b, 0x20, 0x06, 0x41, 0xe0, 0x00, 0x6a, 0x10, 0x18, 0x20, 0x03, + 0x20, 0x04, 0x41, 0x03, 0x74, 0x6a, 0x22, 0x01, 0x41, 0x8c, 0x01, 0x6a, 0x20, 0x05, 0x3a, 0x00, + 0x00, 0x20, 0x01, 0x41, 0x88, 0x01, 0x6a, 0x22, 0x01, 0x28, 0x02, 0x00, 0x20, 0x01, 0x20, 0x0b, + 0x36, 0x02, 0x00, 0x41, 0x08, 0x10, 0x0a, 0x0c, 0x04, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x20, 0x06, + 0x20, 0x15, 0x37, 0x02, 0x74, 0x20, 0x06, 0x20, 0x0f, 0x36, 0x02, 0x70, 0x20, 0x06, 0x41, 0xd0, + 0x00, 0x6a, 0x20, 0x06, 0x41, 0xf0, 0x00, 0x6a, 0x20, 0x0c, 0x20, 0x0d, 0x20, 0x0a, 0x10, 0x0f, + 0x0b, 0x20, 0x00, 0x41, 0x20, 0x6a, 0x22, 0x01, 0x28, 0x02, 0x00, 0x22, 0x02, 0x41, 0x01, 0x6a, + 0x22, 0x03, 0x20, 0x02, 0x49, 0x0d, 0x01, 0x20, 0x01, 0x20, 0x03, 0x36, 0x02, 0x00, 0x0b, 0x20, + 0x00, 0x28, 0x02, 0x14, 0x22, 0x01, 0x41, 0x01, 0x6a, 0x22, 0x02, 0x20, 0x01, 0x48, 0x0d, 0x00, + 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x14, 0x20, 0x06, 0x41, 0x80, 0x01, 0x6a, 0x24, 0x00, 0x0f, + 0x0b, 0x00, 0x0b, 0xba, 0x01, 0x02, 0x05, 0x7f, 0x01, 0x7e, 0x23, 0x00, 0x41, 0x10, 0x6b, 0x22, + 0x01, 0x24, 0x00, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x20, 0x00, 0x41, 0x0c, 0x6a, 0x22, 0x02, 0x10, + 0x46, 0x02, 0x40, 0x20, 0x01, 0x28, 0x02, 0x0c, 0x22, 0x03, 0x28, 0x02, 0x00, 0x22, 0x04, 0x41, + 0x01, 0x6b, 0x22, 0x05, 0x20, 0x04, 0x4e, 0x0d, 0x00, 0x20, 0x01, 0x28, 0x02, 0x08, 0x2d, 0x00, + 0x04, 0x20, 0x03, 0x20, 0x05, 0x36, 0x02, 0x00, 0x41, 0x02, 0x46, 0x04, 0x40, 0x20, 0x00, 0x28, + 0x02, 0x00, 0x20, 0x00, 0x28, 0x02, 0x08, 0x10, 0x41, 0x21, 0x06, 0x20, 0x02, 0x22, 0x00, 0x28, + 0x02, 0x00, 0x04, 0x40, 0x00, 0x0b, 0x20, 0x00, 0x41, 0x7f, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, + 0x00, 0x36, 0x02, 0x04, 0x20, 0x01, 0x20, 0x00, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x01, + 0x28, 0x02, 0x04, 0x21, 0x00, 0x20, 0x01, 0x28, 0x02, 0x00, 0x20, 0x06, 0xa7, 0x41, 0x7f, 0x73, + 0x41, 0x01, 0x71, 0x20, 0x06, 0x42, 0x20, 0x88, 0xa7, 0x10, 0x39, 0x20, 0x00, 0x28, 0x02, 0x00, + 0x22, 0x02, 0x41, 0x01, 0x6a, 0x22, 0x03, 0x20, 0x02, 0x48, 0x0d, 0x01, 0x20, 0x00, 0x20, 0x03, + 0x36, 0x02, 0x00, 0x0b, 0x20, 0x01, 0x41, 0x10, 0x6a, 0x24, 0x00, 0x0f, 0x0b, 0x00, 0x0b, 0xd2, + 0x01, 0x01, 0x01, 0x7f, 0x02, 0x40, 0x20, 0x01, 0x41, 0xff, 0xff, 0xff, 0xff, 0x03, 0x71, 0x20, + 0x01, 0x47, 0x0d, 0x00, 0x20, 0x02, 0x20, 0x02, 0x41, 0x40, 0x6b, 0x22, 0x03, 0x4b, 0x0d, 0x00, + 0x20, 0x03, 0x41, 0xff, 0xff, 0xff, 0xff, 0x01, 0x71, 0x20, 0x03, 0x47, 0x0d, 0x00, 0x20, 0x01, + 0x41, 0x02, 0x74, 0x22, 0x01, 0x20, 0x03, 0x41, 0x03, 0x74, 0x22, 0x02, 0x20, 0x01, 0x20, 0x02, + 0x4b, 0x1b, 0x22, 0x02, 0x41, 0x08, 0x6a, 0x22, 0x01, 0x20, 0x02, 0x49, 0x0d, 0x00, 0x02, 0x7f, + 0x02, 0x40, 0x20, 0x01, 0x20, 0x01, 0x41, 0x80, 0x80, 0x04, 0x6a, 0x22, 0x02, 0x4d, 0x04, 0x40, + 0x20, 0x02, 0x41, 0x01, 0x6b, 0x22, 0x01, 0x20, 0x02, 0x4d, 0x0d, 0x01, 0x0b, 0x00, 0x0b, 0x20, + 0x01, 0x41, 0x10, 0x76, 0x22, 0x02, 0x40, 0x00, 0x22, 0x01, 0x41, 0x7f, 0x46, 0x04, 0x40, 0x41, + 0x00, 0x21, 0x01, 0x41, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x01, 0x41, 0xff, 0xff, 0x03, 0x71, 0x20, + 0x01, 0x47, 0x0d, 0x01, 0x20, 0x02, 0x41, 0x10, 0x74, 0x22, 0x02, 0x41, 0x08, 0x6b, 0x20, 0x02, + 0x4b, 0x0d, 0x01, 0x20, 0x01, 0x41, 0x10, 0x74, 0x22, 0x01, 0x42, 0x00, 0x37, 0x02, 0x04, 0x20, + 0x01, 0x20, 0x01, 0x20, 0x02, 0x6a, 0x41, 0x02, 0x72, 0x36, 0x02, 0x00, 0x41, 0x00, 0x0b, 0x21, + 0x02, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x00, 0x0f, + 0x0b, 0x00, 0x0b, 0xaf, 0x04, 0x01, 0x0a, 0x7f, 0x20, 0x00, 0x41, 0x02, 0x74, 0x21, 0x06, 0x41, + 0x00, 0x20, 0x01, 0x6b, 0x21, 0x08, 0x20, 0x00, 0x41, 0xff, 0xff, 0xff, 0xff, 0x03, 0x71, 0x20, + 0x00, 0x47, 0x21, 0x09, 0x20, 0x01, 0x41, 0x01, 0x6b, 0x22, 0x0a, 0x20, 0x01, 0x4b, 0x21, 0x0b, + 0x20, 0x02, 0x28, 0x02, 0x00, 0x21, 0x00, 0x02, 0x40, 0x03, 0x40, 0x20, 0x00, 0x45, 0x0d, 0x01, + 0x20, 0x00, 0x21, 0x01, 0x02, 0x40, 0x03, 0x40, 0x02, 0x40, 0x20, 0x01, 0x28, 0x02, 0x08, 0x22, + 0x00, 0x41, 0x01, 0x71, 0x45, 0x04, 0x40, 0x20, 0x09, 0x0d, 0x03, 0x20, 0x01, 0x28, 0x02, 0x00, + 0x41, 0x7c, 0x71, 0x22, 0x03, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x22, 0x05, 0x6b, 0x22, 0x04, 0x20, + 0x03, 0x4b, 0x0d, 0x03, 0x20, 0x04, 0x20, 0x06, 0x49, 0x0d, 0x01, 0x20, 0x03, 0x20, 0x06, 0x6b, + 0x22, 0x0c, 0x20, 0x03, 0x4b, 0x0d, 0x03, 0x20, 0x0b, 0x0d, 0x03, 0x20, 0x05, 0x41, 0x08, 0x6a, + 0x22, 0x04, 0x20, 0x05, 0x49, 0x0d, 0x03, 0x20, 0x04, 0x20, 0x04, 0x41, 0x40, 0x6b, 0x22, 0x04, + 0x4b, 0x0d, 0x03, 0x02, 0x40, 0x20, 0x04, 0x20, 0x08, 0x20, 0x0c, 0x71, 0x22, 0x04, 0x4b, 0x04, + 0x40, 0x20, 0x05, 0x20, 0x0a, 0x71, 0x0d, 0x03, 0x20, 0x02, 0x20, 0x00, 0x41, 0x7c, 0x71, 0x36, + 0x02, 0x00, 0x20, 0x01, 0x20, 0x01, 0x28, 0x02, 0x00, 0x41, 0x01, 0x72, 0x36, 0x02, 0x00, 0x20, + 0x01, 0x21, 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x04, 0x41, 0x08, 0x6b, 0x22, 0x00, 0x20, 0x04, 0x4b, + 0x0d, 0x04, 0x20, 0x03, 0x20, 0x00, 0x6b, 0x22, 0x02, 0x20, 0x03, 0x4b, 0x0d, 0x04, 0x20, 0x02, + 0x41, 0x08, 0x6b, 0x20, 0x02, 0x4b, 0x0d, 0x04, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x08, 0x20, + 0x00, 0x42, 0x00, 0x37, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x28, 0x02, 0x00, 0x41, 0x7c, 0x71, + 0x36, 0x02, 0x00, 0x02, 0x40, 0x20, 0x01, 0x28, 0x02, 0x00, 0x22, 0x02, 0x41, 0x7c, 0x71, 0x22, + 0x03, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x03, 0x20, 0x02, 0x41, 0x02, 0x71, 0x1b, 0x22, 0x02, + 0x45, 0x0d, 0x00, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x04, 0x41, 0x03, 0x71, 0x20, 0x00, 0x72, + 0x36, 0x02, 0x04, 0x0b, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x04, 0x41, 0x03, 0x71, 0x20, 0x01, + 0x72, 0x36, 0x02, 0x04, 0x20, 0x01, 0x20, 0x01, 0x28, 0x02, 0x08, 0x41, 0x7e, 0x71, 0x36, 0x02, + 0x08, 0x20, 0x01, 0x20, 0x01, 0x28, 0x02, 0x00, 0x22, 0x02, 0x41, 0x03, 0x71, 0x20, 0x00, 0x72, + 0x22, 0x03, 0x36, 0x02, 0x00, 0x02, 0x40, 0x20, 0x02, 0x41, 0x02, 0x71, 0x45, 0x04, 0x40, 0x20, + 0x00, 0x28, 0x02, 0x00, 0x21, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x01, 0x20, 0x03, 0x41, 0x7d, 0x71, + 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x02, 0x72, 0x22, 0x01, 0x36, + 0x02, 0x00, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x41, 0x01, 0x72, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x00, + 0x41, 0x08, 0x6a, 0x21, 0x07, 0x0c, 0x05, 0x0b, 0x20, 0x01, 0x20, 0x00, 0x41, 0x7e, 0x71, 0x36, + 0x02, 0x08, 0x02, 0x7f, 0x41, 0x00, 0x20, 0x01, 0x28, 0x02, 0x04, 0x41, 0x7c, 0x71, 0x22, 0x00, + 0x45, 0x0d, 0x00, 0x1a, 0x41, 0x00, 0x20, 0x00, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x01, 0x71, + 0x1b, 0x0b, 0x21, 0x00, 0x20, 0x01, 0x10, 0x4b, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x41, 0x02, 0x71, + 0x04, 0x40, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x02, 0x72, 0x36, 0x02, 0x00, 0x0b, + 0x20, 0x02, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x7c, 0x71, 0x22, + 0x01, 0x20, 0x00, 0x6b, 0x41, 0x08, 0x6b, 0x20, 0x01, 0x4b, 0x0d, 0x02, 0x20, 0x00, 0x21, 0x01, + 0x0c, 0x01, 0x0b, 0x0b, 0x20, 0x02, 0x20, 0x00, 0x36, 0x02, 0x00, 0x0c, 0x01, 0x0b, 0x0b, 0x00, + 0x0b, 0x20, 0x07, 0x0b, 0x7d, 0x01, 0x02, 0x7f, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, + 0x01, 0x41, 0x7c, 0x71, 0x22, 0x02, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x02, 0x20, 0x01, 0x41, + 0x02, 0x71, 0x1b, 0x22, 0x01, 0x45, 0x0d, 0x00, 0x20, 0x01, 0x20, 0x01, 0x28, 0x02, 0x04, 0x41, + 0x03, 0x71, 0x20, 0x00, 0x28, 0x02, 0x04, 0x41, 0x7c, 0x71, 0x72, 0x36, 0x02, 0x04, 0x0b, 0x20, + 0x00, 0x20, 0x00, 0x28, 0x02, 0x04, 0x22, 0x01, 0x41, 0x7c, 0x71, 0x22, 0x02, 0x04, 0x7f, 0x20, + 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x41, 0x03, 0x71, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x7c, + 0x71, 0x72, 0x36, 0x02, 0x00, 0x20, 0x00, 0x28, 0x02, 0x04, 0x05, 0x20, 0x01, 0x0b, 0x41, 0x03, + 0x71, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x03, 0x71, 0x36, 0x02, + 0x00, 0x0b, 0x2b, 0x01, 0x01, 0x7f, 0x03, 0x40, 0x20, 0x02, 0x20, 0x03, 0x46, 0x45, 0x04, 0x40, + 0x20, 0x00, 0x20, 0x03, 0x6a, 0x20, 0x01, 0x20, 0x03, 0x6a, 0x2d, 0x00, 0x00, 0x3a, 0x00, 0x00, + 0x20, 0x03, 0x41, 0x01, 0x6a, 0x21, 0x03, 0x0c, 0x01, 0x0b, 0x0b, 0x20, 0x00, 0x0b, 0x6a, 0x00, + 0x02, 0x40, 0x20, 0x02, 0x20, 0x00, 0x20, 0x01, 0x6b, 0x4d, 0x04, 0x40, 0x03, 0x40, 0x20, 0x02, + 0x45, 0x0d, 0x02, 0x20, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x20, 0x02, 0x41, + 0x01, 0x6b, 0x21, 0x02, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x20, 0x00, 0x41, 0x01, 0x6a, + 0x21, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x0b, 0x20, 0x01, 0x41, 0x01, 0x6b, 0x21, 0x01, 0x20, 0x00, + 0x41, 0x01, 0x6b, 0x21, 0x00, 0x03, 0x40, 0x20, 0x02, 0x45, 0x0d, 0x01, 0x20, 0x00, 0x20, 0x02, + 0x6a, 0x20, 0x01, 0x20, 0x02, 0x6a, 0x2d, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x20, 0x02, 0x41, 0x01, + 0x6b, 0x21, 0x02, 0x0c, 0x00, 0x0b, 0x00, 0x0b, 0x0b, 0x22, 0x01, 0x01, 0x7f, 0x03, 0x40, 0x20, + 0x01, 0x20, 0x02, 0x47, 0x04, 0x40, 0x20, 0x00, 0x20, 0x02, 0x6a, 0x41, 0x00, 0x3a, 0x00, 0x00, + 0x20, 0x02, 0x41, 0x01, 0x6a, 0x21, 0x02, 0x0c, 0x01, 0x0b, 0x0b, 0x0b, 0x3f, 0x01, 0x02, 0x7f, + 0x03, 0x40, 0x20, 0x02, 0x45, 0x04, 0x40, 0x41, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x41, 0x01, 0x6b, + 0x21, 0x02, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x21, 0x03, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x21, 0x04, + 0x20, 0x00, 0x41, 0x01, 0x6a, 0x21, 0x00, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x20, 0x03, + 0x20, 0x04, 0x46, 0x0d, 0x00, 0x0b, 0x20, 0x04, 0x20, 0x03, 0x6b, 0x0b, 0x0b, 0xd7, 0x02, 0x03, + 0x00, 0x41, 0x80, 0x80, 0xc0, 0x00, 0x0b, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x41, 0xc2, 0x81, 0xc0, 0x00, 0x0b, + 0x33, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x00, 0x41, 0x80, 0x82, 0xc0, 0x00, 0x0b, 0x89, 0x01, 0x75, 0x6e, 0x6b, + 0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x3a, 0x20, 0x69, + 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x60, 0x60, 0x3a, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, + 0x66, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x61, 0x6c, 0x69, 0x63, 0x65, 0x62, 0x6f, + 0x62, 0x63, 0x68, 0x61, 0x72, 0x6c, 0x69, 0x65, 0x64, 0x61, 0x76, 0x69, 0x64, 0x66, 0x72, 0x6f, + 0x6d, 0x74, 0x6f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x63, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x61, 0x64, + 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74}; +constexpr unsigned int transfer_wasm_len = 17073; diff --git "a/BFPL\345\243\271/bcos-executor/test/liquid/transfer.rs" "b/BFPL\345\243\271/bcos-executor/test/liquid/transfer.rs" new file mode 100644 index 00000000..331b0a6d --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/liquid/transfer.rs" @@ -0,0 +1,39 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use liquid::storage; +use liquid_lang as liquid; + +#[liquid::contract] +mod transfer { + use super::*; + + #[liquid(storage)] + struct Transfer { + accounts: storage::Mapping, + } + + #[liquid(methods)] + impl Transfer { + pub fn new(&mut self) { + self.accounts.initialize(); + self.accounts.insert(String::from("alice"), u32::MAX); + self.accounts.insert(String::from("bob"), 0); + self.accounts.insert(String::from("charlie"), u32::MAX); + self.accounts.insert(String::from("david"), 0); + } + + pub fn transfer(&mut self, from: String ,to: String, amount: u32) -> bool { + if self.accounts[&from] < amount { + false + } else { + self.accounts[&from] -= amount; + self.accounts[&to] += amount; + true + } + } + + pub fn query(&self, name: String) -> u32 { + self.accounts[&name] + } + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/liquid/transfer.wasm" "b/BFPL\345\243\271/bcos-executor/test/liquid/transfer.wasm" new file mode 100644 index 0000000000000000000000000000000000000000..5c66724c0f210b2f274a7973588b51acb6d8b6b4 GIT binary patch literal 17073 zcmdU%ZH!$ypXi+otE=v0v8rER-}%*(B@H|| zZt1{pLk((Cw^4`s?UN^)_IA~UVY7Vrkqei%e(?0ldl%21y=QS{@%?8$eA+dvxiiu1 zf;BZCJ#+rd<;R?}r)fI8f91mR;s;N=BDY-LXeslE<m%D)hTpKi3Lybqz>Y=(|+^0pg%5x5o&er{?`L0ei|yY4!z z+so#;)8{TMe<--A#}+R?_PZ+|x_H{{Z=O1R@$7{Uxw-P(;+gaAH>RIi^Y{IhFIK~R z^s~VYf-dFZaz43!==x6dF{JL&{^0bYJt&f28kDg(Q8FNw`$L;ft`l7h`$Ng4pljzZ zA!79{F2&)X*vZH{{11GeQOO;2+hd_|+w``oH*jV6FL~j6vg8%}4yWX8(V5ys9OE!D8R0&;S%K%}^08JT} zcDl~*jB)nh>_A}pzwdv|XSUD)GX@|U>t)q+vdhd?!kOBtyYmIq=`Cl-BJppj5BWY*}8_X7CB zf;`#1J!XNBlEE{Bz~dM`GYfi~;bI|Lnfx;B5Kw8Qv8$#_vUHu!dGDpgAu}}`0~!jt zkl6wT1NaQuf>htb?9@HGq^a!c`K^wdF6$Y*i&`nA45pdzg2}}7hbN|M#Cp2M>rdB6 z#Y&z|SI^UEI-Z%XJm^Lyq$%Ew#$-W36qZy=#!L*P5g|G$CWVzHG$UI~sgY;V2+Xe< z4oayqeXt#A@xYlWVis9x$7Tj3Q)d(TQf!KgJ~m=m!H@Ke2VI^*EUK=iz6*l}MkTxX zV(^W6w&teUS~go7aAqRo*_z|oS}1I6YqlxiH<_(vv+2oZL!^xw8561Qv3K1=I3^kk zF&zyV4j8h4j2jxkljPkJV+QDrm=xPEUDpP`Vtp$=t4}t(R-7`@&~yk63p0I8LWbjt zc{Lnis%C7-RGZQK0HjDSWy|P$vvx2|V~s@7M^AuX-867!ig44}6kMg+uG2Q2SGVS_ zp9`drr%#%TdT&+*_YDu6O+v_vo#cY&()kJi^zHa9$9Q$ z+JkbKy)6+BUnFBEm9XSQ_c%2E%45)xWS(to*@L?RZy=CoNtoQXNkPoBS7KO@XmCX4 z0bo$|Mw4suhXD&`cLmX^eo7l;J4VKC=G43fz>&I2r_UcTzNFJ)mtQ9f9{dL`^UG zM{&b077;NEIX|pj8ik@Llb3?%?`rTZF?Wjp#>@p$2|>o*mPK5#w;+%j1FQ+U6jK2% zm@ohXQy$JeY>(6G+KkhBjiAJ>W`5lq(q4CMjwA#dOfGFOY}#G3mQWLTwS>4uLWZoQ z(&|rl@pm^$MI4SGD5*>j(FKzVE_PBXjW{KJYxPUXi+FE?PFOUNAY}JbM|kYS>CW4{ zf3ld5o&n4~?E6DLx_3DDi5Q+%W?+hqIWeg7$9O_XZ*T3b!h9v8DU5P+rRk~mU98#U zV#uR@Ek}!@0;E<3HVcL)2ZD!?Qz9l+dXbFNL4Iimc2}_qNjsKOS=|nY18zv8wh5xW zn+*|`Fj7a&G0NBKFttNVA)=~W z;Lec~x9r>CSYCzJ=CpgD&^*9A34+MNB?{yrQV zl!}m0Wv*4^Rc~S(u9Z-cbvsD6JF)D1k~Q*p#+)d@gebq2GYG}M>}xeYnXys~5k50fLd+SAQf(#x+pVhnIJLobymiUQ-rw7t1O_1$mk>^YG1tAwZ%9Xix6n*h z7(@^Xa?tg}HxZ35m7tkMmSm-dB3sg1ad8CP9l(9%6_QKFC4-?n^)+DJaEyX zafde;9#=mCVL3&Ki_!em+cXdHR+rMIvkB>B3p!g#8fF25wqB$<>ReT~BU8Pqwp$3- z^0YhX9*WOAH)xPEp%{cg@*F&0DwbF~$8y+jVVZ)|Q(4;U5vZWT8=D3#A+EX8U`UyJmP9=~5B)blX9U_@qTZ{c^ zyQ%st_(y+4*tNC!;T}i6Fx*nO;I}}wbUG-shC>d&-tFizZa&eIMj5*OFahXgq)pMx zxtxpr4Pm8Al-jyv$ps5RbUafBZN0Afu=KLaulEAg9C6)))MlFQp8?3UFtUXu{2EW4 zu9MfS`6InH$f*Yj9XOxREE&1F$6=^22CTd8^kzz!<3>tg$hxIb&GfI9U$)YlGlpG7 z?vO@g{TI{!8`EzhN+t`0OX(`_EPWc$TvK$FBI)!dN~dbLQ$p|cscqTN8wfli4h2O% zWjq+0Qm?Fc!Sp4_hXs2}d$~mNnA}mQnedP|-18dG-EG#Ib6p{_R-(>^7O4?*neWSS zI(D9Zx-Ur~0%miuJ7-Zq%Nn6v#OXPe7}YMbr(I?*)4{!JI{FY62~USCdB4M#7Txea zs#E$)3OD#=oGhW{cGffIgsxc7v}QOUHVmuVJ4Yv-JdN_k_$fx>>lLO2jCkC&HF08< zOfa5Y^3u^9;=(3C?In2n{yE+T-Ud|`Yyxgh`3pIWC{vB-=VhZ1ZB z2PJ(El(Kp|INWW)h#RUScUU;U8Crxgcep|g?k>ND?nTVpSU;Y9goA{u6y*(O^mMo&Wiq zti2tbG8@^Zd?#7KOW7cb0>W6FAX|sMOVyp7FBGiLa?&gAVKB8B!J0s@vb)5dwdb(E~4`hu;u-WWyWiZOiCY zS~J;TW@*S0Hq%xQw$kT_HmMS{wOM``o<<1PI3`33aruV;dK96FxNut!l!#Rstp#Do zN=cmQh^kCA>82Wc<=Kv6Dw$>}4$Bf-uQ9plm$VF>wEJx;g^^_1T167th7$#lX_M>h zMY4JiL-2wNGt~(zW+s;Qu{ffiV>4SjiM6Cg9G&TuTsnVH=DP4z73`!OC~Wl(F5e>v zyK1MQM)}|&bG7Nmj?#E~$t~^Dwu)wCgXIHr#YI|Ge|G67E|<9LcPn$nnkEG5fefOO zyL4$Mp?Lt4U2P|;(s__K*NK!$+s;C*w)GiLVwK=|^=>gzD>h6FZXV4^Pc|3)FseF& z7?MGxMG(1cKvdaP`Z}Q4!M280nnXQf3IY{$%XL6S&|xUh#pKWWrSvVighsW)2&&sw ztAq93cl5Tn@vR%b{jT1&ZhX5j_WQ2_Igf>NcQL`5&lx4R%8&j-{kIu39;^4{=DdJp zgq+LnN#`&-(n*oBFL!u>fp_wF)uV{2H|$1e)lpj5>OH!UA z5L6kR>13`&Qp{k|oV@@!NwIu&#DH3f%=lOPWvfMMz&7Klpl#yfc)vmKa^Q$~02o>8 zT@z7y{@d8AxHje6^5$z;Yz~IV5%^sUsyZzDxPKr%vf7{rJb9qs2=lO#H%AfJpqW*^ zp@oo@nDN=n=+pcV7H&2mT0}mV+JFFa5B<2z4~mfjpd>bDL8k=n%r7Oe9aQOn*&C7I z5R4LTX29$eE8_%3gvR^_v;q9{%>SMo@ed42n_qk{P4Q|2-f$0O=SHFg)_}-NuqvzY z1g7mav(VBex%&Wv2i+fb^J1S~m}1hm;6K|MD69^#*WLvSEi5H};I)G-=N5XXK4dVI z{)!J4@uZXwVIq+EAQvLe`=EBO!qtnIplwL+12Qb#E6&Dd?vLkDe=t)2*&F)r&LN8Y zV^qwLrSw%q$2$vxIJ(Yl&OXQ_icL5a^RU3ey>yrg&0`rMNokP^8d-4%wzKm5fh&IaQHc)^UCy*F=?_CZ!+ZlA$2<_vNjvUI8K_uF7 zEkrF;NGrz3QAaQ>QGr`p*WN%5xyfQ@VHCiz1K?U!PW)-tR)!muYc7S^C`{7(v9U_N zb-W5NKSgZWW8e(o3VoMvgJ)zxFTRl?Q&5rVWex#2OGL;(EEBm(YV9o(u|!F}Y~xQc z=qfxi->XQ50e&T90~K}rrE6h{9BR;>D?aZ<*0Zd8)lF-|+=o}!Jh)tY=KFv3VR>zz zp8vB?ef~++S$z7*w_bW7P%;1*5$q~gY&?}Lp^|Vbs~z$WJYnYtxLmVPjoP~e`~xX; zr?}V5Ut#<=m|OSPxc{2)SPw$_YYf_@_Vs#K=LMtXhZH#^f|{0H#d?@4uD`}Gp;%PW+fMj>f{rYNMAFp6cnSml&xIN@_F-SEdce54!bwcFT~~ z;UFX>JjsDBN?J>2IsLP8rKaUdsD_9Z-)E78wLNdxe+eZari z1x==TnX6)lcny>hXSkw@Z?9YSYJLp>3-%`_`Wbs#6YTks*n<+&2lfn%HNtu}Jf9hq zl>=OAE7Gi=6=r&#vSmQ}A-=%F5v>WRMdQw5{pQC!C@g->=rk&oN(nbndlh|NXEudG z^G&&javIy{Fo5&JdTGuOV-dP{fLc^$v#jcW!g7m@8QuoVAtZ-P$yb=QpCeaTwJmA( zjX0J@iU3x9rsnnbeoGfNmJYL5ON1idK5?)1f@Q{LSh(PYUW;jDlN^f4k)x?} zvXxeWD{ya7hR|niL|DdYDJIcO$Z*v_rBb{1+%rLJEzGbU6$RA6(D9w>9&<5hqvS5i!YPdXYD zV6fRMgUroC2&R`-bIe&2*2J=X%4*C()sI3&!G=>jjmVCL*WtVDYHOS3@^nqanw{fx zLTXKLX1Vmy9qPul!#hZv<5}h|FlNTG0JRW|j4Acn^q0$w+07jjvHmK)=mux~4BVcK z=#HoV559=n!I+4bL`1mJq5e`35mZj{)vsE;L($6iqpxKRoDBy$eN%BIX79`?7<#SE zJ!rR#NXCM>U^{H`InN5n;+u-6q#qcKnLoaqNL#E+opanU?>~O+65gNKQU?YMEd7yK zn#&!sH_U_4T6>Tto6V&^SC7ITEkx48TOw5bdhVe?59#ORsFNiW7&Y%vtBvV*5#{S> zOEu$5LbJBQw663+3y>`Ai7JfsG#4Px_N5+j*J9?z(5)V|yD}68vhL z05#?WgdJknY6nGoGrg?Z%{HXAQ^@x@;ZBx7qNg`7z>|LUG4vySC=XYdaEx(Y6&-swNknJoFUdA1n}7tcnry(^G5gT6%+%583L#9&5ba#`V7S zXX(vQ2JWc0L)4QsWlj{Z;9W&ESIjbk#YrHEV&h#pk=&6l$MCMew)rQXmKaqkSB3IZ zK5xF|qX;&6j^kA6%*3?WPxX?;v?dB8Vze^mqXSYF-rK31H%)LryXG8}lFDRyY*|?E z?~}_3VX)Fqw-Sqtw|(TsE>u|``INfJHe@O0=R)USd@inMZ0-Dzc4nlvRx*(>@L>BU zr4^D`!e^o9TET#fN8d^`up4Rwl>8CMd_h7YhKbV3zPjiRT6Sm`B(1Oo^Hr~LR=cU* zmG{rL=pz&rWBQ;<$&YkNh;Dr9p*(E#sR#8Js2v}SZK4g7?s6SOl@Fdtjz+9_HiRHLiV@mK;vzj~<1s*+_+y90IG1+RDoa+*(m6Wx z>#%yOdcvg|G(;so?JALlj)v+4TjPPrp|(l;^Ppqfhz3~`v{l}*(gy&o^f8)3hvz1- zGYg5W=hEtBHl|}s87cns2&|B{NO||5Fcy+jI0?z!Z>)jrIx0$cuD-)**z7kjsn#@n*V+&FmO-|wkne$+U7KL<`&45-T2jJ^Hg6c_dx zPr~q#PFj|0YYp>oRW%^V)Ur@o!nD#m(^4J6r0oensNkyzaglorJ8S8R^=(p6rlX2^ zP2q9RU|LGb-F&}fT245`X^D@Y_J)#{rlcHR<=`Kg1?!}GCRO_ESU+=-Zq%TS#P^^M zz&+5{Q3&lTg5CRs-IqCtARjUp9%M$tK6SLIZg>%seucXq+m==TCV^P#->l2Uepyxh z={#c4<%rI}$L>Op)e}S_501sQIpHw+#=_Qe8~|*`E`OZz*`j*{rX9h-*1A^M@5Zfl z3DMrE?5C<5Oz3Z={?=9rqJIl0?Nupvnpb@sd+LHQ#>V7Qk#?-4Fk6J@KF+%6x)*q@ zdMh$+;YLTAn>bVhgT4L?IJ;{&vj(T$#RL!^zu4L6(!Pdh#+$PKz?gXR3`)crvQ~zZ zup8}?PM}?q6xt<8t+SHP+{X1(TQCkgv>T*NvUEHlep6hfg0Rq&mLjKhFXelP2bAkU zE|9J&xF7O;2c0#Q;&rH8R^+s-3Y^tvulm^remj;DwK%QT*ZYv zTj|39V~@!1%fEL|X!RO90@cXh-~g-sm<9&u@|SSt#Jx7xfKIxV47R7CeAU`Ckb9% z_f%7w&^?_IA3M8yRn?HULn{XGVt;takMz(=9`-#HFEUN}2rN`}RjR`)Y6P)UibE&W zNXQ|MaIp_5@$b`W!@G;Y*?#mO-DxO#Lo1OBWr&b#(ZhTNAIVUGl6u=|5=lAQw=L{- zjM+Y|5oQ{)H}H|YfsgD>{!?W z#ULieJRC9sMDk@vT7?3BN>M3qq9cC=a8ke=p4LV?&0qJSF+Tk}N_zp@B`{Wpan)qA z-Mo#-HiNb>N`-S;+idwklB&na4zVeyYf5DR$Y7vnbC-3_;YT^=DCYo3**945a1^hb ziq{-3(C9HyM+sLZXNbxh6$;~j=ZWJ&k#kp^sk#(x^G7~pp6~y3I)hIq_(v8{=>UO?jwNahR%j6Y9^WnEJ{f%BWbf0pG6Xri=`%iQTk~u&WL{mPwMnIDmS9f z9U*s+>m3mA7Vrv6wfoM3JWDYjK}Zi-Y;D$L`DETyKJH_mmlgnkHV8Ox@!Ijg*Iuz81(>l{iqFsM`3W?xkpPzy~U#7qv zKFwe&N#7SpVO;Itv?$0-Ql*tV0Yb$lX{o`mk6AM*(gZ$@)d4A(!h93oz={b(_e;^i zR#ktbI@X|XV2$bMq#xWL_Y7RU3j+sDplHcQ?PFR4hdh>TV?f{$fCMPx zBbjZV7;7$jaQft9wSa+qQ)LXaqxbwkpWIua;H6qH%<3!|s~PWy*p@~q z;o!0E98hcpDvc3Cx#SO<)HKXZ!@kP{>3}Ik+0zZfEedr887PW8u@e z8@?GR*)a!XMzaHrh$w+kl+Q?vP{I&65u?>tG3<2yAruZ*aou&+{Qvbom;UxC5V@28 z--FlrS8V!M>K`?OkDni&zwmqKx$x7SE}}`Neam7ccYw_CWWur{ehwD{=Ys;wfgl`1s0y14vda A)&Kwi literal 0 HcmV?d00001 diff --git "a/BFPL\345\243\271/bcos-executor/test/old/EVMPrecompiledTest.cpp" "b/BFPL\345\243\271/bcos-executor/test/old/EVMPrecompiledTest.cpp" new file mode 100644 index 00000000..8192194b --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/old/EVMPrecompiledTest.cpp" @@ -0,0 +1,1743 @@ +/** + * Copyright (C) 2021 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 EVMPrecompiledTest.cpp + * @author: xingqiangbai + * @date 2021-06-02 + */ + +#include "bcosutilities/DataConvertUtility.h" +#include "vm/Precompiled.h" +#include "wedpr-crypto/WedprCrypto.h" +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::crypto; +using namespace bcos::executor; +namespace ut = boost::unit_test; + +namespace bcos +{ +namespace test +{ +struct EVMPrecompiledTestFixture +{ + EVMPrecompiledTestFixture() {} +}; + +BOOST_FIXTURE_TEST_SUITE(EVMPrecompiledTest, EVMPrecompiledTestFixture) + +/// test ecRecover +BOOST_AUTO_TEST_CASE(testECRecover) +{ + std::pair keyPair; + std::pair KeyPairR; + bytes rlpBytes = *fromHexString( + "f8ef9f65f0d06e39dc3c08e32ac10a5070858962bc6c0f5760baca823f2d5582d03f85174876e7ff" + "8609184e729fff82020394d6f1a71052366dbae2f7ab2d5d5845e77965cf0d80b86448f85bce000000" + "000000000000000000000000000000000000000000000000000000001bf5bd8a9e7ba8b936ea704292" + "ff4aaa5797bf671fdc8526dcd159f23c1f5a05f44e9fa862834dc7cb4541558f2b4961dc39eaaf0af7" + "f7395028658d0e01b86a371ca00b2b3fabd8598fefdda4efdb54f626367fc68e1735a8047f0f1c4f84" + "0255ca1ea0512500bc29f4cfe18ee1c88683006d73e56c934100b8abf4d2334560e1d2f75e"); + + bytes rlpBytesRight = *fromHexString( + "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e" + "000000000000000000000000000000000000000000000000000000000000001b" + "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e" + "789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02"); + + h256 ret("000000000000000000000000ceaccac640adf55b2028469bd36ba501f28b699d"); + bytesConstRef _in(ref(rlpBytes)); + keyPair = ecRecover(_in); + BOOST_CHECK(keyPair.first == true); + BOOST_CHECK(keyPair.second != ret.asBytes()); + KeyPairR = ecRecover(ref(rlpBytesRight)); + cout << toHexStringWithPrefix(KeyPairR.second) << endl; + cout << toHexStringWithPrefix(ret.asBytes()) << endl; + BOOST_CHECK(KeyPairR.second == ret.asBytes()); +} + +// test sha2 +BOOST_AUTO_TEST_CASE(testSha256) +{ + const std::string plainText = "123456ABC+"; + h256 cipherText("0x2218be4abd327ca929399fc73314f3d0cdd03cfc98927fabe7cd40f2059efd01"); + bytes bs; + for (size_t i = 0; i < plainText.length(); i++) + { + bs.push_back((byte)plainText[i]); + } + bytesConstRef bsConst(&bs); + BOOST_CHECK(sha256(bsConst) == cipherText); +} + +BOOST_AUTO_TEST_CASE(testRipemd160) +{ + const std::string plainText = "123456ABC+"; + h160 cipherText("0x74204bedd818292adc1127f9bb24bafd75468b62"); + bytes bs; + for (size_t i = 0; i < plainText.length(); i++) + { + bs.push_back((byte)plainText[i]); + } + bytesConstRef bsConst(&bs); + BOOST_CHECK( + ripemd160(bsConst).hexPrefixed() == string("0x74204bedd818292adc1127f9bb24bafd75468b62")); +} + +BOOST_AUTO_TEST_CASE(modexpFermatTheorem) +{ + PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); + + bytes in = *fromHexString( + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000020" + "03" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"); + auto res = exec(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE(res.first); + bytes expected = + *fromHexString("0000000000000000000000000000000000000000000000000000000000000001"); + BOOST_REQUIRE_EQUAL_COLLECTIONS( + res.second.begin(), res.second.end(), expected.begin(), expected.end()); +} + +BOOST_AUTO_TEST_CASE(modexpZeroBase) +{ + PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); + + bytes in = *fromHexString( + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000020" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"); + auto res = exec(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE(res.first); + bytes expected = + *fromHexString("0000000000000000000000000000000000000000000000000000000000000000"); + BOOST_REQUIRE_EQUAL_COLLECTIONS( + res.second.begin(), res.second.end(), expected.begin(), expected.end()); +} + +BOOST_AUTO_TEST_CASE(modexpExtraByteIgnored) +{ + PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); + + bytes in = *fromHexString( + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000002" + "0000000000000000000000000000000000000000000000000000000000000020" + "03" + "ffff" + "8000000000000000000000000000000000000000000000000000000000000000" + "07"); + auto res = exec(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE(res.first); + bytes expected = + *fromHexString("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab"); + BOOST_REQUIRE_EQUAL_COLLECTIONS( + res.second.begin(), res.second.end(), expected.begin(), expected.end()); +} + +BOOST_AUTO_TEST_CASE(modexpRightPadding) +{ + PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); + + bytes in = *fromHexString( + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000002" + "0000000000000000000000000000000000000000000000000000000000000020" + "03" + "ffff" + "80"); + auto res = exec(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE(res.first); + bytes expected = + *fromHexString("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab"); + BOOST_REQUIRE_EQUAL_COLLECTIONS( + res.second.begin(), res.second.end(), expected.begin(), expected.end()); +} + +BOOST_AUTO_TEST_CASE(modexpMissingValues) +{ + PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); + + bytes in = *fromHexString( + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000002" + "0000000000000000000000000000000000000000000000000000000000000020" + "03"); + auto res = exec(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE(res.first); + bytes expected = + *fromHexString("0000000000000000000000000000000000000000000000000000000000000000"); + BOOST_REQUIRE_EQUAL_COLLECTIONS( + res.second.begin(), res.second.end(), expected.begin(), expected.end()); +} + +BOOST_AUTO_TEST_CASE(modexpEmptyValue) +{ + PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); + + bytes in = *fromHexString( + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000020" + "03" + "8000000000000000000000000000000000000000000000000000000000000000"); + auto res = exec(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE(res.first); + bytes expected = + *fromHexString("0000000000000000000000000000000000000000000000000000000000000001"); + BOOST_REQUIRE_EQUAL_COLLECTIONS( + res.second.begin(), res.second.end(), expected.begin(), expected.end()); +} + +BOOST_AUTO_TEST_CASE(modexpZeroPowerZero) +{ + PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); + + bytes in = *fromHexString( + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000020" + "00" + "00" + "80"); + auto res = exec(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE(res.first); + bytes expected = + *fromHexString("0000000000000000000000000000000000000000000000000000000000000001"); + BOOST_REQUIRE_EQUAL_COLLECTIONS( + res.second.begin(), res.second.end(), expected.begin(), expected.end()); +} + +BOOST_AUTO_TEST_CASE(modexpZeroPowerZeroModZero) +{ + PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); + + bytes in = *fromHexString( + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000020" + "00" + "00" + "00"); + auto res = exec(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE(res.first); + bytes expected = + *fromHexString("0000000000000000000000000000000000000000000000000000000000000000"); + BOOST_REQUIRE_EQUAL_COLLECTIONS( + res.second.begin(), res.second.end(), expected.begin(), expected.end()); +} + +BOOST_AUTO_TEST_CASE(modexpModLengthZero) +{ + PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); + + bytes in = *fromHexString( + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000000" + "01" + "01"); + auto res = exec(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE(res.first); + BOOST_REQUIRE(res.second.empty()); +} + +BOOST_AUTO_TEST_CASE(modexpCostFermatTheorem) +{ + PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); + + bytes in = *fromHexString( + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000020" + "03" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"); + auto res = cost(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE_EQUAL(static_cast(res), 13056); +} + +BOOST_AUTO_TEST_CASE(modexpCostTooLarge) +{ + PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); + + bytes in = *fromHexString( + "0000000000000000000000000000000000000000000000000000000000000000" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "0000000000000000000000000000000000000000000000000000000000000020" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd"); + auto res = cost(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE_MESSAGE( + res == + bigint{ + "47428439751604713645494675459558567056699385719046375030561826409641217900517324"}, + "Got: " + toString(res)); +} + +BOOST_AUTO_TEST_CASE(modexpCostEmptyExponent) +{ + PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); + + bytes in = *fromHexString( + "0000000000000000000000000000000000000000000000000000000000000008" // length of B + "0000000000000000000000000000000000000000000000000000000000000000" // length of E + "0000000000000000000000000000000000000000000000000000000000000010" // length of M + "998877665544332211" // B + "" // E + "998877665544332211998877665544332211" // M + "9978" // Garbage that should be ignored + ); + auto res = cost(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE_MESSAGE(res == bigint{"12"}, "Got: " + toString(res)); +} + +BOOST_AUTO_TEST_CASE(modexpCostZeroExponent) +{ + PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); + + bytes in = *fromHexString( + "0000000000000000000000000000000000000000000000000000000000000000" // length of B + "0000000000000000000000000000000000000000000000000000000000000003" // length of E + "000000000000000000000000000000000000000000000000000000000000000a" // length of M + "" // B + "000000" // E + "112233445566778899aa" // M + ); + auto res = cost(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE_MESSAGE(res == bigint{"5"}, "Got: " + toString(res)); +} + +BOOST_AUTO_TEST_CASE(modexpCostApproximated) +{ + PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); + + bytes in = *fromHexString( + "0000000000000000000000000000000000000000000000000000000000000003" // length of B + "0000000000000000000000000000000000000000000000000000000000000021" // length of E + "000000000000000000000000000000000000000000000000000000000000000a" // length of M + "111111" // B + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" // E + "112233445566778899aa" // M + ); + auto res = cost(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE_MESSAGE(res == bigint{"1315"}, "Got: " + toString(res)); +} + +BOOST_AUTO_TEST_CASE(modexpCostApproximatedPartialByte) +{ + PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); + + bytes in = *fromHexString( + "0000000000000000000000000000000000000000000000000000000000000003" // length of B + "0000000000000000000000000000000000000000000000000000000000000021" // length of E + "000000000000000000000000000000000000000000000000000000000000000a" // length of M + "111111" // B + "02ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" // E + "112233445566778899aa" // M + ); + auto res = cost(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE_MESSAGE(res == bigint{"1285"}, "Got: " + toString(res)); +} + +BOOST_AUTO_TEST_CASE(modexpCostApproximatedGhost) +{ + PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); + + bytes in = *fromHexString( + "0000000000000000000000000000000000000000000000000000000000000003" // length of B + "0000000000000000000000000000000000000000000000000000000000000021" // length of E + "000000000000000000000000000000000000000000000000000000000000000a" // length of M + "111111" // B + "000000000000000000000000000000000000000000000000000000000000000000" // E + "112233445566778899aa" // M + ); + auto res = cost(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE_MESSAGE(res == bigint{"40"}, "Got: " + toString(res)); +} + +BOOST_AUTO_TEST_CASE(modexpCostMidRange) +{ + PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); + + bytes in = *fromHexString( + "0000000000000000000000000000000000000000000000000000000000000003" // length of B + "0000000000000000000000000000000000000000000000000000000000000021" // length of E + "000000000000000000000000000000000000000000000000000000000000004a" // length of M = 74 + "111111" // B + "000000000000000000000000000000000000000000000000000000000000000000" // E + "112233445566778899aa" // M + ); + auto res = cost(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE_MESSAGE( + res == ((74 * 74 / 4 + 96 * 74 - 3072) * 8) / 20, "Got: " + toString(res)); +} + +BOOST_AUTO_TEST_CASE(modexpCostHighRange) +{ + PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); + + bytes in = *fromHexString( + "0000000000000000000000000000000000000000000000000000000000000003" // length of B + "0000000000000000000000000000000000000000000000000000000000000021" // length of E + "0000000000000000000000000000000000000000000000000000000000000401" // length of M = 1025 + "111111" // B + "000000000000000000000000000000000000000000000000000000000000000000" // E + "112233445566778899aa" // M + ); + auto res = cost(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE_MESSAGE( + res == ((1025 * 1025 / 16 + 480 * 1025 - 199680) * 8) / 20, "Got: " + toString(res)); +} + +/// @defgroup PrecompiledTests Test cases for precompiled contracts. +/// +/// These test cases are used for testing and benchmarking precompiled contracts. +/// They are ported from go-ethereum, so formatting is not perfect. +/// https://github.com/ethereum/go-ethereum/blob/master/core/vm/contracts_test.go. +/// @{ + +struct PrecompiledTest +{ + const char* input; + const char* expected; + const char* name; +}; + +constexpr PrecompiledTest ecrecoverTests[] = { + {"38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e00000000000000000000000000000" + "0000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed" + "98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02", + "000000000000000000000000ceaccac640adf55b2028469bd36ba501f28b699d", ""}}; + +constexpr PrecompiledTest modexpTests[] = { + { + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000020" + "03" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "0000000000000000000000000000000000000000000000000000000000000001", + "eip_example1", + }, + { + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000020" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "0000000000000000000000000000000000000000000000000000000000000000", + "eip_example2", + }, + { + "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000" + "000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000" + "000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f" + "39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb502fc9e1f6beb81516545975218075ec2af118cd8" + "798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6" + "b", + "60008f1614cc01dcfb6bfb09c625cf90b47d4468db81b5f8b7a39d42f332eab9b2da8f2d95311648a8f243f4bb" + "13cfb3d8f7f2a3c014122ebb3ed41b02783adc", + "nagydani-1-square", + }, + { + "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000" + "000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000" + "000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f" + "39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb503fc9e1f6beb81516545975218075ec2af118cd8" + "798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6" + "b", + "4834a46ba565db27903b1c720c9d593e84e4cbd6ad2e64b31885d944f68cd801f92225a8961c952ddf2797fa47" + "01b330c85c4b363798100b921a1a22a46a7fec", + "nagydani-1-qube", + }, + { + "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000" + "000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000" + "000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f" + "39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5010001fc9e1f6beb81516545975218075ec2af11" + "8cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a24" + "8c6b", + "c36d804180c35d4426b57b50c5bfcca5c01856d104564cd513b461d3c8b8409128a5573e416d0ebe38f5f73676" + "6d9dc27143e4da981dfa4d67f7dc474cbee6d2", + "nagydani-1-pow0x10001", + }, + { + "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000" + "000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000" + "000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34" + "167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b98" + "22a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5102" + "e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af185" + "3f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4" + "e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + "981dd99c3b113fae3e3eaa9435c0dc96779a23c12a53d1084b4f67b0b053a27560f627b873e3f16ad78f28c94f" + "14b6392def26e4d8896c5e3c984e50fa0b3aa44f1da78b913187c6128baa9340b1e9c9a0fd02cb78885e72576d" + "a4a8f7e5a113e173a7a2889fde9d407bd9f06eb05bc8fc7b4229377a32941a02bf4edcc06d70", + "nagydani-2-square", + }, + { + "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000" + "000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000" + "000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34" + "167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b98" + "22a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5103" + "e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af185" + "3f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4" + "e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + "d89ceb68c32da4f6364978d62aaa40d7b09b59ec61eb3c0159c87ec3a91037f7dc6967594e530a69d049b64adf" + "a39c8fa208ea970cfe4b7bcd359d345744405afe1cbf761647e32b3184c7fbe87cee8c6c7ff3b378faba6c68b8" + "3b6889cb40f1603ee68c56b4c03d48c595c826c041112dc941878f8c5be828154afd4a16311f", + "nagydani-2-qube", + }, + { + "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000" + "000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000" + "000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34" + "167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b98" + "22a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5101" + "0001e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33a" + "f1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7" + "efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + "ad85e8ef13fd1dd46eae44af8b91ad1ccae5b7a1c92944f92a19f21b0b658139e0cabe9c1f679507c2de354bf2" + "c91ebd965d1e633978a830d517d2f6f8dd5fd58065d58559de7e2334a878f8ec6992d9b9e77430d4764e863d77" + "c0f87beede8f2f7f2ab2e7222f85cc9d98b8467f4bb72e87ef2882423ebdb6daf02dddac6db2", + "nagydani-2-pow0x10001", + }, + { + "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000" + "000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000" + "000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf39186" + "05a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d9" + "4c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd5" + "58a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757df" + "f0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f549" + "80241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb02d7a85909174757" + "835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558" + "fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa2" + "9b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4" + "a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec16" + "8b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e0379" + "5ed758220312d101e1e3d87d5313a6d052aebde75110363d", + "affc7507ea6d84751ec6b3f0d7b99dbcc263f33330e450d1b3ff0bc3d0874320bf4edd57debd58730698815795" + "8cb3cfd369cc0c9c198706f635c9e0f15d047df5cb44d03e2727f26b083c4ad8485080e1293f171c1ed52aef59" + "93a5815c35108e848c951cf1e334490b4a539a139e57b68f44fee583306f5b85ffa57206b3ee5660458858534e" + "5386b9584af3c7f67806e84c189d695e5eb96e1272d06ec2df5dc5fabc6e94b793718c60c36be0a4d031fc84cd" + "658aa72294b2e16fc240aef70cb9e591248e38bd49c5a554d1afa01f38dab72733092f7555334bbef6c8c43011" + "9840492380aa95fa025dcf699f0a39669d812b0c6946b6091e6e235337b6f8", + "nagydani-3-square", + }, + { + "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000" + "000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000" + "000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf39186" + "05a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d9" + "4c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd5" + "58a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757df" + "f0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f549" + "80241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb03d7a85909174757" + "835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558" + "fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa2" + "9b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4" + "a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec16" + "8b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e0379" + "5ed758220312d101e1e3d87d5313a6d052aebde75110363d", + "1b280ecd6a6bf906b806d527c2a831e23b238f89da48449003a88ac3ac7150d6a5e9e6b3be4054c7da11dd1e47" + "0ec29a606f5115801b5bf53bc1900271d7c3ff3cd5ed790d1c219a9800437a689f2388ba1a11d68f6a8e5b74e9" + "a3b1fac6ee85fc6afbac599f93c391f5dc82a759e3c6c0ab45ce3f5d25d9b0c1bf94cf701ea6466fc9a478dacc" + "5754e593172b5111eeba88557048bceae401337cd4c1182ad9f700852bc8c99933a193f0b94cf1aedbefc48be3" + "bc93ef5cb276d7c2d5462ac8bb0c8fe8923a1db2afe1c6b90d59c534994a6a633f0ead1d638fdc293486bb634f" + "f2c8ec9e7297c04241a61c37e3ae95b11d53343d4ba2b4cc33d2cfa7eb705e", + "nagydani-3-qube", + }, + { + "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000" + "000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000" + "000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf39186" + "05a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d9" + "4c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd5" + "58a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757df" + "f0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f549" + "80241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb010001d7a8590917" + "4757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b" + "1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa" + "1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea2" + "28d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938" + "ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e" + "03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + "37843d7c67920b5f177372fa56e2a09117df585f81df8b300fba245b1175f488c99476019857198ed459ed8d97" + "99c377330e49f4180c4bf8e8f66240c64f65ede93d601f957b95b83efdee1e1bfde74169ff77002eaf078c7181" + "5a9220c80b2e3b3ff22c2f358111d816ebf83c2999026b6de50bfc711ff68705d2f40b753424aefc9f70f08d90" + "8b5a20276ad613b4ab4309a3ea72f0c17ea9df6b3367d44fb3acab11c333909e02e81ea2ed404a712d3ea96bba" + "87461720e2d98723e7acd0520ac1a5212dbedcd8dc0c1abf61d4719e319ff4758a774790b8d463cdfe131d1b2d" + "cfee52d002694e98e720cb6ae7ccea353bc503269ba35f0f63bf8d7b672a76", + "nagydani-3-pow0x10001", + }, + { + "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000" + "000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000" + "000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e47128" + "39917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e4" + "2e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f" + "6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264" + "dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a" + "2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497" + "c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693" + "892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d77202" + "5791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e83684" + "5e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3" + "bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70" + "f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8102df3143a0057457d75e8c708b6337a6f5a4fd1a0672" + "7acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040" + "ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d24" + "8bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f94" + "45cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490" + "c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a9" + "3cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea" + "6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253" + "bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb4" + "3f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c3" + "45e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcd" + "ae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + "8a5aea5f50dcc03dc7a7a272b5aeebc040554dbc1ffe36753c4fc75f7ed5f6c2cc0de3a922bf96c78bf0643a73" + "025ad21f45a4a5cadd717612c511ab2bff1190fe5f1ae05ba9f8fe3624de1de2a817da6072ddcdb933b5021681" + "1dbe6a9ca79d3a3c6b3a476b079fd0d05f04fb154e2dd3e5cb83b148a006f2bcbf0042efb2ae7b916ea81b27aa" + "c25c3bf9a8b6d35440062ad8eae34a83f3ffa2cc7b40346b62174a4422584f72f95316f6b2bee9ff232ba97393" + "01c97c99a9ded26c45d72676eb856ad6ecc81d36a6de36d7f9dafafee11baa43a4b0d5e4ecffa7b9b7dcefd58c" + "397dd373e6db4acd2b2c02717712e6289bed7c813b670c4a0c6735aa7f3b0f1ce556eae9fcc94b501b2c8781ba" + "50a8c6220e8246371c3c7359fe4ef9da786ca7d98256754ca4e496be0a9174bedbecb384bdf470779186d6a833" + "f068d2838a88d90ef3ad48ff963b67c39cc5a3ee123baf7bf3125f64e77af7f30e105d72c4b9b5b237ed251e4c" + "122c6d8c1405e736299c3afd6db16a28c6a9cfa68241e53de4cd388271fe534a6a9b0dbea6171d170db1b89858" + "468885d08fecbd54c8e471c3e25d48e97ba450b96d0d87e00ac732aaa0d3ce4309c1064bd8a4c0808a97e0143e" + "43a24cfa847635125cd41c13e0574487963e9d725c01375db99c31da67b4cf65eff555f0c0ac416c727ff8d438" + "ad7c42030551d68c2e7adda0abb1ca7c10", + "nagydani-4-square", + }, + { + "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000" + "000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000" + "000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e47128" + "39917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e4" + "2e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f" + "6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264" + "dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a" + "2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497" + "c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693" + "892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d77202" + "5791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e83684" + "5e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3" + "bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70" + "f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8103df3143a0057457d75e8c708b6337a6f5a4fd1a0672" + "7acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040" + "ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d24" + "8bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f94" + "45cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490" + "c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a9" + "3cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea" + "6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253" + "bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb4" + "3f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c3" + "45e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcd" + "ae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + "5a2664252aba2d6e19d9600da582cdd1f09d7a890ac48e6b8da15ae7c6ff1856fc67a841ac2314d283ffa3ca81" + "a0ecf7c27d89ef91a5a893297928f5da0245c99645676b481b7e20a566ee6a4f2481942bee191deec5544600bb" + "2441fd0fb19e2ee7d801ad8911c6b7750affec367a4b29a22942c0f5f4744a4e77a8b654da2a82571037099e9c" + "6d930794efe5cdca73c7b6c0844e386bdca8ea01b3d7807146bb81365e2cdc6475f8c23e0ff84463126189dc97" + "89f72bbce2e3d2d114d728a272f1345122de23df54c922ec7a16e5c2a8f84da8871482bd258c20a7c09bbcd64c" + "7a96a51029bbfe848736a6ba7bf9d931a9b7de0bcaf3635034d4958b20ae9ab3a95a147b0421dd5f7ebff46c97" + "1010ebfc4adbbe0ad94d5498c853e7142c450d8c71de4b2f84edbf8acd2e16d00c8115b150b1c30e553dbb8263" + "5e781379fe2a56360420ff7e9f70cc64c00aba7e26ed13c7c19622865ae07248daced36416080f35f8cc157a85" + "7ed70ea4f347f17d1bee80fa038abd6e39b1ba06b97264388b21364f7c56e192d4b62d9b161405f32ab1e2594e" + "86243e56fcf2cb30d21adef15b9940f91af681da24328c883d892670c6aa47940867a81830a82b82716895db81" + "0df1b834640abefb7db2092dd92912cb9a735175bc447be40a503cf22dfe565b4ed7a3293ca0dfd63a507430b3" + "23ee248ec82e843b673c97ad730728cebc", + "nagydani-4-qube", + }, + { + "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000" + "000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000" + "000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e47128" + "39917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e4" + "2e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f" + "6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264" + "dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a" + "2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497" + "c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693" + "892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d77202" + "5791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e83684" + "5e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3" + "bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70" + "f9f7a823536e2a8f4b3e12e94d9b53a934353451094b81010001df3143a0057457d75e8c708b6337a6f5a4fd1a" + "06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9" + "e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f99" + "4d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d0" + "4f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa5" + "6490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a" + "42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a2" + "24ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed0711" + "7253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd87" + "7fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501c" + "c2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a624" + "5fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + "bed8b970c4a34849fc6926b08e40e20b21c15ed68d18f228904878d4370b56322d0da5789da0318768a374758e" + "6375bfe4641fca5285ec7171828922160f48f5ca7efbfee4d5148612c38ad683ae4e3c3a053d2b7c098cf2b34f" + "2cb19146eadd53c86b2d7ccf3d83b2c370bfb840913ee3879b1057a6b4e07e110b6bcd5e958bc71a14798c91d5" + "18cc70abee264b0d25a4110962a764b364ac0b0dd1ee8abc8426d775ec0f22b7e47b32576afaf1b5a48f64573e" + "d1c5c29f50ab412188d9685307323d990802b81dacc06c6e05a1e901830ba9fcc67688dc29c5e27bde0a6e845c" + "a925f5454b6fb3747edfaa2a5820838fb759eadf57f7cb5cec57fc213ddd8a4298fa079c3c0f472b07fb15aa6a" + "7f0a3780bd296ff6a62e58ef443870b02260bd4fd2bbc98255674b8e1f1f9f8d33c7170b0ebbea4523b695911a" + "bbf26e41885344823bd0587115fdd83b721a4e8457a31c9a84b3d3520a07e0e35df7f48e5a9d534d0ec7feef1f" + "f74de6a11e7f93eab95175b6ce22c68d78a642ad642837897ec11349205d8593ac19300207572c38d29ca5dfa0" + "3bc14cdbc32153c80e5cc3e739403d34c75915e49beb43094cc6dcafb3665b305ddec9286934ae66ec6b777ca5" + "28728c851318eb0f207b39f1caaf96db6eeead6b55ed08f451939314577d42bcc9f97c0b52d0234f88fd07e4c1" + "d7780fdebc025cfffcb572cb27a8c33963", + "nagydani-4-pow0x10001", + }, + { + "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000" + "000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000" + "000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e2022" + "5beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7d" + "ee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f" + "024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78" + "ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c" + "7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a8368" + "45109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859" + "268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec21" + "6ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752a" + "d89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772" + "c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb" + "27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbcc" + "ff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c" + "5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600" + "cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6" + "144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece39" + "86a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0" + "a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da" + "54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a" + "0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4" + "c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f1" + "34168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac" + "9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf02e3004920" + "1ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe2" + "57217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf2" + "9ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568" + "ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2" + "d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682ae" + "eb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473" + "460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d9" + "4ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5" + "b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb052" + "65fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e" + "581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd6" + "1cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87" + "667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d" + "2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501" + "b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23" + "a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff" + "47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898" + "676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b9" + "9d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da5" + "5f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea2" + "6c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6" + "320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e" + "10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + "d61fe4e3f32ac260915b5b03b78a86d11bfc41d973fce5b0cc59035cf8289a8a2e3878ea15fa46565b0d806e2f" + "85b53873ea20ed653869b688adf83f3ef444535bf91598ff7e80f334fb782539b92f39f55310cc4b35349ab7b2" + "78346eda9bc37c0d8acd3557fae38197f412f8d9e57ce6a76b7205c23564cab06e5615be7c6f05c3d05ec690cb" + "a91da5e89d55b152ff8dd2157dc5458190025cf94b1ad98f7cbe64e9482faba95e6b33844afc640892872b44a9" + "932096508f4a782a4805323808f23e54b6ff9b841dbfa87db3505ae4f687972c18ea0f0d0af89d36c1c2a5b145" + "60c153c3fee406f5cf15cfd1c0bb45d767426d465f2f14c158495069d0c5955a00150707862ecaae30624ebacd" + "d8ac33e4e6aab3ff90b6ba445a84689386b9e945d01823a65874444316e83767290fcff630d2477f49d5d8ffdd" + "200e08ee1274270f86ed14c687895f6caf5ce528bd970c20d2408a9ba66216324c6a011ac4999098362dbd98a0" + "38129a2d40c8da6ab88318aa3046cb660327cc44236d9e5d2163bd0959062195c51ed93d0088b6f92051fc9905" + "0ece2538749165976233697ab4b610385366e5ce0b02ad6b61c168ecfbedcdf74278a38de340fd7a5fead8e588" + "e294795f9b011e2e60377a89e25c90e145397cdeabc60fd32444a6b7642a611a83c464d8b8976666351b4865c3" + "7b02e6dc21dbcdf5f930341707b618cc0f03c3122646b3385c9df9f2ec730eec9d49e7dfc9153b6e6289da8c4f" + "0ebea9ccc1b751948e3bb7171c9e4d57423b0eeeb79095c030cb52677b3f7e0b45c30f645391f3f9c957afa549" + "c4e0b2465b03c67993cd200b1af01035962edbc4c9e89b31c82ac121987d6529dafdeef67a132dc04b6dc68e77" + "f22862040b75e2ceb9ff16da0fca534e6db7bd12fa7b7f51b6c08c1e23dfcdb7acbd2da0b51c87ffbced065a61" + "2e9b1c8bba9b7e2d8d7a2f04fcc4aaf355b60d764879a76b5e16762d5f2f55d585d0c8e82df6940960cddfb72c" + "91dfa71f6b4e1c6ca25dfc39a878e998a663c04fe29d5e83b9586d047b4d7ff70a9f0d44f127e7d741685ca75f" + "11629128d916a0ffef4be586a30c4b70389cc746e84ebf177c01ee8a4511cfbb9d1ecf7f7b33c7dd8177896e10" + "bbc82f838dcd6db7ac67de62bf46b6a640fb580c5d1d2708f3862e3d2b645d0d18e49ef088053e3a220adc0e03" + "3c2afcfe61c90e32151152eb3caaf746c5e377d541cafc6cbb0cc0fa48b5caf1728f2e1957f5addfc234f1a9d8" + "9e40d49356c9172d0561a695fce6dab1d412321bbf407f63766ffd7b6b3d79bcfa07991c5a9709849c1008689e" + "3b47c50d613980bec239fb64185249d055b30375ccb4354d71fe4d05648fbf6c80634dfc3575f2f24abb714c1e" + "4c95e8896763bf4316e954c7ad19e5780ab7a040ca6fb9271f90a8b22ae738daf6cb", + "nagydani-5-square", + }, + { + "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000" + "000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000" + "000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e2022" + "5beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7d" + "ee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f" + "024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78" + "ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c" + "7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a8368" + "45109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859" + "268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec21" + "6ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752a" + "d89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772" + "c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb" + "27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbcc" + "ff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c" + "5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600" + "cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6" + "144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece39" + "86a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0" + "a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da" + "54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a" + "0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4" + "c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f1" + "34168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac" + "9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf03e3004920" + "1ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe2" + "57217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf2" + "9ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568" + "ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2" + "d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682ae" + "eb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473" + "460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d9" + "4ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5" + "b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb052" + "65fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e" + "581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd6" + "1cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87" + "667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d" + "2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501" + "b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23" + "a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff" + "47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898" + "676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b9" + "9d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da5" + "5f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea2" + "6c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6" + "320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e" + "10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + "5f9c70ec884926a89461056ad20ac4c30155e817f807e4d3f5bb743d789c83386762435c3627773fa77da51444" + "51f2a8aad8adba88e0b669f5377c5e9bad70e45c86fe952b613f015a9953b8a5de5eaee4566acf98d41e327d93" + "a35bd5cef4607d025e58951167957df4ff9b1627649d3943805472e5e293d3efb687cfd1e503faafeb2840a3e3" + "b3f85d016051a58e1c9498aab72e63b748d834b31eb05d85dcde65e27834e266b85c75cc4ec0135135e0601cb9" + "3eeeb6e0010c8ceb65c4c319623c5e573a2c8c9fbbf7df68a930beb412d3f4dfd146175484f45d7afaa0d2e606" + "84af9b34730f7c8438465ad3e1d0c3237336722f2aa51095bd5759f4b8ab4dda111b684aa3dac62a761722e7ae" + "43495b7709933512c81c4e3c9133a51f7ce9f2b51fcec064f65779666960b4e45df3900f54311f5613e8012dd1" + "b8efd359eda31a778264c72aa8bb419d862734d769076bce2810011989a45374e5c5d8729fec21427f0bf397ea" + "cbb4220f603cf463a4b0c94efd858ffd9768cd60d6ce68d755e0fbad007ce5c2223d70c7018345a102e4ab3c60" + "a13a9e7794303156d4c2063e919f2153c13961fb324c80b240742f47773a7a8e25b3e3fb19b00ce839346c6eb3" + "c732fbc6b888df0b1fe0a3d07b053a2e9402c267b2d62f794d8a2840526e3ade15ce2264496ccd7519571dfde4" + "7f7a4bb16292241c20b2be59f3f8fb4f6383f232d838c5a22d8c95b6834d9d2ca493f5a505ebe8899503b0e8f9" + "b19e6e2dd81c1628b80016d02097e0134de51054c4e7674824d4d758760fc52377d2cad145e259aa2ffaf54139" + "e1a66b1e0c1c191e32ac59474c6b526f5b3ba07d3e5ec286eddf531fcd5292869be58c9f22ef91026159f7cf9d" + "05ef66b4299f4da48cc1635bf2243051d342d378a22c83390553e873713c0454ce5f3234397111ac3fe3207b86" + "f0ed9fc025c81903e1748103692074f83824fda6341be4f95ff00b0a9a208c267e12fa01825054cc0513629bf3" + "dbb56dc5b90d4316f87654a8be18227978ea0a8a522760cad620d0d14fd38920fb7321314062914275a5f99f67" + "7145a6979b156bd82ecd36f23f8e1273cc2759ecc0b2c69d94dad5211d1bed939dd87ed9e07b91d49713a6e16a" + "de0a98aea789f04994e318e4ff2c8a188cd8d43aeb52c6daa3bc29b4af50ea82a247c5cd67b573b34cbadcc0a3" + "76d3bbd530d50367b42705d870f2e27a8197ef46070528bfe408360faa2ebb8bf76e9f388572842bcb119f4d84" + "ee34ae31f5cc594f23705a49197b181fb78ed1ec99499c690f843a4d0cf2e226d118e9372271054fbabdcc5c92" + "ae9fefaef0589cd0e722eaf30c1703ec4289c7fd81beaa8a455ccee5298e31e2080c10c366a6fcf56f7d13582a" + "d0bcad037c612b710fc595b70fbefaaca23623b60c6c39b11beb8e5843b6b3dac60f", + "nagydani-5-qube", + }, + { + "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000" + "000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000" + "000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e2022" + "5beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7d" + "ee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f" + "024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78" + "ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c" + "7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a8368" + "45109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859" + "268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec21" + "6ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752a" + "d89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772" + "c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb" + "27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbcc" + "ff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c" + "5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600" + "cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6" + "144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece39" + "86a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0" + "a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da" + "54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a" + "0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4" + "c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f1" + "34168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac" + "9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf010001e300" + "49201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc8357" + "0fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa35" + "1bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc051" + "4568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099" + "f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e886" + "82aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd0" + "2473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f" + "31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb" + "17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66c" + "b05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc" + "4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd5" + "7bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb" + "1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460" + "ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050e" + "c501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c" + "9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f" + "40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b5295784" + "7898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9a" + "b1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd301" + "6da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce" + "1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f7" + "81c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436a" + "a10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + "5a0eb2bdf0ac1cae8e586689fa16cd4b07dfdedaec8a110ea1fdb059dd5253231b6132987598dfc6e11f867804" + "28982d50cf68f67ae452622c3b336b537ef3298ca645e8f89ee39a26758206a5a3f6409afc709582f95274b57b" + "71fae5c6b74619ae6f089a5393c5b79235d9caf699d23d88fb873f78379690ad8405e34c19f5257d596580c7a6" + "a7206a3712825afe630c76b31cdb4a23e7f0632e10f14f4e282c81a66451a26f8df2a352b5b9f607a7198449d1" + "b926e27036810368e691a74b91c61afa73d9d3b99453e7c8b50fd4f09c039a2f2feb5c419206694c31b92df1d9" + "586140cb3417b38d0c503c7b508cc2ed12e813a1c795e9829eb39ee78eeaf360a169b491a1d4e419574e712402" + "de9d48d54c1ae5e03739b7156615e8267e1fb0a897f067afd11fb33f6e24182d7aaaaa18fe5bc1982f20d6b871" + "e5a398f0f6f718181d31ec225cfa9a0a70124ed9a70031bdf0c1c7829f708b6e17d50419ef361cf77d99c85f44" + "607186c8d683106b8bd38a49b5d0fb503b397a83388c5678dcfcc737499d84512690701ed621a6f0172aecf037" + "184ddf0f2453e4053024018e5ab2e30d6d5363b56e8b41509317c99042f517247474ab3abc848e00a07f69c254" + "f46f2a05cf6ed84e5cc906a518fdcfdf2c61ce731f24c5264f1a25fc04934dc28aec112134dd523f70115074ca" + "34e3807aa4cb925147f3a0ce152d323bd8c675ace446d0fd1ae30c4b57f0eb2c23884bc18f0964c0114796c5b6" + "d080c3d89175665fbf63a6381a6a9da39ad070b645c8bb1779506da14439a9f5b5d481954764ea114fac688930" + "bc68534d403cff4210673b6a6ff7ae416b7cd41404c3d3f282fcd193b86d0f54d0006c2a503b40d5c3930da980" + "565b8f9630e9493a79d1c03e74e5f93ac8e4dc1a901ec5e3b3e57049124c7b72ea345aa359e782285d9e6a5c14" + "4a378111dd02c40855ff9c2be9b48425cb0b2fd62dc8678fd151121cf26a65e917d65d8e0dacfae108eb5508b6" + "01fb8ffa370be1f9a8b749a2d12eeab81f41079de87e2d777994fa4d28188c579ad327f9957fb7bdecec5c6808" + "44dd43cb57cf87aeb763c003e65011f73f8c63442df39a92b946a6bd968a1c1e4d5fa7d88476a68bd8e20e5b70" + "a99259c7d3f85fb1b65cd2e93972e6264e74ebf289b8b6979b9b68a85cd5b360c1987f87235c3c845d62489e33" + "acf85d53fa3561fe3a3aee18924588d9c6eba4edb7a4d106b31173e42929f6f0c48c80ce6a72d54eca7c0fe870" + "068b7a7c89c63cdda593f5b32d3cb4ea8a32c39f00ab449155757172d66763ed9527019d6de6c9f2416aa6203f" + "4d11c9ebee1e1d3845099e55504446448027212616167eb36035726daa7698b075286f5379cd3e93cb3e0cf4f9" + "cb8d017facbb5550ed32d5ec5400ae57e47e2bf78d1eaeff9480cc765ceff39db500", + "nagydani-5-pow0x10001", + }}; + +constexpr PrecompiledTest bn256AddTests[] = { + {"18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9063c909c4720840cb5134cb9f59fa" + "749755796819658d32efc0d288198f3726607c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a" + "014eed06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d7", + "2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703301d1d33be6da8e509df21cc35" + "964723180eed7532537db9ae5e7d48f195c915", + "chfast1"}, + { + "2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703301d1d33be6da8e509df21cc35" + "964723180eed7532537db9ae5e7d48f195c91518b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b" + "74034e093dc9063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266", + "2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb721611ce0a6af85915e2f1d7030" + "0909ce2e49dfad4a4619c8390cae66cefdb204", + "chfast2", + }, + { + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000", + "cdetrio1", + }, + { + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000", + "cdetrio2", + }, + { + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000", + "cdetrio3", + }, + { + "", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000", + "cdetrio4", + }, + { + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000", + "cdetrio5", + }, + { + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000010000000000000000000000000000000000000000000000000000000000000002", + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000" + "00000000000000000000000000000000000002", + "cdetrio6", + }, + { + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000" + "00000000000000000000000000000000000002", + "cdetrio7", + }, + { + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000" + "00000000000000000000000000000000000002", + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000" + "00000000000000000000000000000000000002", + "cdetrio8", + }, + { + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000" + "000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000" + "00000000000000000000000000000000000002", + "cdetrio9", + }, + { + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000" + "000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000" + "00000000000000000000000000000000000002", + "cdetrio10", + }, + { + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000" + "000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000" + "0000000000010000000000000000000000000000000000000000000000000000000000000002", + "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2" + "ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4", + "cdetrio11", + }, + { + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000" + "000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000" + "000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000", + "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2" + "ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4", + "cdetrio12", + }, + { + "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af" + "8a9fe70baa9258e0b959273ffc5718c6d4cc7c039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f" + "18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d98", + "15bf2bb17880144b5d1cd2b1f46eff9d617bffd1ca57c37fb5a49bd84e53cf66049c797f9ce0d17083deb32b5e" + "36f2ea2a212ee036598dd7624c168993d1355f", + "cdetrio13", + }, + { + "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af" + "8a9fe70baa9258e0b959273ffc5718c6d4cc7c17c139df0efee0f766bc0204762b774362e4ded88953a39ce849" + "a8a7fa163fa92e83f8d734803fc370eba25ed1f6b8768bd6d83887b87165fc2434fe11a830cb00000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000", + "cdetrio14", + }}; + +constexpr PrecompiledTest bn256ScalarMulTests[] = { + { + "2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb721611ce0a6af85915e2f1d7030" + "0909ce2e49dfad4a4619c8390cae66cefdb2040000000000000000000000000000000000000000000000001113" + "8ce750fa15c2", + "070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c031b8ce914eba3a9ffb989f9cd" + "d5b0f01943074bf4f0f315690ec3cec6981afc", + "chfast1", + }, + { + "070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c031b8ce914eba3a9ffb989f9cd" + "d5b0f01943074bf4f0f315690ec3cec6981afc30644e72e131a029b85045b68181585d97816a916871ca8d3c20" + "8c16d87cfd46", + "025a6f4181d2b4ea8b724290ffb40156eb0adb514c688556eb79cdea0752c2bb2eff3f31dea215f1eb86023a13" + "3a996eb6300b44da664d64251d05381bb8a02e", + "chfast2", + }, + { + "025a6f4181d2b4ea8b724290ffb40156eb0adb514c688556eb79cdea0752c2bb2eff3f31dea215f1eb86023a13" + "3a996eb6300b44da664d64251d05381bb8a02e183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10" + "460b6c3e7ea3", + "14789d0d4a730b354403b5fac948113739e276c23e0258d8596ee72f9cd9d3230af18a63153e0ec25ff9f2951d" + "d3fa90ed0197bfef6e2a1a62b5095b9d2b4a27", + "chfast3", + }, + { + "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff" + "81504b0fcd6d7cf59996efdc33d92bf7f9f8f6ffffffffffffffffffffffffffffffffffffffffffffffffffff" + "ffffffffffff", + "2cde5879ba6f13c0b5aa4ef627f159a3347df9722efce88a9afbb20b763b4c411aa7e43076f6aee272755a7f9b" + "84832e71559ba0d2e0b17d5f9f01755e5b0d11", + "cdetrio1", + }, + { + "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff" + "81504b0fcd6d7cf59996efdc33d92bf7f9f8f630644e72e131a029b85045b68181585d2833e84879b9709143e1" + "f593f0000000", + "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe3163511ddc1c3f25d3967453882" + "00081287b3fd1472d8339d5fecb2eae0830451", + "cdetrio2", + }, + { + "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff" + "81504b0fcd6d7cf59996efdc33d92bf7f9f8f60000000000000000000000000000000100000000000000000000" + "000000000000", + "1051acb0700ec6d42a88215852d582efbaef31529b6fcbc3277b5c1b300f5cf0135b2394bb45ab04b8bd7611bd" + "2dfe1de6a4e6e2ccea1ea1955f577cd66af85b", + "cdetrio3", + }, + { + "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff" + "81504b0fcd6d7cf59996efdc33d92bf7f9f8f60000000000000000000000000000000000000000000000000000" + "000000000009", + "1dbad7d39dbc56379f78fac1bca147dc8e66de1b9d183c7b167351bfe0aeab742cd757d51289cd8dbd0acf9e67" + "3ad67d0f0a89f912af47ed1be53664f5692575", + "cdetrio4", + }, + { + "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff" + "81504b0fcd6d7cf59996efdc33d92bf7f9f8f60000000000000000000000000000000000000000000000000000" + "000000000001", + "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff" + "81504b0fcd6d7cf59996efdc33d92bf7f9f8f6", + "cdetrio5", + }, + { + "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af" + "8a9fe70baa9258e0b959273ffc5718c6d4cc7cffffffffffffffffffffffffffffffffffffffffffffffffffff" + "ffffffffffff", + "29e587aadd7c06722aabba753017c093f70ba7eb1f1c0104ec0564e7e3e21f6022b1143f6a41008e7755c71c3d" + "00b6b915d386de21783ef590486d8afa8453b1", + "cdetrio6", + }, + { + "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af" + "8a9fe70baa9258e0b959273ffc5718c6d4cc7c30644e72e131a029b85045b68181585d2833e84879b9709143e1" + "f593f0000000", + "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa92e83f8d734803fc370eba25ed1" + "f6b8768bd6d83887b87165fc2434fe11a830cb", + "cdetrio7", + }, + { + "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af" + "8a9fe70baa9258e0b959273ffc5718c6d4cc7c0000000000000000000000000000000100000000000000000000" + "000000000000", + "221a3577763877920d0d14a91cd59b9479f83b87a653bb41f82a3f6f120cea7c2752c7f64cdd7f0e494bff7b60" + "419f242210f2026ed2ec70f89f78a4c56a1f15", + "cdetrio8", + }, + { + "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af" + "8a9fe70baa9258e0b959273ffc5718c6d4cc7c0000000000000000000000000000000000000000000000000000" + "000000000009", + "228e687a379ba154554040f8821f4e41ee2be287c201aa9c3bc02c9dd12f1e691e0fd6ee672d04cfd924ed8fdc" + "7ba5f2d06c53c1edc30f65f2af5a5b97f0a76a", + "cdetrio9", + }, + { + "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af" + "8a9fe70baa9258e0b959273ffc5718c6d4cc7c0000000000000000000000000000000000000000000000000000" + "000000000001", + "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af" + "8a9fe70baa9258e0b959273ffc5718c6d4cc7c", + "cdetrio10", + }, + { + "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e5" + "8ce577356982d65b833a5a5c15bf9024b43d98ffffffffffffffffffffffffffffffffffffffffffffffffffff" + "ffffffffffff", + "00a1a234d08efaa2616607e31eca1980128b00b415c845ff25bba3afcb81dc00242077290ed33906aeb8e42fd9" + "8c41bcb9057ba03421af3f2d08cfc441186024", + "cdetrio11", + }, + { + "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e5" + "8ce577356982d65b833a5a5c15bf9024b43d9830644e72e131a029b85045b68181585d2833e84879b9709143e1" + "f593f0000000", + "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b8692929ee761a352600f54921df9b" + "f472e66217e7bb0cee9032e00acc86b3c8bfaf", + "cdetrio12", + }, + { + "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e5" + "8ce577356982d65b833a5a5c15bf9024b43d980000000000000000000000000000000100000000000000000000" + "000000000000", + "1071b63011e8c222c5a771dfa03c2e11aac9666dd097f2c620852c3951a4376a2f46fe2f73e1cf310a168d56ba" + "a5575a8319389d7bfa6b29ee2d908305791434", + "cdetrio13", + }, + { + "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e5" + "8ce577356982d65b833a5a5c15bf9024b43d980000000000000000000000000000000000000000000000000000" + "000000000009", + "19f75b9dd68c080a688774a6213f131e3052bd353a304a189d7a2ee367e3c2582612f545fb9fc89fde80fd81c6" + "8fc7dcb27fea5fc124eeda69433cf5c46d2d7f", + "cdetrio14", + }, + { + "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e5" + "8ce577356982d65b833a5a5c15bf9024b43d980000000000000000000000000000000000000000000000000000" + "000000000001", + "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e5" + "8ce577356982d65b833a5a5c15bf9024b43d98", + "cdetrio15", + }}; + +// bn256PairingTests are the test and benchmark data for the bn256 pairing check +// precompiled contract. +constexpr PrecompiledTest bn256PairingTests[] = { + { + "1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee2811c" + "678745fc819b55d3e9d294e45c9b03a76aef41209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a" + "452003a35bf704bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416782bb8324af6cfc9" + "3537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f" + "0c9bb048165fe5e4de877550111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c20" + "32c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411198e9393920d483a7260bfb731fb" + "5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd" + "5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb" + "4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "0000000000000000000000000000000000000000000000000000000000000001", + "jeff1", + }, + { + "2eca0c7238bf16e83e7a1e6c5d49540685ff51380f309842a98561558019fc0203d3260361bb8451de5ff5ecd1" + "7f010ff22f5c31cdf184e9020b06fa5997db841213d2149b006137fcfb23036606f848d638d576a120ca981b5b" + "1a5f9300b3ee2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f21ee6226d31426" + "322afcda621464d0611d226783262e21bb3bc86b537e986237096df1f82dff337dd5972e32a8ad43e28a78a96a" + "823ef1cd4debe12b6552ea5f06967a1237ebfeca9aaae0d6d0bab8e28c198c5a339ef8a2407e31cdac516db922" + "160fa257a5fd5b280642ff47b65eca77e626cb685c84fa6d3b6882a283ddd1198e9393920d483a7260bfb731fb" + "5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd" + "5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb" + "4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "0000000000000000000000000000000000000000000000000000000000000001", + "jeff2", + }, + { + "0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53" + "c9bbdfec6c36c7d515536431b3a865468acbba2e89718ad33c8bed92e210e81d1853435399a271913a6520736a" + "4729cf0d51eb01a9e2ffa2e92599b68e44de5bcf354fa2642bd4f26b259daa6f7ce3ed57aeb314a9a87b789a58" + "af499b314e13c3d65bede56c07ea2d418d6874857b70763713178fb49a2d6cd347dc58973ff49613a20757d0fc" + "c22079f9abd10c3baee245901b9e027bd5cfc2cb5db82d4dc9677ac795ec500ecd47deee3b5da006d6d049b811" + "d7511c78158de484232fc68daf8a45cf217d1c2fae693ff5871e8752d73b21198e9393920d483a7260bfb731fb" + "5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd" + "5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb" + "4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "0000000000000000000000000000000000000000000000000000000000000001", + "jeff3", + }, + { + "2f2ea0b3da1e8ef11914acf8b2e1b32d99df51f5f4f206fc6b947eae860eddb6068134ddb33dc888ef446b648d" + "72338684d678d2eb2371c61a50734d78da4b7225f83c8b6ab9de74e7da488ef02645c5a16a6652c3c71a15dc37" + "fe3a5dcb7cb122acdedd6308e3bb230d226d16a105295f523a8a02bfc5e8bd2da135ac4c245d065bbad92e7c4e" + "31bf3757f1fe7362a63fbfee50e7dc68da116e67d600d9bf6806d302580dc0661002994e7cd3a7f224e7ddc278" + "02777486bf80f40e4ca3cfdb186bac5188a98c45e6016873d107f5cd131f3a3e339d0375e58bd6219347b00812" + "2ae2b09e539e152ec5364e7e2204b03d11d3caa038bfc7cd499f8176aacbee1f39e4e4afc4bc74790a4a028aff" + "2c3d2538731fb755edefd8cb48d6ea589b5e283f150794b6736f670d6a1033f9b46c6f5204f50813eb85c8dc4b" + "59db1c5d39140d97ee4d2b36d99bc49974d18ecca3e7ad51011956051b464d9e27d46cc25e0764bb98575bd466" + "d32db7b15f582b2d5c452b36aa394b789366e5e3ca5aabd415794ab061441e51d01e94640b7e3084a07e02c78c" + "f3103c542bc5b298669f211b88da1679b0b64a63b7e0e7bfe52aae524f73a55be7fe70c7e9bfc94b4cf0da1213" + "d2149b006137fcfb23036606f848d638d576a120ca981b5b1a5f9300b3ee2276cf730cf493cd95d64677bbb75f" + "c42db72513a4c1e387b476d056f80aa75f21ee6226d31426322afcda621464d0611d226783262e21bb3bc86b53" + "7e986237096df1f82dff337dd5972e32a8ad43e28a78a96a823ef1cd4debe12b6552ea5f", + "0000000000000000000000000000000000000000000000000000000000000001", + "jeff4", + }, + { + "20a754d2071d4d53903e3b31a7e98ad6882d58aec240ef981fdf0a9d22c5926a29c853fcea789887315916bbeb" + "89ca37edb355b4f980c9a12a94f30deeed30211213d2149b006137fcfb23036606f848d638d576a120ca981b5b" + "1a5f9300b3ee2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f21ee6226d31426" + "322afcda621464d0611d226783262e21bb3bc86b537e986237096df1f82dff337dd5972e32a8ad43e28a78a96a" + "823ef1cd4debe12b6552ea5f1abb4a25eb9379ae96c84fff9f0540abcfc0a0d11aeda02d4f37e4baf74cb0c110" + "73b3ff2cdbb38755f8691ea59e9606696b3ff278acfc098fa8226470d03869217cee0a9ad79a4493b5253e2e4e" + "3a39fc2df38419f230d341f60cb064a0ac290a3d76f140db8418ba512272381446eb73958670f00cf46f1d9e64" + "cba057b53c26f64a8ec70387a13e41430ed3ee4a7db2059cc5fc13c067194bcc0cb49a98552fd72bd9edb65734" + "6127da132e5b82ab908f5816c826acb499e22f2412d1a2d70f25929bcb43d5a57391564615c9e70a992b10eafa" + "4db109709649cf48c50dd2198a1f162a73261f112401aa2db79c7dab1533c9935c77290a6ce3b191f2318d198e" + "9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c44" + "79674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadc" + "d122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "0000000000000000000000000000000000000000000000000000000000000001", + "jeff5", + }, + { + "1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee2811c" + "678745fc819b55d3e9d294e45c9b03a76aef41209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a" + "452003a35bf704bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416782bb8324af6cfc9" + "3537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f" + "0c9bb048165fe5e4de877550111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c10" + "3188585e2364128fe25c70558f1560f4f9350baf3959e603cc91486e110936198e9393920d483a7260bfb731fb" + "5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd" + "5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb" + "4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "0000000000000000000000000000000000000000000000000000000000000000", + "jeff6", + }, + { + // ecpairing_empty_data_insufficient_gas + "", + "0000000000000000000000000000000000000000000000000000000000000001", + "empty_data", + }, + { + // ecpairing_one_point_insufficient_gas + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000" + "00000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e4" + "85b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff0" + "75ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e769" + "0c43d37b4ce6cc0166fa7daa", + "0000000000000000000000000000000000000000000000000000000000000000", + "one_point", + }, + { + // ecpairing_two_point_match_2 + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000" + "00000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e4" + "85b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff0" + "75ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e769" + "0c43d37b4ce6cc0166fa7daa000000000000000000000000000000000000000000000000000000000000000100" + "00000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb" + "5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd" + "5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e" + "6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d", + "0000000000000000000000000000000000000000000000000000000000000001", + "two_point_match_2", + }, + { + // ecpairing_two_point_match_3 + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000" + "00000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957" + "ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b78274" + "63722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4f" + "e6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a" + "76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb" + "5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd" + "5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb" + "4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "0000000000000000000000000000000000000000000000000000000000000001", + "two_point_match_3", + }, + { + // ecpairing_two_point_match_4 + "105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf10160cf031d41b41557f3e7e3ba0c5" + "1bebe5da8e6ecd855ec50fc87efcdeac168bcc0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3" + "ff0037ec57fd3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa114c059d74e5b6c" + "4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a2101b33461f39d9e887dbb100f170a2345dde3c07e" + "256d1dfa2b657ba5cd030427000000000000000000000000000000000000000000000000000000000000000100" + "000000000000000000000000000000000000000000000000000000000000021a2c3013d2ea92e13c800cde68ef" + "56a294b883f6ac35d25f587c09b1b3c635f7290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e600" + "71f08a115f2f997f3dbd66a7afe07fe7862ce239edba9e05c5afff7f8a1259c9733b2dfbb929d1691530ca701b" + "4a106054688728c9972c8512e9789e9567aae23e302ccd75", + "0000000000000000000000000000000000000000000000000000000000000001", + "two_point_match_4", + }, + { + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000" + "00000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e4" + "85b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff0" + "75ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e769" + "0c43d37b4ce6cc0166fa7daa000000000000000000000000000000000000000000000000000000000000000100" + "00000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb" + "5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd" + "5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e" + "6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d000000000000000000000000000000000000000000" + "00000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e" + "9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c44" + "79674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadc" + "d122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa000000000000000000" + "000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000" + "00000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800de" + "ef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7" + "db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571" + "827f9d000000000000000000000000000000000000000000000000000000000000000100000000000000000000" + "00000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e7" + "1297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0" + "585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3" + "d1e7690c43d37b4ce6cc0166fa7daa000000000000000000000000000000000000000000000000000000000000" + "00010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bf" + "b731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd" + "46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05" + "a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d000000000000000000000000000000000000" + "000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000" + "02198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a0066" + "5e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355" + "acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa000000000000" + "000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000" + "00000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2" + "1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac0918" + "7524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39" + "c01571827f9d000000000000000000000000000000000000000000000000000000000000000100000000000000" + "00000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa4933" + "35a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed09" + "0689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb" + "408fe3d1e7690c43d37b4ce6cc0166fa7daa000000000000000000000000000000000000000000000000000000" + "00000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a" + "7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f7" + "5edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9b" + "efcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d", + "0000000000000000000000000000000000000000000000000000000000000001", + "ten_point_match_1", + }, + { + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000" + "00000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957" + "ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b78274" + "63722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4f" + "e6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a" + "76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb" + "5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd" + "5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb" + "4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa000000000000000000000000000000000000000000" + "00000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e" + "205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f" + "149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0" + "509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b" + "85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e" + "3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800de" + "ef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395" + "bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166" + "fa7daa000000000000000000000000000000000000000000000000000000000000000100000000000000000000" + "00000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835" + "849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5" + "b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944" + "a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87" + "cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bf" + "b731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd" + "46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db" + "8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa000000000000000000000000000000000000" + "000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000" + "02203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c5927" + "7c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498" + "e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e13" + "1a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac6" + "47851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2" + "1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad69" + "0c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6" + "cc0166fa7daa000000000000000000000000000000000000000000000000000000000000000100000000000000" + "00000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431" + "c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b919" + "5e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4" + "830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208" + "c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a" + "7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f7" + "5edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c8" + "5ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "0000000000000000000000000000000000000000000000000000000000000001", + "ten_point_match_2", + }, + { + // ecpairing_two_point_match_4 + "105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf10160cf031d41b41557f3e7e3ba0c5" + "1bebe5da8e6ecd855ec50fc87efcdeac168bcc0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3" + "ff0037ec57fd3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa114c059d74e5b6c" + "4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a2101b33461f39d9e887dbb100f170a2345dde3c07e" + "256d1dfa2b657ba5cd030427000000000000000000000000000000000000000000000000000000000000000100" + "000000000000000000000000000000000000000000000000000000000000021a2c3013d2ea92e13c800cde68ef" + "56a294b883f6ac35d25f587c09b1b3c635f7290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e600" + "71f08a115f2f997f3dbd66a7afe07fe7862ce239edba9e05c5afff7f8a1259c9733b2dfbb929d1691530ca701b" + "4a106054688728c9972c8512e9789e9567aae23e302ccd75", + "0000000000000000000000000000000000000000000000000000000000000001", + "ten_point_match_3", + }, +}; + +namespace +{ +void benchmarkPrecompiled(char const name[], RefDataContainer tests, int n) +{ + // FIXME: add option for this benchmark + bool benchmark = true; + if (benchmark) + { + std::cout << "Skipping benchmark test because --all option is not specified.\n"; + return; + } + + PrecompiledExecutor exec = PrecompiledRegistrar::executor(name); + + auto start = std::chrono::high_resolution_clock::now(); + for (auto&& test : tests) + { + bytes input = *fromHexString(test.input); + bytesConstRef inputRef = &input; + + auto res = exec(inputRef); + BOOST_REQUIRE_MESSAGE(res.first, test.name); + BOOST_REQUIRE_EQUAL(*toHexString(res.second), test.expected); + + start = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < n; ++i) + exec(inputRef); + auto d = (std::chrono::high_resolution_clock::now() - start) / n; + + auto t = std::chrono::duration_cast(d).count(); + std::cout << ut::framework::current_test_case().p_name << "/" << test.name << ": " << t + << " ns\n"; + } +} +} // namespace + +/// @} + +BOOST_AUTO_TEST_CASE(bench_ecrecover, *ut::label("bench")) +{ + RefDataContainer tests{ + ecrecoverTests, sizeof(ecrecoverTests) / sizeof(ecrecoverTests[0])}; + benchmarkPrecompiled("ecrecover", tests, 100000); +} + +BOOST_AUTO_TEST_CASE(bench_modexp, *ut::label("bench")) +{ + RefDataContainer tests{ + modexpTests, sizeof(modexpTests) / sizeof(modexpTests[0])}; + benchmarkPrecompiled("modexp", tests, 10000); +} + + +BOOST_AUTO_TEST_CASE(bench_bn256Add, *ut::label("bench")) +{ + RefDataContainer tests{ + bn256AddTests, sizeof(bn256AddTests) / sizeof(bn256AddTests[0])}; + benchmarkPrecompiled("alt_bn128_G1_add", tests, 1000000); +} + +BOOST_AUTO_TEST_CASE(bench_bn256ScalarMul, *ut::label("bench")) +{ + RefDataContainer tests{ + bn256ScalarMulTests, sizeof(bn256ScalarMulTests) / sizeof(bn256ScalarMulTests[0])}; + benchmarkPrecompiled("alt_bn128_G1_mul", tests, 10000); +} + +BOOST_AUTO_TEST_CASE(bench_bn256Pairing, *ut::label("bench")) +{ + RefDataContainer tests{ + bn256PairingTests, sizeof(bn256PairingTests) / sizeof(bn256PairingTests[0])}; + benchmarkPrecompiled("alt_bn128_pairing_product", tests, 1000); +} + +BOOST_AUTO_TEST_CASE(ecpairingCost) +{ + PrecompiledPricer cost = PrecompiledRegistrar::pricer("alt_bn128_pairing_product"); + + bytes in{*fromHexString( + "0x1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee281" + "1c678745fc819b55d3e9d294e45c9b03a76aef41209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b" + "4a452003a35bf704bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416782bb8324af6cf" + "c93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d120a2a4cf30c1bf9845f20c6fe39e07ea2cce6" + "1f0c9bb048165fe5e4de877550111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c" + "2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411198e9393920d483a7260bfb731" + "fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46de" + "bd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6d" + "eb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa")}; + + auto costGas = cost(ref(in)); + BOOST_CHECK_EQUAL(static_cast(costGas), in.size() / 192 * 34000 + 45000); +} + +constexpr PrecompiledTest blake2FCompressionFailTests[] = { + {"00000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2" + "b8c68059b6bbd41fbabd9831f79217e1319cde05b6162630000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000300000000000000000000000000000001", + "", "test1"}, + { + "000000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e51" + "1f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b616263000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000300000000000000000000000000000001", + "", + "test2", + }, + { + "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f" + "6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000300000000000000000000000000000002", + "", + "test3", + }}; + +constexpr PrecompiledTest blake2FCompressionTests[] = { + {"0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3" + "e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000300000000000000000000000000000001", + "08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c" + "68059b9442be0454267ce079217e1319cde05b", + "test4"}, + { + "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f" + "6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000300000000000000000000000000000001", + "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de45" + "33cc9518d38aa8dbf1925ab92386edd4009923", + "test5", + }, + { + "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f" + "6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000300000000000000000000000000000000", + "75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d22603" + "9cd31b4e426ac4f2d3d666a610c2116fde4735", + "test6", + }, + { + "0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f" + "6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000300000000000000000000000000000001", + "b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993" + "d53923de3d64fcc68c034e717b9293fed7a421", + "test7", + }, + { + "000004b048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f" + "6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000300000000000000000000000000000001", + "bedc7f4e18165dc34600826ea6857a373d9afa25d7b6f2c7365f5e9e7f7b654ca2486da7248a6a3f1fd2fc4b00" + "233e6144a130296edf049b605d8c52b6463f70", + "test_1200rounds", + }}; + +constexpr PrecompiledTest blake2FCompressionLargeTests[] = {{ + "ffffffff48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f" + "6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000300000000000000000000000000000001", + "fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01" + "846e6b5df8cc7703041bbceb571de6631d2615", + "test8", +}}; + +namespace +{ +void testPrecompiled(char const name[], RefDataContainer tests) +{ + PrecompiledExecutor exec = PrecompiledRegistrar::executor(name); + + for (auto&& test : tests) + { + bytes input = *fromHexString(test.input); + bytesConstRef inputRef = &input; + + auto res = exec(inputRef); + BOOST_REQUIRE_MESSAGE(res.first, test.name); + BOOST_REQUIRE_EQUAL(*toHexString(res.second), test.expected); + + std::cout << ut::framework::current_test_case().p_name << "/" << test.name << " PASSED\n"; + } +} + +void testPrecompiledFail(char const name[], RefDataContainer tests) +{ + PrecompiledExecutor exec = PrecompiledRegistrar::executor(name); + + for (auto&& test : tests) + { + bytes input = *fromHexString(test.input); + bytesConstRef inputRef = &input; + + auto res = exec(inputRef); + BOOST_REQUIRE_MESSAGE(!res.first, test.name); + + std::cout << ut::framework::current_test_case().p_name << "/" << test.name << " PASSED\n"; + } +} +} // namespace + +BOOST_AUTO_TEST_CASE(blake2compression) +{ + RefDataContainer tests{blake2FCompressionTests, + sizeof(blake2FCompressionTests) / sizeof(blake2FCompressionTests[0])}; + testPrecompiled("blake2_compression", tests); +} + +BOOST_AUTO_TEST_CASE(blake2compressionFail) +{ + RefDataContainer tests{blake2FCompressionFailTests, + sizeof(blake2FCompressionFailTests) / sizeof(blake2FCompressionFailTests[0])}; + testPrecompiledFail("blake2_compression", tests); +} + +BOOST_AUTO_TEST_CASE(bench_blake2compression, *ut::label("bench")) +{ + RefDataContainer tests{blake2FCompressionTests, + sizeof(blake2FCompressionTests) / sizeof(blake2FCompressionTests[0])}; + benchmarkPrecompiled("blake2_compression", tests, 100000); +} + +BOOST_AUTO_TEST_CASE(bench_blake2compression_maxrounds, *ut::label("bench")) +{ + RefDataContainer tests{blake2FCompressionLargeTests, + sizeof(blake2FCompressionLargeTests) / sizeof(blake2FCompressionLargeTests[0])}; + benchmarkPrecompiled("blake2_compression", tests, 1); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/old/MemoryStorage.h" "b/BFPL\345\243\271/bcos-executor/test/old/MemoryStorage.h" new file mode 100644 index 00000000..771b3cdf --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/old/MemoryStorage.h" @@ -0,0 +1,183 @@ +/** + * Copyright (C) 2021 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. + * + * @brief the StorageInterface implement in memory + * @file MemoryStorage.h + */ +#pragma once +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-framework/storage/Table.h" +#include "bcos-table/src/StateStorage.h" +#include +#include + +namespace bcos +{ +namespace storage +{ +class MemoryStorage : public StorageInterface +{ +public: + MemoryStorage() = default; + virtual ~MemoryStorage() = default; + + std::vector getPrimaryKeys(const std::shared_ptr& _tableInfo, + const Condition::Ptr& _condition) const override + { + std::vector ret; + std::lock_guard lock(m_mutex); + if (data.count(_tableInfo->name)) + { + for (auto& kv : data.at(_tableInfo->name)) + { + if (!_condition || _condition->isValid(kv.first)) + { + ret.emplace_back(kv.first); + } + } + } + return ret; + } + Entry::Ptr getRow( + const std::shared_ptr& _tableInfo, const std::string_view& _key) override + { + Entry::Ptr ret = nullptr; + std::lock_guard lock(m_mutex); + if (data.count(_tableInfo->name)) + { + if (data[_tableInfo->name].count(std::string(_key))) + { + if (data[_tableInfo->name][std::string(_key)]->getStatus() == Entry::Status::NORMAL) + { + return data[_tableInfo->name][std::string(_key)]; + } + } + } + return ret; + } + std::map getRows(const std::shared_ptr& _tableInfo, + const std::vector& _keys) override + { + std::map ret; + std::lock_guard lock(m_mutex); + if (data.count(_tableInfo->name)) + { + for (auto& key : _keys) + { + if (data[_tableInfo->name].count(std::string(key))) + { + if (data[_tableInfo->name][key]->getStatus() == Entry::Status::NORMAL) + { // this if is unnecessary + ret[key] = data[_tableInfo->name][key]; + } + } + } + } + return ret; + } + std::pair commitBlock(protocol::BlockNumber, + const std::vector>& _tableInfos, + const std::vector>>& _tableDatas) override + { + size_t total = 0; + if (_tableInfos.size() != _tableDatas.size()) + { + return {0, nullptr}; + } + std::lock_guard lock(m_mutex); + for (size_t i = 0; i < _tableInfos.size(); ++i) + { + for (auto& item : *_tableDatas[i]) + { + if (item.second->getStatus() == Entry::Status::NORMAL) + { + data[_tableInfos[i]->name][item.first] = item.second; + ++total; + } + } + } + return {total, nullptr}; + } + + void asyncGetPrimaryKeys(const std::shared_ptr&, const Condition::Ptr&, + std::function&)>) override + {} + void asyncGetRow(const std::shared_ptr&, const std::string_view&, + std::function) override + {} + void asyncGetRows(const std::shared_ptr&, + const std::shared_ptr>&, + std::function&)>) override + {} + + void asyncCommitBlock(protocol::BlockNumber, + const std::shared_ptr>>&, + const std::shared_ptr>>>&, + std::function) override + {} + + // cache StateStorage + void asyncAddStateCache(protocol::BlockNumber, const std::shared_ptr&, + std::function) override + {} + void asyncDropStateCache(protocol::BlockNumber, std::function) override + {} + void asyncGetStateCache(protocol::BlockNumber, + std::function&)>) + override + {} + std::shared_ptr getStateCache(protocol::BlockNumber) override + { + return nullptr; + } + void dropStateCache(protocol::BlockNumber) override {} + void addStateCache( + protocol::BlockNumber, const std::shared_ptr&) override + {} + // KV store in split database, used to store data off-chain + Error::Ptr put( + const std::string_view&, const std::string_view&, const std::string_view&) override + { + return nullptr; + } + std::pair get( + const std::string_view&, const std::string_view&) override + { + return {"", nullptr}; + } + Error::Ptr remove(const std::string_view&, const std::string_view&) override { return nullptr; } + void asyncPut(const std::string_view&, const std::string_view&, const std::string_view&, + std::function) override + {} + void asyncGet(const std::string_view&, const std::string_view&, + std::function) override + {} + void asyncRemove(const std::string_view&, const std::string_view&, + std::function) override + {} + void asyncGetBatch(const std::string_view&, const std::shared_ptr>&, + std::function>&)>) + override + {} + +private: + std::map> data; + mutable std::mutex m_mutex; +}; + + +} // namespace storage + +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/test/old/StateTest.cpp" "b/BFPL\345\243\271/bcos-executor/test/old/StateTest.cpp" new file mode 100644 index 00000000..0d660536 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/old/StateTest.cpp" @@ -0,0 +1,225 @@ +/** + * Copyright (C) 2021 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. + * + * @brief the test of State + * @file StateTest.cpp + */ + +#include "state/State.h" +#include "MemoryStorage.h" +#include "bcos-protocol/protobuf/PBBlock.h" +#include "bcos-protocol/protobuf/PBBlockFactory.h" +#include "bcos-protocol/protobuf/PBBlockHeaderFactory.h" +#include "bcos-protocol/protobuf/PBTransactionFactory.h" +#include "bcos-protocol/protobuf/PBTransactionReceiptFactory.h" +#include "bcos-table/src/StateStorage.h" +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::storage; +using namespace bcos::protocol; +using namespace bcos::crypto; +using namespace bcos::executor; + +namespace bcos +{ +namespace test +{ +namespace test_StorageState +{ +struct StorageStateFixture +{ + StorageStateFixture() + { + hashImpl = std::make_shared(); + memoryStorage = make_shared(); + tableFactory = make_shared(memoryStorage, hashImpl, m_blockNumber); + state = make_shared(tableFactory, hashImpl, false); + } + + std::shared_ptr hashImpl = nullptr; + std::shared_ptr memoryStorage = nullptr; + protocol::BlockNumber m_blockNumber = 0; + std::shared_ptr tableFactory = nullptr; + std::shared_ptr state = nullptr; +}; + +BOOST_FIXTURE_TEST_SUITE(StorageState, StorageStateFixture) + +BOOST_AUTO_TEST_CASE(Balance) +{ + string addr1("0x100001"); + string addr2("0x100002"); + BOOST_TEST(state->balance(addr1) == u256(0)); + state->addBalance(addr1, u256(10)); + BOOST_TEST(state->balance(addr1) == u256(10)); + state->addBalance(addr1, u256(15)); + BOOST_TEST(state->balance(addr1) == u256(25)); + state->subBalance(addr1, u256(3)); + BOOST_TEST(state->balance(addr1) == u256(22)); + state->setBalance(addr1, u256(25)); + BOOST_TEST(state->balance(addr1) == u256(25)); + state->setBalance(addr2, u256(100)); + BOOST_TEST(state->balance(addr2) == u256(100)); + state->transferBalance(addr2, addr1, u256(55)); + BOOST_TEST(state->balance(addr2) == u256(45)); + BOOST_TEST(state->balance(addr1) == u256(80)); +} + +BOOST_AUTO_TEST_CASE(Account) +{ + string addr1("0x100001"); + auto isUse = state->addressInUse(addr1); + BOOST_TEST(isUse == false); + state->checkAuthority(addr1, addr1); + state->createContract(addr1); + isUse = state->addressInUse(addr1); + BOOST_TEST(isUse == true); + auto balance = state->balance(addr1); + BOOST_TEST(balance == u256(0)); + auto nonce = state->getNonce(addr1); + BOOST_TEST(nonce == u256(0)); + auto hash = state->codeHash(addr1); + BOOST_TEST(hash == hashImpl->emptyHash()); + auto sign = state->accountNonemptyAndExisting(addr1); + BOOST_TEST(sign == false); + state->kill(addr1); + state->rootHash(); + nonce = state->accountStartNonce(); + BOOST_TEST(nonce == u256(0)); + state->checkAuthority(addr1, addr1); +} + +BOOST_AUTO_TEST_CASE(Storage) +{ + string addr1("0x100001"); + state->addBalance(addr1, u256(10)); + state->storageRoot(addr1); + auto value = state->storage(addr1, "123"); + BOOST_TEST(value == ""); + state->setStorage(addr1, "123", "456"); + value = state->storage(addr1, "123"); + BOOST_TEST(value == "456"); + state->setStorage(addr1, "123", "567"); + state->clearStorage(addr1); +} + +BOOST_AUTO_TEST_CASE(Code) +{ + string addr1("0x100001"); + auto hasCode = state->addressHasCode(addr1); + BOOST_TEST(hasCode == false); + auto code = state->code(addr1); + BOOST_TEST(code == nullptr); + auto hash = state->codeHash(addr1); + BOOST_TEST(hash == hashImpl->emptyHash()); + state->addBalance(addr1, u256(10)); + code = state->code(addr1); + BOOST_TEST(code == nullptr); + std::string codeString("aaaaaaaaaaaaa"); + auto codeBytes = bytes(codeString.begin(), codeString.end()); + state->setCode(addr1, bytesConstRef(codeBytes.data(), codeBytes.size())); + auto code2 = state->code(addr1); + BOOST_TEST(codeBytes == *code2); + hash = state->codeHash(addr1); + BOOST_TEST(hash == hashImpl->hash(codeBytes)); + auto size = state->codeSize(addr1); + BOOST_TEST(codeBytes.size() == size); + hasCode = state->addressHasCode(addr1); + BOOST_TEST(hasCode == true); +} + +BOOST_AUTO_TEST_CASE(Nonce) +{ + string addr1("0x100001"); + state->addBalance(addr1, u256(10)); + auto nonce = state->getNonce(addr1); + BOOST_TEST(nonce == state->accountStartNonce()); + state->setNonce(addr1, u256(5)); + nonce = state->getNonce(addr1); + BOOST_TEST(nonce == u256(5)); + state->incNonce(addr1); + nonce = state->getNonce(addr1); + BOOST_TEST(nonce == u256(6)); + state->incNonce(addr1); + nonce = state->getNonce(addr1); + BOOST_TEST(nonce == u256(7)); + state->incNonce(addr1); + nonce = state->getNonce(addr1); + BOOST_TEST(nonce == u256(8)); + state->incNonce(addr1); + nonce = state->getNonce(addr1); + BOOST_TEST(nonce == u256(9)); + + string addr2("0x100002"); + state->incNonce(addr2); + nonce = state->getNonce(addr2); + BOOST_TEST(nonce == state->accountStartNonce() + 1); + state->incNonce(addr2); + nonce = state->getNonce(addr2); + BOOST_TEST(nonce == state->accountStartNonce() + 2); + state->incNonce(addr2); + nonce = state->getNonce(addr2); + BOOST_TEST(nonce == state->accountStartNonce() + 3); + state->incNonce(addr2); + nonce = state->getNonce(addr2); + BOOST_TEST(nonce == state->accountStartNonce() + 4); + + string addr3("0x100003"); + state->setNonce(addr3, nonce); + nonce = state->getNonce(addr3); + BOOST_TEST(nonce == state->accountStartNonce() + 4); +} + +BOOST_AUTO_TEST_CASE(Operate) +{ + string addr1("0x100001"); + auto savepoint0 = state->savepoint(); + BOOST_TEST(state->balance(addr1) == u256(0)); + state->addBalance(addr1, u256(10)); + BOOST_TEST(state->balance(addr1) == u256(10)); + auto sign = state->accountNonemptyAndExisting(addr1); + BOOST_TEST(sign == true); + auto savepoint1 = state->savepoint(); + state->addBalance(addr1, u256(10)); + BOOST_TEST(state->balance(addr1) == u256(20)); + auto savepoint2 = state->savepoint(); + BOOST_TEST(savepoint1 < savepoint2); + + state->addBalance(addr1, u256(10)); + BOOST_TEST(state->balance(addr1) == u256(30)); + state->rollback(savepoint2); + BOOST_TEST(state->balance(addr1) == u256(20)); + state->rollback(savepoint1); + BOOST_TEST(state->balance(addr1) == u256(10)); + + state->rollback(savepoint0); + // BOOST_TEST(state->addressInUse(addr1) == false); + state->commit(); + state->clear(); + + state->commit(); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace test_StorageState + +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/test/old/libexecutor/DAGTest.cpp" "b/BFPL\345\243\271/bcos-executor/test/old/libexecutor/DAGTest.cpp" new file mode 100644 index 00000000..0dd96880 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/old/libexecutor/DAGTest.cpp" @@ -0,0 +1,180 @@ +/** + * Copyright (C) 2021 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. + */ +/** + * @brief : unitest for DAG(Directed Acyclic Graph) basic implement + * @author: jimmyshi + * @date: 2019-1-9 + */ + +#include "executor/DAG.h" +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::executor; + +namespace bcos +{ +namespace test +{ +struct DAGFixture +{ + DAGFixture() {} +}; +BOOST_FIXTURE_TEST_SUITE(DAGTest, DAGFixture) + +bool have(set& _set, ID _id) +{ + return _set.find(_id) != _set.end(); +} + +void consumeAndPush(DAG& _dag, ID _id, set& _topSet) +{ + ID top = _dag.consume(_id); + if (top != INVALID_ID) + _topSet.insert(top); +} + +BOOST_AUTO_TEST_CASE(DAGPopConsumeTest) +{ + DAG dag; + dag.init(9); + dag.addEdge(0, 1); + dag.addEdge(1, 2); + dag.addEdge(4, 5); + dag.addEdge(2, 4); + dag.addEdge(3, 4); + dag.addEdge(0, 3); + dag.addEdge(6, 7); + // single 8 vertex + + // BOOST_CHECK(dag.isQueueEmpty()); + + dag.generate(); + // BOOST_CHECK(!dag.isQueueEmpty()); + + set topSet; + for (int i = 0; i < 9; i++) + { + auto id = dag.waitPop(false); + std::cout << "pop " << id << std::endl; + if (id == INVALID_ID) + { + break; + } + topSet.insert(id); + } + BOOST_CHECK_EQUAL(topSet.size(), 3); + BOOST_CHECK(have(topSet, 0)); + BOOST_CHECK(have(topSet, 6)); + BOOST_CHECK(have(topSet, 8)); + topSet.clear(); + consumeAndPush(dag, 0, topSet); + std::cout << "consume " << 0 << std::endl; + consumeAndPush(dag, 6, topSet); + std::cout << "consume " << 6 << std::endl; + consumeAndPush(dag, 8, topSet); + std::cout << "consume " << 8 << std::endl; + + for (int i = 0; i < 9; i++) + { + auto id = dag.waitPop(false); + std::cout << "pop " << id << std::endl; + if (id == INVALID_ID) + { + break; + } + topSet.insert(id); + } + BOOST_CHECK_EQUAL(topSet.size(), 3); + BOOST_CHECK(have(topSet, 1)); + BOOST_CHECK(have(topSet, 3)); + BOOST_CHECK(have(topSet, 7)); + topSet.clear(); + consumeAndPush(dag, 1, topSet); + std::cout << "consume " << 1 << std::endl; + consumeAndPush(dag, 3, topSet); + std::cout << "consume " << 3 << std::endl; + consumeAndPush(dag, 7, topSet); + std::cout << "consume " << 7 << std::endl; + + for (int i = 0; i < 9; i++) + { + auto id = dag.waitPop(false); + std::cout << "pop " << id << std::endl; + if (id == INVALID_ID) + { + break; + } + topSet.insert(id); + } + BOOST_CHECK_EQUAL(topSet.size(), 1); + BOOST_CHECK(have(topSet, 2)); + topSet.clear(); + consumeAndPush(dag, 2, topSet); + std::cout << "consume " << 2 << std::endl; + + for (int i = 0; i < 9; i++) + { + auto id = dag.waitPop(false); + std::cout << "pop " << id << std::endl; + if (id == INVALID_ID) + { + break; + } + topSet.insert(id); + } + BOOST_CHECK_EQUAL(topSet.size(), 1); + BOOST_CHECK(have(topSet, 4)); + topSet.clear(); + consumeAndPush(dag, 4, topSet); + std::cout << "consume " << 4 << std::endl; + + for (int i = 0; i < 9; i++) + { + auto id = dag.waitPop(false); + std::cout << "pop " << id << std::endl; + if (id == INVALID_ID) + { + break; + } + topSet.insert(id); + } + BOOST_CHECK_EQUAL(topSet.size(), 1); + BOOST_CHECK(have(topSet, 5)); + topSet.clear(); + consumeAndPush(dag, 5, topSet); + std::cout << "consume " << 5 << std::endl; + + for (int i = 0; i < 9; i++) + { + auto id = dag.waitPop(false); + std::cout << "pop " << id << std::endl; + if (id == INVALID_ID) + { + break; + } + topSet.insert(id); + } + BOOST_CHECK_EQUAL(topSet.size(), 0); +} + + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/test/old/libexecutor/ExecutorTest.cpp" "b/BFPL\345\243\271/bcos-executor/test/old/libexecutor/ExecutorTest.cpp" new file mode 100644 index 00000000..01ef6711 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/old/libexecutor/ExecutorTest.cpp" @@ -0,0 +1,324 @@ +/** + * Copyright (C) 2021 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. + */ +/** + * @brief : unitest for executor implement + * @author: xingqiangbai + * @date: 2021-06-09 + */ + +#include "bcos-executor/Executor.h" +#include "../MemoryStorage.h" +#include "../mock/MockDispatcher.h" +#include "../mock/MockLedger.h" +#include "bcos-framework/ledger/LedgerTypeDef.h" +#include "bcos-protocol/testutils/protocol/FakeBlock.h" +#include "bcos-protocol/testutils/protocol/FakeBlockHeader.h" +#include "libprecompiled/Common.h" +#include "vm/BlockContext.h" +#include "vm/TransactionExecutive.h" +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::precompiled; +using namespace bcos::crypto; + +namespace bcos +{ +namespace test +{ +struct ExecutorFixture +{ + ExecutorFixture() + { + auto hashImpl = std::make_shared(); + assert(hashImpl); + auto signatureImpl = std::make_shared(); + assert(signatureImpl); + cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + assert(cryptoSuite); + blockFactory = createBlockFactory(cryptoSuite); + auto header = blockFactory->blockHeaderFactory()->createBlockHeader(1); + header->setNumber(1); + ledger = make_shared(header, blockFactory); + storage = make_shared(); + dispatcher = make_shared(); + executor = make_shared(blockFactory, dispatcher, ledger, storage, false); + // create sys table + auto tableFactory = std::make_shared(storage, hashImpl, 0); + tableFactory->createTable(ledger::SYS_CONFIG, SYS_KEY, "value,enable_number"); + auto table = tableFactory->openTable(ledger::SYS_CONFIG); + auto entry = table->newEntry(); + entry->setField(SYS_VALUE, "3000000"); + entry->setField(SYS_CONFIG_ENABLE_BLOCK_NUMBER, "0"); + table->setRow(SYSTEM_KEY_TX_GAS_LIMIT, entry); + tableFactory->commit(); + executiveContext = executor->createExecutiveContext(header, tableFactory); + } + CryptoSuite::Ptr cryptoSuite = nullptr; + protocol::BlockFactory::Ptr blockFactory; + MockLedger::Ptr ledger; + MemoryStorage::Ptr storage; + MockDispatcher::Ptr dispatcher; + Executor::Ptr executor; + BlockContext::Ptr executiveContext = nullptr; + string helloBin = + "0x60806040526040805190810160405280600181526020017f3100000000000000000000000000000000000000" + "0000000000000000000000008152506001908051906020019061004f9291906100ae565b5034801561005c5760" + "0080fd5b506040805190810160405280600d81526020017f48656c6c6f2c20576f726c64210000000000000000" + "0000000000000000000000815250600090805190602001906100a89291906100ae565b50610153565b82805460" + "0181600116156101000203166002900490600052602060002090601f016020900481019282601f106100ef5780" + "5160ff191683800117855561011d565b8280016001018555821561011d579182015b8281111561011c57825182" + "5591602001919060010190610101565b5b50905061012a919061012e565b5090565b61015091905b8082111561" + "014c576000816000905550600101610134565b5090565b90565b6104ac806101626000396000f3006080604052" + "60043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ff" + "ffffff1680634ed3885e1461005c57806354fd4d50146100c55780636d4ce63c14610155575b600080fd5b3480" + "1561006857600080fd5b506100c3600480360381019080803590602001908201803590602001908080601f0160" + "208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192" + "9192905050506101e5565b005b3480156100d157600080fd5b506100da61029b565b6040518080602001828103" + "825283818151815260200191508051906020019080838360005b8381101561011a578082015181840152602081" + "0190506100ff565b50505050905090810190601f1680156101475780820380516001836020036101000a031916" + "815260200191505b509250505060405180910390f35b34801561016157600080fd5b5061016a610339565b6040" + "518080602001828103825283818151815260200191508051906020019080838360005b838110156101aa578082" + "01518184015260208101905061018f565b50505050905090810190601f1680156101d757808203805160018360" + "20036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101fb" + "9291906103db565b507f93a093529f9c8a0c300db4c55fcd27c068c4f5e0e8410bc288c7e76f3d71083e816040" + "518080602001828103825283818151815260200191508051906020019080838360005b8381101561025e578082" + "015181840152602081019050610243565b50505050905090810190601f16801561028b57808203805160018360" + "20036101000a031916815260200191505b509250505060405180910390a150565b600180546001816001161561" + "01000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460" + "0181600116156101000203166002900480156103315780601f1061030657610100808354040283529160200191" + "610331565b820191906000526020600020905b81548152906001019060200180831161031457829003601f1682" + "01915b505050505081565b606060008054600181600116156101000203166002900480601f0160208091040260" + "200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103" + "d15780601f106103a6576101008083540402835291602001916103d1565b820191906000526020600020905b81" + "54815290600101906020018083116103b457829003601f168201915b5050505050905090565b82805460018160" + "0116156101000203166002900490600052602060002090601f016020900481019282601f1061041c57805160ff" + "191683800117855561044a565b8280016001018555821561044a579182015b8281111561044957825182559160" + "200191906001019061042e565b5b509050610457919061045b565b5090565b61047d91905b8082111561047957" + "6000816000905550600101610461565b5090565b905600a165627a7a723058204736027ad6b97d7cd2685379ac" + "b35b386dcb18799934be8283f1e08cd1f0c6ec0029"; +}; +BOOST_FIXTURE_TEST_SUITE(ExecutorTest, ExecutorFixture) + +BOOST_AUTO_TEST_CASE(construct) +{ + auto tmp = make_shared(blockFactory, dispatcher, ledger, storage, true); +} + +BOOST_AUTO_TEST_CASE(executeTransaction_DeployHelloWorld) +{ + auto keyPair = cryptoSuite->signatureImpl()->generateKeyPair(); + memcpy(keyPair->secretKey()->mutableData(), + fromHexString("ff6f30856ad3bae00b1169808488502786a13e3c174d85682135ffd51310310e")->data(), + 32); + memcpy(keyPair->publicKey()->mutableData(), + fromHexString("ccd8de502ac45462767e649b462b5f4ca7eadd69c7e1f1b410bdf754359be29b1b88ffd79744" + "03f56e250af52b25682014554f7b3297d6152401e85d426a06ae") + ->data(), + 64); + cout << keyPair->secretKey()->hex() << endl << keyPair->publicKey()->hex() << endl; + auto to = keyPair->address(cryptoSuite->hashImpl()).asBytes(); + auto helloworld = string(helloBin); + + auto input = *fromHexString(helloworld); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1"); + auto sender = string_view((char*)tx->sender().data(), tx->sender().size()); + auto executive = std::make_shared(executiveContext); + auto receipt = executor->dmcExecuteTransaction(tx, executive); + BOOST_TEST(receipt->status() == (int32_t)TransactionStatus::None); + BOOST_TEST(receipt->gasUsed() == 430575); + // std::cout << "##### hash:" << receipt->hash().hexPrefixed() << std::endl; + BOOST_TEST(receipt->hash().hexPrefixed() == + "0x1ea6ad9487c4a45408908d70478ba23e7354ee8beddb7ecae1c4bcb3c02604dd"); + BOOST_TEST(receipt->contractAddress() == "8968B494F66b2508330B24A7d1caFA06a14f6315"); + BOOST_TEST(*toHexString(receipt->output()) == ""); + BOOST_TEST(receipt->blockNumber() == 1); + auto addressbytes = asString(*fromHexString(string(receipt->contractAddress()))); + auto nonce = executiveContext->getState()->getNonce(addressbytes); + BOOST_TEST(nonce == executiveContext->getState()->accountStartNonce()); + nonce = executiveContext->getState()->getNonce(sender); + BOOST_TEST(nonce == executiveContext->getState()->accountStartNonce() + 1); + auto newAddress = string(receipt->contractAddress()); + + // call helloworld get + input = *fromHexString("0x6d4ce63c"); + auto getTx = fakeTransaction(cryptoSuite, keyPair, newAddress, input, 101, 100001, "1", "1"); + receipt = executor->dmcExecuteTransaction(getTx, executive); + BOOST_TEST(receipt->status() == (int32_t)TransactionStatus::None); + BOOST_TEST(receipt->gasUsed() == 22742); + // std::cout << "##### hash:" << receipt->hash().hexPrefixed() << std::endl; + BOOST_TEST(receipt->hash().hexPrefixed() == + "0xeeef9c8a72141a2d3184509fa21fb5496f59acae2596f0c027747a0a9ffbf38b"); + BOOST_TEST(receipt->contractAddress() == ""); + // Hello, World! == 48656c6c6f2c20576f726c6421 + BOOST_TEST(*toHexString(receipt->output()) == + "00000000000000000000000000000000000000000000000000000000000000200000000000000000000" + "00000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000" + "00000000000000000000000000"); + BOOST_TEST(receipt->blockNumber() == 1); + nonce = executiveContext->getState()->getNonce(sender); + BOOST_TEST(nonce == executiveContext->getState()->accountStartNonce() + 1); + + // call helloworld set fisco + input = *fromHexString( + "0x4ed3885e00000000000000000000000000000000000000000000000000000000000000200000000000000000" + "000000000000000000000000000000000000000000000005666973636f00000000000000000000000000000000" + "0000000000000000000000"); + // cout << "##### newAddress: " << newAddress << endl; + auto setTx = fakeTransaction(cryptoSuite, keyPair, newAddress, input, 101, 100001, "1", "1"); + receipt = executor->dmcExecuteTransaction(setTx, executive); + BOOST_TEST(receipt->status() == (int32_t)TransactionStatus::None); + BOOST_TEST(receipt->gasUsed() == 30791); + // std::cout << "##### hash:" << receipt->hash().hexPrefixed() << std::endl; + BOOST_TEST(receipt->hash().hexPrefixed() == + "0x92f866a0f12010ed7b8a41b82aece81db64ee1eef4d12619fb5cf401e0b8cdff"); + BOOST_TEST(receipt->contractAddress() == ""); + BOOST_TEST(*toHexString(receipt->output()) == ""); + BOOST_TEST(receipt->blockNumber() == 1); + // get + receipt = executor->dmcExecuteTransaction(getTx, executive); + // Hello, World! == 666973636f + BOOST_TEST(*toHexString(receipt->output()) == + "00000000000000000000000000000000000000000000000000000000000000200000000000000000000" + "000000000000000000000000000000000000000000005666973636f0000000000000000000000000000" + "00000000000000000000000000"); + nonce = executiveContext->getState()->getNonce(sender); + BOOST_TEST(nonce == executiveContext->getState()->accountStartNonce() + 1); + + executor->dmcExecuteTransaction(tx, executive); + nonce = executiveContext->getState()->getNonce(sender); + BOOST_TEST(nonce == executiveContext->getState()->accountStartNonce() + 2); + executor->dmcExecuteTransaction(tx, executive); + nonce = executiveContext->getState()->getNonce(sender); + BOOST_TEST(nonce == executiveContext->getState()->accountStartNonce() + 3); +} + +BOOST_AUTO_TEST_CASE(executeBlock) +{ + auto block = blockFactory->createBlock(); + auto header = blockFactory->blockHeaderFactory()->createBlockHeader(1); + block->setBlockHeader(header); + auto keyPair = cryptoSuite->signatureImpl()->generateKeyPair(); + memcpy(keyPair->secretKey()->mutableData(), + fromHexString("ff6f30856ad3bae00b1169808488502786a13e3c174d85682135ffd51310310e")->data(), + 32); + memcpy(keyPair->publicKey()->mutableData(), + fromHexString("ccd8de502ac45462767e649b462b5f4ca7eadd69c7e1f1b410bdf754359be29b1b88ffd79744" + "03f56e250af52b25682014554f7b3297d6152401e85d426a06ae") + ->data(), + 64); + auto to = keyPair->address(cryptoSuite->hashImpl()).asBytes(); + auto helloworld = string(helloBin); + auto input = *fromHexString(helloworld); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1"); + block->appendTransaction(tx); + tx = fakeTransaction(cryptoSuite, keyPair, "", input, 102, 100002, "1", "1"); + block->appendTransaction(tx); + auto getTx = + fakeTransaction(cryptoSuite, keyPair, string("8968B494F66b2508330B24A7d1caFA06a14f6315"), + *fromHexString("0x6d4ce63c"), 101, 100001, "1", "1"); + block->appendTransaction(getTx); + block->setBlockType(BlockType::CompleteBlock); + block->blockHeader()->setParentInfo({ParentInfo{100, crypto::HashType()}}); + auto txsRoot = block->blockHeader()->txsRoot(); + auto receiptsRoot = block->blockHeader()->receiptsRoot(); + auto stateRoot = block->blockHeader()->stateRoot(); + BOOST_TEST(block->blockHeader()->gasUsed() == 0); + + // asyncExecuteTransaction should has no affect on state + promise prom; + executor->asyncdmcExecuteTransaction( + tx, [&prom](const Error::Ptr& err, const protocol::TransactionReceipt::ConstPtr&) { + prom.set_value(err); + }); + prom.get_future().get(); + + // execute block + auto parentHeader = blockFactory->blockHeaderFactory()->createBlockHeader(0); + auto result = executor->executeBlock(block); + auto deployReceipt = block->receipt(0); + BOOST_TEST(deployReceipt->status() == (int32_t)TransactionStatus::None); + BOOST_TEST(deployReceipt->gasUsed() == 430575); + // std::cout << "##### hash:" << deployReceipt->hash().hexPrefixed() << std::endl; + BOOST_TEST(deployReceipt->hash().hexPrefixed() == + "0x1ea6ad9487c4a45408908d70478ba23e7354ee8beddb7ecae1c4bcb3c02604dd"); + BOOST_TEST(deployReceipt->contractAddress() == "8968B494F66b2508330B24A7d1caFA06a14f6315"); + BOOST_TEST(*toHexString(deployReceipt->output()) == ""); + BOOST_TEST(deployReceipt->blockNumber() == 1); + + deployReceipt = block->receipt(1); + BOOST_TEST(deployReceipt->status() == (int32_t)TransactionStatus::None); + BOOST_TEST(deployReceipt->gasUsed() == 430575); + // std::cout << "##### hash:" << deployReceipt->hash().hexPrefixed() << std::endl; + BOOST_TEST(deployReceipt->hash().hexPrefixed() == + "0xadbaadfd1f16d7f44248cff9a09add0b1cbbf83e2265094a7d79f444941ffb88"); + BOOST_TEST(deployReceipt->contractAddress() == "21f7F2c888221d771e103CB2E56A7Da15a2d898e"); + BOOST_TEST(*toHexString(deployReceipt->output()) == ""); + BOOST_TEST(deployReceipt->blockNumber() == 1); + + auto getReceipt = block->receipt(2); + BOOST_TEST(getReceipt->status() == (int32_t)TransactionStatus::None); + BOOST_TEST(getReceipt->gasUsed() == 22742); + + // std::cout << "##### hash:" << getReceipt->hash().hexPrefixed() << std::endl; + BOOST_TEST(getReceipt->hash().hexPrefixed() == + "0xeeef9c8a72141a2d3184509fa21fb5496f59acae2596f0c027747a0a9ffbf38b"); + BOOST_TEST(getReceipt->contractAddress() == ""); + // Hello, World! == 48656c6c6f2c20576f726c6421 + BOOST_TEST(*toHexString(getReceipt->output()) == + "00000000000000000000000000000000000000000000000000000000000000200000000000000000000" + "00000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000" + "00000000000000000000000000"); + BOOST_TEST(getReceipt->blockNumber() == 1); + + // TODO: check block + BOOST_TEST(block->blockType() == BlockType::CompleteBlock); + BOOST_TEST(block->blockHeader()->gasUsed() == 883892); + BOOST_TEST(txsRoot == block->blockHeader()->txsRoot()); + BOOST_TEST(receiptsRoot != block->blockHeader()->receiptsRoot()); + BOOST_TEST(block->blockHeader()->stateRoot() != stateRoot); + + // std::cout << "##### receiptsRoot:" << block->blockHeader()->receiptsRoot().hexPrefixed() << + // std::endl; std::cout << "##### stateRoot:" << block->blockHeader()->stateRoot().hexPrefixed() + // << std::endl; + + BOOST_TEST(block->blockHeader()->receiptsRoot().hexPrefixed() == + "0x07e51cac5e5869abc628c8f95db66a54a8917152e5ea6b48b75f9c381426bcb7"); + BOOST_TEST(block->blockHeader()->stateRoot().hexPrefixed() == + "0x21dab5dd8587fc014a29aaa96aa6ba93911fe84f05b1c7c4402c54303e73941b"); + BOOST_TEST(block->blockHeader()->stateRoot().hexPrefixed() == + result->getTableFactory()->hash().hexPrefixed()); +} + + +BOOST_AUTO_TEST_CASE(start_stop) +{ + executor = make_shared(blockFactory, dispatcher, ledger, storage, true); + executor->start(); + executor->stop(); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/test/old/mock/MemoryStorage.h" "b/BFPL\345\243\271/bcos-executor/test/old/mock/MemoryStorage.h" new file mode 100644 index 00000000..010a3b48 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/old/mock/MemoryStorage.h" @@ -0,0 +1,307 @@ +/** + * Copyright (C) 2021 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. + */ +/** + * @brief the storage implement in memory + * @file MemoryStorage.h + * @author: xingqiangbai + * @date: 2021-06-09 + */ + +#pragma once + +#include "bcos-framework/storage/StorageInterface.h" +#include +#include + +namespace bcos +{ +namespace storage +{ +class MemoryStorage : public StorageInterface +{ +public: + typedef std::shared_ptr Ptr; + MemoryStorage() { data[storage::SYS_TABLE] = std::map(); } + virtual ~MemoryStorage() = default; + + std::vector getPrimaryKeys(const std::shared_ptr& _tableInfo, + const Condition::Ptr& _condition) const override + { + std::vector ret; + std::lock_guard lock(m_mutex); + if (data.count(_tableInfo->name)) + { + for (auto& kv : data.at(_tableInfo->name)) + { + if (!_condition || _condition->isValid(kv.first)) + { + ret.emplace_back(kv.first); + } + } + } + return ret; + } + std::shared_ptr getRow( + const std::shared_ptr& _tableInfo, const std::string_view& _key) override + { + std::shared_ptr ret = nullptr; + std::lock_guard lock(m_mutex); + if (data.count(_tableInfo->name)) + { + if (data[_tableInfo->name].count(std::string(_key))) + { + return data[_tableInfo->name][std::string(_key)]; + } + } + return ret; + } + std::map> getRows( + const std::shared_ptr& _tableInfo, + const std::vector& _keys) override + { + std::map> ret; + std::lock_guard lock(m_mutex); + if (data.count(_tableInfo->name)) + { + for (auto& key : _keys) + { + if (data[_tableInfo->name].count(std::string(key))) + { + ret[key] = data[_tableInfo->name][key]; + } + } + } + return ret; + } + std::pair commitBlock(protocol::BlockNumber _number, + const std::vector>& _tableInfos, + const std::vector>>& _tableDatas) override + { + size_t total = 0; + if (_tableInfos.size() != _tableDatas.size()) + { + auto error = std::make_shared(-1, ""); + return {0, error}; + } + std::shared_ptr stateTableFactory = nullptr; + if (_number != 0) + { + if (m_number2TableFactory.count(_number)) + { + stateTableFactory = m_number2TableFactory[_number]; + } + else + { + return {0, std::make_shared(StorageErrorCode::StateCacheNotFound, + std::to_string(_number) + "state cache not found")}; + } + auto stateData = stateTableFactory->exportData(); + stateData.first.insert(stateData.first.end(), _tableInfos.begin(), _tableInfos.end()); + stateData.second.insert(stateData.second.end(), _tableDatas.begin(), _tableDatas.end()); + std::lock_guard lock(m_mutex); + for (size_t i = 0; i < stateData.first.size(); ++i) + { + for (auto& item : *stateData.second[i]) + { + if (item.second->getStatus() == Entry::Status::NORMAL) + { + data[stateData.first[i]->name][item.first] = item.second; + ++total; + } + } + } + } + else + { + std::lock_guard lock(m_mutex); + for (size_t i = 0; i < _tableInfos.size(); ++i) + { + for (auto& item : *_tableDatas[i]) + { + if (item.second->getStatus() == Entry::Status::NORMAL) + { + data[_tableInfos[i]->name][item.first] = item.second; + ++total; + } + } + } + } + return {total, nullptr}; + } + + void asyncGetPrimaryKeys(const std::shared_ptr& _tableInfo, + const Condition::Ptr& _condition, + std::function&)> _callback) override + { + auto keyList = getPrimaryKeys(_tableInfo, _condition); + boost::this_thread::sleep_for(boost::chrono::milliseconds(SLEEP_MILLI_SECONDS)); + auto error = std::make_shared(0, ""); + _callback(error, keyList); + } + + void asyncGetRow(const TableInfo::Ptr& _tableInfo, const std::string_view& _key, + std::function _callback) override + { + auto entry = getRow(_tableInfo, _key); + boost::this_thread::sleep_for(boost::chrono::milliseconds(SLEEP_MILLI_SECONDS)); + auto error = std::make_shared(0, ""); + _callback(error, entry); + } + void asyncGetRows(const std::shared_ptr& _tableInfo, + const std::shared_ptr>& _keyList, + std::function&)> _callback) + override + { + auto rowMap = getRows(_tableInfo, *_keyList); + boost::this_thread::sleep_for(boost::chrono::milliseconds(SLEEP_MILLI_SECONDS)); + auto error = std::make_shared(0, ""); + _callback(error, rowMap); + } + + void asyncCommitBlock(protocol::BlockNumber _number, + const std::shared_ptr>>& _tableInfo, + const std::shared_ptr>>>& + _tableMap, + std::function _callback) override + { + auto retPair = commitBlock(_number, *_tableInfo, *_tableMap); + boost::this_thread::sleep_for(boost::chrono::milliseconds(SLEEP_MILLI_SECONDS)); + auto error = std::make_shared(0, ""); + _callback(error, retPair.first); + } + + // cache StateStorage + void asyncAddStateCache(protocol::BlockNumber _number, + const std::shared_ptr& _table, + std::function _callback) override + { + boost::this_thread::sleep_for(boost::chrono::milliseconds(SLEEP_MILLI_SECONDS)); + addStateCache(_number, _table); + _callback(nullptr); + } + void asyncDropStateCache(protocol::BlockNumber, std::function) override + {} + void asyncGetStateCache(protocol::BlockNumber _blockNumber, + std::function&)> + _callback) override + { + auto tableFactory = getStateCache(_blockNumber); + boost::this_thread::sleep_for(boost::chrono::milliseconds(SLEEP_MILLI_SECONDS)); + auto error = std::make_shared(0, ""); + _callback(error, tableFactory); + } + + std::shared_ptr getStateCache( + protocol::BlockNumber _blockNumber) override + { + if (m_number2TableFactory.count(_blockNumber)) + { + return m_number2TableFactory[_blockNumber]; + } + return nullptr; + } + + void dropStateCache(protocol::BlockNumber) override {} + void addStateCache(protocol::BlockNumber _blockNumber, + const std::shared_ptr& _tableFactory) override + { + m_number2TableFactory[_blockNumber] = _tableFactory; + } + // KV store in split database, used to store data off-chain + Error::Ptr put( + const std::string_view&, const std::string_view&, const std::string_view&) override + { + return nullptr; + } + std::pair get( + const std::string_view&, const std::string_view&) override + { + return {"", nullptr}; + } + Error::Ptr remove(const std::string_view&, const std::string_view&) override { return nullptr; } + + void asyncPut(const std::string_view& _columnFamily, const std::string_view& _key, + const std::string_view& _value, std::function _callback) override + { + auto key = getKey(_columnFamily, _key); + m_key2Data[key] = _value; + _callback(nullptr); + } + + void asyncRemove(const std::string_view& _columnFamily, const std::string_view& _key, + std::function _callback) override + { + auto key = getKey(_columnFamily, _key); + if (!m_key2Data.count(key)) + { + _callback(std::make_shared(StorageErrorCode::NotFound, "key NotFound")); + return; + } + m_key2Data.erase(key); + _callback(nullptr); + } + + void asyncGet(const std::string_view& _columnFamily, const std::string_view& _key, + std::function _callback) override + { + auto key = getKey(_columnFamily, _key); + if (!m_key2Data.count(key)) + { + _callback(std::make_shared(StorageErrorCode::NotFound, "key NotFound"), ""); + return; + } + _callback(nullptr, m_key2Data[key]); + } + + void asyncGetBatch(const std::string_view& _columnFamily, + const std::shared_ptr>& _keys, + std::function>&)> + callback) override + { + auto result = std::make_shared>(); + for (auto const& _key : *_keys) + { + auto key = getKey(_columnFamily, _key); + if (!m_key2Data.count(key)) + { + result->push_back(""); + continue; + } + result->push_back(m_key2Data[key]); + } + callback(nullptr, result); + } + + +protected: + std::string getKey(const std::string_view& _columnFamily, const std::string_view& _key) + { + std::string columnFamily(_columnFamily.data(), _columnFamily.size()); + std::string key(_key.data(), _key.size()); + return columnFamily + "_" + key; + } + + std::map m_key2Data; + +private: + std::map> data; + mutable std::mutex m_mutex; + std::map m_number2TableFactory; +}; + +} // namespace storage + +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/test/old/mock/MockDispatcher.h" "b/BFPL\345\243\271/bcos-executor/test/old/mock/MockDispatcher.h" new file mode 100644 index 00000000..b3e3e2a9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/old/mock/MockDispatcher.h" @@ -0,0 +1,94 @@ +/** + * Copyright (C) 2021 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. + * + * @brief mock the Dispatcher + * @file MockDispatcher.h + * @author: xingqiangbai + * @date 2021-06-09 + + */ +#pragma once +#include "bcos-framework/dispatcher/DispatcherInterface.h" +#include + +using namespace bcos; +using namespace bcos::dispatcher; +using namespace bcos::protocol; + +namespace bcos +{ +namespace test +{ +class MockDispatcher : public DispatcherInterface +{ +public: + using Ptr = std::shared_ptr; + MockDispatcher() = default; + ~MockDispatcher() override {} + + void asyncExecuteBlock(const Block::Ptr& _block, bool, + std::function _callback, ssize_t) override + { + if (m_blocks.empty()) + { + m_blocks.push_back(_block); + _callback(nullptr, _block->blockHeader()); + return; + } + auto latestBlock = m_blocks[m_blocks.size() - 1]; + if (_block->blockHeader()->number() != latestBlock->blockHeader()->number() + 1) + { + _callback(std::make_shared(-1, "invalid block for not consistent"), nullptr); + return; + } + std::vector parentList; + if (_block->blockHeader()->parentInfo().size() == 0) + { + parentList.push_back(ParentInfo{ + latestBlock->blockHeader()->number(), latestBlock->blockHeader()->hash()}); + _block->blockHeader()->setParentInfo(parentList); + } + m_blocks.push_back(_block); + _callback(nullptr, _block->blockHeader()); + } + + void asyncGetLatestBlock( + std::function _callback) override + { + if (m_blocks.empty()) + { + _callback(std::make_shared(-1, "no new block"), nullptr); + } + _callback(nullptr, m_blocks.front()); + } + + void asyncNotifyExecutionResult(const Error::Ptr&, bcos::crypto::HashType const&, + const std::shared_ptr& _header, + std::function) override + { + // the process it in order + m_blockHeaders[_header->number()] = _header; + m_blocks.erase(m_blocks.begin()); + } + + void stop() override {} + void start() override {} + +private: + std::vector m_blocks; + std::map m_blockHeaders; +}; +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/old/mock/MockLedger.h" "b/BFPL\345\243\271/bcos-executor/test/old/mock/MockLedger.h" new file mode 100644 index 00000000..5b9fe6ed --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/old/mock/MockLedger.h" @@ -0,0 +1,73 @@ +/** + * Copyright (C) 2021 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. + */ +/** + * @brief mock ledger + * @file MockLedger.h + * @author: xingqiangbai + * @date: 2021-06-09 + */ + +#pragma once + +#include "bcos-framework/testutils/faker/FakeLedger.h" +#include +#include + +namespace bcos +{ +namespace test +{ +class MockLedger : public FakeLedger +{ +public: + typedef std::shared_ptr Ptr; + MockLedger(protocol::BlockHeader::Ptr _header, protocol::BlockFactory::Ptr _blockFactory) + : m_number(_header->number()), m_blockHeader(_header), blockFactory(_blockFactory) + {} + virtual ~MockLedger() = default; + + // maybe sync module or rpc module need this interface to return header/txs/receipts + + void asyncGetBlockDataByNumber(BlockNumber _number, int32_t, + std::function _callback) override + { + if (_number < 1000) + { + auto block = blockFactory->createBlock(); + auto header = blockFactory->blockHeaderFactory()->createBlockHeader(_number); + block->setBlockHeader(header); + _callback(nullptr, block); + } + else + { + _callback(std::make_shared(-1, "failed"), nullptr); + } + } + + void asyncGetBlockNumber(std::function _onGetBlock) override + { + _onGetBlock(nullptr, m_number); + } + +protected: + protocol::BlockNumber m_number; + BlockHeader::Ptr m_blockHeader; + protocol::BlockFactory::Ptr blockFactory; +}; + +} // namespace test + +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/test/solidity/ParallelOk.sol" "b/BFPL\345\243\271/bcos-executor/test/solidity/ParallelOk.sol" new file mode 100644 index 00000000..5d97052a --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/solidity/ParallelOk.sol" @@ -0,0 +1,31 @@ +pragma solidity ^0.4.25; + +contract ParallelOk +{ + mapping (string => uint256) _balance; + + // Just an example, overflow is ok, use 'SafeMath' if needed + function transfer(string from, string to, uint256 num) public + { + _balance[from] -= num; + _balance[to] += num; + } + + // Just for testing whether the parallel revert function is working well, no practical use + function transferWithRevert(string from, string to, uint256 num) public + { + _balance[from] -= num; + _balance[to] += num; + require(num <= 100); + } + + function set(string name, uint256 num) public + { + _balance[name] = num; + } + + function balanceOf(string name) public view returns (uint256) + { + return _balance[name]; + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/solidity/TestEvmPrecompiled.sol" "b/BFPL\345\243\271/bcos-executor/test/solidity/TestEvmPrecompiled.sol" new file mode 100644 index 00000000..cc717af6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/solidity/TestEvmPrecompiled.sol" @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.6.10 <0.8.20; + +contract TestEvmPrecompiled { + event callGasUsed(uint256); + + function keccak256Test(bytes memory b) public returns (bytes32 result) { + uint256 gasLeft1 = gasleft(); + result = keccak256(b); + uint256 gasLeft2 = gasleft(); + uint256 gasUsed = gasLeft1 - gasLeft2; + emit callGasUsed(gasUsed); + require(gasUsed <= 10000 && gasUsed > 0, "gasleft check error"); + } + + // 0x01 + function ecRecoverTest( + bytes32 _hash, + uint8 _v, + bytes32 _r, + bytes32 _s + ) public returns (address pub) { + uint256 gasLeft1 = gasleft(); + pub = ecrecover(_hash, _v, _r, _s); + uint256 gasLeft2 = gasleft(); + uint256 gasUsed = gasLeft1 - gasLeft2; + emit callGasUsed(gasUsed); + require(gasUsed <= 10000 && gasUsed >= 3000, "gasleft check error"); + } + + // 0x02 + function sha256Test(bytes memory b) public returns (bytes32 result) { + uint256 gasLeft1 = gasleft(); + result = sha256(b); + uint256 gasLeft2 = gasleft(); + uint256 gasUsed = gasLeft1 - gasLeft2; + emit callGasUsed(gasUsed); + require(gasUsed <= 10000 && gasUsed >= 60, "gasleft check error"); + } + + // 0x03 + function ripemd160Test(bytes memory b) public returns (bytes32 result) { + uint256 gasLeft1 = gasleft(); + result = ripemd160(b); + uint256 gasLeft2 = gasleft(); + uint256 gasUsed = gasLeft1 - gasLeft2; + emit callGasUsed(gasUsed); + require(gasUsed <= 10000 && gasUsed >= 600, "gasleft check error"); + } + + // 0x04 + function callDatacopy(bytes memory data) public returns (bytes memory) { + bytes memory ret = new bytes(data.length); + uint256 gasLeft1 = gasleft(); + assembly { + let len := mload(data) + if iszero( + call(gas(), 0x04, 0, add(data, 0x20), len, add(ret, 0x20), len) + ) { + invalid() + } + } + uint256 gasLeft2 = gasleft(); + uint256 gasUsed = gasLeft1 - gasLeft2; + emit callGasUsed(gasUsed); + require(gasUsed <= 10000 && gasUsed >= 15, "gasleft check error"); + return ret; + } + + // 0x05 + function callBigModExp( + bytes32 base, + bytes32 exponent, + bytes32 modulus + ) public returns (bytes32 result) { + uint256 gasLeft1 = gasleft(); + assembly { + // free memory pointer + let memPtr := mload(0x40) + + // length of base, exponent, modulus + mstore(memPtr, 0x20) + mstore(add(memPtr, 0x20), 0x20) + mstore(add(memPtr, 0x40), 0x20) + + // assign base, exponent, modulus + mstore(add(memPtr, 0x60), base) + mstore(add(memPtr, 0x80), exponent) + mstore(add(memPtr, 0xa0), modulus) + + // call the precompiled contract BigModExp (0x05) + let success := call(gas(), 0x05, 0x0, memPtr, 0xc0, memPtr, 0x20) + switch success + case 0 { + revert(0x0, 0x0) + } + default { + result := mload(memPtr) + } + } + uint256 gasLeft2 = gasleft(); + uint256 gasUsed = gasLeft1 - gasLeft2; + emit callGasUsed(gasUsed); + require(gasUsed <= 100000, "gasleft check error"); + } + + // 0x06 + function callBn256Add( + bytes32 ax, + bytes32 ay, + bytes32 bx, + bytes32 by + ) public returns (bytes32[2] memory result) { + bytes32[4] memory input; + input[0] = ax; + input[1] = ay; + input[2] = bx; + input[3] = by; + uint256 gasLeft1 = gasleft(); + assembly { + let success := call(gas(), 0x06, 0, input, 0x80, result, 0x40) + switch success + case 0 { + revert(0, 0) + } + } + uint256 gasLeft2 = gasleft(); + uint256 gasUsed = gasLeft1 - gasLeft2; + emit callGasUsed(gasUsed); + require(gasUsed <= 10000 && gasUsed >= 150, "gasleft check error"); + } + + // 0x07 + function callBn256ScalarMul( + bytes32 x, + bytes32 y, + bytes32 scalar + ) public returns (bytes32[2] memory result) { + bytes32[3] memory input; + input[0] = x; + input[1] = y; + input[2] = scalar; + uint256 gasLeft1 = gasleft(); + assembly { + let success := call(gas(), 0x07, 0, input, 0x60, result, 0x40) + switch success + case 0 { + revert(0, 0) + } + } + uint256 gasLeft2 = gasleft(); + uint256 gasUsed = gasLeft1 - gasLeft2; + emit callGasUsed(gasUsed); + require(gasUsed <= 10000 && gasUsed >= 6000, "gasleft check error"); + } + + // 0x08 + function callBn256Pairing(bytes memory input) + public + returns (bytes32 result) + { + // input is a serialized bytes stream of (a1, b1, a2, b2, ..., ak, bk) from (G_1 x G_2)^k + uint256 len = input.length; + require(len % 192 == 0); + uint256 gasLeft1 = gasleft(); + assembly { + let memPtr := mload(0x40) + let success := call( + gas(), + 0x08, + 0, + add(input, 0x20), + len, + memPtr, + 0x20 + ) + switch success + case 0 { + revert(0, 0) + } + default { + result := mload(memPtr) + } + } + uint256 gasLeft2 = gasleft(); + uint256 gasUsed = gasLeft1 - gasLeft2; + emit callGasUsed(gasUsed); + require(gasUsed <= 100000, "gasleft check error"); + } + + // 0x09 + function callBlake2F( + uint32 rounds, + bytes32[2] memory h, + bytes32[4] memory m, + bytes8[2] memory t, + bool f + ) public returns (bytes32[2] memory) { + bytes32[2] memory output; + + bytes memory args = abi.encodePacked( + rounds, + h[0], + h[1], + m[0], + m[1], + m[2], + m[3], + t[0], + t[1], + f + ); + uint256 gasLeft1 = gasleft(); + assembly { + if iszero( + staticcall(not(0), 0x09, add(args, 32), 0xd5, output, 0x40) + ) { + revert(0, 0) + } + } + uint256 gasLeft2 = gasleft(); + uint256 gasUsed = gasLeft1 - gasLeft2; + emit callGasUsed(gasUsed); + require(gasUsed <= 100000, "gasleft check error"); + return output; + } + + function addmodTest( + uint256 x, + uint256 y, + uint256 k + ) public returns (uint256) { + uint256 gasLeft1 = gasleft(); + uint256 result = addmod(x, y, k); + uint256 gasLeft2 = gasleft(); + uint256 gasUsed = gasLeft1 - gasLeft2; + emit callGasUsed(gasUsed); + require(gasUsed <= 10000 && gasUsed >= 0, "gasleft check error"); + return result; + } + + function mulmodTest( + uint256 x, + uint256 y, + uint256 k + ) public view returns (uint256) { + uint256 gasLeft1 = gasleft(); + uint256 result = mulmod(x, y, k); + uint256 gasLeft2 = gasleft(); + uint256 gasUsed = gasLeft1 - gasLeft2; + require(gasUsed <= 10000 && gasUsed >= 0, "gasleft check error"); + return result; + } + + function gasLimitTest() public view returns (uint256) { + return block.gaslimit; + } + + function numberTest() public view returns (uint256) { + return block.number; + } + + function timeStampTest() public view returns (uint256) { + return block.timestamp; + } + + function callDataTest() public view returns (bytes memory) { + return msg.data; + } + + function senderTest() public returns (address) { + return msg.sender; + } + + function originTest() public returns (address) { + return tx.origin; + } + + function blockHashTest(uint256 blockNumber) public view returns (bytes32) { + return blockhash(blockNumber); + } +} diff --git "a/BFPL\345\243\271/bcos-executor/test/solidity/precompiled/ConsensusPrecompiled.sol" "b/BFPL\345\243\271/bcos-executor/test/solidity/precompiled/ConsensusPrecompiled.sol" new file mode 100644 index 00000000..a01b9d93 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/solidity/precompiled/ConsensusPrecompiled.sol" @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.6.0; + +contract ConsensusPrecompiled { + function addSealer(string memory,uint256) public returns (int32){} + function addObserver(string memory) public returns (int32){} + function remove(string memory) public returns (int32){} + function setWeight(string memory,uint256) public returns (int32){} +} diff --git "a/BFPL\345\243\271/bcos-executor/test/solidity/precompiled/Crypto.sol" "b/BFPL\345\243\271/bcos-executor/test/solidity/precompiled/Crypto.sol" new file mode 100644 index 00000000..45f5e2da --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/solidity/precompiled/Crypto.sol" @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.6.10 <0.8.20; +pragma experimental ABIEncoderV2; + +abstract contract Crypto +{ + function sm3(bytes memory data) public view returns(bytes32){} + function keccak256Hash(bytes memory data) public view returns(bytes32){} + function sm2Verify(bytes32 message, bytes memory publicKey, bytes32 r, bytes32 s) public view returns(bool, address){} + function curve25519VRFVerify(bytes memory message, bytes memory publicKey, bytes memory proof) public view returns(bool, uint256){} +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/solidity/precompiled/SystemConfigPrecompiled.sol" "b/BFPL\345\243\271/bcos-executor/test/solidity/precompiled/SystemConfigPrecompiled.sol" new file mode 100644 index 00000000..9168fc74 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/solidity/precompiled/SystemConfigPrecompiled.sol" @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.6.0; + +contract SystemConfigPrecompiled +{ + function setValueByKey(string memory key, string memory value) public returns(int32){} + function getValueByKey(string memory key) public view returns(string memory,int256){} +} diff --git "a/BFPL\345\243\271/bcos-executor/test/solidity/test_config.sol" "b/BFPL\345\243\271/bcos-executor/test/solidity/test_config.sol" new file mode 100644 index 00000000..f1c06032 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/solidity/test_config.sol" @@ -0,0 +1,56 @@ +pragma solidity ^0.4.25; + +import "./precompiled/ConsensusPrecompiled.sol"; +import "./precompiled/SystemConfigPrecompiled.sol"; +import "./precompiled/ParallelConfigPrecompiled.sol"; + +contract TestConsensusPrecompiled { + ConsensusPrecompiled consensus; + constructor () public { + consensus = ConsensusPrecompiled(0x1003); + } + + function addSealerTest(string nodeId, uint256 weight) returns (int256){ + return consensus.addSealer(nodeId, weight); + } + + function addObserverTest(string nodeId) returns (int256){ + return consensus.addObserver(nodeId); + } + + function removeTest(string nodeId) returns (int256){ + return consensus.remove(nodeId); + } + + function setWeightTest(string nodeId, uint256 weight) returns (int256){ + return consensus.setWeight(nodeId, weight); + } +} + +contract TestSysConfig { + SystemConfigPrecompiled sys; + constructor () public { + sys = SystemConfigPrecompiled(0x1000); + } + function setValueByKeyTest(string key, string value) public returns (int256){ + return sys.setValueByKey(key, value); + } + + function getValueByKeyTest(string key) public returns (string, int256){ + return sys.getValueByKey(key); + } +} + +contract TestParaConfig { + ParallelConfigPrecompiled parallel; + constructor() public { + parallel = ParallelConfigPrecompiled(0x1006); + } + function registerParallelFunctionInternal(address selector, string func, uint256 size) public returns (int256){ + return parallel.registerParallelFunctionInternal(selector, func, size); + } + + function unregisterParallelFunctionInternal(address selector, string func) public returns (int256){ + return parallel.unregisterParallelFunctionInternal(selector, func); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/solidity/test_crypto.sol" "b/BFPL\345\243\271/bcos-executor/test/solidity/test_crypto.sol" new file mode 100644 index 00000000..cfb1bc0f --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/solidity/test_crypto.sol" @@ -0,0 +1,31 @@ +pragma solidity ^0.6.0; + +import "./precompiled/Crypto.sol"; + +contract TestCrypto { + Crypto crypto; + constructor () public { + crypto = Crypto(0x100a); + } + function sm3(bytes memory data) public view returns (bytes32){ + return crypto.sm3(data); + } + + function keccak256Hash(bytes memory data) public view returns (bytes32){ + return crypto.keccak256Hash(data); + } + + function sm2Verify(bytes32 message, bytes memory publicKey, bytes32 r, bytes32 s) public view returns (bool, address){ + return crypto.sm2Verify(message, publicKey, r, s); + } + + function getSha256(bytes memory _memory) public returns(bytes32 result) + { + return sha256(_memory); + } + + function getKeccak256(bytes memory _memory) public returns(bytes32 result) + { + return keccak256(_memory); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/solidity/test_external_call.sol" "b/BFPL\345\243\271/bcos-executor/test/solidity/test_external_call.sol" new file mode 100644 index 00000000..a8954d8b --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/solidity/test_external_call.sol" @@ -0,0 +1,33 @@ + +pragma solidity ^0.6.0; + +contract B { + + event LogCreateB(int value); + event LogIncrement(int value); + constructor(int value) public { + m_value = value; + emit LogCreateB(value); + } + + function value() public view returns(int) { + return m_value; + } + + function incValue() public { + ++m_value; + emit LogIncrement(m_value); + } + + int m_value; +} + +contract A { + B b; + event LogCallB(int value); + function createAndCallB (int amount) public returns(int) { + b = new B(amount); + emit LogCallB(amount); + return b.value(); + } +} diff --git "a/BFPL\345\243\271/bcos-executor/test/trie-test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-executor/test/trie-test/CMakeLists.txt" new file mode 100644 index 00000000..baadbddd --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/trie-test/CMakeLists.txt" @@ -0,0 +1,4 @@ +add_executable(trie-test main.cpp) +target_include_directories(trie-test PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../src/dag/) +# target_link_libraries(trie-test PUBLIC executor TBB::tbb) + diff --git "a/BFPL\345\243\271/bcos-executor/test/trie-test/main.cpp" "b/BFPL\345\243\271/bcos-executor/test/trie-test/main.cpp" new file mode 100644 index 00000000..53404825 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/trie-test/main.cpp" @@ -0,0 +1,80 @@ +#include "../../src/dag/TrieSet.h" +#include +#include +#include + +using namespace std; +using namespace bcos::executor; + +int main() { + TrieSet trie; + + vector root = {"1", "2"}; + //vector root = {"1", "2", "8"}; + + vector path1 = {"1", "2","4"}; + trie.set(path1, 4); + + vector path2 = {"1", "2", "5"}; + trie.set(path2, 5); + + vector path3 = {"1", "3", "6"}; + trie.set(path3, 6); + + vector path4 = {"1", "2", "5", "7"}; + trie.set(path4, 7); + + for(auto v : trie.get(root)) { + cout << v << " "; + } + return 0; +} + +int main1() { + TrieSet trie; + + vector root = {"1", "2"}; + + vector path1 = {"1", "2","4"}; + trie.set(path1, 4); + + vector path2 = {"1", "2", "5"}; + trie.set(path2, 5); + + vector path3 = {"1", "3", "6"}; + trie.set(path3, 6); + + vector path4 = {"1", "2", "5", "7"}; + trie.set(path4, 7); + + for(auto v : trie.get(root)) { + cout << v << " "; + } + return 0; +} + +int main2() { + TrieSet trie; + + vector root = {}; + + for (int i = 0; i < 100; i++) { + vector path = {i / 10, i % 10}; + trie.set(path, i); + } + + for(auto v : trie.get(root)) { + cout << v << " "; + } + cout << endl; + + vector path = {2}; + trie.set(path, 666); + + for(auto v : trie.get(root)) { + cout << v << " "; + } + cout << endl; + + return 0; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/CMakeLists.txt" "b/BFPL\345\243\271/bcos-executor/test/unittest/CMakeLists.txt" new file mode 100644 index 00000000..5a989d5e --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/CMakeLists.txt" @@ -0,0 +1,25 @@ +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libvm/WasmPath.h.in ${CMAKE_CURRENT_SOURCE_DIR}/libvm/WasmPath.h @ONLY) + +file(GLOB_RECURSE SOURCES "*.cpp" "*.h") +# cmake settings +set(TEST_BINARY_NAME test-bcos-executor) + +add_executable(${TEST_BINARY_NAME} ${SOURCES} libprecompiled/PrecompiledCallTest.cpp) +target_include_directories(${TEST_BINARY_NAME} PRIVATE ./ ../../src ../../include) + +find_package(Boost REQUIRED unit_test_framework) +find_package(TBB CONFIG REQUIRED) +include(SearchTestCases) + +target_link_libraries(${TEST_BINARY_NAME} ${EXECUTOR_TARGET} ${CRYPTO_TARGET} ${TARS_PROTOCOL_TARGET} ${PROTOCOL_TARGET} bcos-framework Boost::serialization Boost::unit_test_framework) +target_compile_options(${TEST_BINARY_NAME} PRIVATE -fno-var-tracking -Wno-unused-parameter) +include(SearchTestCases) +config_test_cases("" "${SOURCES}" ${TEST_BINARY_NAME} "${EXCLUDE_SUITES}") +#add_test(NAME test-executor WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) +# config_test_cases("" "${SOURCES}" test-executor "${EXCLUDE_SUITES}") + +# for code coverage +if (COVERAGE) + include(Coverage) + config_coverage("executor-coverage" "'/usr*' 'boost/**' 'deps/**'") +endif () diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/container/TestHashMap.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/container/TestHashMap.cpp" new file mode 100644 index 00000000..d1b92409 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/container/TestHashMap.cpp" @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::test +{ +struct HashMapFixture +{ + HashMapFixture() + { + keys.resize(count); + for (size_t i = 0; i < count; ++i) + { + keys[i] = boost::lexical_cast(i); + } + } + + std::vector keys; + size_t count = 100 * 1000; +}; + +struct KeyType +{ + KeyType(std::string_view key) : m_key(std::move(key)) {} + KeyType(std::string key) : m_key(std::move(key)) {} + + std::string_view key() const + { + std::string_view view; + std::visit([&view](auto&& key) { view = std::string_view(key); }, m_key); + + return view; + } + + bool operator==(const KeyType& rhs) const { return key() == rhs.key(); } + + std::variant m_key; +}; + +struct KeyTypeHasher +{ + size_t hash(const KeyType& key) const { return hasher(key.key()); } + bool equal(const KeyType& lhs, const KeyType& rhs) const { return lhs.key() == rhs.key(); } + + std::hash hasher; +}; + +struct KeyTypeHasherForUnordered +{ + size_t operator()(const KeyType& key) const { return hasher(key.key()); } + + std::hash hasher; +}; + +BOOST_FIXTURE_TEST_SUITE(TestHashMap, HashMapFixture) + +BOOST_AUTO_TEST_CASE(hashmap) +{ + tbb::concurrent_hash_map map; + + auto now = std::chrono::system_clock::now(); + for (auto& it : keys) + { + map.emplace(std::move(it), 0); + } + + auto elapsed = std::chrono::duration_cast( + std::chrono::system_clock::now() - now); + std::cout << "hashmap elapsed: " << elapsed.count() << std::endl; +} + +BOOST_AUTO_TEST_CASE(unorderedmap) +{ + tbb::concurrent_unordered_map map; + + auto now = std::chrono::system_clock::now(); + for (auto& it : keys) + { + map.emplace(std::move(it), 0); + } + + auto elapsed = std::chrono::duration_cast( + std::chrono::system_clock::now() - now); + std::cout << "unorderedmap elapsed: " << elapsed.count() << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestAbiReader.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestAbiReader.cpp" new file mode 100644 index 00000000..078eb157 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestAbiReader.cpp" @@ -0,0 +1,246 @@ +/** + * Copyright (C) 2021 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. + */ +/** + * @brief : unitest for abi reader + * @author: catli + * @date: 2021-09-26 + */ + +#include "../src/dag/Abi.h" +#include +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::crypto; + +namespace bcos +{ +namespace test +{ +struct AbiReaderFixture +{ + AbiReaderFixture() { hashImpl = std::make_shared(); } + + Hash::Ptr hashImpl; +}; + +BOOST_FIXTURE_TEST_SUITE(TestAbiReader, AbiReaderFixture) + +BOOST_AUTO_TEST_CASE(NormalCase) +{ + auto abiStr = R"( + [ + { + "inputs":[ + { + "components":[ + { + "internalType":"string", + "name":"name", + "type":"string" + }, + { + "internalType":"uint32", + "name":"price", + "type":"uint32" + }, + { + "internalType":"uint128", + "name":"count", + "type":"uint128" + } + ], + "internalType":"struct.Product[]", + "name":"init_products", + "type":"tuple[]" + } + ], + "type":"constructor" + }, + { + "conflictFields":[ + { + "kind":0, + "value":[ + + ], + "read_only":true, + "slot":0 + }, + { + "kind":4, + "value":[ + 0, 1, 2 + ], + "read_only":false, + "slot":1 + } + ], + "constant":false, + "inputs":[ + { + "components":[ + { + "internalType":"string", + "name":"name", + "type":"string" + }, + { + "internalType":"uint32", + "name":"price", + "type":"uint32" + }, + { + "internalType":"uint128", + "name":"count", + "type":"uint128" + } + ], + "internalType":"struct.Product[]", + "name":"prods", + "type":"tuple[]" + } + ], + "selector": [352741043,0], + "name":"add_prod_batch", + "outputs":[ + + ], + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"admin", + "outputs":[ + { + "internalType":"string", + "type":"string" + } + ], + "selector": [352741043,0], + "type":"function" + } + ] + )"sv; + + auto result = FunctionAbi::deserialize(abiStr, *fromHexString("150666b3"), false); + BOOST_CHECK(result.get() != nullptr); + BOOST_CHECK_EQUAL(result->inputs.size(), 1); + + auto& input = result->inputs[0]; + BOOST_CHECK_EQUAL(input.type, "tuple[]"); + BOOST_CHECK_EQUAL(input.components.size(), 3); + BOOST_CHECK_EQUAL(input.components[0].type, "string"); + BOOST_CHECK_EQUAL(input.components[1].type, "uint32"); + BOOST_CHECK_EQUAL(input.components[2].type, "uint128"); + + auto& conflictFields = result->conflictFields; + BOOST_CHECK_EQUAL(conflictFields.size(), 2); + BOOST_CHECK_EQUAL(conflictFields[0].kind, 0); + + auto accessPath = vector{}; + BOOST_CHECK(std::equal(accessPath.begin(), accessPath.end(), conflictFields[0].value.begin())); + BOOST_CHECK_EQUAL(conflictFields[0].slot.value(), 0); + + accessPath = vector{0, 1, 2}; + cout << conflictFields[1].value.size() << endl; + BOOST_CHECK(std::equal(accessPath.begin(), accessPath.end(), conflictFields[1].value.begin())); + BOOST_CHECK_EQUAL(conflictFields[1].slot.value(), 1); +} + +BOOST_AUTO_TEST_CASE(InvalidAbi) +{ + auto abiStr = "vita"sv; + auto result = FunctionAbi::deserialize(abiStr, *fromHexString("150666b3"), false); + BOOST_CHECK(!result); +} + +BOOST_AUTO_TEST_CASE(InvalidSelector) +{ + auto abiStr = R"( + [ + { + "conflictFields":[ + { + "kind":0, + "value":[ + + ], + "read_only":false, + "slot":0 + } + ], + "constant":false, + "inputs":[ + { + "internalType":"string", + "name":"name", + "type":"string" + } + ], + "name":"set", + "outputs":[ + + ], + "selector": [1322485854,0], + "type":"function" + } + ] + )"sv; + + auto result = FunctionAbi::deserialize(abiStr, *fromHexString("150666b3"), false); + BOOST_CHECK(!result); +} + +BOOST_AUTO_TEST_CASE(EmptyConflictFields) +{ + auto abiStr = R"( + [ + { + "constant":false, + "inputs":[ + { + "internalType":"string", + "name":"name", + "type":"string" + } + ], + "name":"set", + "outputs":[ + + ], + "selector": [1322485854,0], + "type":"function" + } + ] + )"sv; + + auto result = FunctionAbi::deserialize(abiStr, *fromHexString("4ed3885e"), false); + BOOST_CHECK(result.get() != nullptr); + BOOST_CHECK(result->conflictFields.empty()); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestBlockContext.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestBlockContext.cpp" new file mode 100644 index 00000000..a3a54878 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestBlockContext.cpp" @@ -0,0 +1,75 @@ +#include "../../../src/executive/BlockContext.h" +#include "../../../src/executive/ExecutiveFactory.h" +#include "../../../src/executive/ExecutiveFlowInterface.h" +#include "../mock/MockExecutiveFlow.h" +#include "../mock/MockLedger.h" +#include "bcos-table/src/StateStorage.h" +#include +#include + + +using namespace std; +using namespace bcos; +using namespace bcos::executor; + +namespace bcos +{ +namespace test +{ +BOOST_AUTO_TEST_SUITE(TestBlockContext) + +BOOST_AUTO_TEST_CASE(BlockContextTest) +{ + auto codeAddressArr = std::vector{ + "addr0", "addr1", "addr2", "addr3", "addr4", "addr5", "addr6", "addr7", "addr8", "addr9"}; + auto executiveFlowName = std::vector{ + "flow0", "flow1", "flow2", "flow3", "flow4", "flow5", "flow6", "flow7", "flow8", "flow9"}; + + LedgerCache::Ptr ledgerCache = std::make_shared(std::make_shared()); + + BlockContext::Ptr blockContext = std::make_shared( + nullptr, ledgerCache, nullptr, 0, h256(), 0, 0, FiscoBcosScheduleV4, false, false); + + h256 blockhash = blockContext->hash(); + EXECUTOR_LOG(DEBUG) << blockhash; + BOOST_CHECK(blockContext->storage() == nullptr); + // BOOST_CHECK(blockContext->lastStorage() == nullptr); + BOOST_CHECK(!blockContext->isWasm()); + BOOST_CHECK(!blockContext->isAuthCheck()); + // BOOST_CHECK(blockContext->hash() != nullptr); + BOOST_CHECK_EQUAL(blockContext->number(), 0); + BOOST_CHECK_EQUAL(blockContext->timestamp(), 0); + BOOST_CHECK_EQUAL(blockContext->blockVersion(), 0); + + + for (int i = 0; i < 10; ++i) + { + MockExecutiveFlow::Ptr executiveFlow = + std::make_shared(executiveFlowName[i]); + blockContext->setExecutiveFlow(codeAddressArr[i], executiveFlow); + } + int count = 0; + for (int i = 0; i < 10; ++i) + { + auto executiveFlow = std::dynamic_pointer_cast( + blockContext->getExecutiveFlow(codeAddressArr[i])); + if (executiveFlow->name() == executiveFlowName[i]) + ++count; + } + BOOST_CHECK_EQUAL(count, 10); + blockContext->clear(); + // int count1 = 0; + // for (int i = 0; i < 10; ++i) + // { + // auto executiveFlow = std::dynamic_pointer_cast( + // blockContext->getExecutiveFlow(codeAddressArr[i])); + // if (executiveFlow->name() == executiveFlowName[i]) + // ++count1; + // } + BOOST_CHECK(blockContext->getExecutiveFlow(codeAddressArr[0]) == nullptr); + blockContext->stop(); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestClockCache.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestClockCache.cpp" new file mode 100644 index 00000000..a6d63236 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestClockCache.cpp" @@ -0,0 +1,155 @@ +/** + * Copyright (C) 2021 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. + */ +/** + * @brief : unitest for clock tinyCache implementation + * @author: catli + * @date: 2021-09-26 + */ + +#include "../src/dag/ClockCache.h" +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::executor; + +namespace bcos +{ +namespace test +{ +struct ClockCacheFixture +{ + ClockCacheFixture() + { + // The capacity of this tinyCache is 1. + // The num of shard of this tinyCache is 1. + tinyCache = make_shared>(1, 0); + + bigCache = make_shared>(64, 6); + } + + shared_ptr> tinyCache; + shared_ptr> bigCache; +}; + + +BOOST_FIXTURE_TEST_SUITE(TestClockCache, ClockCacheFixture) + +BOOST_AUTO_TEST_CASE(InsertSameKey) +{ + BOOST_CHECK_EQUAL(tinyCache->insert(1, new int(1)), true); + BOOST_CHECK_EQUAL(tinyCache->insert(1, new int(2)), true); + auto handle = tinyCache->lookup(1); + BOOST_CHECK(handle.isValid()); + BOOST_CHECK_EQUAL(handle.value(), 2); +} + +BOOST_AUTO_TEST_CASE(HitAndMiss) +{ + auto handle = tinyCache->lookup(100); + BOOST_CHECK(!handle.isValid()); + + BOOST_CHECK_EQUAL(tinyCache->insert(100, new int(101)), true); + handle = tinyCache->lookup(100); + BOOST_CHECK(handle.isValid()); + BOOST_CHECK_EQUAL(handle.value(), 101); + handle = tinyCache->lookup(200); + BOOST_CHECK(!handle.isValid()); + handle = tinyCache->lookup(300); + BOOST_CHECK(!handle.isValid()); + + handle = tinyCache->lookup(100); + BOOST_CHECK(handle.isValid()); + // For now, the tinyCache is full and the item in it cannot be + // replaced due to `handle` reference to it. + BOOST_CHECK_EQUAL(tinyCache->insert(200, new int(201)), false); + // Releases tinyCache handle manually, now tinyCache has enough space + // to insert a new entry. + handle.release(); + BOOST_CHECK_EQUAL(tinyCache->insert(200, new int(201)), true); + handle = tinyCache->lookup(100); + BOOST_CHECK(!handle.isValid()); + handle = tinyCache->lookup(200); + BOOST_CHECK(handle.isValid()); + BOOST_CHECK_EQUAL(handle.value(), 201); + handle = tinyCache->lookup(300); + BOOST_CHECK(!handle.isValid()); + + // Pair (200, 201) still exists in tinyCache, but it isn't referenced + // by any handle, so we can insert new entry to replace it. + BOOST_CHECK_EQUAL(tinyCache->insert(100, new int(102)), true); + handle = tinyCache->lookup(100); + BOOST_CHECK(handle.isValid()); + BOOST_CHECK_EQUAL(handle.value(), 102); + handle = tinyCache->lookup(200); + BOOST_CHECK(!handle.isValid()); + handle = tinyCache->lookup(300); + BOOST_CHECK(!handle.isValid()); +} + +BOOST_AUTO_TEST_CASE(EvictionPolicy) +{ + BOOST_CHECK(bigCache->insert(100, new int(101))); + BOOST_CHECK(bigCache->insert(101, new int(102))); + BOOST_CHECK(bigCache->insert(102, new int(103))); + BOOST_CHECK(bigCache->insert(103, new int(104))); + + BOOST_CHECK(bigCache->insert(200, new int(201))); + BOOST_CHECK(bigCache->insert(201, new int(202))); + BOOST_CHECK(bigCache->insert(202, new int(203))); + BOOST_CHECK(bigCache->insert(203, new int(204))); + + auto h200 = bigCache->lookup(200); + auto h201 = bigCache->lookup(201); + auto h202 = bigCache->lookup(202); + auto h203 = bigCache->lookup(203); + + BOOST_CHECK(bigCache->insert(300, new int(301))); + BOOST_CHECK(bigCache->insert(301, new int(302))); + BOOST_CHECK(bigCache->insert(302, new int(303))); + BOOST_CHECK(bigCache->insert(303, new int(304))); + + // Insert entries much more than cache capacity. + bool insertResult = true; + for (auto i = 0; i < 10000; ++i) + { + insertResult = insertResult && bigCache->insert(1000 + i, new int(2000 + i)); + } + BOOST_CHECK(insertResult); + + // Check whether the entries inserted in the beginning + // are evicted. Ones without extra ref are evicted and + // those with are not. + BOOST_CHECK(!bigCache->lookup(100).isValid()); + BOOST_CHECK(!bigCache->lookup(101).isValid()); + BOOST_CHECK(!bigCache->lookup(102).isValid()); + BOOST_CHECK(!bigCache->lookup(103).isValid()); + + BOOST_CHECK(!bigCache->lookup(300).isValid()); + BOOST_CHECK(!bigCache->lookup(301).isValid()); + BOOST_CHECK(!bigCache->lookup(302).isValid()); + BOOST_CHECK(!bigCache->lookup(303).isValid()); + + BOOST_CHECK(bigCache->lookup(200).value() == 201); + BOOST_CHECK(bigCache->lookup(201).value() == 202); + BOOST_CHECK(bigCache->lookup(202).value() == 203); + BOOST_CHECK(bigCache->lookup(203).value() == 204); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestDagExecutor.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestDagExecutor.cpp" new file mode 100644 index 00000000..3040ac75 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestDagExecutor.cpp" @@ -0,0 +1,1131 @@ +/** + * Copyright (C) 2021 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. + */ +/** + * @brief : unitest for DAG executor implementation + * @author: catli + * @date: 2021-10-27 + */ +#include "bcos-tars-protocol/protocol/BlockHeaderImpl.h" +#include "bcos-tars-protocol/tars/Block.h" + +// if wasm ut crash on aarch64 linux check https://github.com/bytecodealliance/wasmtime/issues/4972 +// #if !defined(__aarch64__) && !defined(__linux__) + +#include "../liquid/hello_world.h" +#include "../liquid/transfer.h" +#include "../mock/MockLedger.h" +#include "../mock/MockTransactionalStorage.h" +#include "../mock/MockTxPool.h" +#include "bcos-codec/wrapper/CodecWrapper.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/protocol/Transaction.h" +#include "bcos-table/src/StateStorage.h" +#include "executor/TransactionExecutor.h" +#include "executor/TransactionExecutorFactory.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::precompiled; + +namespace bcos +{ +namespace test +{ +struct DagExecutorFixture +{ + DagExecutorFixture() + { + boost::log::core::get()->set_logging_enabled(false); + hashImpl = std::make_shared(); + assert(hashImpl); + auto signatureImpl = std::make_shared(); + assert(signatureImpl); + cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + + txpool = std::make_shared(); + backend = std::make_shared(hashImpl); + ledger = std::make_shared(); + + keyPair = cryptoSuite->signatureImpl()->generateKeyPair(); + memcpy(keyPair->secretKey()->mutableData(), + fromHexString("ff6f30856ad3bae00b1169808488502786a13e3c174d85682135ffd51310310e") + ->data(), + 32); + memcpy(keyPair->publicKey()->mutableData(), + fromHexString( + "ccd8de502ac45462767e649b462b5f4ca7eadd69c7e1f1b410bdf754359be29b1b88ffd79744" + "03f56e250af52b25682014554f7b3297d6152401e85d426a06ae") + ->data(), + 64); + createSysTable(); + } + ~DagExecutorFixture() { boost::log::core::get()->set_logging_enabled(true); } + + void createSysTable() + { + // create / table + { + std::promise> promise2; + backend->asyncCreateTable( + "/", "value", [&](Error::UniquePtr&& _error, std::optional&& _table) { + BOOST_CHECK(!_error); + promise2.set_value(std::move(_table)); + }); + auto rootTable = promise2.get_future().get(); + storage::Entry tEntry, newSubEntry, aclTypeEntry, aclWEntry, aclBEntry, extraEntry; + std::map newSubMap; + newSubMap.insert(std::make_pair("apps", FS_TYPE_DIR)); + newSubMap.insert(std::make_pair("/", FS_TYPE_DIR)); + newSubMap.insert(std::make_pair("tables", FS_TYPE_DIR)); + tEntry.importFields({FS_TYPE_DIR}); + newSubEntry.importFields({asString(codec::scale::encode(newSubMap))}); + aclTypeEntry.importFields({"0"}); + aclWEntry.importFields({""}); + aclBEntry.importFields({""}); + extraEntry.importFields({""}); + rootTable->setRow(FS_KEY_TYPE, std::move(tEntry)); + rootTable->setRow(FS_KEY_SUB, std::move(newSubEntry)); + rootTable->setRow(FS_ACL_TYPE, std::move(aclTypeEntry)); + rootTable->setRow(FS_ACL_WHITE, std::move(aclWEntry)); + rootTable->setRow(FS_ACL_BLACK, std::move(aclBEntry)); + rootTable->setRow(FS_KEY_EXTRA, std::move(extraEntry)); + } + + // create /tables table + { + std::promise> promise3; + backend->asyncCreateTable( + "/tables", "value", [&](Error::UniquePtr&& _error, std::optional
&& _table) { + BOOST_CHECK(!_error); + promise3.set_value(std::move(_table)); + }); + auto tablesTable = promise3.get_future().get(); + storage::Entry tEntry, newSubEntry, aclTypeEntry, aclWEntry, aclBEntry, extraEntry; + std::map newSubMap; + tEntry.importFields({FS_TYPE_DIR}); + newSubEntry.importFields({asString(codec::scale::encode(newSubMap))}); + aclTypeEntry.importFields({"0"}); + aclWEntry.importFields({""}); + aclBEntry.importFields({""}); + extraEntry.importFields({""}); + tablesTable->setRow(FS_KEY_TYPE, std::move(tEntry)); + tablesTable->setRow(FS_KEY_SUB, std::move(newSubEntry)); + tablesTable->setRow(FS_ACL_TYPE, std::move(aclTypeEntry)); + tablesTable->setRow(FS_ACL_WHITE, std::move(aclWEntry)); + tablesTable->setRow(FS_ACL_BLACK, std::move(aclBEntry)); + tablesTable->setRow(FS_KEY_EXTRA, std::move(extraEntry)); + } + + // create /apps table + { + std::promise> promise4; + backend->asyncCreateTable( + "/apps", "value", [&](Error::UniquePtr&& _error, std::optional
&& _table) { + BOOST_CHECK(!_error); + promise4.set_value(std::move(_table)); + }); + auto appsTable = promise4.get_future().get(); + storage::Entry tEntry, newSubEntry, aclTypeEntry, aclWEntry, aclBEntry, extraEntry; + std::map newSubMap; + tEntry.importFields({FS_TYPE_DIR}); + newSubEntry.importFields({asString(codec::scale::encode(newSubMap))}); + aclTypeEntry.importFields({"0"}); + aclWEntry.importFields({""}); + aclBEntry.importFields({""}); + extraEntry.importFields({""}); + appsTable->setRow(FS_KEY_TYPE, std::move(tEntry)); + appsTable->setRow(FS_KEY_SUB, std::move(newSubEntry)); + appsTable->setRow(FS_ACL_TYPE, std::move(aclTypeEntry)); + appsTable->setRow(FS_ACL_WHITE, std::move(aclWEntry)); + appsTable->setRow(FS_ACL_BLACK, std::move(aclBEntry)); + appsTable->setRow(FS_KEY_EXTRA, std::move(extraEntry)); + } + } + CryptoSuite::Ptr cryptoSuite; + std::shared_ptr txpool; + std::shared_ptr backend; + std::shared_ptr ledger; + std::shared_ptr hashImpl; + + KeyPairInterface::Ptr keyPair; + int64_t gas = 3000000000; +}; +BOOST_FIXTURE_TEST_SUITE(TestDagExecutor, DagExecutorFixture) + +BOOST_AUTO_TEST_CASE(callWasmConcurrentlyTransfer) +{ + auto executionResultFactory = std::make_shared(); + auto executor = bcos::executor::TransactionExecutorFactory::build( + + ledger, txpool, nullptr, backend, executionResultFactory, hashImpl, true, false, false); + + auto codec = std::make_unique(hashImpl, true); + + bytes transferBin(transfer_wasm, transfer_wasm + transfer_wasm_len); + transferBin = codec->encode(transferBin); + auto transferAbi = string( + R"([{"inputs":[],"type":"constructor"},{"conflictFields":[{"kind":3,"value":[0],"read_only":false,"slot":0},{"kind":3,"value":[1],"read_only":false,"slot":0}],"constant":false,"inputs":[{"internalType":"string","name":"from","type":"string"},{"internalType":"string","name":"to","type":"string"},{"internalType":"uint32","name":"amount","type":"uint32"}],"name":"transfer","selector":[683988646,0],"outputs":[{"internalType":"bool","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"query","outputs":[{"internalType":"uint32","type":"uint32"}],"type":"function"}])"); + + bytes input; + input.insert(input.end(), transferBin.begin(), transferBin.end()); + input.push_back(0); + + string transferAddress = "usr/alice/transfer"; + + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1", transferAbi); + auto sender = boost::algorithm::hex_lower(std::string(tx->sender())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setContextID(99); + params->setSeq(1000); + params->setDepth(0); + params->setOrigin(std::string(sender)); + params->setFrom(std::string(sender)); + params->setTo(transferAddress); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(1); + blockHeader->setParentInfo({{0, h256(0)}}); + + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + + // -------------------------------- + // Create contract transfer + // -------------------------------- + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + result->setSeq(1001); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(result), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + + auto result2 = executePromise2.get_future().get(); + result2->setSeq(1000); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + + auto result3 = executePromise3.get_future().get(); + + BOOST_CHECK_EQUAL(result3->status(), 0); + BOOST_CHECK_EQUAL(result3->origin(), sender); + BOOST_CHECK_EQUAL(result3->from(), paramsBak.to()); + BOOST_CHECK_EQUAL(result3->to(), sender); + + BOOST_CHECK(result3->message().empty()); + BOOST_CHECK(!result3->newEVMContractAddress().empty()); + BOOST_CHECK_LT(result3->gasAvailable(), gas); + + auto address = result3->newEVMContractAddress(); + + bcos::protocol::TwoPCParams commitParams; + commitParams.number = 1; + + std::promise preparePromise; + executor->prepare(commitParams, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + preparePromise.set_value(); + }); + preparePromise.get_future().get(); + + std::promise commitPromise; + executor->commit(commitParams, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + commitPromise.set_value(); + }); + commitPromise.get_future().get(); + auto tableName = std::string("/apps/") + string(address); + + EXECUTOR_LOG(TRACE) << "Checking table: " << tableName; + std::promise
tablePromise; + backend->asyncOpenTable(tableName, [&](Error::UniquePtr&& error, std::optional
&& table) { + BOOST_CHECK(!error); + BOOST_CHECK(table); + tablePromise.set_value(std::move(*table)); + }); + auto table = tablePromise.get_future().get(); + + auto entry = table.getRow("code"); + BOOST_CHECK(entry); + BOOST_CHECK_GT(entry->getField(0).size(), 0); + + entry = table.getRow("abi"); + BOOST_CHECK(entry); + BOOST_CHECK_GT(entry->getField(0).size(), 0); + + std::vector requests; + auto cases = vector>(); + + cases.push_back(make_tuple("alice", "bob", 1000)); + cases.push_back(make_tuple("charlie", "david", 2000)); + cases.push_back(make_tuple("bob", "david", 200)); + cases.push_back(make_tuple("david", "alice", 400)); + + for (size_t i = 0; i < cases.size(); ++i) + { + std::string from = std::get<0>(cases[i]); + std::string to = std::get<1>(cases[i]); + uint32_t amount = std::get<2>(cases[i]); + bytes input; + auto encodedParams = + codec->encodeWithSig("transfer(string,string,uint32)", from, to, amount); + input.insert(input.end(), encodedParams.begin(), encodedParams.end()); + + auto tx = fakeTransaction(cryptoSuite, keyPair, address, input, 101 + i, 100001, "1", "1"); + auto sender = boost::algorithm::hex_lower(std::string(tx->sender())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setType(bcos::protocol::ExecutionMessage::TXHASH); + params->setContextID(i); + params->setSeq(6000); + params->setDepth(0); + params->setFrom(std::string(sender)); + params->setTo(std::string(address)); + params->setOrigin(std::string(sender)); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setTransactionHash(hash); + params->setCreate(false); + + requests.emplace_back(std::move(params)); + } + + executor->dagExecuteTransactions( + requests, [&](bcos::Error::UniquePtr error, + std::vector results) { + BOOST_CHECK(!error); + + vector> expected; + expected.push_back({"alice", numeric_limits::max() - 1000 + 400}); + expected.push_back({"bob", 1000 - 200}); + expected.push_back({"charlie", numeric_limits::max() - 2000}); + expected.push_back({"david", 2000 + 200 - 400}); + + for (size_t i = 0; i < results.size(); ++i) + { + auto& result = results[i]; + BOOST_CHECK_EQUAL(result->status(), 0); + BOOST_CHECK(result->message().empty()); + bool flag; + codec->decode(result->data(), flag); + BOOST_CHECK(flag); + } + + for (size_t i = 0; i < expected.size(); ++i) + { + bytes queryBytes; + auto encodedParams = + codec->encodeWithSig("query(string)", std::get<0>(expected[i])); + queryBytes.insert(queryBytes.end(), encodedParams.begin(), encodedParams.end()); + + auto params = std::make_unique(); + params->setContextID(888 + i); + params->setSeq(999); + params->setDepth(0); + params->setFrom(std::string(sender)); + params->setTo(transferAddress); + params->setOrigin(std::string(sender)); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(std::move(queryBytes)); + params->setType(ExecutionMessage::MESSAGE); + + std::promise executePromise; + executor->dmcExecuteTransaction( + std::move(params), [&executePromise](bcos::Error::UniquePtr&& error, + ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + result = executePromise.get_future().get(); + + BOOST_CHECK(result); + BOOST_CHECK_EQUAL(result->status(), 0); + BOOST_CHECK_EQUAL(result->message(), ""); + BOOST_CHECK_EQUAL(result->newEVMContractAddress(), ""); + BOOST_CHECK_LT(result->gasAvailable(), gas); + + uint32_t dept; + codec->decode(result->data(), dept); + BOOST_CHECK_EQUAL(dept, std::get<1>(expected[i])); + } + }); +} // namespace test + +BOOST_AUTO_TEST_CASE(callWasmConcurrentlyHelloWorld) +{ + auto executionResultFactory = std::make_shared(); + auto executor = bcos::executor::TransactionExecutorFactory::build( + ledger, txpool, nullptr, backend, executionResultFactory, hashImpl, true, false, false); + + auto codec = std::make_unique(hashImpl, true); + + bytes helloWorldBin(hello_world_wasm, hello_world_wasm + hello_world_wasm_len); + helloWorldBin = codec->encode(helloWorldBin); + auto helloWorldAbi = string( + R"([{"inputs":[{"internalType":"string","name":"name","type":"string"}],"type":"constructor"},{"conflictFields":[{"kind":0,"value":[],"read_only":false,"slot":0}],"constant":false,"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"set","selector":[1322485854,0],"outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"internalType":"string","type":"string"}],"type":"function"}])"); + + bytes input; + input.insert(input.end(), helloWorldBin.begin(), helloWorldBin.end()); + + bytes constructorParam = codec->encode(string("alice")); + constructorParam = codec->encode(constructorParam); + input.insert(input.end(), constructorParam.begin(), constructorParam.end()); + + string helloWorldAddress = "usr/alice/hello_world"; + + auto tx = + fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1", helloWorldAbi); + auto sender = boost::algorithm::hex_lower(std::string(tx->sender())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setContextID(99); + params->setSeq(1000); + params->setDepth(0); + params->setOrigin(std::string(sender)); + params->setFrom(std::string(sender)); + params->setTo(helloWorldAddress); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(1); + blockHeader->setParentInfo({{0, h256(0)}}); + + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + + // -------------------------------- + // Create contract hello world + // -------------------------------- + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + + result->setSeq(1001); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(result), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + + auto result2 = executePromise2.get_future().get(); + result2->setSeq(1000); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + + auto result3 = executePromise3.get_future().get(); + + BOOST_CHECK_EQUAL(result3->status(), 0); + BOOST_CHECK_EQUAL(result3->origin(), sender); + BOOST_CHECK_EQUAL(result3->from(), paramsBak.to()); + BOOST_CHECK_EQUAL(result3->to(), sender); + + BOOST_CHECK(result3->message().empty()); + BOOST_CHECK(!result3->newEVMContractAddress().empty()); + BOOST_CHECK_LT(result3->gasAvailable(), gas); + + auto address = result3->newEVMContractAddress(); + + bcos::protocol::TwoPCParams commitParams; + commitParams.number = 1; + + std::promise preparePromise; + executor->prepare(commitParams, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + preparePromise.set_value(); + }); + preparePromise.get_future().get(); + + std::promise commitPromise; + executor->commit(commitParams, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + commitPromise.set_value(); + }); + commitPromise.get_future().get(); + auto tableName = std::string("/apps/") + string(address); + + EXECUTOR_LOG(TRACE) << "Checking table: " << tableName; + std::promise
tablePromise; + backend->asyncOpenTable(tableName, [&](Error::UniquePtr&& error, std::optional
&& table) { + BOOST_CHECK(!error); + BOOST_CHECK(table); + tablePromise.set_value(std::move(*table)); + }); + auto table = tablePromise.get_future().get(); + + auto entry = table.getRow("code"); + BOOST_CHECK(entry); + BOOST_CHECK_GT(entry->getField(0).size(), 0); + + entry = table.getRow("abi"); + BOOST_CHECK(entry); + BOOST_CHECK_GT(entry->getField(0).size(), 0); + + std::vector requests; + auto cases = vector(); + + cases.push_back("alice"); + cases.push_back("charlie"); + cases.push_back("bob"); + cases.push_back("david"); + + for (size_t i = 0; i < cases.size(); ++i) + { + std::string name = cases[i]; + bytes input; + + auto encodedParams = codec->encodeWithSig("set(string)", name); + input.insert(input.end(), encodedParams.begin(), encodedParams.end()); + + auto tx = fakeTransaction(cryptoSuite, keyPair, address, input, 101 + i, 100001, "1", "1"); + auto sender = boost::algorithm::hex_lower(std::string(tx->sender())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setType(bcos::protocol::ExecutionMessage::TXHASH); + params->setContextID(i); + params->setSeq(6000); + params->setDepth(0); + params->setFrom(std::string(sender)); + params->setTo(std::string(address)); + params->setOrigin(std::string(sender)); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setTransactionHash(hash); + params->setCreate(false); + + requests.emplace_back(std::move(params)); + } + + executor->dagExecuteTransactions( + requests, [&](bcos::Error::UniquePtr error, + std::vector results) { + BOOST_CHECK(!error); + + for (size_t i = 0; i < results.size(); ++i) + { + auto& result = results[i]; + BOOST_CHECK_EQUAL(result->status(), 0); + BOOST_CHECK(result->message().empty()); + BOOST_CHECK(result->type() == ExecutionMessage::SEND_BACK); + } + + bytes getBytes; + + auto encodedParams = codec->encodeWithSig("get()"); + getBytes.insert(getBytes.end(), encodedParams.begin(), encodedParams.end()); + + auto params = std::make_unique(); + params->setContextID(888); + params->setSeq(999); + params->setDepth(0); + params->setFrom(std::string(sender)); + params->setTo(helloWorldAddress); + params->setOrigin(std::string(sender)); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(std::move(getBytes)); + params->setType(ExecutionMessage::MESSAGE); + + std::promise executePromise; + executor->dmcExecuteTransaction( + std::move(params), [&executePromise](bcos::Error::UniquePtr&& error, + ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + result = executePromise.get_future().get(); + + BOOST_CHECK(result); + BOOST_CHECK_EQUAL(result->status(), 0); + BOOST_CHECK_EQUAL(result->message(), ""); + BOOST_CHECK_EQUAL(result->newEVMContractAddress(), ""); + BOOST_CHECK_LT(result->gasAvailable(), gas); + + string name; + codec->decode(result->data(), name); + BOOST_CHECK_EQUAL(name, "alice"); + }); +} + +BOOST_AUTO_TEST_CASE(callEvmConcurrentlyTransfer) +{ + size_t count = 100; + auto executionResultFactory = std::make_shared(); + auto executor = bcos::executor::TransactionExecutorFactory::build( + ledger, txpool, nullptr, backend, executionResultFactory, hashImpl, false, false, false); + + auto codec = std::make_unique(hashImpl, false); + + std::string bin = + "608060405234801561001057600080fd5b506105db806100206000396000f30060806040526004361061006257" + "6000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806335ee" + "5f87146100675780638a42ebe9146100e45780639b80b05014610157578063fad42f8714610210575b600080fd" + "5b34801561007357600080fd5b506100ce60048036038101908080359060200190820180359060200190808060" + "1f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050" + "5091929192905050506102c9565b6040518082815260200191505060405180910390f35b3480156100f0576000" + "80fd5b50610155600480360381019080803590602001908201803590602001908080601f016020809104026020" + "016040519081016040528093929190818152602001838380828437820191505050505050919291929080359060" + "20019092919050505061033d565b005b34801561016357600080fd5b5061020e60048036038101908080359060" + "2001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020" + "018383808284378201915050505050509192919290803590602001908201803590602001908080601f01602080" + "910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192" + "90803590602001909291905050506103b1565b005b34801561021c57600080fd5b506102c76004803603810190" + "80803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190" + "818152602001838380828437820191505050505050919291929080359060200190820180359060200190808060" + "1f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050" + "509192919290803590602001909291905050506104a8565b005b60008082604051808280519060200190808383" + "5b60208310151561030257805182526020820191506020810190506020830392506102dd565b60018360200361" + "01000a038019825116818451168082178552505050505050905001915050908152602001604051809103902054" + "9050919050565b806000836040518082805190602001908083835b602083101515610376578051825260208201" + "9150602081019050602083039250610351565b6001836020036101000a03801982511681845116808217855250" + "50505050509050019150509081526020016040518091039020819055505050565b806000846040518082805190" + "602001908083835b6020831015156103ea57805182526020820191506020810190506020830392506103c5565b" + "6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040" + "51809103902060008282540392505081905550806000836040518082805190602001908083835b602083101515" + "610463578051825260208201915060208101905060208303925061043e565b6001836020036101000a03801982" + "511681845116808217855250505050505090500191505090815260200160405180910390206000828254019250" + "5081905550505050565b806000846040518082805190602001908083835b6020831015156104e1578051825260" + "20820191506020810190506020830392506104bc565b6001836020036101000a03801982511681845116808217" + "855250505050505090500191505090815260200160405180910390206000828254039250508190555080600083" + "6040518082805190602001908083835b60208310151561055a5780518252602082019150602081019050602083" + "039250610535565b6001836020036101000a038019825116818451168082178552505050505050905001915050" + "908152602001604051809103902060008282540192505081905550606481111515156105aa57600080fd5b5050" + "505600a165627a7a723058205669c1a68cebcef35822edcec77a15792da5c32a8aa127803290253b3d5f627200" + "29"; + + std::string abi = + R"([{"conflictFields":[{"kind":3,"slot":0,"value":[0]}],"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"selector":[904814471,0],"stateMutability":"view","type":"function"},{"conflictFields":[{"kind":3,"slot":0,"value":[0]}],"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"num","type":"uint256"}],"name":"set","outputs":[],"selector":[2319641577,0],"stateMutability":"nonpayable","type":"function"},{"conflictFields":[{"kind":3,"slot":0,"value":[0]},{"kind":3,"slot":0,"value":[1]}],"inputs":[{"internalType":"string","name":"from","type":"string"},{"internalType":"string","name":"to","type":"string"},{"internalType":"uint256","name":"num","type":"uint256"}],"name":"transfer","outputs":[],"selector":[2608902224,0],"stateMutability":"nonpayable","type":"function"},{"conflictFields":[{"kind":3,"slot":0,"value":[0]},{"kind":3,"slot":0,"value":[1]}],"inputs":[{"internalType":"string","name":"from","type":"string"},{"internalType":"string","name":"to","type":"string"},{"internalType":"uint256","name":"num","type":"uint256"}],"name":"transferWithRevert","outputs":[],"selector":[4208209799,0],"stateMutability":"nonpayable","type":"function"}])"; + + bytes input; + boost::algorithm::unhex(bin, std::back_inserter(input)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1", abi); + auto sender = boost::algorithm::hex_lower(std::string(tx->sender())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setContextID(99); + params->setSeq(1000); + params->setDepth(0); + + params->setOrigin(std::string(sender)); + params->setFrom(std::string(sender)); + + // The contract address + h256 addressCreate("ff6f30856ad3bae00b1169808488502786a13e3c174d85682135ffd51310310e"); + std::string addressString = addressCreate.hex().substr(0, 40); + // toChecksumAddress(addressString, hashImpl); + params->setTo(std::move(addressString)); + + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(1); + blockHeader->setParentInfo({{0, h256(0)}}); + + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + + // -------------------------------- + // Create contract ParallelOk + // -------------------------------- + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + + auto address = result->newEVMContractAddress(); + + // Set user + for (size_t i = 0; i < count; ++i) + { + params = std::make_unique(); + params->setContextID(i); + params->setSeq(5000); + params->setDepth(0); + params->setFrom(std::string(sender)); + params->setTo(std::string(address)); + params->setOrigin(std::string(sender)); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setCreate(false); + + std::string user = "user" + boost::lexical_cast(i); + bcos::u256 value(1000000); + params->setData(codec->encodeWithSig("set(string,uint256)", user, value)); + params->setType(NativeExecutionMessage::MESSAGE); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params), + [&](bcos::Error::UniquePtr&& error, NativeExecutionMessage::UniquePtr&& result) { + if (error) + { + std::cout << "Error!" << boost::diagnostic_information(*error); + } + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + // BOOST_CHECK_EQUAL(result->status(), 0); + } + + std::vector requests; + requests.reserve(count); + // Transfer + for (size_t i = 0; i < count; ++i) + { + std::string from = "user" + boost::lexical_cast(i); + std::string to = "user" + boost::lexical_cast(count - 1); + bcos::u256 value(10); + + auto input = codec->encodeWithSig("transfer(string,string,uint256)", from, to, value); + auto tx = fakeTransaction(cryptoSuite, keyPair, address, input, 101 + i, 100001, "1", "1"); + auto sender = boost::algorithm::hex_lower(std::string(tx->sender())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + params = std::make_unique(); + params->setContextID(i); + params->setSeq(6000); + params->setDepth(0); + params->setFrom(std::string(sender)); + params->setTo(std::string(address)); + params->setOrigin(std::string(sender)); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setCreate(false); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + + requests.emplace_back(std::move(params)); + } + + std::promise> tablePromise; + backend->asyncCreateTable("cp_ff6f30856ad3bae00b1169808488502786a13e3c", + "functionName,criticalSize", [&](Error::UniquePtr&& error, std::optional
&& table) { + BOOST_CHECK(!error); + BOOST_CHECK(table); + tablePromise.set_value(std::move(*table)); + }); + auto table = tablePromise.get_future().get(); + + Entry entry = table->newEntry(); + auto selector = getFuncSelector("transfer(string,string,uint256)", hashImpl); + table->setRow(to_string(selector), entry); + + executor->dagExecuteTransactions( + requests, [&](bcos::Error::UniquePtr error, + std::vector results) { + BOOST_CHECK(!error); + + for (size_t i = 0; i < results.size(); ++i) + { + auto& result = results[i]; + BOOST_CHECK_EQUAL(result->status(), 0); + BOOST_CHECK(result->message().empty()); + } + + // Check result + for (size_t i = 0; i < count; ++i) + { + params = std::make_unique(); + params->setContextID(i); + params->setSeq(7000); + params->setDepth(0); + params->setFrom(std::string(sender)); + params->setTo(std::string(address)); + params->setOrigin(std::string(sender)); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setCreate(false); + + std::string account = "user" + boost::lexical_cast(i); + params->setData(codec->encodeWithSig("balanceOf(string)", account)); + params->setType(NativeExecutionMessage::MESSAGE); + + std::promise> outputPromise; + executor->dmcExecuteTransaction( + std::move(params), [&outputPromise](bcos::Error::UniquePtr&& error, + NativeExecutionMessage::UniquePtr&& result) { + if (error) + { + std::cout << "Error!" << boost::diagnostic_information(*error); + } + // BOOST_CHECK(!error); + outputPromise.set_value(std::move(result)); + }); + ExecutionMessage::UniquePtr balanceResult = + std::move(*outputPromise.get_future().get()); + + bcos::u256 value(0); + codec->decode(balanceResult->data(), value); + + if (i < count - 1) + { + BOOST_CHECK_EQUAL(value, u256(1000000 - 10)); + } + else + { + BOOST_CHECK_EQUAL(value, u256(1000000 + 10 * (count - 1))); + } + } + }); +} + +BOOST_AUTO_TEST_CASE(callEvmConcurrentlyTransferByMessage) +{ + size_t count = 100; + auto executionResultFactory = std::make_shared(); + + auto executor = bcos::executor::TransactionExecutorFactory::build( + ledger, txpool, nullptr, backend, executionResultFactory, hashImpl, false, false, false); + + auto codec = std::make_unique(hashImpl, false); + + std::string bin = + "608060405234801561001057600080fd5b50610519806100206000396000f30060806040526004361061006157" + "63ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166335ee5f87" + "81146100665780638a42ebe9146100d15780639b80b0501461012e578063fad42f87146101c7575b600080fd5b" + "34801561007257600080fd5b506040805160206004803580820135601f81018490048402850184019095528484" + "526100bf9436949293602493928401919081908401838280828437509497506102609650505050505050565b60" + "408051918252519081900360200190f35b3480156100dd57600080fd5b50604080516020600480358082013560" + "1f810184900484028501840190955284845261012c943694929360249392840191908190840183828082843750" + "94975050933594506102c79350505050565b005b34801561013a57600080fd5b50604080516020600480358082" + "0135601f810184900484028501840190955284845261012c943694929360249392840191908190840183828082" + "84375050604080516020601f89358b018035918201839004830284018301909452808352979a99988101979196" + "5091820194509250829150840183828082843750949750509335945061032f9350505050565b3480156101d357" + "600080fd5b506040805160206004803580820135601f810184900484028501840190955284845261012c943694" + "92936024939284019190819084018382808284375050604080516020601f89358b018035918201839004830284" + "018301909452808352979a99988101979196509182019450925082915084018382808284375094975050933594" + "506104079350505050565b600080826040518082805190602001908083835b602083106102935780518252601f" + "199092019160209182019101610274565b51815160209384036101000a60001901801990921691161790529201" + "94855250604051938490030190922054949350505050565b806000836040518082805190602001908083835b60" + "2083106102fa5780518252601f1990920191602091820191016102db565b51815160209384036101000a600019" + "018019909216911617905292019485525060405193849003019092209290925550505050565b80600084604051" + "8082805190602001908083835b602083106103625780518252601f199092019160209182019101610343565b51" + "815160209384036101000a60001901801990921691161790529201948552506040519384900381018420805495" + "909503909455505083518392600092869290918291908401908083835b602083106103cc5780518252601f1990" + "920191602091820191016103ad565b51815160209384036101000a600019018019909216911617905292019485" + "525060405193849003019092208054939093019092555050505050565b80600084604051808280519060200190" + "8083835b6020831061043a5780518252601f19909201916020918201910161041b565b51815160209384036101" + "000a60001901801990921691161790529201948552506040519384900381018420805495909503909455505083" + "518392600092869290918291908401908083835b602083106104a45780518252601f1990920191602091820191" + "01610485565b51815160209384036101000a600019018019909216911617905292019485525060405193849003" + "01909220805493909301909255505060648111156104e857600080fd5b5050505600a165627a7a72305820ac1d" + "5088b99f786303e96862fc6a312862e1edd4c8e070e1f4fd52e469ab5d240029"; + std::string abi = + R"([{"conflictFields":[{"kind":3,"slot":0,"value":[0]}],"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"selector":[904814471,0],"stateMutability":"view","type":"function"},{"conflictFields":[{"kind":3,"slot":0,"value":[0]}],"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"num","type":"uint256"}],"name":"set","outputs":[],"selector":[2319641577,0],"stateMutability":"nonpayable","type":"function"},{"conflictFields":[{"kind":3,"slot":0,"value":[0]},{"kind":3,"slot":0,"value":[1]}],"inputs":[{"internalType":"string","name":"from","type":"string"},{"internalType":"string","name":"to","type":"string"},{"internalType":"uint256","name":"num","type":"uint256"}],"name":"transfer","outputs":[],"selector":[2608902224,0],"stateMutability":"nonpayable","type":"function"},{"conflictFields":[{"kind":3,"slot":0,"value":[0]},{"kind":3,"slot":0,"value":[1]}],"inputs":[{"internalType":"string","name":"from","type":"string"},{"internalType":"string","name":"to","type":"string"},{"internalType":"uint256","name":"num","type":"uint256"}],"name":"transferWithRevert","outputs":[],"selector":[4208209799,0],"stateMutability":"nonpayable","type":"function"}])"; + + bytes input; + boost::algorithm::unhex(bin, std::back_inserter(input)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1", abi); + auto sender = boost::algorithm::hex_lower(std::string(tx->sender())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setContextID(99); + params->setSeq(1000); + params->setDepth(0); + + params->setOrigin(std::string(sender)); + params->setFrom(std::string(sender)); + + // The contract address + h256 addressCreate("ff6f30856ad3bae00b1169808488502786a13e3c174d85682135ffd51310310e"); + std::string addressString = addressCreate.hex().substr(0, 40); + // toChecksumAddress(addressString, hashImpl); + params->setTo(std::move(addressString)); + + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(1); + blockHeader->setParentInfo({{0, h256(0)}}); + + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + + // -------------------------------- + // Create contract ParallelOk + // -------------------------------- + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + + auto address = result->newEVMContractAddress(); + + // Set user + for (size_t i = 0; i < count; ++i) + { + params = std::make_unique(); + params->setContextID(i); + params->setSeq(5000); + params->setDepth(0); + params->setFrom(std::string(sender)); + params->setTo(std::string(address)); + params->setOrigin(std::string(sender)); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setCreate(false); + + std::string user = "user" + boost::lexical_cast(i); + bcos::u256 value(1000000); + params->setData(codec->encodeWithSig("set(string,uint256)", user, value)); + params->setType(NativeExecutionMessage::MESSAGE); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params), + [&](bcos::Error::UniquePtr&& error, NativeExecutionMessage::UniquePtr&& result) { + if (error) + { + std::cout << "Error!" << boost::diagnostic_information(*error); + } + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + // BOOST_CHECK_EQUAL(result->status(), 0); + } + + std::vector requests; + requests.reserve(count); + // Transfer + for (size_t i = 0; i < count; ++i) + { + std::string from = "user" + boost::lexical_cast(i); + std::string to = "user" + boost::lexical_cast(count - 1); + bcos::u256 value(10); + + auto input = codec->encodeWithSig("transfer(string,string,uint256)", from, to, value); + auto sender = boost::algorithm::hex_lower(std::string(tx->sender())); + + params = std::make_unique(); + params->setContextID(i); + params->setSeq(6000); + params->setDepth(0); + params->setFrom(std::string(sender)); + params->setTo(std::string(address)); + params->setOrigin(std::string(sender)); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setCreate(false); + params->setType(NativeExecutionMessage::MESSAGE); + params->setData(std::move(input)); + params->setFrom(sender); + + requests.emplace_back(std::move(params)); + } + + std::promise> tablePromise; + backend->asyncCreateTable("cp_ff6f30856ad3bae00b1169808488502786a13e3c", + "functionName,criticalSize", [&](Error::UniquePtr&& error, std::optional
&& table) { + BOOST_CHECK(!error); + BOOST_CHECK(table); + tablePromise.set_value(std::move(*table)); + }); + auto table = tablePromise.get_future().get(); + + Entry entry = table->newEntry(); + auto selector = getFuncSelector("transfer(string,string,uint256)", hashImpl); + table->setRow(to_string(selector), entry); + + executor->dagExecuteTransactions( + requests, [&](bcos::Error::UniquePtr error, + std::vector results) { + BOOST_CHECK(!error); + + for (size_t i = 0; i < results.size(); ++i) + { + auto& result = results[i]; + BOOST_CHECK_EQUAL(result->status(), 0); + BOOST_CHECK(result->message().empty()); + } + + // Check result + for (size_t i = 0; i < count; ++i) + { + params = std::make_unique(); + params->setContextID(i); + params->setSeq(7000); + params->setDepth(0); + params->setFrom(std::string(sender)); + params->setTo(std::string(address)); + params->setOrigin(std::string(sender)); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setCreate(false); + + std::string account = "user" + boost::lexical_cast(i); + params->setData(codec->encodeWithSig("balanceOf(string)", account)); + params->setType(NativeExecutionMessage::MESSAGE); + + std::promise> outputPromise; + executor->dmcExecuteTransaction( + std::move(params), [&outputPromise](bcos::Error::UniquePtr&& error, + NativeExecutionMessage::UniquePtr&& result) { + if (error) + { + std::cout << "Error!" << boost::diagnostic_information(*error); + } + // BOOST_CHECK(!error); + outputPromise.set_value(std::move(result)); + }); + ExecutionMessage::UniquePtr balanceResult = + std::move(*outputPromise.get_future().get()); + + bcos::u256 value(0); + codec->decode(balanceResult->data(), value); + + if (i < count - 1) + { + BOOST_CHECK_EQUAL(value, u256(1000000 - 10)); + } + else + { + BOOST_CHECK_EQUAL(value, u256(1000000 + 10 * (count - 1))); + } + } + }); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos +// #endif diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestEVMExecutor.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestEVMExecutor.cpp" new file mode 100644 index 00000000..c1a148cf --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestEVMExecutor.cpp" @@ -0,0 +1,1771 @@ +/** + * Copyright (C) 2021 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. + */ +/** + * @brief : unitest for executor implement + * @author: ancelmo + * @date: 2021-09-14 + */ + +#include "../mock/MockLedger.h" +#include "../mock/MockTransactionalStorage.h" +#include "../mock/MockTxPool.h" +// #include "Common.h" +#include "bcos-codec/wrapper/CodecWrapper.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/protocol/Transaction.h" +#include "bcos-table/src/StateStorage.h" +#include "bcos-tars-protocol/testutil/FakeBlockHeader.h" +#include "evmc/evmc.h" +#include "executor/TransactionExecutorFactory.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::crypto; +using namespace bcos::protocol; + +namespace bcos +{ +namespace test +{ +struct TransactionExecutorFixture +{ + TransactionExecutorFixture() + { + boost::log::core::get()->set_logging_enabled(false); + hashImpl = std::make_shared(); + assert(hashImpl); + auto signatureImpl = std::make_shared(); + assert(signatureImpl); + cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + + txpool = std::make_shared(); + backend = std::make_shared(hashImpl); + ledger = std::make_shared(); + auto executionResultFactory = std::make_shared(); + + auto lruStorage = std::make_shared(backend); + + executor = bcos::executor::TransactionExecutorFactory::build(ledger, txpool, lruStorage, + backend, executionResultFactory, hashImpl, false, false, false); + + + keyPair = cryptoSuite->signatureImpl()->generateKeyPair(); + memcpy(keyPair->secretKey()->mutableData(), + fromHexString("ff6f30856ad3bae00b1169808488502786a13e3c174d85682135ffd51310310e") + ->data(), + 32); + memcpy(keyPair->publicKey()->mutableData(), + fromHexString( + "ccd8de502ac45462767e649b462b5f4ca7eadd69c7e1f1b410bdf754359be29b1b88ffd79744" + "03f56e250af52b25682014554f7b3297d6152401e85d426a06ae") + ->data(), + 64); + + codec = std::make_unique(hashImpl, false); + } + ~TransactionExecutorFixture() { boost::log::core::get()->set_logging_enabled(true); } + + TransactionExecutor::Ptr executor; + CryptoSuite::Ptr cryptoSuite; + std::shared_ptr txpool; + std::shared_ptr backend; + std::shared_ptr ledger; + std::shared_ptr hashImpl; + + KeyPairInterface::Ptr keyPair; + int64_t gas = 3000000; + std::unique_ptr codec; + + string helloBin = + "60806040526040805190810160405280600181526020017f3100000000000000000000000000000000000000" + "0000000000000000000000008152506001908051906020019061004f9291906100ae565b5034801561005c5760" + "0080fd5b506040805190810160405280600d81526020017f48656c6c6f2c20576f726c64210000000000000000" + "0000000000000000000000815250600090805190602001906100a89291906100ae565b50610153565b82805460" + "0181600116156101000203166002900490600052602060002090601f016020900481019282601f106100ef5780" + "5160ff191683800117855561011d565b8280016001018555821561011d579182015b8281111561011c57825182" + "5591602001919060010190610101565b5b50905061012a919061012e565b5090565b61015091905b8082111561" + "014c576000816000905550600101610134565b5090565b90565b6104ac806101626000396000f3006080604052" + "60043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ff" + "ffffff1680634ed3885e1461005c57806354fd4d50146100c55780636d4ce63c14610155575b600080fd5b3480" + "1561006857600080fd5b506100c3600480360381019080803590602001908201803590602001908080601f0160" + "208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192" + "9192905050506101e5565b005b3480156100d157600080fd5b506100da61029b565b6040518080602001828103" + "825283818151815260200191508051906020019080838360005b8381101561011a578082015181840152602081" + "0190506100ff565b50505050905090810190601f1680156101475780820380516001836020036101000a031916" + "815260200191505b509250505060405180910390f35b34801561016157600080fd5b5061016a610339565b6040" + "518080602001828103825283818151815260200191508051906020019080838360005b838110156101aa578082" + "01518184015260208101905061018f565b50505050905090810190601f1680156101d757808203805160018360" + "20036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101fb" + "9291906103db565b507f93a093529f9c8a0c300db4c55fcd27c068c4f5e0e8410bc288c7e76f3d71083e816040" + "518080602001828103825283818151815260200191508051906020019080838360005b8381101561025e578082" + "015181840152602081019050610243565b50505050905090810190601f16801561028b57808203805160018360" + "20036101000a031916815260200191505b509250505060405180910390a150565b600180546001816001161561" + "01000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460" + "0181600116156101000203166002900480156103315780601f1061030657610100808354040283529160200191" + "610331565b820191906000526020600020905b81548152906001019060200180831161031457829003601f1682" + "01915b505050505081565b606060008054600181600116156101000203166002900480601f0160208091040260" + "200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103" + "d15780601f106103a6576101008083540402835291602001916103d1565b820191906000526020600020905b81" + "54815290600101906020018083116103b457829003601f168201915b5050505050905090565b82805460018160" + "0116156101000203166002900490600052602060002090601f016020900481019282601f1061041c57805160ff" + "191683800117855561044a565b8280016001018555821561044a579182015b8281111561044957825182559160" + "200191906001019061042e565b5b509050610457919061045b565b5090565b61047d91905b8082111561047957" + "6000816000905550600101610461565b5090565b905600a165627a7a723058204736027ad6b97d7cd2685379ac" + "b35b386dcb18799934be8283f1e08cd1f0c6ec0029"; +}; +BOOST_FIXTURE_TEST_SUITE(TestTransactionExecutor, TransactionExecutorFixture) + +BOOST_AUTO_TEST_CASE(deployAndCall) +{ + auto helloworld = string(helloBin); + + bytes input; + boost::algorithm::unhex(helloworld, std::back_inserter(input)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1"); + auto sender = *toHexString(string_view((char*)tx->sender().data(), tx->sender().size())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setType(bcos::protocol::ExecutionMessage::TXHASH); + params->setContextID(100); + params->setSeq(1000); + params->setDepth(0); + + // The contract address + h256 addressCreate("ff6f30856ad3bae00b1169808488502786a13e3c174d85682135ffd51310310e"); + std::string addressString = addressCreate.hex().substr(0, 40); + params->setTo(std::move(addressString)); + + params->setStaticCall(false); + params->setGasAvailable(gas); + // params->setData(input); + params->setType(ExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(1); + blockHeader->setParentInfo({{blockHeader->number() - 1, h256(blockHeader->number() - 1)}}); + ledger->setBlockNumber(blockHeader->number() - 1); + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + BOOST_CHECK_EQUAL(result->status(), 0); + BOOST_CHECK_EQUAL(result->evmStatus(), 0); + BOOST_CHECK_EQUAL(result->origin(), sender); + BOOST_CHECK_EQUAL(result->from(), paramsBak.to()); + BOOST_CHECK_EQUAL(result->to(), sender); + + BOOST_CHECK(result->message().empty()); + BOOST_CHECK(!result->newEVMContractAddress().empty()); + BOOST_CHECK_LT(result->gasAvailable(), gas); + + auto address = result->newEVMContractAddress(); + + bcos::protocol::TwoPCParams commitParams{}; + commitParams.number = 1; + + std::promise preparePromise; + executor->prepare(commitParams, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + preparePromise.set_value(); + }); + preparePromise.get_future().get(); + + ledger->setBlockHeader(blockHeader); + std::promise commitPromise; + executor->commit(commitParams, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + commitPromise.set_value(); + }); + commitPromise.get_future().get(); + auto tableName = + std::string("/apps/") + + std::string(result->newEVMContractAddress()); // TODO: ensure the contract// address is hex + + + // test getCode() + executor->getCode(address, [](Error::Ptr error, bcos::bytes code) { + BOOST_CHECK(!error); + BOOST_CHECK(!code.empty()); + BOOST_CHECK_GT(code.size(), 0); + }); + + EXECUTOR_LOG(TRACE) << "Checking table: " << tableName; + std::promise
tablePromise; + backend->asyncOpenTable(tableName, [&](Error::UniquePtr&& error, std::optional
&& table) { + BOOST_CHECK(!error); + BOOST_CHECK(table); + tablePromise.set_value(std::move(*table)); + }); + auto table = tablePromise.get_future().get(); + + auto entry = table.getRow("code"); + BOOST_CHECK(entry); + BOOST_CHECK_GT(entry->getField(0).size(), 0); + + // start new block + auto blockHeader2 = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader2->setNumber(2); + blockHeader2->setParentInfo({{blockHeader2->number() - 1, h256(blockHeader2->number() - 1)}}); + ledger->setBlockNumber(blockHeader2->number() - 1); + std::promise nextPromise2; + executor->nextBlockHeader(0, std::move(blockHeader2), [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + + nextPromise2.set_value(); + }); + + nextPromise2.get_future().get(); + + // set "fisco bcos" + bytes txInput; + char inputBytes[] = + "4ed3885e0000000000000000000000000000000000000000000000000000000000000020000000000000000000" + "0000000000000000000000000000000000000000000005666973636f0000000000000000000000000000000000" + "00000000000000000000"; + boost::algorithm::unhex( + &inputBytes[0], inputBytes + sizeof(inputBytes) - 1, std::back_inserter(txInput)); + auto params2 = std::make_unique(); + params2->setContextID(101); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(std::string(sender)); + params2->setTo(std::string(address)); + params2->setOrigin(std::string(sender)); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(txInput)); + params2->setType(NativeExecutionMessage::MESSAGE); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + BOOST_CHECK(result2); + BOOST_CHECK_EQUAL(result2->status(), 0); + BOOST_CHECK_EQUAL(result2->evmStatus(), 0); + BOOST_CHECK_EQUAL(result2->message(), ""); + BOOST_CHECK_EQUAL(result2->newEVMContractAddress(), ""); + BOOST_CHECK_LT(result2->gasAvailable(), gas); + ledger->setBlockHeader(blockHeader2); + + // read "fisco bcos" + bytes queryBytes; + char inputBytes2[] = "6d4ce63c"; + boost::algorithm::unhex( + &inputBytes2[0], inputBytes2 + sizeof(inputBytes2) - 1, std::back_inserter(queryBytes)); + + auto params3 = std::make_unique(); + params3->setContextID(102); + params3->setSeq(1000); + params3->setDepth(0); + params3->setFrom(std::string(sender)); + params3->setTo(std::string(address)); + params3->setOrigin(std::string(sender)); + params3->setStaticCall(false); + params3->setGasAvailable(gas); + params3->setData(std::move(queryBytes)); + params3->setType(ExecutionMessage::MESSAGE); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(params3), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + BOOST_CHECK(result3); + BOOST_CHECK_EQUAL(result3->status(), 0); + BOOST_CHECK_EQUAL(result3->evmStatus(), 0); + BOOST_CHECK_EQUAL(result3->message(), ""); + BOOST_CHECK_EQUAL(result3->newEVMContractAddress(), ""); + BOOST_CHECK_LT(result3->gasAvailable(), gas); + + std::string output; + boost::algorithm::hex_lower( + result3->data().begin(), result3->data().end(), std::back_inserter(output)); + BOOST_CHECK_EQUAL(output, + "00000000000000000000000000000000000000000000000000000000000000200000000000000000000" + "000000000000000000000000000000000000000000005666973636f0000000000000000000000000000" + "00000000000000000000000000"); +} + + +BOOST_AUTO_TEST_CASE(externalCall) +{ + // Solidity source code from test_external_call.sol, using remix + // 0.6.10+commit.00c0fcaf + + std::string ABin = + "608060405234801561001057600080fd5b5061037f806100206000396000f3fe60806040523480156100105760" + "0080fd5b506004361061002b5760003560e01c80635b975a7314610030575b600080fd5b61005c600480360360" + "2081101561004657600080fd5b8101908080359060200190929190505050610072565b60405180828152602001" + "91505060405180910390f35b600081604051610081906101c7565b808281526020019150506040518091039060" + "00f0801580156100a7573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffff" + "ffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507fd8e189e965" + "f1ff506594c5c65110ea4132cee975b58710da78ea19bc094414ae826040518082815260200191505060405180" + "910390a16000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffff" + "ffffffffffffffffffffffffffff16633fa4f2456040518163ffffffff1660e01b815260040160206040518083" + "038186803b15801561018557600080fd5b505afa158015610199573d6000803e3d6000fd5b505050506040513d" + "60208110156101af57600080fd5b81019080805190602001909291905050509050919050565b610175806101d5" + "8339019056fe608060405234801561001057600080fd5b50604051610175380380610175833981810160405260" + "2081101561003357600080fd5b8101908080519060200190929190505050806000819055507fdc509bfccbee28" + "6f248e0904323788ad0c0e04e04de65c04b482b056acb1a0658160405180828152602001915050604051809103" + "90a15060e4806100916000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e0" + "1c80633fa4f245146037578063a16fe09b146053575b600080fd5b603d605b565b604051808281526020019150" + "5060405180910390f35b60596064565b005b60008054905090565b6000808154600101919050819055507f052f" + "6b9dfac9e4e1257cb5b806b7673421c54730f663c8ab02561743bb23622d600054604051808281526020019150" + "5060405180910390a156fea264697066735822122006eea3bbe24f3d859a9cb90efc318f26898aeb4dffb31cac" + "e105776a6c272f8464736f6c634300060a0033a2646970667358221220b441da8ba792a40e444d0ed767a4417e" + "944c55578d1c8d0ca4ad4ec050e05a9364736f6c634300060a0033"; + + std::string BBin = + "608060405234801561001057600080fd5b50604051610175380380610175833981810160405260208110156100" + "3357600080fd5b8101908080519060200190929190505050806000819055507fdc509bfccbee286f248e090432" + "3788ad0c0e04e04de65c04b482b056acb1a065816040518082815260200191505060405180910390a15060e480" + "6100916000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80633fa4f2" + "45146037578063a16fe09b146053575b600080fd5b603d605b565b604051808281526020019150506040518091" + "0390f35b60596064565b005b60008054905090565b6000808154600101919050819055507f052f6b9dfac9e4e1" + "257cb5b806b7673421c54730f663c8ab02561743bb23622d600054604051808281526020019150506040518091" + "0390a156fea264697066735822122006eea3bbe24f3d859a9cb90efc318f26898aeb4dffb31cace105776a6c27" + "2f8464736f6c634300060a0033"; + + bytes input; + boost::algorithm::unhex(ABin, std::back_inserter(input)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1"); + auto sender = boost::algorithm::hex_lower(std::string(tx->sender())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setContextID(100); + params->setSeq(1000); + params->setDepth(0); + + params->setOrigin(std::string(sender)); + params->setFrom(std::string(sender)); + + // The contract address + h256 addressCreate("ff6f30856ad3bae00b1169808488502786a13e3c174d85682135ffd51310310e"); + std::string addressString = addressCreate.hex().substr(0, 40); + params->setTo(std::move(addressString)); + + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(1); + blockHeader->setParentInfo({{blockHeader->number() - 1, h256(blockHeader->number() - 1)}}); + ledger->setBlockNumber(blockHeader->number() - 1); + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + + // -------------------------------- + // Create contract A + // -------------------------------- + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + + auto address = result->newEVMContractAddress(); + BOOST_CHECK_EQUAL(result->type(), NativeExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(result->status(), 0); + BOOST_CHECK_EQUAL(result->evmStatus(), 0); + BOOST_CHECK_GT(address.size(), 0); + BOOST_CHECK(result->keyLocks().empty()); + + // -------------------------------- + // Call A createAndCallB(int256) + // -------------------------------- + auto params2 = std::make_unique(); + params2->setContextID(101); + params2->setSeq(1001); + params2->setDepth(0); + params2->setFrom(std::string(sender)); + params2->setTo(std::string(address)); + params2->setOrigin(std::string(sender)); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setCreate(false); + + bcos::u256 value(1000); + params2->setData(codec->encodeWithSig("createAndCallB(int256)", value)); + params2->setType(NativeExecutionMessage::MESSAGE); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, NativeExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + BOOST_CHECK(result2); + BOOST_CHECK_EQUAL(result2->type(), ExecutionMessage::MESSAGE); + BOOST_CHECK(result2->data().size() > 0); + BOOST_CHECK_EQUAL(result2->contextID(), 101); + BOOST_CHECK_EQUAL(result2->seq(), 1001); + BOOST_CHECK_EQUAL(result2->create(), true); + BOOST_CHECK_EQUAL(result2->newEVMContractAddress(), ""); + BOOST_CHECK_EQUAL(result2->origin(), std::string(sender)); + BOOST_CHECK_EQUAL(result2->from(), std::string(address)); + BOOST_CHECK(result2->to().empty()); + BOOST_CHECK_LT(result2->gasAvailable(), gas); + BOOST_CHECK_EQUAL(result2->keyLocks().size(), 1); + BOOST_CHECK_EQUAL(result2->keyLocks()[0], "code"); + + // -------------------------------- + // Message 1: Create contract B, set new seq 1002 + // A -> B + // -------------------------------- + result2->setSeq(1002); + + // Clear the key lock to avoid effect + result2->setKeyLocks({}); + + h256 addressCreate2( + "ee6f30856ad3bae00b1169808488502786a13e3c174d85682135ffd51310310e"); // ee6f30856ad3bae00b1169808488502786a13e3c + std::string addressString2 = addressCreate2.hex().substr(0, 40); + result2->setTo(addressString2); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + BOOST_CHECK(result3); + BOOST_CHECK_EQUAL(result3->type(), ExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(result3->data().size(), 0); + BOOST_CHECK_EQUAL(result3->contextID(), 101); + BOOST_CHECK_EQUAL(result3->seq(), 1002); + BOOST_CHECK_EQUAL(result3->origin(), std::string(sender)); + BOOST_CHECK_EQUAL(result3->from(), addressString2); + BOOST_CHECK_EQUAL(result3->to(), std::string(address)); + BOOST_CHECK_EQUAL(result3->newEVMContractAddress(), addressString2); + BOOST_CHECK_EQUAL(result3->create(), false); + BOOST_CHECK_EQUAL(result3->status(), 0); + BOOST_CHECK_EQUAL(result3->evmStatus(), 0); + BOOST_CHECK(result3->logEntries().size() == 0); + BOOST_CHECK(result3->keyLocks().empty()); + + // -------------------------------- + // Message 2: Create contract B success return, set previous seq 1001 + // B -> A + // -------------------------------- + result3->setSeq(1001); + std::promise executePromise4; + executor->dmcExecuteTransaction(std::move(result3), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise4.set_value(std::move(result)); + }); + auto result4 = executePromise4.get_future().get(); + + BOOST_CHECK(result4); + BOOST_CHECK_EQUAL(result4->type(), ExecutionMessage::MESSAGE); + BOOST_CHECK_GT(result4->data().size(), 0); + auto param = codec->encodeWithSig("value()"); + BOOST_CHECK(result4->data().toBytes() == param); + BOOST_CHECK_EQUAL(result4->contextID(), 101); + BOOST_CHECK_EQUAL(result4->seq(), 1001); + BOOST_CHECK_EQUAL(result4->from(), std::string(address)); + BOOST_CHECK_EQUAL(result4->to(), std::string(addressString2)); + BOOST_CHECK_EQUAL(result4->keyLocks().size(), 1); + BOOST_CHECK_EQUAL(toHex(result4->keyLocks()[0]), h256(0).hex()); // first member + + // Request message without status + // BOOST_CHECK_EQUAL(result4->status(), 0); + BOOST_CHECK(result4->message().empty()); + BOOST_CHECK(result4->newEVMContractAddress().empty()); + BOOST_CHECK_GT(result4->keyLocks().size(), 0); + + // -------------------------------- + // Message 3: A call B's value(), set new seq 1003 + // A -> B + // -------------------------------- + result4->setSeq(1003); + // Clear the keylock + result4->setKeyLocks({}); + + std::promise executePromise5; + executor->dmcExecuteTransaction(std::move(result4), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise5.set_value(std::move(result)); + }); + auto result5 = executePromise5.get_future().get(); + + BOOST_CHECK(result5); + BOOST_CHECK_EQUAL(result5->type(), ExecutionMessage::FINISHED); + BOOST_CHECK_GT(result5->data().size(), 0); + param = codec->encode(s256(1000)); + BOOST_CHECK(result5->data().toBytes() == param); + BOOST_CHECK_EQUAL(result5->contextID(), 101); + BOOST_CHECK_EQUAL(result5->seq(), 1003); + BOOST_CHECK_EQUAL(result5->from(), std::string(addressString2)); + BOOST_CHECK_EQUAL(result5->to(), std::string(address)); + BOOST_CHECK_EQUAL(result5->status(), 0); + BOOST_CHECK_EQUAL(result5->evmStatus(), 0); + BOOST_CHECK(result5->message().empty()); + BOOST_CHECK_EQUAL(result5->keyLocks().size(), 0); + + // -------------------------------- + // Message 4: A call B's success return, set previous seq 1001 + // B -> A + // -------------------------------- + result5->setSeq(1001); + std::promise executePromise6; + executor->dmcExecuteTransaction(std::move(result5), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise6.set_value(std::move(result)); + }); + auto result6 = executePromise6.get_future().get(); + BOOST_CHECK(result6); + BOOST_CHECK_EQUAL(result6->type(), ExecutionMessage::FINISHED); + BOOST_CHECK_GT(result6->data().size(), 0); + BOOST_CHECK(result6->data().toBytes() == param); + BOOST_CHECK_EQUAL(result6->contextID(), 101); + BOOST_CHECK_EQUAL(result6->seq(), 1001); + BOOST_CHECK_EQUAL(result6->from(), std::string(address)); + BOOST_CHECK_EQUAL(result6->to(), std::string(sender)); + BOOST_CHECK_EQUAL(result6->origin(), std::string(sender)); + BOOST_CHECK_EQUAL(result6->status(), 0); + BOOST_CHECK_EQUAL(result6->evmStatus(), 0); + BOOST_CHECK(result6->message().empty()); + BOOST_CHECK(result6->logEntries().size() == 1); + BOOST_CHECK_EQUAL(result6->keyLocks().size(), 0); + + executor->getHash(1, [&](bcos::Error::UniquePtr&& error, crypto::HashType&& hash) { + BOOST_CHECK(!error); + BOOST_CHECK_NE(hash.hex(), h256().hex()); + }); + + // commit the state + TwoPCParams commitParams; + commitParams.number = 1; + + executor->prepare(commitParams, [](bcos::Error::Ptr error) { BOOST_CHECK(!error); }); + ledger->setBlockHeader(blockHeader); + executor->commit(commitParams, [](bcos::Error::Ptr error) { BOOST_CHECK(!error); }); + + + // execute a call request + auto callParam = std::make_unique(); + callParam->setType(executor::NativeExecutionMessage::MESSAGE); + callParam->setContextID(500); + callParam->setSeq(7778); + callParam->setDepth(0); + callParam->setFrom(std::string(sender)); + callParam->setTo(std::string(addressString2)); + callParam->setData(codec->encodeWithSig("value()")); + callParam->setOrigin(std::string(sender)); + callParam->setStaticCall(true); + callParam->setGasAvailable(gas); + callParam->setCreate(false); + + std::promise callResultPromise; + executor->dmcCall(std::move(callParam), + [&](bcos::Error::UniquePtr error, bcos::protocol::ExecutionMessage::UniquePtr response) { + BOOST_CHECK(!error); + callResultPromise.set_value(std::move(response)); + }); + + bcos::protocol::ExecutionMessage::UniquePtr callResult = callResultPromise.get_future().get(); + + + BOOST_CHECK_EQUAL(callResult->type(), protocol::ExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(callResult->status(), 0); + BOOST_CHECK_EQUAL(callResult->evmStatus(), 0); + + auto expectResult = codec->encode(s256(1000)); + BOOST_CHECK(callResult->data().toBytes() == expectResult); + + // commit the state, and call + // bcos::protocol::TwoPCParams commitParams; + // commitParams.number = 1; + // executor->prepare(commitParams, [&](bcos::Error::Ptr error) { BOOST_CHECK(!error); }); + // executor->commit(commitParams, [&](bcos::Error::Ptr error) { BOOST_CHECK(!error); }); + + auto callParam2 = std::make_unique(); + callParam2->setType(executor::NativeExecutionMessage::MESSAGE); + callParam2->setContextID(501); + callParam2->setSeq(7779); + callParam2->setDepth(0); + callParam2->setFrom(std::string(sender)); + callParam2->setTo(std::string(addressString2)); + callParam2->setData(codec->encodeWithSig("value()")); + callParam2->setOrigin(std::string(sender)); + callParam2->setStaticCall(true); + callParam2->setGasAvailable(gas); + callParam2->setCreate(false); + + std::promise callResult2Promise; + executor->dmcCall(std::move(callParam2), + [&](bcos::Error::UniquePtr error, bcos::protocol::ExecutionMessage::UniquePtr response) { + BOOST_CHECK(!error); + callResult2Promise.set_value(std::move(response)); + }); + + bcos::protocol::ExecutionMessage::UniquePtr callResult2 = callResult2Promise.get_future().get(); + + + BOOST_CHECK_EQUAL(callResult2->type(), protocol::ExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(callResult2->status(), 0); + BOOST_CHECK_EQUAL(callResult2->evmStatus(), 0); + + auto expectResult2 = codec->encode(s256(1000)); + BOOST_CHECK(callResult2->data().toBytes() == expectResult); +} + +BOOST_AUTO_TEST_CASE(performance) +{ + size_t count = 10 * 100; + + bcos::crypto::HashType hash; + for (size_t blockNumber = 1; blockNumber < 10; ++blockNumber) + { + std::string bin = + "608060405234801561001057600080fd5b506105db806100206000396000f3006080604052600436106100" + "6257" + "6000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063" + "35ee" + "5f87146100675780638a42ebe9146100e45780639b80b05014610157578063fad42f8714610210575b6000" + "80fd" + "5b34801561007357600080fd5b506100ce6004803603810190808035906020019082018035906020019080" + "8060" + "1f016020809104026020016040519081016040528093929190818152602001838380828437820191505050" + "5050" + "5091929192905050506102c9565b6040518082815260200191505060405180910390f35b3480156100f057" + "6000" + "80fd5b50610155600480360381019080803590602001908201803590602001908080601f01602080910402" + "6020" + "01604051908101604052809392919081815260200183838082843782019150505050505091929192908035" + "9060" + "20019092919050505061033d565b005b34801561016357600080fd5b5061020e6004803603810190808035" + "9060" + "2001908201803590602001908080601f016020809104026020016040519081016040528093929190818152" + "6020" + "018383808284378201915050505050509192919290803590602001908201803590602001908080601f0160" + "2080" + "91040260200160405190810160405280939291908181526020018383808284378201915050505050509192" + "9192" + "90803590602001909291905050506103b1565b005b34801561021c57600080fd5b506102c7600480360381" + "0190" + "80803590602001908201803590602001908080601f01602080910402602001604051908101604052809392" + "9190" + "81815260200183838082843782019150505050505091929192908035906020019082018035906020019080" + "8060" + "1f016020809104026020016040519081016040528093929190818152602001838380828437820191505050" + "5050" + "509192919290803590602001909291905050506104a8565b005b6000808260405180828051906020019080" + "8383" + "5b60208310151561030257805182526020820191506020810190506020830392506102dd565b6001836020" + "0361" + "01000a03801982511681845116808217855250505050505090500191505090815260200160405180910390" + "2054" + "9050919050565b806000836040518082805190602001908083835b60208310151561037657805182526020" + "8201" + "9150602081019050602083039250610351565b6001836020036101000a0380198251168184511680821785" + "5250" + "50505050509050019150509081526020016040518091039020819055505050565b80600084604051808280" + "5190" + "602001908083835b6020831015156103ea57805182526020820191506020810190506020830392506103c5" + "565b" + "6001836020036101000a038019825116818451168082178552505050505050905001915050908152602001" + "6040" + "51809103902060008282540392505081905550806000836040518082805190602001908083835b60208310" + "1515" + "610463578051825260208201915060208101905060208303925061043e565b6001836020036101000a0380" + "1982" + "51168184511680821785525050505050509050019150509081526020016040518091039020600082825401" + "9250" + "5081905550505050565b806000846040518082805190602001908083835b6020831015156104e157805182" + "5260" + "20820191506020810190506020830392506104bc565b6001836020036101000a0380198251168184511680" + "8217" + "85525050505050509050019150509081526020016040518091039020600082825403925050819055508060" + "0083" + "6040518082805190602001908083835b60208310151561055a578051825260208201915060208101905060" + "2083" + "039250610535565b6001836020036101000a03801982511681845116808217855250505050505090500191" + "5050" + "908152602001604051809103902060008282540192505081905550606481111515156105aa57600080fd5b" + "5050" + "505600a165627a7a723058205669c1a68cebcef35822edcec77a15792da5c32a8aa127803290253b3d5f62" + "7200" + "29"; + + bytes input; + boost::algorithm::unhex(bin, std::back_inserter(input)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1"); + auto sender = boost::algorithm::hex_lower(std::string(tx->sender())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setContextID(99); + params->setSeq(1000); + params->setDepth(0); + + params->setOrigin(std::string(sender)); + params->setFrom(std::string(sender)); + + // The contract address + std::string addressSeed = "address" + boost::lexical_cast(blockNumber); + h256 addressCreate(hashImpl->hash(addressSeed)); + // h256 addressCreate("ff6f30856ad3bae00b1169808488502786a13e3c174d85682135ffd51310310e"); + std::string addressString = addressCreate.hex().substr(0, 40); + // toChecksumAddress(addressString, hashImpl); + params->setTo(std::move(addressString)); + + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(blockNumber); + blockHeader->setParentInfo({{blockHeader->number() - 1, h256(blockHeader->number() - 1)}}); + ledger->setBlockNumber(blockHeader->number() - 1); + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + + // -------------------------------- + // Create contract ParallelOk + // -------------------------------- + std::promise executePromise; + executor->dmcExecuteTransaction( + std::move(params), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + + auto address = result->newEVMContractAddress(); + + // Set user + for (size_t i = 0; i < count; ++i) + { + params = std::make_unique(); + params->setContextID(i); + params->setSeq(5000); + params->setDepth(0); + params->setFrom(std::string(sender)); + params->setTo(std::string(address)); + params->setOrigin(std::string(sender)); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setCreate(false); + + std::string user = "user" + boost::lexical_cast(i); + bcos::u256 value(1000000); + params->setData(codec->encodeWithSig("set(string,uint256)", user, value)); + params->setType(NativeExecutionMessage::MESSAGE); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params), + [&](bcos::Error::UniquePtr&& error, NativeExecutionMessage::UniquePtr&& result) { + if (error) + { + std::cout << "Error!" << boost::diagnostic_information(*error); + } + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + // BOOST_CHECK_EQUAL(result->status(), 0); + } + + std::vector requests; + requests.reserve(count); + // Transfer + for (size_t i = 0; i < count; ++i) + { + params = std::make_unique(); + params->setContextID(i); + params->setSeq(6000); + params->setDepth(0); + params->setFrom(std::string(sender)); + params->setTo(std::string(address)); + params->setOrigin(std::string(sender)); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setCreate(false); + + std::string from = "user" + boost::lexical_cast(i); + std::string to = "user" + boost::lexical_cast(count - 1); + bcos::u256 value(10); + params->setData( + codec->encodeWithSig("transfer(string,string,uint256)", from, to, value)); + params->setType(NativeExecutionMessage::MESSAGE); + + requests.emplace_back(std::move(params)); + } + + auto now = std::chrono::system_clock::now(); + + for (auto& it : requests) + { + std::promise> outputPromise; + + executor->dmcExecuteTransaction( + std::move(it), [&outputPromise](bcos::Error::UniquePtr&& error, + NativeExecutionMessage::UniquePtr&& result) { + if (error) + { + std::cout << "Error!" << boost::diagnostic_information(*error); + } + // BOOST_CHECK(!error); + outputPromise.set_value(std::move(result)); + }); + ExecutionMessage::UniquePtr transResult = std::move(*outputPromise.get_future().get()); + if (transResult->status() != 0) + { + std::cout << "Error: " << transResult->status() << std::endl; + } + } + + std::cout << "Execute elapsed: " + << (std::chrono::system_clock::now() - now).count() / 1000 / 1000 << std::endl; + + now = std::chrono::system_clock::now(); + // Check the result + std::vector values = {}; + values.reserve(count); + for (size_t i = 0; i < count; ++i) + { + params = std::make_unique(); + params->setContextID(i); + params->setSeq(7000); + params->setDepth(0); + params->setFrom(std::string(sender)); + params->setTo(std::string(address)); + params->setOrigin(std::string(sender)); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setCreate(false); + + std::string account = "user" + boost::lexical_cast(i); + params->setData(codec->encodeWithSig("balanceOf(string)", account)); + params->setType(NativeExecutionMessage::MESSAGE); + + std::promise> outputPromise; + executor->dmcExecuteTransaction( + std::move(params), [&outputPromise](bcos::Error::UniquePtr&& error, + NativeExecutionMessage::UniquePtr&& result) { + if (error) + { + std::cout << "Error!" << boost::diagnostic_information(*error); + } + // BOOST_CHECK(!error); + outputPromise.set_value(std::move(result)); + }); + + + ExecutionMessage::UniquePtr balanceResult = + std::move(*outputPromise.get_future().get()); + + bcos::u256 value(0); + codec->decode(balanceResult->data(), value); + values.push_back(value); + // + // if (i < count - 1) + // { + // BOOST_CHECK_EQUAL(value, u256(1000000 - 10)); + // } + // else + // { + // BOOST_CHECK_EQUAL(value, u256(1000000 + 10 * (count - 1))); + // } + } + size_t c = std::count(values.begin(), values.end(), u256(1000000 - 10)); + BOOST_CHECK(c == count - 1); + BOOST_CHECK_EQUAL(values.at(values.size() - 1), u256(1000000 + 10 * (count - 1))); + + std::cout << "Check elapsed: " + << (std::chrono::system_clock::now() - now).count() / 1000 / 1000 << std::endl; + + executor->getHash( + blockNumber, [&hash](bcos::Error::UniquePtr error, crypto::HashType resultHash) { + BOOST_CHECK(!error); + BOOST_CHECK_NE(resultHash, h256()); + hash = resultHash; + }); + } +} + +BOOST_AUTO_TEST_CASE(multiDeploy) +{ + size_t count = 100; + std::vector paramsList; + + for (size_t i = 0; i < count; ++i) + { + auto helloworld = string(helloBin); + bytes input; + boost::algorithm::unhex(helloworld, std::back_inserter(input)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 100 + i, 100001, "1", "1"); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params = std::make_unique(); + params->setType(bcos::protocol::ExecutionMessage::TXHASH); + params->setContextID(100 + i); + params->setSeq(1000); + params->setDepth(0); + auto sender = *toHexString(string_view((char*)tx->sender().data(), tx->sender().size())); + + auto addressCreate = + cryptoSuite->hashImpl()->hash("i am a address" + boost::lexical_cast(i)); + std::string addressString = addressCreate.hex().substr(0, 40); + params->setTo(std::move(addressString)); + + params->setStaticCall(false); + params->setGasAvailable(gas); + // params->setData(input); + params->setType(ExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + paramsList.emplace_back(std::move(params)); + } + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(1); + blockHeader->setParentInfo({{blockHeader->number() - 1, h256(blockHeader->number() - 1)}}); + ledger->setBlockNumber(blockHeader->number() - 1); + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + + + std::vector> + responses(count); + for (size_t i = 0; i < paramsList.size(); ++i) + { + // not support multi-thread executeTransaction + boost::latch latch(1); + executor->dmcExecuteTransaction(std::move(std::move(paramsList[i])), + [&](bcos::Error::UniquePtr error, bcos::protocol::ExecutionMessage::UniquePtr result) { + responses[i] = std::make_tuple(std::move(error), std::move(result)); + latch.count_down(); + }); + latch.wait(); + } + + + for (auto& it : responses) + { + auto& [error, result] = it; + + BOOST_CHECK(!error); + if (error) + { + std::cout << boost::diagnostic_information(*error) << std::endl; + } + + BOOST_CHECK_EQUAL(result->status(), 0); + BOOST_CHECK_EQUAL(result->evmStatus(), 0); + BOOST_CHECK(result->message().empty()); + BOOST_CHECK(!result->newEVMContractAddress().empty()); + BOOST_CHECK_LT(result->gasAvailable(), gas); + } +} + +BOOST_AUTO_TEST_CASE(keyLock) {} + +BOOST_AUTO_TEST_CASE(deployErrorCode) +{ + // an infinity-loop constructor + { + std::string errorBin = + "608060405234801561001057600080fd5b505b60011561006a576040518060400160405280600381526020" + "017f" + "31323300000000000000000000000000000000000000000000000000000000008152506000908051906020" + "0190" + "61006492919061006f565b50610012565b610114565b828054600181600116156101000203166002900490" + "6000" + "52602060002090601f016020900481019282601f106100b057805160ff19168380011785556100de565b82" + "8001" + "600101855582156100de579182015b828111156100dd5782518255916020019190600101906100c2565b5b" + "5090" + "506100eb91906100ef565b5090565b61011191905b8082111561010d5760008160009055506001016100f5" + "565b" + "5090565b90565b6101f8806101236000396000f3fe608060405234801561001057600080fd5b5060043610" + "6100" + "365760003560e01c806344733ae11461003b5780638e397a0314610059575b600080fd5b61004361006356" + "5b60" + "40516100509190610140565b60405180910390f35b610061610105565b005b606060008054600181600116" + "1561" + "01000203166002900480601f01602080910402602001604051908101604052809291908181526020018280" + "5460" + "0181600116156101000203166002900480156100fb5780601f106100d05761010080835404028352916020" + "0191" + "6100fb565b820191906000526020600020905b8154815290600101906020018083116100de57829003601f" + "1682" + "01915b5050505050905090565b565b600061011282610162565b61011c818561016d565b935061012c8185" + "6020" + "860161017e565b610135816101b1565b840191505092915050565b60006020820190508181036000830152" + "6101" + "5a8184610107565b905092915050565b600081519050919050565b60008282526020820190509291505056" + "5b60" + "005b8381101561019c578082015181840152602081019050610181565b838111156101ab57600084840152" + "5b50" + "505050565b6000601f19601f830116905091905056fea2646970667358221220e4e19dff46d31f82111f92" + "61d8" + "687c52312c9221962991e27bbddc409dfbd7c564736f6c634300060a0033"; + bytes input; + boost::algorithm::unhex(errorBin, std::back_inserter(input)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1"); + auto sender = boost::algorithm::hex_lower(std::string(tx->sender())); + h256 addressCreate("ff6f30856ad3bae00b1169808488502786a13e3c174d85682135ffd51310310e"); + std::string addressString = addressCreate.hex().substr(0, 40); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setContextID(99); + params->setSeq(1000); + params->setDepth(0); + + params->setOrigin(sender); + params->setFrom(sender); + + // toChecksumAddress(addressString, hashImpl); + params->setTo(addressString); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(1); + blockHeader->setParentInfo({{blockHeader->number() - 1, h256(blockHeader->number() - 1)}}); + ledger->setBlockNumber(blockHeader->number() - 1); + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + // -------------------------------- + // Create contract + // -------------------------------- + + std::promise executePromise; + executor->dmcExecuteTransaction( + std::move(params), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + BOOST_CHECK(result); + BOOST_CHECK_EQUAL(result->type(), ExecutionMessage::REVERT); + BOOST_CHECK_EQUAL(result->status(), (int32_t)TransactionStatus::OutOfGas); + BOOST_CHECK_EQUAL(result->evmStatus(), (int32_t)evmc_status_code::EVMC_OUT_OF_GAS); + BOOST_CHECK_EQUAL(result->contextID(), 99); + BOOST_CHECK_EQUAL(result->seq(), 1000); + BOOST_CHECK_EQUAL(result->create(), false); + BOOST_CHECK_EQUAL(result->newEVMContractAddress(), ""); + BOOST_CHECK_EQUAL(result->origin(), sender); + BOOST_CHECK_EQUAL(result->from(), addressString); + BOOST_CHECK(result->to() == sender); + + TwoPCParams commitParams{}; + commitParams.number = 1; + + std::promise preparePromise; + executor->prepare(commitParams, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + preparePromise.set_value(); + }); + preparePromise.get_future().get(); + + std::promise commitPromise; + executor->commit(commitParams, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + commitPromise.set_value(); + }); + commitPromise.get_future().get(); + } + + // wasm code in evm + { + std::string errorBin = + "0061736d01000000018b011660037f7f7f017f60027f7f017f60047f7f7f7f017f6000017f60017f006002" + "7f7f0060047f7f7f7f0060077f7f7f7f7f7f7f0060037f7e7e0060037e7f7f0060057f7f7f7f7f017f6003" + "7f7f7f0060017f017f60057f7f7f7e7e0060000060037e7e7f0060057f7f7f7f7f0060027f7f017e60027f" + "7e0060057f7e7e7e7e0060027e7e017e60017e017f02b0010a0462636f730463616c6c00020462636f7311" + "67657452657475726e4461746153697a6500030462636f730d67657452657475726e446174610004046263" + "6f730666696e69736800050462636f730a73657453746f7261676500060462636f73036c6f670007046263" + "6f730672657665727400050462636f730a67657453746f7261676500000462636f730f67657443616c6c44" + "61746153697a6500030462636f730b67657443616c6c44617461000403a601a4010808090a040404040507" + "01050b0005060b050605040b0b01040b0b0c0505050505050305040504040d0c0506040b06010201000400" + "01010b05050505030e0505050404040e0505050f050505040404060610021102060b120504050c05050505" + "0b050506050505050b050505040005040b05050b0b0b04050505010b01020602050b0605040b1005050505" + "0505000b010c0004131313000000001413151514140811121204050170010a0a05030100110609017f0141" + "8080c0000b072604066d656d6f7279020009686173685f747970650046066465706c6f790047046d61696e" + "004e0910010041010b0984010f17143b3d3e3f400a91fd01a401f90201027f23004180016b220324002000" + "4200370204200041002802908540360200200341cc006a418080c000360200200341033a00502003428080" + "808080043703302003200036024820034100360240200341003602382003412736027c200341186a200120" + "02100b200341206a2903002101200329031821022003290328200341d5006a200341fc006a100c02400240" + "02402002200184500d00200328027c2200416c6a220420004e0d01200341d5006a41146a4130200410a201" + "1a2003411436027c200320022001100b200341086a2903002102200329030021012003290310200341d500" + "6a200341fc006a100c2001200284500d00200328027c2200417f6a220420004e0d01200341d6006a413020" + "0410a2011a2003410036027c2001a741ff017141306a220041ff01712000470d01200320003a00550b4127" + "200328027c22006b220441284f0d00200341306a41d082c0004100200341d5006a20006a2004100d0d0120" + "034180016a24000f0b00000b4137100e000bfd0202017f047e230041d0006b220324000240024020024280" + "8020540d00200341206a2001420042d2e1aadaeda7c987f6004200109d01200341106a2001420042f3b2d8" + "c19e9ebdcc957f4200109d01200341c0006a2002420042d2e1aadaeda7c987f6004200109d01200341306a" + "2002420042f3b2d8c19e9ebdcc957f4200109d01200341c0006a41086a290300200341206a41086a290300" + "200341106a41086a290300220420032903207c2205200454ad7c220620032903407c2204200654ad7c2004" + "200341306a41086a290300200520032903307c200554ad7c7c2206200454ad7c2204423e8821052006423e" + "8820044202868421040c010b20014213882002422d868442bda282a38eab04802104420021050b20032004" + "2005428080a0cfc8e0c8e38a7f4200109d0102402001200329030022067d22072001562002200341086a29" + "03007d2001200654ad7d220120025620012002511b0d002000200437030020002007370310200020053703" + "08200341d0006a24000f0b00000bf00503027f017e017f0240200228020022034114480d00024002402000" + "42ffff83fea6dee111580d002002200341706a2204360200200320016a2203417e6a200042808084fea6de" + "e11182220542e40082a7410174418881c0006a2f00003b00002003417c6a200542e4008042e40082a74101" + "74418881c0006a2f00003b00002003417a6a20054290ce008042e40082a7410174418881c0006a2f00003b" + "0000200341786a200542c0843d8042e40082a7410174418881c0006a2f00003b0000200341766a20054280" + "c2d72f80a741e40070410174418881c0006a2f00003b0000200341746a20054280c8afa02580a741e40070" + "410174418881c0006a2f00003b0000200341726a20054280a094a58d1d80a741e40070410174418881c000" + "6a2f00003b0000200120046a2005428080e983b1de1680a741e40070410174418881c0006a2f00003b0000" + "200042808084fea6dee1118021000c010b024020004280c2d72f5a0d00200321040c010b2002200341786a" + "2204360200200320016a2206417e6a20004280c2d72f82a7220341e40070410174418881c0006a2f00003b" + "00002006417c6a200341e4006e41e40070410174418881c0006a2f00003b00002006417a6a20034190ce00" + "6e41e40070410174418881c0006a2f00003b0000200120046a200341c0843d6e41e40070410174418881c0" + "006a2f00003b000020004280c2d72f8021000b02402000a722034190ce00490d00200420016a417e6a2003" + "4190ce0070220641e40070410174418881c0006a2f00003b000020012004417c6a22046a200641e4006e41" + "0174418881c0006a2f00003b000020034190ce006e21030b0240200341ffff0371220641e400490d002001" + "2004417e6a22046a200641e40070410174418881c0006a2f00003b0000200641e4006e21030b0240200341" + "ffff037141094b0d0020022004417f6a2204360200200120046a200341306a3a00000f0b20022004417e6a" + "2204360200200120046a200341ffff0371410174418881c0006a2f00003b00000f0b00000bfa0301077f23" + "0041106b22052400418080c40021062004210702400240024020002802002208410171450d00200441016a" + "22072004490d01412b21060b0240024020084104710d0041002101200721090c010b20072001200120026a" + "1086016a22092007490d010b41012107024020002802084101460d0020002006200120021087010d022000" + "280218200320042000411c6a28020028020c11000021070c020b0240024020092000410c6a280200220a4f" + "0d0020084108710d01200a20096b2208200a4b0d0241012107200520002008410110880120052802002208" + "418080c400460d032005280204210920002006200120021087010d0320002802182201200320042000411c" + "6a280200220028020c1100000d03200820092001200010890121070c030b20002006200120021087010d02" + "2000280218200320042000411c6a28020028020c11000021070c020b200028020421082000413036020420" + "002d0020210b41012107200041013a002020002006200120021087010d01200a20096b2201200a4b0d0041" + "012107200541086a20002001410110880120052802082201418080c400460d01200528020c210220002802" + "182206200320042000411c6a280200220928020c1100000d0120012002200620091089010d012000200b3a" + "002020002008360204410021070c010b00000b200541106a240020070b040000000b0600200010100b2601" + "017f024020002802004100200028020422001b2201450d002000450d002001200010120b0b3901017f2000" + "10100240200041146a2d00004102460d00024020002802102201280200450d002001101020002802102101" + "0b2001410c10120b0bf90101037f02402000450d002001109a011a41002802a888402102200041786a2201" + "20012802002203417e713602000240024002402003417c71220420006b20044b0d00200041003602002000" + "417c6a280200417c712204450d0120042d00004101710d012001109c0120042802002100024020012d0000" + "410271450d002004200041027222003602000b200221012000417c71220020046b41786a20004d0d020b00" + "000b02402003417c712204450d004100200420034102711b2203450d0020032d00004101710d0020002003" + "280208417c7136020020032001410172360208200221010c010b200020023602000b410020013602a88840" + "0b0b990403077f017e067f024002402005417f6a220720054b0d0020012802082208417f6a210920052001" + "280210220a6b220b20054b210c2001280214210d2001290300210e0340200d20076a220f200d490d010240" + "200f2003490d00200120033602144100210f0c030b0240200e2002200f6a310000423f8388420183500d00" + "20082008200128021c221020061b200820104b1b220f2005200f20054b1b2111034002402011200f470d00" + "4100201020061b21112009210f034002402011200f41016a490d00200d20056a220f200d490d062001200f" + "360214024020060d002001410036021c0b2000200d360204200041086a200f3602004101210f0c070b200f" + "20054f0d05200d200f6a2212200d490d05201220034f0d052004200f6a2113200f417f6a210f20132d0000" + "41ff0171200220126a2d0000460d000b200d200a6a220f200d490d042001200f360214200f210d20060d03" + "200c0d042001200b36021c200f210d0c030b200d200f6a2212200d490d03201220034f0d032004200f6a21" + "13200f41016a2214210f20132d000041ff0171200220126a2d0000460d000b2014417f6a221220086b220f" + "20124b0d02200f41016a2212200f490d02200d20126a220f200d490d022001200f360214200f210d20060d" + "012001410036021c200f210d0c010b200d20056a220f200d490d012001200f360214200f210d20060d0020" + "01410036021c200f210d0c000b0b00000b2000200f3602000bad0201027f230041106b2202240002400240" + "024002400240200141ff004b0d000240200028020822032000280204470d00200041011015200028020821" + "030b200028020020036a20013a0000200341016a22012003490d01200020013602080c040b200241003602" + "0c2001418010490d0102402001418080044f0d0020022001413f71418001723a000e20022001410c7641e0" + "01723a000c20022001410676413f71418001723a000d410321010c030b20022001413f71418001723a000f" + "2002200141127641f001723a000c20022001410676413f71418001723a000e20022001410c76413f714180" + "01723a000d410421010c020b00000b20022001413f71418001723a000d2002200141067641c001723a000c" + "410221010b20002002410c6a200110160b200241106a240041000bb20101037f230041206b220224000240" + "024020002802042203200028020822046b20014f0d00200420016a22012004490d01200320036a22042003" + "490d0120042001200420014b1b22014108200141084b1b2101024002402003450d00200241106a41086a41" + "0136020020022003360214200220002802003602100c010b200241003602100b200220014101200241106a" + "101c20022802004101460d01200020022902043702000b200241206a24000f0b00000b3701017f20002002" + "10152000280200200028020822036a2001200210a0011a0240200320026a220220034f0d0000000b200020" + "023602080b0c00200020012002101641000bb00201047f230041306b220224000240024002402000280208" + "22032000280204470d0020"; + bytes input; + boost::algorithm::unhex(errorBin, std::back_inserter(input)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1"); + auto sender = boost::algorithm::hex_lower(std::string(tx->sender())); + h256 addressCreate("ff6f30856ad3bae00b1169808488502786a13e3c174d85682135ffd51310310e"); + std::string addressString = addressCreate.hex().substr(0, 40); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setContextID(99); + params->setSeq(1000); + params->setDepth(0); + + params->setOrigin(sender); + params->setFrom(sender); + + // toChecksumAddress(addressString, hashImpl); + params->setTo(addressString); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(2); + blockHeader->setParentInfo({{blockHeader->number() - 1, h256(blockHeader->number() - 1)}}); + ledger->setBlockNumber(blockHeader->number() - 1); + + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + // -------------------------------- + // Create contract + // -------------------------------- + + std::promise executePromise; + executor->dmcExecuteTransaction( + std::move(params), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + BOOST_CHECK(result); + BOOST_CHECK_EQUAL(result->type(), ExecutionMessage::REVERT); + BOOST_CHECK_EQUAL(result->status(), (int32_t)TransactionStatus::Unknown); + BOOST_CHECK_EQUAL(result->evmStatus(), (int32_t)evmc_status_code::EVMC_SUCCESS); + BOOST_CHECK_EQUAL(result->contextID(), 99); + BOOST_CHECK_EQUAL(result->seq(), 1000); + BOOST_CHECK_EQUAL(result->create(), false); + BOOST_CHECK_EQUAL(result->newEVMContractAddress(), ""); + BOOST_CHECK_EQUAL(result->origin(), sender); + BOOST_CHECK_EQUAL(result->from(), addressString); + BOOST_CHECK(result->to() == sender); + + TwoPCParams commitParams{}; + commitParams.number = 2; + + std::promise preparePromise; + executor->prepare(commitParams, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + preparePromise.set_value(); + }); + preparePromise.get_future().get(); + + std::promise commitPromise; + executor->commit(commitParams, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + commitPromise.set_value(); + }); + commitPromise.get_future().get(); + } +} + +BOOST_AUTO_TEST_CASE(delegateCall) +{ + /* +pragma solidity>=0.6.10 <0.8.20; + +contract DelegateCallTest { + int public value = 0; + address public sender; + + function add() public returns(bytes memory) { + value += 1; + return "1"; + } + + function testFailed() public { + address addr = address(0x1001); + + dCall(addr, "add()"); + } + + function testSuccess() public { + address addr = address(this); + dCall(addr, "add()"); + } + + function dCall(address addr, string memory func) private returns(bytes memory) { + bool success; + bytes memory ret; + (success, ret) = addr.delegatecall(abi.encodeWithSignature(func)); + require(success, "delegatecall failed"); + + return ret; + } +} + +{ + "4f2be91f": "add()", + "67e404ce": "sender()", + "0ddbcc82": "testFailed()", + "713d3e3e": "testSuccess()", + "3fa4f245": "value()" +} + + */ + + std::string codeBin = + "60806040526000805534801561001457600080fd5b50610361806100246000396000f3fe608060405234801561" + "001057600080fd5b50600436106100575760003560e01c80630ddbcc821461005c5780633fa4f2451461006657" + "80634f2be91f1461008257806367e404ce14610097578063713d3e3e146100c2575b600080fd5b6100646100ca" + "565b005b61006f60005481565b6040519081526020015b60405180910390f35b61008a6100fc565b6040516100" + "799190610285565b6001546100aa906001600160a01b031681565b6040516001600160a01b0390911681526020" + "01610079565b610064610132565b600061100190506100f8816040518060400160405280600581526020016461" + "6464282960d81b81525061015a565b5050565b6060600160008082825461011091906102ce565b909155505060" + "40805180820190915260018152603160f81b6020820152919050565b60003090506100f8816040518060400160" + "4052806005815260200164616464282960d81b8152505b60408051600481526024810191829052606091600091" + "83916001600160a01b038716919061018990879061030f565b6040805191829003909120602083018051600160" + "0160e01b03166001600160e01b0319909216919091179052516101c0919061030f565b60006040518083038185" + "5af49150503d80600081146101fb576040519150601f19603f3d011682016040523d82523d6000602084013e61" + "0200565b606091505b5090925090508161024d5760405162461bcd60e51b815260206004820152601360248201" + "527219195b1959d85d1958d85b1b0819985a5b1959606a1b604482015260640160405180910390fd5b94935050" + "5050565b60005b83811015610270578181015183820152602001610258565b8381111561027f57600084840152" + "5b50505050565b60208152600082518060208401526102a4816040850160208701610255565b601f01601f1916" + "9190910160400192915050565b634e487b7160e01b600052601160045260246000fd5b60008082128015600160" + "0160ff1b03849003851316156102f0576102f06102b8565b600160ff1b83900384128116156103095761030961" + "02b8565b50500190565b60008251610321818460208701610255565b919091019291505056fea2646970667358" + "22122064d7211bf906a8ecf7041b98d7e2d2c243d6d8b7a79b19bb1e0968091916398e64736f6c634300080b00" + "33"; + + + bytes input; + boost::algorithm::unhex(codeBin, std::back_inserter(input)); + + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1"); + auto sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setContextID(100); + params->setSeq(1000); + params->setDepth(0); + + params->setOrigin(std::string(sender)); + params->setFrom(std::string(sender)); + + // The contract address + h256 addressCreate("ff6f30856ad3bae00b1169808488502786a13e3c174d85682135ffd51310310e"); + std::string address = addressCreate.hex().substr(0, 40); + params->setTo(address); + + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::MESSAGE); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setVersion((uint32_t)bcos::protocol::BlockVersion::MAX_VERSION); + blockHeader->setNumber(1); + blockHeader->setParentInfo({{blockHeader->number() - 1, h256(blockHeader->number() - 1)}}); + ledger->setBlockNumber(blockHeader->number() - 1); + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + + // -------------------------------- + // Deploy + // -------------------------------- + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + + BOOST_CHECK_EQUAL(result->type(), NativeExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(result->status(), 0); + BOOST_CHECK_EQUAL(result->evmStatus(), 0); + + + // -------------------------------- + // Get code + // -------------------------------- + std::promise executePromise1; + executor->getCode(address, [&](Error::Ptr error, bcos::bytes code) { + BOOST_CHECK(!error); + BOOST_CHECK(!code.empty()); + BOOST_CHECK_GT(code.size(), 0); + executePromise1.set_value(code); + }); + auto code = executePromise1.get_future().get(); + + // -------------------------------- + // DelegateCall + // -------------------------------- + std::string testFailedSelector = "0ddbcc82"; + input = bcos::bytes(); + boost::algorithm::unhex(testFailedSelector, std::back_inserter(input)); + + tx = fakeTransaction(cryptoSuite, keyPair, "", input, 102, 100002, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + params = std::make_unique(); + params->setContextID(101); + params->setSeq(1001); + params->setDepth(0); + params->setOrigin(std::string(sender)); + params->setFrom(std::string(sender)); + // The contract address + params->setTo(address); + + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::MESSAGE); + params->setTransactionHash(hash); + params->setCreate(false); + params->setDelegateCall(true); + params->setDelegateCallCode(code); + params->setDelegateCallSender(sender); + params->setDelegateCallAddress(address); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + + result = executePromise2.get_future().get(); + + BOOST_CHECK_EQUAL(result->delegateCall(), true); + BOOST_CHECK_EQUAL(result->delegateCallAddress(), "0000000000000000000000000000000000001001"); + BOOST_CHECK_EQUAL(result->delegateCallSender(), sender); + BOOST_CHECK(result->keyLocks().empty()); +} + +BOOST_AUTO_TEST_CASE(selfdestruct) +{ + // test-bcos-executor -t TestTransactionExecutor/selfdestruct + + /* + pragma solidity>=0.6.10 <0.8.20; + +contract HelloWorld { + string name; + + constructor() public { + name = "Hello, World!"; + } + + function get() public view returns (string memory) { + return name; + } + + function set(string memory n) public { + name = n; + } + + function selfdestructTest() public { + selfdestruct(payable(address(0))); + } +} + +{ + "6d4ce63c": "get()", + "fa967e1f": "selfdestructTest()", + "4ed3885e": "set(string)" +} + */ + + std::string codeBin = + "608060405234801561001057600080fd5b506040518060400160405280600d81526020017f48656c6c6f2c2057" + "6f726c6421000000000000000000000000000000000000008152506000908051906020019061005c9291906100" + "62565b50610166565b82805461006e90610105565b90600052602060002090601f016020900481019282610090" + "57600085556100d7565b82601f106100a957805160ff19168380011785556100d7565b82800160010185558215" + "6100d7579182015b828111156100d65782518255916020019190600101906100bb565b5b5090506100e4919061" + "00e8565b5090565b5b808211156101015760008160009055506001016100e9565b5090565b6000600282049050" + "600182168061011d57607f821691505b6020821081141561013157610130610137565b5b50919050565b7f4e48" + "7b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b61" + "04d7806101756000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c" + "80634ed3885e146100465780636d4ce63c14610062578063fa967e1f14610080575b600080fd5b610060600480" + "360381019061005b9190610263565b61008a565b005b61006a6100a4565b60405161007791906102e5565b6040" + "5180910390f35b610088610136565b005b80600090805190602001906100a0929190610150565b5050565b6060" + "600080546100b3906103bb565b80601f0160208091040260200160405190810160405280929190818152602001" + "8280546100df906103bb565b801561012c5780601f106101015761010080835404028352916020019161012c56" + "5b820191906000526020600020905b81548152906001019060200180831161010f57829003601f168201915b50" + "50505050905090565b600073ffffffffffffffffffffffffffffffffffffffff16ff5b82805461015c906103bb" + "565b90600052602060002090601f01602090048101928261017e57600085556101c5565b82601f106101975780" + "5160ff19168380011785556101c5565b828001600101855582156101c5579182015b828111156101c457825182" + "55916020019190600101906101a9565b5b5090506101d291906101d6565b5090565b5b808211156101ef576000" + "8160009055506001016101d7565b5090565b60006102066102018461032c565b610307565b9050828152602081" + "0184848401111561022257610221610481565b5b61022d848285610379565b509392505050565b600082601f83" + "011261024a5761024961047c565b5b813561025a8482602086016101f3565b91505092915050565b6000602082" + "840312156102795761027861048b565b5b600082013567ffffffffffffffff8111156102975761029661048656" + "5b5b6102a384828501610235565b91505092915050565b60006102b78261035d565b6102c18185610368565b93" + "506102d1818560208601610388565b6102da81610490565b840191505092915050565b60006020820190508181" + "0360008301526102ff81846102ac565b905092915050565b6000610311610322565b905061031d82826103ed56" + "5b919050565b6000604051905090565b600067ffffffffffffffff8211156103475761034661044d565b5b6103" + "5082610490565b9050602081019050919050565b600081519050919050565b6000828252602082019050929150" + "50565b82818337600083830152505050565b60005b838110156103a65780820151818401526020810190506103" + "8b565b838111156103b5576000848401525b50505050565b600060028204905060018216806103d357607f8216" + "91505b602082108114156103e7576103e661041e565b5b50919050565b6103f682610490565b810181811067ff" + "ffffffffffffff821117156104155761041461044d565b5b80604052505050565b7f4e487b7100000000000000" + "000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b710000000000" + "0000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080" + "fd5b600080fd5b600080fd5b6000601f19601f830116905091905056fea2646970667358221220b86cdade4230" + "cd8c479fc0822250b79a3f731a0e6d2dd04df1f043536198e02464736f6c63430008070033"; + + + bytes input; + boost::algorithm::unhex(codeBin, std::back_inserter(input)); + + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1"); + auto sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setContextID(100); + params->setSeq(1000); + params->setDepth(0); + + params->setOrigin(std::string(sender)); + params->setFrom(std::string(sender)); + + // The contract address + h256 addressCreate("ff6f30856ad3bae00b1169808488502786a13e3c174d85682135ffd51310310f"); + std::string address = addressCreate.hex().substr(0, 40); + params->setTo(address); + + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::MESSAGE); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setVersion((uint32_t)bcos::protocol::BlockVersion::MAX_VERSION); + blockHeader->setNumber(1); + blockHeader->setParentInfo({{blockHeader->number() - 1, h256(blockHeader->number() - 1)}}); + ledger->setBlockNumber(blockHeader->number() - 1); + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + + // -------------------------------- + // Deploy + // -------------------------------- + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + + BOOST_CHECK_EQUAL(result->type(), NativeExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(result->status(), 0); + BOOST_CHECK_EQUAL(result->evmStatus(), 0); + + + // -------------------------------- + // Get code + // -------------------------------- + std::promise executePromise1; + executor->getCode(address, [&](Error::Ptr error, bcos::bytes code) { + BOOST_CHECK(!error); + BOOST_CHECK(!code.empty()); + BOOST_CHECK_GT(code.size(), 0); + executePromise1.set_value(code); + }); + auto code = executePromise1.get_future().get(); + + // -------------------------------- + // selfdestruct + // -------------------------------- + std::string testFailedSelector = "fa967e1f"; + input = bcos::bytes(); + boost::algorithm::unhex(testFailedSelector, std::back_inserter(input)); + + tx = fakeTransaction(cryptoSuite, keyPair, "", input, 102, 100002, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + params = std::make_unique(); + params->setContextID(101); + params->setSeq(1001); + params->setDepth(0); + params->setOrigin(std::string(sender)); + params->setFrom(std::string(sender)); + // The contract address + params->setTo(address); + + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::MESSAGE); + params->setTransactionHash(hash); + params->setCreate(false); + params->setDelegateCall(false); + params->setDelegateCallCode(code); + params->setDelegateCallSender(sender); + params->setDelegateCallAddress(address); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + executePromise2.get_future().get(); + + // -------------------------------- + // Get code again(should not get) + // -------------------------------- + std::promise executePromise3; + executor->getHash(1, [&](bcos::Error::UniquePtr, crypto::HashType) { + executor->getCode(address, [&](Error::Ptr error, bcos::bytes code) { + BOOST_CHECK(!error); + BOOST_CHECK(code.empty()); + BOOST_CHECK_EQUAL(code.size(), 0); + executePromise3.set_value(code); + }); + }); + + executePromise3.get_future().get(); +} + + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestExecutiveStackFlow.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestExecutiveStackFlow.cpp" new file mode 100644 index 00000000..a4901dcc --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestExecutiveStackFlow.cpp" @@ -0,0 +1,195 @@ +/* + * 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. + * + */ + +#include "../../../src/CallParameters.h" +#include "../../../src/Common.h" +#include "../../../src/executive/BlockContext.h" +#include "../../../src/executive/ExecutiveFlowInterface.h" +#include "../../../src/executive/ExecutiveStackFlow.h" +#include "../../../src/executive/ExecutiveState.h" +#include "../mock/MockExecutiveFactory.h" +#include "../mock/MockLedger.h" +#include +#include +#include +#include + + +using namespace std; +using namespace bcos; +using namespace bcos::executor; + +namespace bcos +{ +namespace test +{ +struct ExecutiveStackFlowFixture +{ + ExecutiveStackFlowFixture() + { + for (int i = 1; i <= 20; ++i) + { + if (i <= 5) + { + auto input = std::make_unique(CallParameters::Type::MESSAGE); + input->contextID = i; + input->seq = 0; + txInputs->push_back(std::move(input)); + } + else if (i <= 10) + { + auto input = std::make_unique(CallParameters::Type::KEY_LOCK); + input->contextID = i; + input->seq = 1; + txInputs->push_back(std::move(input)); + } + else if (i <= 15) + { + auto input = std::make_unique(CallParameters::Type::FINISHED); + input->contextID = i; + input->seq = 1; + txInputs->push_back(std::move(input)); + } + else + { + auto input = std::make_unique(CallParameters::Type::REVERT); + input->contextID = i; + input->seq = 1; + txInputs->push_back(std::move(input)); + } + } + + LedgerCache::Ptr ledgerCache = + std::make_shared(std::make_shared()); + std::shared_ptr blockContext = std::make_shared( + nullptr, ledgerCache, nullptr, 0, h256(), 0, 0, FiscoBcosScheduleV4, false, false); + + executiveFactory = std::make_shared( + blockContext, nullptr, nullptr, nullptr, nullptr); + } + std::shared_ptr executiveStackFlow; + std::shared_ptr executiveFactory; + std::shared_ptr executiveState; + std::shared_ptr> txInputs = + make_shared>(); +}; + +BOOST_FIXTURE_TEST_SUITE(TestExecutiveStackFlow, ExecutiveStackFlowFixture) +BOOST_AUTO_TEST_CASE(RunTest) +{ + /* + EXECUTOR_LOG(DEBUG) << "RunTest begin"; + // std::shared_ptr> sequence = make_shared>(); + auto sequence = std::make_shared>(); + ExecutiveStackFlow::Ptr executiveStackFlow = + std::make_shared(executiveFactory); + BOOST_CHECK(executiveStackFlow != nullptr); + + executiveStackFlow->submit(txInputs); + EXECUTOR_LOG(DEBUG) << "submit 20 transaction success!"; + auto input1 = std::make_unique(CallParameters::Type::MESSAGE); + input1->contextID = 11; + input1->seq = 0; + executiveStackFlow->submit(std::move(input1)); + EXECUTOR_LOG(DEBUG) << "submit 1 transaction success!"; + + std::promise finish1; + std::promise finish2; + + executiveStackFlow->asyncRun( + // onTxReturn + [sequence](CallParameters::UniquePtr output) { + EXECUTOR_LOG(DEBUG) << "one transaction perform success! the seq is :" << output->seq + << ",the conntextID is:" << output->contextID; + sequence->push_back(output->contextID); + }, + // onFinished + [sequence,&finish1](bcos::Error::UniquePtr error) { + if (error != nullptr) + { + EXECUTOR_LOG(ERROR) + << "ExecutiveFlow asyncRun error: " << LOG_KV("errorCode", error->errorCode()) + << LOG_KV("errorMessage", error->errorMessage()); + EXECUTOR_LOG(ERROR) << "all transaction perform error, sequence clear!"; + sequence->clear(); + // callback(std::move(error), std::vector()); + } + else + { + EXECUTOR_LOG(DEBUG) << "all transaction perform end."; + BOOST_CHECK_EQUAL(sequence->size(), 15); + } + finish1.set_value(); + }); + + executiveStackFlow->asyncRun( + // onTxReturn + [sequence](CallParameters::UniquePtr output) { + EXECUTOR_LOG(DEBUG) << "one transaction perform success! the seq is :" << output->seq + << ",the conntextID is:" << output->contextID; + sequence->push_back(output->contextID); + }, + // onFinished + [sequence,&finish2](bcos::Error::UniquePtr error) { + if (error != nullptr) + { + EXECUTOR_LOG(ERROR) + << "ExecutiveFlow asyncRun error: " << LOG_KV("errorCode", error->errorCode()) + << LOG_KV("errorMessage", error->errorMessage()); + EXECUTOR_LOG(ERROR) << "all transaction perform error, sequence clear!"; + sequence->clear(); + // callback(std::move(error), std::vector()); + } + else + { + EXECUTOR_LOG(DEBUG) << "all transaction perform end."; + } + finish2.set_value(); + }); + + finish1.get_future().get(); + finish2.get_future().get(); + + EXECUTOR_LOG(DEBUG) << "asyncRun end. " << LOG_KV("the sequence size is :", sequence->size()); + [[maybe_unused]] bool flag = true; + for (int64_t i = 0u; i < (int64_t)sequence->size(); ++i) + { + if (i <= 10) + { + if (sequence->at(i) != 11 + i) + { + flag = false; + break; + } + } + else + { + if (sequence->at(i) != i - 9) + { + flag = false; + break; + } + } + } + /// FIXME + // BOOST_CHECK(flag); + */ +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestExecutiveState.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestExecutiveState.cpp" new file mode 100644 index 00000000..af28c064 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestExecutiveState.cpp" @@ -0,0 +1,153 @@ +#include "../../../src/CallParameters.h" +#include "../../../src/executive/BlockContext.h" +#include "../../../src/executive/ExecutiveFactory.h" +#include "../../../src/executive/ExecutiveState.h" +#include "../../../src/executive/TransactionExecutive.h" +#include "../mock/MockExecutiveFactory.h" +#include "../mock/MockLedger.h" +#include "../mock/MockTransactionExecutive.h" +#include + +using namespace std; +using namespace bcos; +using namespace bcos::executor; + + +namespace bcos +{ +namespace test +{ +struct ExecutiveStateFixture +{ + ExecutiveStateFixture() + { + ledgerCache = std::make_shared(std::make_shared()); + auto input = std::make_unique(CallParameters::Type::KEY_LOCK); + input->staticCall = false; + input->codeAddress = "aabbccddee"; + input->contextID = 1; + input->seq = 1; + std::shared_ptr blockContext = std::make_shared( + nullptr, ledgerCache, nullptr, 0, h256(), 0, 0, FiscoBcosScheduleV4, false, false); + + executiveFactory = std::make_shared( + blockContext, nullptr, nullptr, nullptr, nullptr); + } + LedgerCache::Ptr ledgerCache; + std::shared_ptr executive; + std::shared_ptr executiveFactory; + std::shared_ptr executiveState; + CallParameters::UniquePtr input; +}; + +BOOST_FIXTURE_TEST_SUITE(TestExecutiveState, ExecutiveStateFixture) + +BOOST_AUTO_TEST_CASE(goTest) +{ + CallParameters::UniquePtr output; + for (int8_t i = 0; i < 2; ++i) + { + EXECUTOR_LOG(DEBUG) << "goTest begin"; + if (i == 0) + { + EXECUTOR_LOG(DEBUG) << "i == 0 begin"; + auto callParameters = std::make_unique(CallParameters::MESSAGE); + callParameters->staticCall = false; + callParameters->codeAddress = "aabbccddee"; + callParameters->contextID = i; + callParameters->seq = i; + auto executiveState = + std::make_shared(executiveFactory, std::move(callParameters)); + // EXECUTOR_LOG(DEBUG) << executiveState->getStatus(); + auto output = executiveState->go(); + BOOST_CHECK(executiveState->getStatus() == ExecutiveState::PAUSED); + executiveState->setResumeParam(std::move(input)); + EXECUTOR_LOG(DEBUG) << "goTest: " << executiveState->getStatus(); + BOOST_CHECK(executiveState->getStatus() == ExecutiveState::NEED_RESUME); + executiveState->go(); + EXECUTOR_LOG(DEBUG) << "i == 0 end!"; + } + else if (i == 1) + { + EXECUTOR_LOG(DEBUG) << "i == 1 begin"; + auto callParameters = std::make_unique(CallParameters::FINISHED); + callParameters->staticCall = false; + callParameters->codeAddress = "aabbccddee"; + callParameters->contextID = i; + callParameters->seq = i; + auto executiveState = + std::make_shared(executiveFactory, std::move(callParameters)); + EXECUTOR_LOG(DEBUG) << "goTest: " << executiveState->getStatus(); + executiveState->go(); + EXECUTOR_LOG(DEBUG) << "goTest: " << executiveState->getStatus(); + BOOST_CHECK(executiveState->getStatus() == ExecutiveState::FINISHED); + // executiveState->go(); + EXECUTOR_LOG(DEBUG) << "goTest: " << executiveState->getStatus(); + EXECUTOR_LOG(DEBUG) << "i == 1 end!"; + } + } +} + +BOOST_AUTO_TEST_CASE(appendKeyLocksTest) +{ + EXECUTOR_LOG(DEBUG) << "appendKeyLocks begin"; + std::vector keyLocks{"123", "134", "125"}; + for (int i = 0; i < 3; ++i) + { + if (i == 0) + { + auto callParameters = std::make_unique(CallParameters::MESSAGE); + callParameters->staticCall = false; + callParameters->codeAddress = "aabbccddee"; + callParameters->contextID = i; + callParameters->seq = i; + callParameters->keyLocks = {"987"}; + auto executiveState = + std::make_shared(executiveFactory, std::move(callParameters)); + BOOST_CHECK(executiveState->getStatus() == ExecutiveState::NEED_RUN); + executiveState->appendKeyLocks(keyLocks); + EXECUTOR_LOG(DEBUG) << "i == 1 end ! status is :" << executiveState->getStatus(); + } + else if (i == 1) + { + EXECUTOR_LOG(DEBUG) << "i == 2 begin"; + auto callParameters = std::make_unique(CallParameters::MESSAGE); + callParameters->staticCall = false; + callParameters->codeAddress = "aabbccddee"; + callParameters->contextID = i; + callParameters->seq = i; + auto executiveState = + std::make_shared(executiveFactory, std::move(callParameters)); + executiveState->go(); + auto input1 = std::make_unique(CallParameters::MESSAGE); + input1->staticCall = false; + input1->codeAddress = "aabbccddee"; + input1->contextID = i; + input1->seq = i; + input1->keyLocks = {"987"}; + executiveState->setResumeParam(std::move(input1)); + executiveState->appendKeyLocks(keyLocks); + EXECUTOR_LOG(DEBUG) << "i == 2 end ! status is :" << executiveState->getStatus(); + } + else + { + EXECUTOR_LOG(DEBUG) << "i == 3 begin"; + auto callParameters = std::make_unique(CallParameters::REVERT); + callParameters->staticCall = false; + callParameters->codeAddress = "aabbccddee"; + callParameters->contextID = i; + callParameters->seq = i; + auto executiveState = + std::make_shared(executiveFactory, std::move(callParameters)); + executiveState->go(); + BOOST_CHECK(executiveState->getStatus() == ExecutiveState::FINISHED); + executiveState->appendKeyLocks(keyLocks); + EXECUTOR_LOG(DEBUG) << "i == 3 end ! status is :" << executiveState->getStatus(); + } + } +} + + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestLedgerCache.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestLedgerCache.cpp" new file mode 100644 index 00000000..88c0d121 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestLedgerCache.cpp" @@ -0,0 +1,85 @@ +/** + * Copyright (C) 2021 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. + */ +/** + * @brief : unitest for LedgerCache + * @author: jimmyshi + * @date: 2022-11-08 + */ + +#include "../mock/MockLedger.h" +#include "bcos-executor/src/executive/LedgerCache.h" +#include + +using namespace std; +using namespace bcos; +using namespace bcos::executor; + +namespace bcos +{ +namespace test +{ +struct LedgerCacheFixture +{ + LedgerCacheFixture() + { + mockLedger = std::make_shared(); + ledgerCache = std::make_shared(mockLedger); + } + + MockLedger::Ptr mockLedger; + LedgerCache::Ptr ledgerCache; +}; + +BOOST_FIXTURE_TEST_SUITE(LedgerCacheTest, LedgerCacheFixture) + +BOOST_AUTO_TEST_CASE(fetchBlockHashTest) +{ + for (int i = 1; i < 10; i++) + { + ledgerCache->setBlockNumber2Hash(i, h256(100 + i)); + } + + BOOST_CHECK_EQUAL(h256(0), ledgerCache->fetchBlockHash(0)); + + for (int i = 1; i < 10; i++) + { + BOOST_CHECK_EQUAL(h256(100 + i), ledgerCache->fetchBlockHash(i)); + } + + ledgerCache->clearCacheByNumber(5); + + for (int i = 0; i < 5; i++) + { + BOOST_CHECK_EQUAL(h256(i), ledgerCache->fetchBlockHash(i)); + } + + for (int i = 5; i < 10; i++) + { + BOOST_CHECK_EQUAL(h256(100 + i), ledgerCache->fetchBlockHash(i)); + } +} + +BOOST_AUTO_TEST_CASE(fetchGasLimitTest) +{ + auto gasLimit = MockLedger::TX_GAS_LIMIT; + BOOST_CHECK_EQUAL(gasLimit, ledgerCache->fetchTxGasLimit()); +} + + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestScaleUtils.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestScaleUtils.cpp" new file mode 100644 index 00000000..3743d247 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestScaleUtils.cpp" @@ -0,0 +1,129 @@ +/** + * Copyright (C) 2021 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. + */ +/** + * @brief : unitest for SCALE utils + * @author: catli + * @date: 2021-09-26 + */ + +#include "../src/dag/Abi.h" +#include "../src/dag/ScaleUtils.h" +#include +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::executor; + +namespace bcos +{ +namespace test +{ +BOOST_AUTO_TEST_SUITE(TestScaleUtils) + +BOOST_AUTO_TEST_CASE(DecodeCompactInteger) +{ + auto testCases = + vector>{{0, {0}}, {1, {4}}, {63, {252}}, {64, {1, 1}}, {255, {253, 3}}, + {511, {253, 7}}, {16383, {253, 255}}, {16384, {2, 0, 1, 0}}, {65535, {254, 255, 3, 0}}, + {1073741823ul, {254, 255, 255, 255}}, {1073741824, {3, 0, 0, 0, 64}}}; + + for (auto& testCase : testCases) + { + auto value = testCase.first; + auto& encodedBytes = testCase.second; + auto result = decodeCompactInteger(encodedBytes, 0); + BOOST_CHECK(result.has_value()); + BOOST_CHECK_EQUAL(result.value(), value); + } + + auto badCase = bytes{255, 255, 255, 255}; + auto result = decodeCompactInteger(badCase, 0); + BOOST_CHECK(!result.has_value()); +} + +BOOST_AUTO_TEST_CASE(CalculateEncodingLength) +{ + // Encoding of string "Alice" + auto encodedBytes = fromHexString("14616c696365"); + auto paramAbi = ParameterAbi("string"); + + auto result = scaleEncodingLength(paramAbi, *encodedBytes, 0); + BOOST_CHECK(result.has_value()); + BOOST_CHECK_EQUAL(result.value(), 6); + + // Encoding of uint32 number 20210926 + encodedBytes = fromHexString("ee643401"); + paramAbi.type = "uint32"; + result = scaleEncodingLength(paramAbi, *encodedBytes, 0); + BOOST_CHECK(result.has_value()); + BOOST_CHECK_EQUAL(result.value(), 4); + + // Encoding of int128 number 20180710 + encodedBytes = fromHexString("e6ee3301000000000000000000000000"); + paramAbi.type = "int128"; + result = scaleEncodingLength(paramAbi, *encodedBytes, 0); + BOOST_CHECK(result.has_value()); + BOOST_CHECK_EQUAL(result.value(), 16); + + // Encoding of vector of string ["Alice", "Bob"] + encodedBytes = fromHexString("0814416c6963650c426f62"); + paramAbi.type = "string[]"; + result = scaleEncodingLength(paramAbi, *encodedBytes, 0); + BOOST_CHECK(result.has_value()); + BOOST_CHECK_EQUAL(result.value(), 11); + + // Encoding of static array of string ["Alice", "Bob"] + encodedBytes = fromHexString("14416c6963650c426f62"); + paramAbi.type = "string[2]"; + result = scaleEncodingLength(paramAbi, *encodedBytes, 0); + BOOST_CHECK(result.has_value()); + BOOST_CHECK_EQUAL(result.value(), 10); + + // Encoding of tuple("Alice", [0, 0, ...], [0, 0, 0], false) + encodedBytes = fromHexString( + "14416c69636500000000000000000000000000000000000000000000000000000000000000000c00000000"); + paramAbi.type = "tuple"; + paramAbi.components.push_back(ParameterAbi("string")); + paramAbi.components.push_back(ParameterAbi("bytes32")); + paramAbi.components.push_back(ParameterAbi("bytes")); + paramAbi.components.push_back(ParameterAbi("bool")); + result = scaleEncodingLength(paramAbi, *encodedBytes, 0); + BOOST_CHECK(result.has_value()); + BOOST_CHECK_EQUAL(result.value(), 43); + + // Encoding of tuple>("Alice", ([0, 1], "Dwell not negative + // signs")) + encodedBytes = fromHexString( + "14416c696365080000000001000000604477656c6c206e6f74206e65676174697665207369676e73"); + paramAbi.type = "tuple"; + paramAbi.components.clear(); + paramAbi.components.push_back(ParameterAbi("string")); + paramAbi.components.push_back(ParameterAbi("tuple", vector{ + ParameterAbi("uint32[]"), + ParameterAbi("string"), + })); + result = scaleEncodingLength(paramAbi, *encodedBytes, 0); + BOOST_CHECK(result.has_value()); + BOOST_CHECK_EQUAL(result.value(), 40); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestTxDAG.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestTxDAG.cpp" new file mode 100644 index 00000000..45b724d0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestTxDAG.cpp" @@ -0,0 +1,237 @@ +/** + * Copyright (C) 2021 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. + */ +/** + * @brief : unitest for TxDAG + * @author: jimmyshi + * @date: 2022-01-19 + */ + +#include "../../../src/dag/TxDAG2.h" +#include "TxDAG.h" +#include "bcos-utilities/Common.h" +#include "bcos-utilities/DataConvertUtility.h" +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::executor::critical; + +namespace bcos +{ +namespace test +{ +BOOST_AUTO_TEST_SUITE(TestTxDAG) + +CriticalFieldsInterface::Ptr makeCriticals( + int _totalTx, std::function(ID)> _id2CriticalFunc) +{ + CriticalFields::Ptr criticals = make_shared(_totalTx); + for (int i = 0; i < _totalTx; i++) + { + criticals->put(i, make_shared(_id2CriticalFunc(i))); + } + return criticals; +} + +void testTxDAG( + CriticalFieldsInterface::Ptr criticals, shared_ptr _txDag, string name) +{ + auto startTime = utcSteadyTime(); + cout << endl << name << " test start" << endl; + _txDag->init(criticals, [&](ID id) { + if (id % 100000 == 0) + { + std::cout << " [" << id << "] "; + } + }); + auto initTime = utcSteadyTime(); + try + { + _txDag->run(8); + } + catch (exception& e) + { + std::cout << "Exception" << boost::diagnostic_information(e) << std::endl; + } + auto endTime = utcSteadyTime(); + cout << endl + << name << " cost(ms): initDAG=" << initTime - startTime << " run=" << endTime - initTime + << " total=" << endTime - startTime << endl; +} + + +void runDagTest(shared_ptr _txDag, int _total, + std::function(ID)> _id2CriticalFunc, std::function _beforeRunCheck, + std::function _afterRunCheck) +{ + // ./test-bcos-executor --run_test=TestTxDAG/TestRun + CriticalFieldsInterface::Ptr criticals = makeCriticals(_total, _id2CriticalFunc); + + _txDag->init(criticals, [&](ID id) { + _beforeRunCheck(id); + if (id % 1000 == 0) + { + std::cout << " [" << id << "] "; + } + _afterRunCheck(id); + }); + + try + { + _txDag->run(8); + } + catch (exception& e) + { + std::cout << "Exception" << boost::diagnostic_information(e) << std::endl; + } +} + +void txDagTest(shared_ptr txDag) +{ + int total = 100; + ID criticalNum = 6; + vector runnings(criticalNum, -1); + + auto id2CriticalFun = [&](ID id) -> vector { + return {bytes{static_cast(id % criticalNum)}}; + }; + auto beforeRunCheck = [&](ID id) { + BOOST_CHECK_MESSAGE(runnings[id % criticalNum] == -1, + "conflict at beginning: " << id << "-" << id % criticalNum << "-" + << runnings[id % criticalNum]); + runnings[id % criticalNum] = id; + }; + auto afterRunCheck = [&](ID id) { + BOOST_CHECK_MESSAGE(runnings[id % criticalNum] != -1, + "conflict at ending: " << id << "-" << id % criticalNum << "-" + << runnings[id % criticalNum]); + runnings[id % criticalNum] = -1; + }; + + runDagTest(txDag, total, id2CriticalFun, beforeRunCheck, afterRunCheck); +} + +void txDagDeepTreeTest(shared_ptr txDag) +{ + int total = 100; + ID slotNum = 2; + ID valueNum = 3; // values num under a slot + map runnings; + + auto id2CriticalFun = [&](ID id) -> vector { + ID slot = id % slotNum; + ID value = id % (slotNum * valueNum); + + if (value / slotNum == 0) + { + // return only slot + return {bytes{static_cast(slot)}}; + } + else + { + return {bytes{static_cast(slot), static_cast(value)}}; + } + }; + + auto beforeRunCheck = [&](ID id) { + if (id == 0) + { + return; + } + + auto critical = id2CriticalFun(id); + if (critical[0].size() == 1) + { + // only has slot + ID slot = critical[0][0]; + for (ID i = 0; i < valueNum; i++) + { + ID conflictValue = i * slotNum + slot; + ID unfinishedId = runnings[conflictValue]; + BOOST_CHECK_MESSAGE(unfinishedId == 0, + "conflict at beginning, id: " << id << " unfinishedId: " << unfinishedId); + runnings[conflictValue] = id; // update to my id + } + } + else + { + ID slot = critical[0][0]; + ID unfinishedId = runnings[slot]; + BOOST_CHECK_MESSAGE(unfinishedId == 0, + "parent conflict at beginning, id: " << id << " unfinishedId: " << unfinishedId); + + ID value = critical[0][1]; + unfinishedId = runnings[value]; + BOOST_CHECK_MESSAGE(unfinishedId == 0, + "myself conflict at beginning, id: " << id << " unfinishedId: " << unfinishedId); + runnings[value] = id; // update to my id + } + }; + auto afterRunCheck = [&](ID id) { + if (id == 0) + { + return; + } + + auto critical = id2CriticalFun(id); + if (critical[0].size() == 1) + { + // only has slot + ID slot = critical[0][0]; + for (ID i = 0; i < valueNum; i++) + { + ID conflictValue = i * slotNum + slot; + ID unfinishedId = runnings[conflictValue]; + BOOST_CHECK_MESSAGE(unfinishedId == id, + "conflict at ending, id: " << id << " unfinishedId: " << unfinishedId); + runnings[conflictValue] = 0; // update to 0 + } + } + else + { + ID slot = critical[0][0]; + ID unfinishedId = runnings[slot]; + BOOST_CHECK_MESSAGE(unfinishedId == 0, + "parent conflict at ending, id: " << id << " unfinishedId: " << unfinishedId); + + ID value = critical[0][1]; + unfinishedId = runnings[value]; + BOOST_CHECK_MESSAGE(unfinishedId == id, + "myself conflict at ending, id: " << id << " unfinishedId: " << unfinishedId); + runnings[value] = 0; // update to my id + } + }; + + runDagTest(txDag, total, id2CriticalFun, beforeRunCheck, afterRunCheck); +} +BOOST_AUTO_TEST_CASE(TestRun2) +{ + shared_ptr txDag = make_shared(); + txDagTest(txDag); +} + +BOOST_AUTO_TEST_CASE(TestRun4) +{ + shared_ptr txDag = make_shared(); + // FIXME + // txDagDeepTreeTest(txDag); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestWasmExecutor.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestWasmExecutor.cpp" new file mode 100644 index 00000000..f5151d80 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TestWasmExecutor.cpp" @@ -0,0 +1,1281 @@ +/** + * Copyright (C) 2021 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. + */ +/** + * @brief : unittest for Wasm executor implementation + * @author: catli + * @date: 2021-10-19 + */ + +// if wasm ut crash on aarch64 linux check https://github.com/bytecodealliance/wasmtime/issues/4972 +// #if !defined(__aarch64__) && !defined(__linux__) + +#include "../liquid/hello_world.h" +#include "../liquid/hello_world_caller.h" +#include "../liquid/transfer.h" +#include "../mock/MockLedger.h" +#include "../mock/MockTransactionalStorage.h" +#include "../mock/MockTxPool.h" +// #include "Common.h" +#include "bcos-codec/wrapper/CodecWrapper.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/protocol/Transaction.h" +#include "bcos-table/src/StateStorage.h" +#include "executor/TransactionExecutor.h" +#include "executor/TransactionExecutorFactory.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::protocol; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::crypto; + +namespace bcos +{ +namespace test +{ +struct WasmExecutorFixture +{ + WasmExecutorFixture() + { + boost::log::core::get()->set_logging_enabled(false); + hashImpl = std::make_shared(); + assert(hashImpl); + auto signatureImpl = std::make_shared(); + assert(signatureImpl); + cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + + txpool = std::make_shared(); + backend = std::make_shared(hashImpl); + ledger = std::make_shared(); + auto executionResultFactory = std::make_shared(); + + executor = bcos::executor::TransactionExecutorFactory::build( + ledger, txpool, nullptr, backend, executionResultFactory, hashImpl, true, false, false); + + + keyPair = cryptoSuite->signatureImpl()->generateKeyPair(); + memcpy(keyPair->secretKey()->mutableData(), + fromHexString("ff6f30856ad3bae00b1169808488502786a13e3c174d85682135ffd51310310e") + ->data(), + 32); + memcpy(keyPair->publicKey()->mutableData(), + fromHexString( + "ccd8de502ac45462767e649b462b5f4ca7eadd69c7e1f1b410bdf754359be29b1b88ffd79744" + "03f56e250af52b25682014554f7b3297d6152401e85d426a06ae") + ->data(), + 64); + + codec = std::make_unique(hashImpl, true); + + helloWorldBin.assign(hello_world_wasm, hello_world_wasm + hello_world_wasm_len); + helloWorldBin = codec->encode(helloWorldBin); + helloWorldAbi = string( + R"([{"inputs":[{"internalType":"string","name":"name","type":"string"}],"type":"constructor"},{"conflictFields":[{"kind":0,"path":[],"read_only":false,"slot":0}],"constant":false,"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"set","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"internalType":"string","type":"string"}],"type":"function"}])"); + + helloWorldCallerBin.assign( + hello_world_caller_wasm, hello_world_caller_wasm + hello_world_caller_wasm_len); + helloWorldCallerBin = codec->encode(helloWorldCallerBin); + helloWorldCallerAbi = string( + R"([{"inputs":[{"internalType":"string","name":"addr","type":"string"}],"type":"constructor"},{"constant":false,"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"set","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"internalType":"string","type":"string"}],"type":"function"}])"); + + transferBin.assign(transfer_wasm, transfer_wasm + transfer_wasm_len); + transferBin = codec->encode(transferBin); + transferAbi = string( + R"([{"inputs":[],"type":"constructor"},{"conflictFields":[{"kind":3,"path":[0],"read_only":false,"slot":0},{"kind":3,"path":[1],"read_only":false,"slot":0}],"constant":false,"inputs":[{"internalType":"string","name":"from","type":"string"},{"internalType":"string","name":"to","type":"string"},{"internalType":"uint32","name":"amount","type":"uint32"}],"name":"transfer","outputs":[{"internalType":"bool","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"query","outputs":[{"internalType":"uint32","type":"uint32"}],"type":"function"}])"); + createSysTable(); + } + ~WasmExecutorFixture() { boost::log::core::get()->set_logging_enabled(true); } + + void createSysTable() + { + // create / table + { + std::promise> promise2; + backend->asyncCreateTable( + "/", "value", [&](Error::UniquePtr&& _error, std::optional
&& _table) { + BOOST_CHECK(!_error); + promise2.set_value(std::move(_table)); + }); + auto rootTable = promise2.get_future().get(); + storage::Entry tEntry, newSubEntry, aclTypeEntry, aclWEntry, aclBEntry, extraEntry; + std::map newSubMap; + newSubMap.insert(std::make_pair("apps", FS_TYPE_DIR)); + newSubMap.insert(std::make_pair("/", FS_TYPE_DIR)); + newSubMap.insert(std::make_pair("tables", FS_TYPE_DIR)); + tEntry.importFields({FS_TYPE_DIR}); + newSubEntry.importFields({asString(codec::scale::encode(newSubMap))}); + aclTypeEntry.importFields({"0"}); + aclWEntry.importFields({""}); + aclBEntry.importFields({""}); + extraEntry.importFields({""}); + rootTable->setRow(FS_KEY_TYPE, std::move(tEntry)); + rootTable->setRow(FS_KEY_SUB, std::move(newSubEntry)); + rootTable->setRow(FS_ACL_TYPE, std::move(aclTypeEntry)); + rootTable->setRow(FS_ACL_WHITE, std::move(aclWEntry)); + rootTable->setRow(FS_ACL_BLACK, std::move(aclBEntry)); + rootTable->setRow(FS_KEY_EXTRA, std::move(extraEntry)); + } + + // create /tables table + { + std::promise> promise3; + backend->asyncCreateTable( + "/tables", "value", [&](Error::UniquePtr&& _error, std::optional
&& _table) { + BOOST_CHECK(!_error); + promise3.set_value(std::move(_table)); + }); + auto tablesTable = promise3.get_future().get(); + storage::Entry tEntry, newSubEntry, aclTypeEntry, aclWEntry, aclBEntry, extraEntry; + std::map newSubMap; + tEntry.importFields({FS_TYPE_DIR}); + newSubEntry.importFields({asString(codec::scale::encode(newSubMap))}); + aclTypeEntry.importFields({"0"}); + aclWEntry.importFields({""}); + aclBEntry.importFields({""}); + extraEntry.importFields({""}); + tablesTable->setRow(FS_KEY_TYPE, std::move(tEntry)); + tablesTable->setRow(FS_KEY_SUB, std::move(newSubEntry)); + tablesTable->setRow(FS_ACL_TYPE, std::move(aclTypeEntry)); + tablesTable->setRow(FS_ACL_WHITE, std::move(aclWEntry)); + tablesTable->setRow(FS_ACL_BLACK, std::move(aclBEntry)); + tablesTable->setRow(FS_KEY_EXTRA, std::move(extraEntry)); + } + + // create /apps table + { + std::promise> promise4; + backend->asyncCreateTable( + "/apps", "value", [&](Error::UniquePtr&& _error, std::optional
&& _table) { + BOOST_CHECK(!_error); + promise4.set_value(std::move(_table)); + }); + auto appsTable = promise4.get_future().get(); + storage::Entry tEntry, newSubEntry, aclTypeEntry, aclWEntry, aclBEntry, extraEntry; + std::map newSubMap; + tEntry.importFields({FS_TYPE_DIR}); + newSubEntry.importFields({asString(codec::scale::encode(newSubMap))}); + aclTypeEntry.importFields({"0"}); + aclWEntry.importFields({""}); + aclBEntry.importFields({""}); + extraEntry.importFields({""}); + appsTable->setRow(FS_KEY_TYPE, std::move(tEntry)); + appsTable->setRow(FS_KEY_SUB, std::move(newSubEntry)); + appsTable->setRow(FS_ACL_TYPE, std::move(aclTypeEntry)); + appsTable->setRow(FS_ACL_WHITE, std::move(aclWEntry)); + appsTable->setRow(FS_ACL_BLACK, std::move(aclBEntry)); + appsTable->setRow(FS_KEY_EXTRA, std::move(extraEntry)); + } + } + + TransactionExecutor::Ptr executor; + CryptoSuite::Ptr cryptoSuite; + std::shared_ptr txpool; + std::shared_ptr backend; + std::shared_ptr ledger; + std::shared_ptr hashImpl; + + KeyPairInterface::Ptr keyPair; + int64_t gas = 3000000000; + std::unique_ptr codec; + + bytes helloWorldBin; + std::string helloWorldAbi; + + bytes helloWorldCallerBin; + std::string helloWorldCallerAbi; + + bytes transferBin; + std::string transferAbi; +}; +BOOST_FIXTURE_TEST_SUITE(TestWasmExecutor, WasmExecutorFixture) + +BOOST_AUTO_TEST_CASE(deployAndCall) +{ + bytes input; + + input.insert(input.end(), helloWorldBin.begin(), helloWorldBin.end()); + + bytes constructorParam = codec->encode(string("alice")); + constructorParam = codec->encode(constructorParam); + input.insert(input.end(), constructorParam.begin(), constructorParam.end()); + + string selfAddress = "usr/alice/hello_world"; + + auto tx = + fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1", helloWorldAbi); + auto sender = *toHexString(string_view((char*)tx->sender().data(), tx->sender().size())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setType(bcos::protocol::ExecutionMessage::TXHASH); + params->setContextID(100); + params->setSeq(1000); + params->setDepth(0); + params->setTo(selfAddress); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setType(ExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(1); + blockHeader->setParentInfo({{blockHeader->number() - 1, h256(blockHeader->number() - 1)}}); + ledger->setBlockNumber(blockHeader->number() - 1); + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + result->setSeq(1001); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(result), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + + auto result2 = executePromise2.get_future().get(); + result2->setSeq(1000); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + + auto result3 = executePromise3.get_future().get(); + + BOOST_CHECK_EQUAL(result3->status(), 0); + BOOST_CHECK_EQUAL(result3->origin(), sender); + BOOST_CHECK_EQUAL(result3->from(), paramsBak.to()); + BOOST_CHECK_EQUAL(result3->to(), sender); + + BOOST_CHECK(result3->message().empty()); + BOOST_CHECK(!result3->newEVMContractAddress().empty()); + BOOST_CHECK_LT(result3->gasAvailable(), gas); + + auto address = result3->newEVMContractAddress(); + + TwoPCParams commitParams; + commitParams.number = 1; + + std::promise preparePromise; + executor->prepare(commitParams, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + preparePromise.set_value(); + }); + preparePromise.get_future().get(); + + std::promise commitPromise; + executor->commit(commitParams, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + commitPromise.set_value(); + }); + commitPromise.get_future().get(); + auto tableName = std::string("/apps/") + std::string(result3->newEVMContractAddress()); + + EXECUTOR_LOG(TRACE) << "Checking table: " << tableName; + std::promise
tablePromise; + backend->asyncOpenTable(tableName, [&](Error::UniquePtr&& error, std::optional
&& table) { + BOOST_CHECK(!error); + BOOST_CHECK(table); + tablePromise.set_value(std::move(*table)); + }); + auto table = tablePromise.get_future().get(); + + auto entry = table.getRow("code"); + BOOST_CHECK(entry); + BOOST_CHECK_GT(entry->getField(0).size(), 0); + + // start new block + auto blockHeader2 = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader2->setNumber(2); + blockHeader2->setParentInfo({{blockHeader2->number() - 1, h256(blockHeader2->number() - 1)}}); + ledger->setBlockNumber(blockHeader2->number() - 1); + std::promise nextPromise2; + executor->nextBlockHeader(0, std::move(blockHeader2), [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + + nextPromise2.set_value(); + }); + + nextPromise2.get_future().get(); + + // set "fisco bcos" + bytes txInput; + char inputBytes[] = "4ed3885e28666973636f2062636f73"; + boost::algorithm::unhex( + &inputBytes[0], inputBytes + sizeof(inputBytes) - 1, std::back_inserter(txInput)); + auto params2 = std::make_unique(); + params2->setContextID(101); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(std::string(sender)); + params2->setTo(std::string(address)); + params2->setOrigin(std::string(sender)); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(txInput)); + params2->setType(NativeExecutionMessage::MESSAGE); + + std::promise executePromise4; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise4.set_value(std::move(result)); + }); + auto result4 = executePromise4.get_future().get(); + + BOOST_CHECK(result4); + BOOST_CHECK_EQUAL(result4->status(), 0); + BOOST_CHECK_EQUAL(result4->message(), ""); + BOOST_CHECK_EQUAL(result4->newEVMContractAddress(), ""); + BOOST_CHECK_LT(result4->gasAvailable(), gas); + + // read "fisco bcos" + bytes queryBytes; + char inputBytes2[] = "6d4ce63c"; + boost::algorithm::unhex( + &inputBytes2[0], inputBytes2 + sizeof(inputBytes2) - 1, std::back_inserter(queryBytes)); + + auto params3 = std::make_unique(); + params3->setContextID(102); + params3->setSeq(1000); + params3->setDepth(0); + params3->setFrom(std::string(sender)); + params3->setTo(std::string(address)); + params3->setOrigin(std::string(sender)); + params3->setStaticCall(true); + params3->setGasAvailable(gas); + params3->setData(std::move(queryBytes)); + params3->setType(ExecutionMessage::MESSAGE); + + std::promise executePromise5; + executor->dmcExecuteTransaction(std::move(params3), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise5.set_value(std::move(result)); + }); + auto result5 = executePromise5.get_future().get(); + + BOOST_CHECK(result5); + BOOST_CHECK_EQUAL(result5->status(), 0); + BOOST_CHECK_EQUAL(result5->message(), ""); + BOOST_CHECK_EQUAL(result5->newEVMContractAddress(), ""); + BOOST_CHECK_LT(result5->gasAvailable(), gas); + + std::string output; + codec->decode(result5->data(), output); + BOOST_CHECK_EQUAL(output, "fisco bcos"); +} + +BOOST_AUTO_TEST_CASE(deployError) +{ + bytes input; + + input.insert(input.end(), helloWorldBin.begin(), helloWorldBin.end()); + + bytes constructorParam = codec->encode(string("alice")); + constructorParam = codec->encode(constructorParam); + input.insert(input.end(), constructorParam.begin(), constructorParam.end()); + + string selfAddress = "usr/alice/hello_world"; + + auto tx = + fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1", helloWorldAbi); + auto sender = *toHexString(string_view((char*)tx->sender().data(), tx->sender().size())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + { + auto params = std::make_unique(); + params->setType(bcos::protocol::ExecutionMessage::TXHASH); + params->setContextID(100); + params->setSeq(1000); + params->setDepth(0); + params->setTo(selfAddress); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setType(ExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(1); + blockHeader->setParentInfo({{blockHeader->number() - 1, h256(blockHeader->number() - 1)}}); + ledger->setBlockNumber(blockHeader->number() - 1); + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + + std::promise executePromise; + executor->dmcExecuteTransaction( + std::move(params), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + result->setSeq(1001); + + std::promise executePromise2; + executor->dmcExecuteTransaction( + std::move(result), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + + auto result2 = executePromise2.get_future().get(); + result2->setSeq(1000); + + std::promise executePromise3; + executor->dmcExecuteTransaction( + std::move(result2), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + + auto result3 = executePromise3.get_future().get(); + + BOOST_CHECK_EQUAL(result3->status(), 0); + BOOST_CHECK_EQUAL(result3->origin(), sender); + BOOST_CHECK_EQUAL(result3->from(), paramsBak.to()); + BOOST_CHECK_EQUAL(result3->to(), sender); + + BOOST_CHECK(result3->message().empty()); + BOOST_CHECK(!result3->newEVMContractAddress().empty()); + BOOST_CHECK_LT(result3->gasAvailable(), gas); + + TwoPCParams commitParams; + commitParams.number = 1; + + std::promise preparePromise; + executor->prepare(commitParams, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + preparePromise.set_value(); + }); + preparePromise.get_future().get(); + + std::promise commitPromise; + executor->commit(commitParams, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + commitPromise.set_value(); + }); + commitPromise.get_future().get(); + auto tableName = std::string("/apps/") + std::string(result3->newEVMContractAddress()); + + EXECUTOR_LOG(TRACE) << "Checking table: " << tableName; + std::promise
tablePromise; + backend->asyncOpenTable( + tableName, [&](Error::UniquePtr&& error, std::optional
&& table) { + BOOST_CHECK(!error); + BOOST_CHECK(table); + tablePromise.set_value(std::move(*table)); + }); + auto table = tablePromise.get_future().get(); + + auto entry = table.getRow("code"); + BOOST_CHECK(entry); + BOOST_CHECK_GT(entry->getField(0).size(), 0); + } + + string errorAddress = "usr/alice/hello_world/hello_world"; + + { + auto params = std::make_unique(); + params->setType(bcos::protocol::ExecutionMessage::TXHASH); + params->setContextID(100); + params->setSeq(1000); + params->setDepth(0); + // error address + params->setTo(errorAddress); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setType(ExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(2); + blockHeader->setParentInfo({{blockHeader->number() - 1, h256(blockHeader->number() - 1)}}); + ledger->setBlockNumber(blockHeader->number() - 1); + std::promise p1; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + p1.set_value(); + }); + p1.get_future().get(); + + std::promise p2; + executor->dmcExecuteTransaction( + std::move(params), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + p2.set_value(std::move(result)); + }); + + auto r2 = p2.get_future().get(); + r2->setSeq(1001); + + std::promise p3; + executor->dmcExecuteTransaction( + std::move(r2), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + p3.set_value(std::move(result)); + }); + + auto r3 = p3.get_future().get(); + r3->setSeq(1000); + + BOOST_CHECK_EQUAL(r3->status(), 15); + + std::promise p4; + executor->dmcExecuteTransaction( + std::move(r3), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + p4.set_value(std::move(result)); + }); + + auto r4 = p4.get_future().get(); + + BOOST_CHECK_EQUAL(r4->status(), 16); + BOOST_CHECK_EQUAL(r4->origin(), sender); + BOOST_CHECK_EQUAL(r4->from(), paramsBak.to()); + BOOST_CHECK_EQUAL(r4->to(), sender); + + BOOST_CHECK(r4->message() == "Error occurs in build BFS dir"); + BOOST_CHECK_LT(r4->gasAvailable(), gas); + } +} + +BOOST_AUTO_TEST_CASE(deployAndCall_100) +{ + bytes input; + + input.insert(input.end(), helloWorldBin.begin(), helloWorldBin.end()); + + bytes constructorParam = codec->encode(string("alice")); + constructorParam = codec->encode(constructorParam); + input.insert(input.end(), constructorParam.begin(), constructorParam.end()); + + string selfAddress = "usr/alice/hello_world"; + + auto tx = + fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1", helloWorldAbi); + auto sender = *toHexString(string_view((char*)tx->sender().data(), tx->sender().size())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setType(bcos::protocol::ExecutionMessage::TXHASH); + params->setContextID(100); + params->setSeq(1000); + params->setDepth(0); + params->setTo(selfAddress); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setType(ExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(1); + blockHeader->setParentInfo({{blockHeader->number() - 1, h256(blockHeader->number() - 1)}}); + ledger->setBlockNumber(blockHeader->number() - 1); + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + + result->setSeq(1001); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(result), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + + auto result2 = executePromise2.get_future().get(); + result2->setSeq(1000); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + + auto result3 = executePromise3.get_future().get(); + + BOOST_CHECK_EQUAL(result3->status(), 0); + BOOST_CHECK_EQUAL(result3->origin(), sender); + BOOST_CHECK_EQUAL(result3->from(), paramsBak.to()); + BOOST_CHECK_EQUAL(result3->to(), sender); + + BOOST_CHECK(result3->message().empty()); + BOOST_CHECK(!result3->newEVMContractAddress().empty()); + BOOST_CHECK_EQUAL(result3->gasAvailable(), 2999552552); + + auto address = result3->newEVMContractAddress(); + BOOST_CHECK_EQUAL(result3->newEVMContractAddress(), selfAddress); + TwoPCParams commitParams; + commitParams.number = 1; + + std::promise preparePromise; + executor->prepare(commitParams, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + preparePromise.set_value(); + }); + preparePromise.get_future().get(); + + std::promise commitPromise; + executor->commit(commitParams, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + commitPromise.set_value(); + }); + commitPromise.get_future().get(); + auto tableName = std::string("/apps/") + std::string(result3->newEVMContractAddress()); + + EXECUTOR_LOG(TRACE) << "Checking table: " << tableName; + std::promise
tablePromise; + backend->asyncOpenTable(tableName, [&](Error::UniquePtr&& error, std::optional
&& table) { + BOOST_CHECK(!error); + BOOST_CHECK(table); + tablePromise.set_value(std::move(*table)); + }); + auto table = tablePromise.get_future().get(); + + auto entry = table.getRow("code"); + BOOST_CHECK(entry); + BOOST_CHECK_GT(entry->getField(0).size(), 0); + + // start new block + auto blockHeader2 = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader2->setNumber(2); + blockHeader2->setParentInfo({{blockHeader2->number() - 1, h256(blockHeader2->number() - 1)}}); + ledger->setBlockNumber(blockHeader2->number() - 1); + std::promise nextPromise2; + executor->nextBlockHeader(0, std::move(blockHeader2), [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + + nextPromise2.set_value(); + }); + + nextPromise2.get_future().get(); + char inputBytes[] = "4ed3885e28666973636f2062636f73"; + + auto helloSet = [&](size_t i) -> int64_t { + // set "fisco bcos" + bytes txInput; + boost::algorithm::unhex( + &inputBytes[0], inputBytes + sizeof(inputBytes) - 1, std::back_inserter(txInput)); + auto params2 = std::make_unique(); + params2->setContextID(i); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(std::string(sender)); + params2->setTo(std::string(address)); + params2->setOrigin(std::string(sender)); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(txInput)); + params2->setType(NativeExecutionMessage::MESSAGE); + cout << ">>>>>>>>>>>>Executing set id=" << i << endl; + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + BOOST_CHECK(result2); + BOOST_CHECK_EQUAL(result2->status(), 0); + BOOST_CHECK_EQUAL(result2->message(), ""); + BOOST_CHECK_EQUAL(result2->newEVMContractAddress(), ""); + return result2->gasAvailable(); + }; + + auto helloGet = [&](size_t i, const string& ret = string("fisco bcos")) -> int64_t { + // read "fisco bcos" + bytes queryBytes; + + char inputBytes2[] = "6d4ce63c"; + boost::algorithm::unhex( + &inputBytes2[0], inputBytes2 + sizeof(inputBytes2) - 1, std::back_inserter(queryBytes)); + + auto params3 = std::make_unique(); + params3->setContextID(i); + params3->setSeq(1000); + params3->setDepth(0); + params3->setFrom(std::string(sender)); + params3->setTo(std::string(address)); + params3->setOrigin(std::string(sender)); + params3->setStaticCall(false); + params3->setGasAvailable(gas); + params3->setData(std::move(queryBytes)); + params3->setType(ExecutionMessage::MESSAGE); + cout << ">>>>>>>>>>>>Executing get id=" << i << endl; + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(params3), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + BOOST_CHECK(result3); + BOOST_CHECK_EQUAL(result3->status(), 0); + BOOST_CHECK_EQUAL(result3->message(), ""); + BOOST_CHECK_EQUAL(result3->newEVMContractAddress(), ""); + std::string output; + codec->decode(result3->data(), output); + BOOST_CHECK_EQUAL(output, "fisco bcos"); + return result3->gasAvailable(); + }; + int64_t getGas = 2999989808; + int64_t setGas = 2999982821; + size_t id = 101; + BOOST_CHECK_EQUAL(helloSet(id++), 2999983105); + BOOST_CHECK_EQUAL(helloSet(id++), setGas); + BOOST_CHECK_EQUAL(helloGet(id++), getGas); + BOOST_CHECK_EQUAL(helloSet(id++), setGas); + BOOST_CHECK_EQUAL(helloSet(id++), setGas); + + for (size_t i = 899; i < 1001; ++i) + { + if (i % 3 == 0) + { + BOOST_CHECK_EQUAL(helloSet(i), setGas); + } + else if (i % 3 == 1) + { + BOOST_CHECK_EQUAL(helloGet(i), getGas); + } + else + { + BOOST_CHECK_EQUAL(helloSet(i), setGas); + } + } + BOOST_CHECK_EQUAL(helloGet(id++), getGas); +} + +BOOST_AUTO_TEST_CASE(externalCall) +{ + string aliceAddress = "usr/alice/hello_world"; + string bobAddress = "/usr/bob/hello_world_caller"; + string sender; + // -------------------------------- + // Create contract HelloWorld + // -------------------------------- + { + bytes input; + + input.insert(input.end(), helloWorldBin.begin(), helloWorldBin.end()); + + bytes constructorParam = codec->encode(string("alice")); + constructorParam = codec->encode(constructorParam); + input.insert(input.end(), constructorParam.begin(), constructorParam.end()); + + auto tx = + fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1", helloWorldAbi); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setContextID(100); + params->setSeq(1000); + params->setDepth(0); + params->setOrigin(std::string(sender)); + params->setFrom(std::string(sender)); + params->setTo(aliceAddress); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(1); + blockHeader->setParentInfo({{blockHeader->number() - 1, h256(blockHeader->number() - 1)}}); + ledger->setBlockNumber(blockHeader->number() - 1); + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + + std::promise executePromise; + executor->dmcExecuteTransaction( + std::move(params), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + + result->setSeq(1001); + + std::promise executePromise2; + executor->dmcExecuteTransaction( + std::move(result), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + + auto result2 = executePromise2.get_future().get(); + result2->setSeq(1000); + + std::promise executePromise3; + executor->dmcExecuteTransaction( + std::move(result2), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + + auto result3 = executePromise3.get_future().get(); + + auto address = result3->newEVMContractAddress(); + BOOST_CHECK_EQUAL(result3->type(), NativeExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(result3->status(), 0); + BOOST_CHECK_EQUAL(address, aliceAddress); + } + + // -------------------------------- + // Create contract HelloWorldCaller + // -------------------------------- + { + bytes input; + + input.insert(input.end(), helloWorldCallerBin.begin(), helloWorldCallerBin.end()); + + bytes constructorParam = codec->encode(aliceAddress); + constructorParam = codec->encode(constructorParam); + input.insert(input.end(), constructorParam.begin(), constructorParam.end()); + + auto tx = fakeTransaction( + cryptoSuite, keyPair, "", input, 102, 100001, "1", "1", helloWorldCallerAbi); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setContextID(200); + params->setSeq(1002); + params->setDepth(0); + params->setOrigin(std::string(sender)); + params->setFrom(std::string(sender)); + params->setTo(bobAddress); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(2); + blockHeader->setParentInfo({{blockHeader->number() - 1, h256(blockHeader->number() - 1)}}); + ledger->setBlockNumber(blockHeader->number() - 1); + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + + std::promise executePromise; + executor->dmcExecuteTransaction( + std::move(params), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + + result->setSeq(1003); + + std::promise executePromise2; + executor->dmcExecuteTransaction( + std::move(result), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + + auto result2 = executePromise2.get_future().get(); + result2->setSeq(1002); + + std::promise executePromise3; + executor->dmcExecuteTransaction( + std::move(result2), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + + auto result3 = executePromise3.get_future().get(); + + auto address = result3->newEVMContractAddress(); + BOOST_CHECK_EQUAL(result3->type(), NativeExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(result3->status(), 0); + BOOST_CHECK_EQUAL(address, bobAddress); + } + + // -------------------------------- + // HelloWorldCaller calls `set` of HelloWorld + // -------------------------------- + { + auto params = std::make_unique(); + params->setContextID(300); + params->setSeq(1003); + params->setDepth(0); + params->setFrom(std::string(sender)); + params->setTo(std::string(bobAddress)); + params->setOrigin(std::string(sender)); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setCreate(false); + + bytes data; + auto encodedParams = codec->encodeWithSig("set(string)", string("fisco bcos")); + data.insert(data.end(), encodedParams.begin(), encodedParams.end()); + + params->setData(data); + params->setType(NativeExecutionMessage::MESSAGE); + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(3); + blockHeader->setParentInfo({{blockHeader->number() - 1, h256(blockHeader->number() - 1)}}); + ledger->setBlockNumber(blockHeader->number() - 1); + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params), + [&](bcos::Error::UniquePtr&& error, NativeExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + auto result = executePromise.get_future().get(); + + BOOST_CHECK(result); + BOOST_CHECK_EQUAL(result->type(), ExecutionMessage::MESSAGE); + BOOST_CHECK_EQUAL(result->data().size(), 15); + BOOST_CHECK_EQUAL(result->contextID(), 300); + BOOST_CHECK_EQUAL(result->seq(), 1003); + BOOST_CHECK_EQUAL(result->create(), false); + BOOST_CHECK_EQUAL(result->newEVMContractAddress(), ""); + BOOST_CHECK_EQUAL(result->origin(), std::string(sender)); + BOOST_CHECK_EQUAL(result->from(), std::string(bobAddress)); + BOOST_CHECK_EQUAL(result->to(), aliceAddress); + BOOST_CHECK_LT(result->gasAvailable(), gas); + BOOST_CHECK_GT(result->keyLocks().size(), 0); + + result->setSeq(1004); + + // clear the keylock + result->setKeyLocks({}); + + std::promise executePromise2; + executor->dmcExecuteTransaction( + std::move(result), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + BOOST_CHECK(result2); + BOOST_CHECK_EQUAL(result2->type(), ExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(result2->data().size(), 0); + BOOST_CHECK_EQUAL(result2->contextID(), 300); + BOOST_CHECK_EQUAL(result2->seq(), 1004); + BOOST_CHECK_EQUAL(result2->origin(), std::string(sender)); + BOOST_CHECK_EQUAL(result2->from(), aliceAddress); + BOOST_CHECK_EQUAL(result2->to(), bobAddress); + BOOST_CHECK_EQUAL(result2->create(), false); + BOOST_CHECK_EQUAL(result2->status(), 0); + } +} + +BOOST_AUTO_TEST_CASE(performance) +{ + size_t count = 10 * 1000; + + bytes input; + input.insert(input.end(), transferBin.begin(), transferBin.end()); + + input.push_back(0); + + string transferAddress = "usr/alice/transfer"; + + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1", transferAbi); + auto sender = boost::algorithm::hex_lower(std::string(tx->sender())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + { + auto params = std::make_unique(); + params->setContextID(99); + params->setSeq(1000); + params->setDepth(0); + params->setOrigin(std::string(sender)); + params->setFrom(std::string(sender)); + params->setTo(transferAddress); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(1); + blockHeader->setParentInfo({{blockHeader->number() - 1, h256(blockHeader->number() - 1)}}); + ledger->setBlockNumber(blockHeader->number() - 1); + std::promise nextPromise; + executor->nextBlockHeader(0, blockHeader, [&](bcos::Error::Ptr&& error) { + BOOST_CHECK(!error); + nextPromise.set_value(); + }); + nextPromise.get_future().get(); + + // -------------------------------- + // Create contract transfer + // -------------------------------- + std::promise executePromise; + executor->dmcExecuteTransaction( + std::move(params), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + + result->setSeq(1001); + + std::promise executePromise2; + executor->dmcExecuteTransaction( + std::move(result), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + + auto result2 = executePromise2.get_future().get(); + result2->setSeq(1000); + + std::promise executePromise3; + executor->dmcExecuteTransaction( + std::move(result2), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + + auto result3 = executePromise3.get_future().get(); + + auto address = result3->newEVMContractAddress(); + + std::vector requests; + requests.reserve(count); + // Transfer + for (size_t i = 0; i < count; ++i) + { + params = std::make_unique(); + params->setContextID(i); + params->setSeq(6000); + params->setDepth(0); + params->setFrom(std::string(sender)); + params->setTo(std::string(address)); + params->setOrigin(std::string(sender)); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setCreate(false); + + std::string from = "alice"; + std::string to = "bob"; + uint32_t amount = 1; + bytes data; + auto encodedParams = + codec->encodeWithSig("transfer(string,string,uint32)", from, to, amount); + data.insert(data.end(), encodedParams.begin(), encodedParams.end()); + params->setData(data); + params->setType(NativeExecutionMessage::MESSAGE); + + requests.emplace_back(std::move(params)); + } + + auto now = std::chrono::system_clock::now(); + + for (auto& it : requests) + { + std::promise> outputPromise; + executor->dmcExecuteTransaction( + std::move(it), [&outputPromise](bcos::Error::UniquePtr&& error, + NativeExecutionMessage::UniquePtr&& result) { + if (error) + { + std::cout << "Error!" << boost::diagnostic_information(*error); + } + // BOOST_CHECK(!error); + outputPromise.set_value(std::move(result)); + }); + ExecutionMessage::UniquePtr result4 = std::move(*outputPromise.get_future().get()); + if (result4->status() != 0) + { + std::cout << "Error: " << result->status() << std::endl; + } + } + + std::cout << "Execute elapsed: " + << std::chrono::duration_cast( + std::chrono::system_clock::now() - now) + .count() + << std::endl; + } + + { + bytes queryBytes; + + auto encodedParams = codec->encodeWithSig("query(string)", string("alice")); + queryBytes.insert(queryBytes.end(), encodedParams.begin(), encodedParams.end()); + + auto params = std::make_unique(); + params->setContextID(102); + params->setSeq(1000); + params->setDepth(0); + params->setFrom(std::string(sender)); + params->setTo(std::string(transferAddress)); + params->setOrigin(std::string(sender)); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(std::move(queryBytes)); + params->setType(ExecutionMessage::MESSAGE); + + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + auto result5 = executePromise.get_future().get(); + + BOOST_CHECK(result5); + BOOST_CHECK_EQUAL(result5->status(), 0); + BOOST_CHECK_EQUAL(result5->message(), ""); + BOOST_CHECK_EQUAL(result5->newEVMContractAddress(), ""); + BOOST_CHECK_LT(result5->gasAvailable(), gas); + + uint32_t dept; + codec->decode(result5->data(), dept); + BOOST_CHECK_EQUAL(dept, numeric_limits::max() - count); + } +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos +// #endif diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TxDAG.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TxDAG.cpp" new file mode 100644 index 00000000..35712d2c --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TxDAG.cpp" @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2021 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. + */ +/** + * @brief : Generate transaction DAG for parallel execution + * @author: jimmyshi + * @date: 2019-1-8 + */ + +#include "TxDAG.h" +#include "bcos-executor/src/dag/CriticalFields.h" +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::executor::critical; + +#define DAG_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("EXECUTOR") + +// Generate DAG according with given transactions +void TxDAG::init(critical::CriticalFieldsInterface::Ptr _txsCriticals, ExecuteTxFunc const& _f) +{ + auto txsSize = _txsCriticals->size(); + DAG_LOG(DEBUG) << LOG_DESC("Begin init transaction DAG") << LOG_KV("transactionNum", txsSize); + + f_executeTx = _f; + m_totalParaTxs = _txsCriticals->size(); + + // init DAG + m_dag.init(txsSize); + + // define conflict handler + auto onConflictHandler = [&](ID pId, ID id) { m_dag.addEdge(pId, id); }; + auto onFirstConflictHandler = [&](ID id) { + // do nothing + (void)id; + }; + auto onEmptyConflictHandler = [&](ID id) { + // do nothing + (void)id; + }; + auto onAllConflictHandler = [&](ID id) { + // do nothing + // ignore normal tx, only handle DAG tx, normal tx has been sent back to be executed by DMT + (void)id; + }; + + // parse criticals + _txsCriticals->traverseDag( + onConflictHandler, onFirstConflictHandler, onEmptyConflictHandler, onAllConflictHandler); + + // Generate DAG + m_dag.generate(); + + DAG_LOG(TRACE) << LOG_DESC("End init transaction DAG"); +} + +void TxDAG::run(unsigned int threadNum) +{ + auto parallelTimeOut = utcSteadyTime() + 30000; // 30 timeout + + std::atomic isWarnedTimeout(false); + tbb::parallel_for(tbb::blocked_range(0, threadNum), + [&](const tbb::blocked_range& _r) { + (void)_r; + + while (!hasFinished()) + { + if (!isWarnedTimeout.load() && utcSteadyTime() >= parallelTimeOut) + { + isWarnedTimeout.store(true); + EXECUTOR_LOG(WARNING) + << LOG_BADGE("executeBlock") << LOG_DESC("Para execute block timeout") + << LOG_KV("txNum", m_totalParaTxs); + } + executeUnit(); + } + }); +} + +int TxDAG::executeUnit() +{ + int exeCnt = 0; + ID id = m_dag.waitPop(); + while (id != INVALID_ID) + { + do + { + exeCnt += 1; + f_executeTx(id); + id = m_dag.consume(id); + } while (id != INVALID_ID); + id = m_dag.waitPop(); + } + if (exeCnt > 0) + { + Guard l(x_exeCnt); + m_exeCnt += exeCnt; + } + return exeCnt; +} diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TxDAG.h" "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TxDAG.h" new file mode 100644 index 00000000..04363390 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libexecutor/TxDAG.h" @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2021 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. + */ +/** + * @brief : Generate transaction DAG for parallel execution + * @author: jimmyshi + * @date: 2019-1-8 + */ + +#pragma once +#include +#include +#include +#include "bcos-executor/src/dag/TxDAGInterface.h" +#include "bcos-executor/src/executive/BlockContext.h" +#include "bcos-executor/src/executive/TransactionExecutive.h" +#include "bcos-executor/src/executor/TransactionExecutor.h" +#include "bcos-framework/protocol/Block.h" +#include "bcos-framework/protocol/Transaction.h" +#include +#include +#include +#include + +namespace bcos +{ +namespace executor +{ +class TransactionExecutive; + +class TxDAG : public virtual TxDAGInterface +{ +public: + TxDAG() : m_dag() {} + virtual ~TxDAG() {} + + // Generate DAG according with given transactions + void init( + critical::CriticalFieldsInterface::Ptr _txsCriticals, ExecuteTxFunc const& _f) override; + + void run(unsigned int threadNum) override; + + // Called by thread + // Has the DAG reach the end? + // process-exit related: + // if the m_stop is true(may be the storage has exceptioned), return true + // directly + bool hasFinished() { return (m_exeCnt >= m_totalParaTxs) || (m_stop.load()); } + + // Called by thread + // Execute a unit in DAG + // This function can be parallel + int executeUnit(); + + ID paraTxsNumber() { return m_totalParaTxs; } + + ID haveExecuteNumber() { return m_exeCnt; } + void stop() { m_stop.store(true); } + +private: + ExecuteTxFunc f_executeTx; + bcos::protocol::TransactionsPtr m_transactions; + DAG m_dag; + + ID m_exeCnt = 0; + ID m_totalParaTxs = 0; + + mutable std::mutex x_exeCnt; + std::atomic_bool m_stop = {false}; +}; + +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/AccountPrecompiledTest.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/AccountPrecompiledTest.cpp" new file mode 100644 index 00000000..96cdc37e --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/AccountPrecompiledTest.cpp" @@ -0,0 +1,875 @@ +/** + * Copyright (C) 2021 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 AccountPrecompiledTest.cpp + * @author: kyonRay + * @date 2021-11-15 + */ + +#include "libprecompiled/PreCompiledFixture.h" +#include "precompiled/extension/AccountManagerPrecompiled.h" +#include +#include + +using namespace bcos; +using namespace bcos::precompiled; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::ledger; +using namespace bcos::crypto; +using namespace bcos::codec; + +namespace bcos::test +{ +class AccountPrecompiledFixture : public PrecompiledFixture +{ +public: + AccountPrecompiledFixture() + { + codec = std::make_shared(hashImpl, false); + helloAddress = Address("0x1234654b49838bd3e9466c85a4cc3428c9601234").hex(); + setIsWasm(false, true); + } + + ~AccountPrecompiledFixture() override = default; + + ExecutionMessage::UniquePtr deployHelloInAuthCheck(std::string newAddress, BlockNumber _number, + Address _address = Address(), bool _errorInFrozen = false) + { + bytes input; + boost::algorithm::unhex(helloBin, std::back_inserter(input)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1"); + if (_address != Address()) + { + tx->forceSender(_address.asBytes()); + } + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + // force cover write + txpool->hash2Transaction[hash] = tx; + auto params = std::make_unique(); + params->setContextID(99); + params->setSeq(1000); + params->setDepth(0); + + params->setOrigin(sender); + params->setFrom(sender); + helloAddress = newAddress; + + // toChecksumAddress(addressString, hashImpl); + params->setTo(newAddress); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + nextBlock(_number, m_blockVersion); + // -------------------------------- + // Create contract + // -------------------------------- + + std::promise executePromise; + executor->dmcExecuteTransaction( + std::move(params), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + + if (_errorInFrozen) + { + commitBlock(_number); + return result; + } + + /// call Auth manager to check deploy auth + result->setSeq(1001); + + std::promise executePromise2; + executor->dmcExecuteTransaction( + std::move(result), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + + auto result2 = executePromise2.get_future().get(); + + /// callback to create context + result2->setSeq(1000); + + std::promise executePromise3; + executor->dmcExecuteTransaction( + std::move(result2), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + + auto result3 = executePromise3.get_future().get(); + + + BOOST_CHECK_EQUAL(result3->type(), ExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(result3->newEVMContractAddress(), newAddress); + BOOST_CHECK_LT(result3->gasAvailable(), gas); + + BOOST_CHECK_EQUAL(result3->contextID(), 99); + BOOST_CHECK_EQUAL(result3->seq(), 1000); + BOOST_CHECK_EQUAL(result3->create(), false); + BOOST_CHECK_EQUAL(result3->origin(), sender); + BOOST_CHECK_EQUAL(result3->from(), newAddress); + BOOST_CHECK(result3->to() == sender); + + commitBlock(_number); + + return result3; + } + + ExecutionMessage::UniquePtr helloGet( + protocol::BlockNumber _number, int _errorCode = 0, Address _address = Address()) + { + nextBlock(_number, m_blockVersion); + bytes in = codec->encodeWithSig("get()"); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + if (_address != Address()) + { + tx->forceSender(_address.asBytes()); + } + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + // force cover write + txpool->hash2Transaction[hash] = tx; + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(_number); + params2->setSeq(1001); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(helloAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode))); + } + commitBlock(_number); + + return result2; + }; + + ExecutionMessage::UniquePtr helloSet(protocol::BlockNumber _number, const std::string& _value, + int _errorCode = 0, Address _address = Address()) + { + nextBlock(_number, m_blockVersion); + bytes in = codec->encodeWithSig("set(string)", _value); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + if (_address != Address()) + { + tx->forceSender(_address.asBytes()); + } + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + // force cover write + txpool->hash2Transaction[hash] = tx; + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(_number); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(helloAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + + return result2; + }; + + ExecutionMessage::UniquePtr setAccountStatus(protocol::BlockNumber _number, Address account, + uint8_t status, int _errorCode = 0, bool exist = false, bool errorInAccountManager = false, + std::string _sender = "1111654b49838bd3e9466c85a4cc3428c9601111") + { + nextBlock(_number, m_blockVersion); + bytes in = codec->encodeWithSig("setAccountStatus(address,uint8)", account, status); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + auto newSender = Address(_sender); + tx->forceSender(newSender.asBytes()); + auto hash = tx->hash(); + txpool->hash2Transaction[hash] = tx; + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(_number); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(precompiled::ACCOUNT_MGR_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + // call account manager + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + result2->setSeq(1001); + result2->takeKeyLocks(); + + // call committee manager to get _committee + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + BOOST_CHECK(result3->status() == 0); + BOOST_CHECK(result3->to() == ACCOUNT_MGR_ADDRESS); + BOOST_CHECK(result3->from() == AUTH_COMMITTEE_ADDRESS); + BOOST_CHECK(result3->type() == ExecutionMessage::FINISHED); + + result3->setSeq(1000); + + /// committee manager call back to account manager + std::promise executePromise4; + executor->dmcExecuteTransaction(std::move(result3), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise4.set_value(std::move(result)); + }); + auto result4 = executePromise4.get_future().get(); + + BOOST_CHECK(result4->status() == 0); + BOOST_CHECK(result4->from() == ACCOUNT_MGR_ADDRESS); + BOOST_CHECK(result4->type() == ExecutionMessage::MESSAGE); + + result4->setSeq(1002); + + /// account manager call to committee, get committee info + std::promise executePromise5; + executor->dmcExecuteTransaction(std::move(result4), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise5.set_value(std::move(result)); + }); + auto result5 = executePromise5.get_future().get(); + + BOOST_CHECK(result5->status() == 0); + BOOST_CHECK(result5->to() == ACCOUNT_MGR_ADDRESS); + BOOST_CHECK(result5->type() == ExecutionMessage::FINISHED); + + result5->setSeq(1000); + + /// committee call back to account manager + std::promise executePromise6; + executor->dmcExecuteTransaction(std::move(result5), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise6.set_value(std::move(result)); + }); + auto result6 = executePromise6.get_future().get(); + + if (errorInAccountManager) + { + if (_errorCode != 0) + { + BOOST_CHECK(result6->data().toBytes() == codec->encode(int32_t(_errorCode))); + } + commitBlock(_number); + return result6; + } + + result6->setSeq(1003); + + // external create + std::promise executePromise7; + executor->dmcExecuteTransaction(std::move(result6), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise7.set_value(std::move(result)); + }); + auto result7 = executePromise7.get_future().get(); + + if (exist) + { + // if account exist, just callback + result7->setSeq(1000); + std::promise executePromise8; + executor->dmcExecuteTransaction(std::move(result7), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise8.set_value(std::move(result)); + }); + auto result8 = executePromise8.get_future().get(); + if (_errorCode != 0) + { + BOOST_CHECK(result8->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result8; + } + + result7->setSeq(1004); + + // external get deploy auth + std::promise executePromise8; + executor->dmcExecuteTransaction(std::move(result7), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise8.set_value(std::move(result)); + }); + auto result8 = executePromise8.get_future().get(); + + result8->setSeq(1003); + + // get deploy auth + std::promise executePromise9; + executor->dmcExecuteTransaction(std::move(result8), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise9.set_value(std::move(result)); + }); + auto result9 = executePromise9.get_future().get(); + + result9->setSeq(1005); + + // external call bfs + std::promise executePromise10; + executor->dmcExecuteTransaction(std::move(result9), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise10.set_value(std::move(result)); + }); + auto result10 = executePromise10.get_future().get(); + + // call bfs success, callback to create + result10->setSeq(1003); + + std::promise executePromise11; + executor->dmcExecuteTransaction(std::move(result10), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise11.set_value(std::move(result)); + }); + auto result11 = executePromise11.get_future().get(); + + // create success, callback to precompiled + result11->setSeq(1000); + + std::promise executePromise12; + executor->dmcExecuteTransaction(std::move(result11), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise12.set_value(std::move(result)); + }); + auto result12 = executePromise12.get_future().get(); + + // external call, set account status + result12->setSeq(1006); + std::promise executePromise13; + executor->dmcExecuteTransaction(std::move(result12), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise13.set_value(std::move(result)); + }); + auto result13 = executePromise13.get_future().get(); + + // external call back + result13->setSeq(1000); + std::promise executePromise14; + executor->dmcExecuteTransaction(std::move(result13), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise14.set_value(std::move(result)); + }); + auto result14 = executePromise14.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result14->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result14; + }; + + ExecutionMessage::UniquePtr getAccountStatusByManager(protocol::BlockNumber _number, + Address account, int _errorCode = 0, bool errorInAccountManager = false, + bool noExist = true) + { + nextBlock(_number, m_blockVersion); + bytes in = codec->encodeWithSig("getAccountStatus(address)", account); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + auto newSender = Address("0000000000000000000000000000000000010001"); + tx->forceSender(newSender.asBytes()); + auto hash = tx->hash(); + txpool->hash2Transaction[hash] = tx; + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(_number); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(precompiled::ACCOUNT_MGR_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + // call precompiled + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + if (errorInAccountManager) + { + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(int32_t(_errorCode))); + } + commitBlock(_number); + return result2; + } + + result2->setSeq(1001); + result2->takeKeyLocks(); + + // external call + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + result3->setSeq(1000); + result3->takeKeyLocks(); + + // external call callback + std::promise executePromise4; + executor->dmcExecuteTransaction(std::move(result3), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise4.set_value(std::move(result)); + }); + auto result4 = executePromise4.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result4->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result4; + }; + + ExecutionMessage::UniquePtr getAccountStatus( + protocol::BlockNumber _number, Address account, int _errorCode = 0) + { + nextBlock(_number, m_blockVersion); + bytes in = codec->encodeWithSig("getAccountStatus()"); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + auto hash = tx->hash(); + txpool->hash2Transaction[hash] = tx; + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(_number); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(account.hex()); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + // call precompiled + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result2; + }; + + std::string sender; + std::string helloAddress; + + // clang-format off + std::string helloBin = + "608060405234801561001057600080fd5b506040518060400160405280600d81526020017f48656c6c6f2c20576f726c6421000000000000000000000000000000000000008152506000908051906020019061005c929190610062565b50610107565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100a357805160ff19168380011785556100d1565b828001600101855582156100d1579182015b828111156100d05782518255916020019190600101906100b5565b5b5090506100de91906100e2565b5090565b61010491905b808211156101005760008160009055506001016100e8565b5090565b90565b610310806101166000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80634ed3885e1461003b5780636d4ce63c146100f6575b600080fd5b6100f46004803603602081101561005157600080fd5b810190808035906020019064010000000081111561006e57600080fd5b82018360208201111561008057600080fd5b803590602001918460018302840111640100000000831117156100a257600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610179565b005b6100fe610193565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561013e578082015181840152602081019050610123565b50505050905090810190601f16801561016b5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b806000908051906020019061018f929190610235565b5050565b606060008054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561022b5780601f106102005761010080835404028352916020019161022b565b820191906000526020600020905b81548152906001019060200180831161020e57829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061027657805160ff19168380011785556102a4565b828001600101855582156102a4579182015b828111156102a3578251825591602001919060010190610288565b5b5090506102b191906102b5565b5090565b6102d791905b808211156102d35760008160009055506001016102bb565b5090565b9056fea2646970667358221220bf4a4547462412a2d27d205b50ba5d4dba42f506f9ea3628eb3d0299c9c28d5664736f6c634300060a0033"; + // clang-format on +}; + +BOOST_FIXTURE_TEST_SUITE(AccountPrecompiledTest, AccountPrecompiledFixture) + +BOOST_AUTO_TEST_CASE(createAccountTest) +{ + Address newAccount = Address("27505f128bd4d00c2698441b1f54ef843b837215"); + Address errorAccount = Address("17505f128bd4d00c2698441b1f54ef843b837211"); + BlockNumber number = 2; + { + auto response = setAccountStatus(number++, newAccount, 0); + BOOST_CHECK(response->status() == 0); + int32_t result = -1; + codec->decode(response->data(), result); + BOOST_CHECK(result == 0); + } + + // check create + { + auto response2 = list(number++, "/usr"); + int32_t ret; + std::vector bfsInfos; + codec->decode(response2->data(), ret, bfsInfos); + BOOST_CHECK(ret == 0); + BOOST_CHECK(bfsInfos.size() == 1); + auto fileInfo = bfsInfos[0]; + BOOST_CHECK(std::get<0>(fileInfo) == "27505f128bd4d00c2698441b1f54ef843b837215"); + BOOST_CHECK(std::get<1>(fileInfo) == bcos::executor::FS_TYPE_LINK); + + auto response3 = list(number++, "/usr/27505f128bd4d00c2698441b1f54ef843b837215"); + int32_t ret2; + std::vector bfsInfos2; + codec->decode(response3->data(), ret2, bfsInfos2); + BOOST_CHECK(ret2 == 0); + BOOST_CHECK(bfsInfos2.size() == 1); + BOOST_CHECK(std::get<0>(bfsInfos2[0]) == "27505f128bd4d00c2698441b1f54ef843b837215"); + } + + // setAccount exist again + { + setAccountStatus(number++, newAccount, 0, 0, true); + } + + // check status is 0 + { + auto response = getAccountStatusByManager(number++, newAccount); + BOOST_CHECK(response->status() == 0); + uint8_t status = UINT8_MAX; + codec->decode(response->data(), status); + BOOST_CHECK(status == 0); + + // not exist account in chain, return 0 by default + auto response2 = getAccountStatusByManager(number++, errorAccount); + BOOST_CHECK(response2->status() == 0); + uint8_t status2 = UINT8_MAX; + codec->decode(response2->data(), status2); + BOOST_CHECK(status2 == 0); + } + + // check status by account contract + { + auto response = getAccountStatus(number++, newAccount); + BOOST_CHECK(response->status() == 0); + uint8_t status = UINT8_MAX; + codec->decode(response->data(), status); + BOOST_CHECK(status == 0); + auto response2 = getAccountStatus(number++, errorAccount); + BOOST_CHECK(response2->status() == (int32_t)TransactionStatus::CallAddressError); + } +} + +BOOST_AUTO_TEST_CASE(setAccountStatusTest) +{ + Address newAccount = Address("27505f128bd4d00c2698441b1f54ef843b837215"); + Address errorAccount = Address("17505f128bd4d00c2698441b1f54ef843b837211"); + Address h1 = Address("12305f128bd4d00c2698441b1f54ef843b837123"); + BlockNumber number = 2; + + // setAccountStatus account not exist + { + auto response = setAccountStatus(number++, newAccount, 0); + BOOST_CHECK(response->status() == 0); + int32_t result = -1; + codec->decode(response->data(), result); + BOOST_CHECK(result == 0); + } + + // use account to deploy + { + auto response1 = deployHelloInAuthCheck(helloAddress, number++, newAccount); + BOOST_CHECK(response1->status() == 0); + auto response2 = helloSet(number++, "test1", 0, newAccount); + BOOST_CHECK(response2->status() == 0); + auto response3 = helloGet(number++, 0, newAccount); + std::string hello; + codec->decode(response3->data(), hello); + BOOST_CHECK(hello == "test1"); + } + + // setAccountStatus account exist + { + auto response = setAccountStatus(number++, newAccount, 1, 0, true); + BOOST_CHECK(response->status() == 0); + int32_t result = -1; + codec->decode(response->data(), result); + BOOST_CHECK(result == 0); + + auto rsp2 = getAccountStatus(number++, newAccount); + BOOST_CHECK(rsp2->status() == 0); + uint8_t status = UINT8_MAX; + codec->decode(rsp2->data(), status); + BOOST_CHECK(status == 1); + } + + // use freeze account to use + { + auto response1 = deployHelloInAuthCheck(h1.hex(), number++, newAccount, true); + BOOST_CHECK(response1->status() == (uint32_t)TransactionStatus::AccountFrozen); + auto response2 = helloSet(number++, "test2", 0, newAccount); + BOOST_CHECK(response2->status() == (uint32_t)TransactionStatus::AccountFrozen); + + auto response3 = helloGet(number++, 0); + BOOST_CHECK(response3->status() == (uint32_t)TransactionStatus::CallAddressError); + } + + // use error account to setAccountStatus + { + auto response = + setAccountStatus(number++, newAccount, 1, 0, true, true, errorAccount.hex()); + int32_t result = -1; + codec->decode(response->data(), result); + BOOST_CHECK(result == CODE_NO_AUTHORIZED); + } +} + +BOOST_AUTO_TEST_CASE(setAccountStatusErrorTest) +{ + Address newAccount = Address("27505f128bd4d00c2698441b1f54ef843b837215"); + Address errorAccount = Address("17505f128bd4d00c2698441b1f54ef843b837211"); + BlockNumber number = 2; + + // setAccountStatus account not exist + { + auto response = setAccountStatus(number++, newAccount, 0); + BOOST_CHECK(response->status() == 0); + int32_t result = -1; + codec->decode(response->data(), result); + BOOST_CHECK(result == 0); + } + + // use not governor account set + { + auto response = setAccountStatus( + number++, newAccount, 1, CODE_NO_AUTHORIZED, false, true, errorAccount.hex()); + BOOST_CHECK(response->status() == 0); + } + + // set governor account status + { + auto response = setAccountStatus(number++, Address(admin), 1, 0, false, true); + BOOST_CHECK(response->status() == 15); + BOOST_CHECK(response->message() == "Should not set governor's status."); + } +} + +BOOST_AUTO_TEST_CASE(abolishTest) +{ + Address newAccount = Address("27505f128bd4d00c2698441b1f54ef843b837215"); + Address h1 = Address("12305f128bd4d00c2698441b1f54ef843b837123"); + BlockNumber number = 2; + + // setAccountStatus account not exist + { + auto response = setAccountStatus(number++, newAccount, 0); + BOOST_CHECK(response->status() == 0); + int32_t result = -1; + codec->decode(response->data(), result); + BOOST_CHECK(result == 0); + } + + // use account to deploy + { + auto response1 = deployHelloInAuthCheck(helloAddress, number++, newAccount); + BOOST_CHECK(response1->status() == 0); + auto response2 = helloSet(number++, "test1", 0, newAccount); + BOOST_CHECK(response2->status() == 0); + auto response3 = helloGet(number++, 0, newAccount); + std::string hello; + codec->decode(response3->data(), hello); + BOOST_CHECK(hello == "test1"); + } + + // setAccountStatus account exist, abolish account + { + auto response = setAccountStatus(number++, newAccount, 2, 0, true); + BOOST_CHECK(response->status() == 0); + int32_t result = -1; + codec->decode(response->data(), result); + BOOST_CHECK(result == 0); + + auto rsp2 = getAccountStatus(number++, newAccount); + BOOST_CHECK(rsp2->status() == 0); + uint8_t status = UINT8_MAX; + codec->decode(rsp2->data(), status); + BOOST_CHECK(status == 2); + } + + // use abolish account to use + { + auto response1 = deployHelloInAuthCheck(h1.hex(), number++, newAccount, true); + BOOST_CHECK(response1->status() == (uint32_t)TransactionStatus::AccountAbolished); + auto response2 = helloSet(number++, "test2", 0, newAccount); + BOOST_CHECK(response2->status() == (uint32_t)TransactionStatus::AccountAbolished); + } + + // freeze/unfreeze abolish account status + { + auto response = setAccountStatus(number++, newAccount, 1, 0, true); + BOOST_CHECK(response->status() == (uint32_t)TransactionStatus::PrecompiledError); + + auto response2 = setAccountStatus(number++, newAccount, 0, 0, true); + BOOST_CHECK(response2->status() == (uint32_t)TransactionStatus::PrecompiledError); + } + + // abolish account again, success + { + auto response = setAccountStatus(number++, newAccount, 2, 0, true); + BOOST_CHECK(response->status() == 0); + int32_t result = -1; + codec->decode(response->data(), result); + BOOST_CHECK(result == 0); + + auto rsp2 = getAccountStatus(number++, newAccount); + BOOST_CHECK(rsp2->status() == 0); + uint8_t status = UINT8_MAX; + codec->decode(rsp2->data(), status); + BOOST_CHECK(status == 2); + } +} + +BOOST_AUTO_TEST_CASE(parallelSetTest) +{ + Address newAccount = Address("27505f128bd4d00c2698441b1f54ef843b837215"); + BlockNumber number = 2; + + // setAccountStatus account not exist + { + auto response = setAccountStatus(number++, newAccount, 0); + BOOST_CHECK(response->status() == 0); + int32_t result = -1; + codec->decode(response->data(), result); + BOOST_CHECK(result == 0); + } + + // use account to deploy + { + auto response1 = deployHelloInAuthCheck(helloAddress, number++, newAccount); + BOOST_CHECK(response1->status() == 0); + auto response2 = helloSet(number++, "test1", 0, newAccount); + BOOST_CHECK(response2->status() == 0); + auto response3 = helloGet(number++, 0, newAccount); + std::string hello; + codec->decode(response3->data(), hello); + BOOST_CHECK(hello == "test1"); + } + + // setAccountStatus account exist, freeze account, in the same block, still can call + { + auto num = number++; + nextBlock(num); + auto response = setAccountStatus(-1, newAccount, 1, 0, true); + BOOST_CHECK(response->status() == 0); + int32_t result = -1; + codec->decode(response->data(), result); + BOOST_CHECK(result == 0); + + // get last status + auto rsp2 = getAccountStatus(-1, newAccount); + BOOST_CHECK(rsp2->status() == 0); + uint8_t status = UINT8_MAX; + codec->decode(rsp2->data(), status); + BOOST_CHECK(status == 0); + + auto response2 = helloSet(-1, "test2", 0, newAccount); + BOOST_CHECK(response2->status() == 0); + auto response3 = helloGet(-1, 0, newAccount); + std::string hello; + codec->decode(response3->data(), hello); + BOOST_CHECK(hello == "test2"); + commitBlock(num); + } + + // next block will be frozen + { + auto response2 = helloSet(number++, "test2", 0, newAccount); + BOOST_CHECK(response2->status() == (uint32_t)TransactionStatus::AccountFrozen); + } +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/AuthPrecompiledTest.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/AuthPrecompiledTest.cpp" new file mode 100644 index 00000000..879cccd7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/AuthPrecompiledTest.cpp" @@ -0,0 +1,1893 @@ +/** + * Copyright (C) 2021 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 AuthPrecompiledTest.cpp + * @author: kyonRay + * @date 2021-11-15 + */ + +#include "libprecompiled/PreCompiledFixture.h" +#include "precompiled/extension/AuthManagerPrecompiled.h" +#include "precompiled/extension/ContractAuthMgrPrecompiled.h" +#include +#include +#include + +using namespace bcos; +using namespace bcos::precompiled; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::ledger; +using namespace bcos::crypto; +using namespace bcos::codec; + +namespace bcos::test +{ +class PermissionPrecompiledFixture : public PrecompiledFixture +{ +public: + PermissionPrecompiledFixture() + { + codec = std::make_shared(hashImpl, false); + setIsWasm(false, true); + helloAddress = Address("0x1234654b49838bd3e9466c85a4cc3428c9601234").hex(); + hello2Address = Address("0x0987654b49838bd3e9466c85a4cc3428c9601234").hex(); + } + + ~PermissionPrecompiledFixture() override {} + + void deployHello() + { + bytes input; + boost::algorithm::unhex(helloBin, std::back_inserter(input)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setContextID(99); + params->setSeq(1000); + params->setDepth(0); + + params->setOrigin(sender); + params->setFrom(sender); + + params->setTo(helloAddress); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + nextBlock(2); + // -------------------------------- + // Create contract + // -------------------------------- + + std::promise executePromise; + executor->dmcExecuteTransaction( + std::move(params), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + + /// call Auth manager to check deploy auth + result->setSeq(1001); + + std::promise executePromise2; + executor->dmcExecuteTransaction( + std::move(result), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + + auto result2 = executePromise2.get_future().get(); + + /// callback to create context + result2->setSeq(1000); + + std::promise executePromise3; + executor->dmcExecuteTransaction( + std::move(result2), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + + auto result3 = executePromise3.get_future().get(); + + BOOST_CHECK_EQUAL(result3->type(), ExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(result3->contextID(), 99); + BOOST_CHECK_EQUAL(result3->seq(), 1000); + BOOST_CHECK_EQUAL(result3->create(), false); + BOOST_CHECK_EQUAL(result3->newEVMContractAddress(), helloAddress); + BOOST_CHECK_EQUAL(result3->origin(), sender); + BOOST_CHECK_EQUAL(result3->from(), helloAddress); + BOOST_CHECK(result3->to() == sender); + BOOST_CHECK_LT(result3->gasAvailable(), gas); + + commitBlock(2); + } + + ExecutionMessage::UniquePtr deployHelloInAuthCheck(std::string newAddress, BlockNumber _number, + Address _address = Address(), bool _noAuth = false) + { + bytes input; + boost::algorithm::unhex(helloBin, std::back_inserter(input)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1"); + if (_address != Address()) + { + tx->forceSender(_address.asBytes()); + } + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + // force cover write + txpool->hash2Transaction[hash] = tx; + auto params = std::make_unique(); + params->setContextID(99); + params->setSeq(1000); + params->setDepth(0); + + params->setOrigin(sender); + params->setFrom(sender); + + // toChecksumAddress(addressString, hashImpl); + params->setTo(newAddress); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + nextBlock(_number); + // -------------------------------- + // Create contract + // -------------------------------- + + std::promise executePromise; + executor->dmcExecuteTransaction( + std::move(params), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + + /// call Auth manager to check deploy auth + result->setSeq(1001); + + std::promise executePromise2; + executor->dmcExecuteTransaction( + std::move(result), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + + auto result2 = executePromise2.get_future().get(); + + /// callback to create context + result2->setSeq(1000); + + std::promise executePromise3; + executor->dmcExecuteTransaction( + std::move(result2), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + + auto result3 = executePromise3.get_future().get(); + + if (_noAuth) + { + BOOST_CHECK_EQUAL(result3->type(), ExecutionMessage::REVERT); + BOOST_CHECK_EQUAL(result3->newEVMContractAddress(), ""); + } + else + { + BOOST_CHECK_EQUAL(result3->type(), ExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(result3->newEVMContractAddress(), newAddress); + BOOST_CHECK_LT(result3->gasAvailable(), gas); + } + BOOST_CHECK_EQUAL(result3->contextID(), 99); + BOOST_CHECK_EQUAL(result3->seq(), 1000); + BOOST_CHECK_EQUAL(result3->create(), false); + BOOST_CHECK_EQUAL(result3->origin(), sender); + BOOST_CHECK_EQUAL(result3->from(), newAddress); + BOOST_CHECK(result3->to() == sender); + + commitBlock(_number); + + return result3; + } + + ExecutionMessage::UniquePtr deployHello2InAuthCheck( + std::string newAddress, BlockNumber _number, Address _address = Address()) + { + bytes input; + boost::algorithm::unhex(h2, std::back_inserter(input)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1"); + if (_address != Address()) + { + tx->forceSender(_address.asBytes()); + } + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + // force cover write + txpool->hash2Transaction[hash] = tx; + auto params = std::make_unique(); + params->setContextID(99); + params->setSeq(1000); + params->setDepth(0); + + params->setOrigin(sender); + params->setFrom(sender); + + // toChecksumAddress(addressString, hashImpl); + params->setTo(newAddress); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + nextBlock(_number); + // -------------------------------- + // Create contract + // -------------------------------- + + std::promise executePromise; + executor->dmcExecuteTransaction( + std::move(params), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + + /// call Auth manager to check deploy auth + result->setSeq(1001); + + std::promise executePromise2; + executor->dmcExecuteTransaction( + std::move(result), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + + auto result2 = executePromise2.get_future().get(); + + /// callback to create context + result2->setSeq(1000); + + std::promise executePromise3; + executor->dmcExecuteTransaction( + std::move(result2), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + + auto result3 = executePromise3.get_future().get(); + + result3->setSeq(1002); + result3->setType(NativeExecutionMessage::MESSAGE); + result3->setTransactionHash(hash); + result3->setKeyLocks({}); + result3->setNewEVMContractAddress(hello2Address); + result3->setTo(hello2Address); + + /// create new hello + std::promise executePromise4; + executor->dmcExecuteTransaction( + std::move(result3), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise4.set_value(std::move(result)); + }); + + auto result4 = executePromise4.get_future().get(); + + result4->setSeq(1003); + /// call to check deploy auth + std::promise executePromise5; + executor->dmcExecuteTransaction( + std::move(result4), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise5.set_value(std::move(result)); + }); + + auto result5 = executePromise5.get_future().get(); + + result5->setSeq(1002); + /// callback to deploy hello2 context + std::promise executePromise6; + executor->dmcExecuteTransaction( + std::move(result5), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise6.set_value(std::move(result)); + }); + + auto result6 = executePromise6.get_future().get(); + + commitBlock(_number); + + return result6; + } + + ExecutionMessage::UniquePtr helloGet(protocol::BlockNumber _number, int _contextId, + int _errorCode = 0, Address _address = Address()) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("get()"); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + if (_address != Address()) + { + tx->forceSender(_address.asBytes()); + } + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + // force cover write + txpool->hash2Transaction[hash] = tx; + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(_contextId); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(helloAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode))); + } + commitBlock(_number); + + return result2; + }; + + ExecutionMessage::UniquePtr helloSet(protocol::BlockNumber _number, int _contextId, + const std::string& _value, int _errorCode = 0, Address _address = Address()) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("set(string)", _value); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + if (_address != Address()) + { + tx->forceSender(_address.asBytes()); + } + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + // force cover write + txpool->hash2Transaction[hash] = tx; + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(_contextId); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(helloAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + + return result2; + }; + + ExecutionMessage::UniquePtr setMethodType(protocol::BlockNumber _number, int _contextId, + Address const& _path, std::string const& helloMethod, precompiled::AuthType _type, + int _errorCode = 0) + { + nextBlock(_number); + bytes func = codec->encodeWithSig(helloMethod); + auto fun = toString32(h256(func, FixedBytes<32>::AlignLeft)); + uint8_t type = (_type == AuthType::WHITE_LIST_MODE) ? 1 : 2; + auto t = toString32(h256(type)); + bytes in = codec->encodeWithSig("setMethodAuthType(address,bytes4,uint8)", _path, fun, t); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params1 = std::make_unique(); + params1->setTransactionHash(hash); + params1->setContextID(_contextId); + params1->setSeq(1000); + params1->setDepth(0); + params1->setFrom(sender); + params1->setTo(precompiled::AUTH_MANAGER_ADDRESS); + params1->setOrigin(sender); + params1->setStaticCall(false); + params1->setGasAvailable(gas); + params1->setData(std::move(in)); + params1->setType(NativeExecutionMessage::TXHASH); + + /// call precompiled + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params1), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + auto result = executePromise.get_future().get(); + + result->setSeq(1001); + + /// internal call get admin + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(result), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + /// callback to precompiled + result2->setSeq(1000); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + result3->setSeq(1002); + + /// internal call set contract auth type + std::promise executePromise4; + executor->dmcExecuteTransaction(std::move(result3), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise4.set_value(std::move(result)); + }); + auto result4 = executePromise4.get_future().get(); + + result4->setSeq(1000); + + /// callback to precompiled + std::promise executePromise5; + executor->dmcExecuteTransaction(std::move(result4), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise5.set_value(std::move(result)); + }); + auto result5 = executePromise5.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result5->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result5; + }; + + ExecutionMessage::UniquePtr modifyMethodAuth(protocol::BlockNumber _number, int _contextId, + std::string const& authMethod, Address const& _path, std::string const& helloMethod, + Address const& _account, int _errorCode = 0) + { + nextBlock(_number); + bytes fun = codec->encodeWithSig(helloMethod); + auto func = toString32(h256(fun, FixedBytes<32>::AlignLeft)); + bytes in = codec->encodeWithSig(authMethod, _path, func, _account); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params1 = std::make_unique(); + params1->setTransactionHash(hash); + params1->setContextID(_contextId); + params1->setSeq(1000); + params1->setDepth(0); + params1->setFrom(sender); + params1->setTo(precompiled::AUTH_MANAGER_ADDRESS); + params1->setOrigin(sender); + params1->setStaticCall(false); + params1->setGasAvailable(gas); + params1->setData(std::move(in)); + params1->setType(NativeExecutionMessage::TXHASH); + + /// call precompiled + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params1), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + auto result = executePromise.get_future().get(); + + result->setSeq(1001); + + /// internal call get admin + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(result), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + /// callback to precompiled + result2->setSeq(1000); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + result3->setSeq(1002); + + /// internal call set contract auth type + std::promise executePromise4; + executor->dmcExecuteTransaction(std::move(result3), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise4.set_value(std::move(result)); + }); + auto result4 = executePromise4.get_future().get(); + + result4->setSeq(1000); + + /// callback to precompiled + std::promise executePromise5; + executor->dmcExecuteTransaction(std::move(result4), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise5.set_value(std::move(result)); + }); + auto result5 = executePromise5.get_future().get(); + + // call precompiled + if (_errorCode != 0) + { + BOOST_CHECK(result5->data().toBytes() == codec->encode(s256(_errorCode))); + } + commitBlock(_number); + + return result5; + }; + + ExecutionMessage::UniquePtr getMethodAuth(protocol::BlockNumber _number, Address const& _path, + std::string const& helloMethod, int _errorCode = 0) + { + nextBlock(_number); + bytes fun = codec->encodeWithSig(helloMethod); + auto func = toString32(h256(fun, FixedBytes<32>::AlignLeft)); + bytes in = codec->encodeWithSig("getMethodAuth(address,bytes4)", _path, func); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + auto hash = tx->hash(); + txpool->hash2Transaction[hash] = tx; + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto params1 = std::make_unique(); + params1->setTransactionHash(hash); + params1->setContextID(1000); + params1->setSeq(1000); + params1->setDepth(0); + params1->setFrom(sender); + params1->setTo(precompiled::AUTH_MANAGER_ADDRESS); + params1->setOrigin(sender); + params1->setStaticCall(false); + params1->setGasAvailable(gas); + params1->setData(std::move(in)); + params1->setType(NativeExecutionMessage::TXHASH); + + /// call precompiled + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params1), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + auto result1 = executePromise.get_future().get(); + + result1->setSeq(1001); + + /// internal call + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(result1), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + /// internal callback + result2->setSeq(1000); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result3->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result3; + }; + + ExecutionMessage::UniquePtr checkMethodAuth(protocol::BlockNumber _number, Address const& _path, + std::string const& helloMethod, Address const& _account, int _errorCode = 0) + { + nextBlock(_number); + bytes fun = codec->encodeWithSig(helloMethod); + auto func = toString32(h256(fun, FixedBytes<32>::AlignLeft)); + bytes in = codec->encodeWithSig("checkMethodAuth(address,bytes4,address)", _path, func); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + auto hash = tx->hash(); + txpool->hash2Transaction[hash] = tx; + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto params1 = std::make_unique(); + params1->setTransactionHash(hash); + params1->setContextID(1000); + params1->setSeq(1000); + params1->setDepth(0); + params1->setFrom(sender); + params1->setTo(precompiled::AUTH_MANAGER_ADDRESS); + params1->setOrigin(sender); + params1->setStaticCall(false); + params1->setGasAvailable(gas); + params1->setData(std::move(in)); + params1->setType(NativeExecutionMessage::TXHASH); + + /// call precompiled + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params1), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + auto result1 = executePromise.get_future().get(); + + result1->setSeq(1001); + + /// internal call + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(result1), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + /// internal callback + result2->setSeq(1000); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result3->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result3; + }; + + ExecutionMessage::UniquePtr resetAdmin(protocol::BlockNumber _number, int _contextId, + Address const& _path, Address const& _newAdmin, int _errorCode = 0, + bool _useWrongSender = false) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("resetAdmin(address,address)", _path, _newAdmin); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + auto newSender = Address("0000000000000000000000000000000000010001"); + auto wrongSender = Address("0000000000000000000000000000000000011111"); + tx->forceSender(_useWrongSender ? wrongSender.asBytes() : newSender.asBytes()); + auto hash = tx->hash(); + txpool->hash2Transaction[hash] = tx; + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto params1 = std::make_unique(); + params1->setTransactionHash(hash); + params1->setContextID(_contextId); + params1->setSeq(1000); + params1->setDepth(0); + params1->setFrom(sender); + params1->setTo(precompiled::AUTH_MANAGER_ADDRESS); + params1->setOrigin(sender); + params1->setStaticCall(false); + params1->setGasAvailable(gas); + params1->setData(std::move(in)); + params1->setType(NativeExecutionMessage::TXHASH); + + /// call precompiled + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params1), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + auto result1 = executePromise.get_future().get(); + + if (_useWrongSender) + { + commitBlock(_number); + return result1; + } + + result1->setSeq(1001); + + /// internal call + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(result1), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + result2->setSeq(1000); + /// internal callback + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result3->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result3; + }; + + ExecutionMessage::UniquePtr setContractStatus( + protocol::BlockNumber _number, Address const& _path, bool _isFreeze, int _errorCode = 0) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("setContractStatus(address,bool)", _path, _isFreeze); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params1 = std::make_unique(); + params1->setTransactionHash(hash); + params1->setContextID(1000); + params1->setSeq(1000); + params1->setDepth(0); + params1->setFrom(sender); + params1->setTo(precompiled::AUTH_MANAGER_ADDRESS); + params1->setOrigin(sender); + params1->setStaticCall(false); + params1->setGasAvailable(gas); + params1->setData(std::move(in)); + params1->setType(NativeExecutionMessage::TXHASH); + + /// call precompiled + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params1), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + auto result = executePromise.get_future().get(); + + result->setSeq(1001); + + /// internal call get admin + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(result), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + /// callback to precompiled + result2->setSeq(1000); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + result3->setSeq(1002); + + /// internal call set contract status + std::promise executePromise4; + executor->dmcExecuteTransaction(std::move(result3), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise4.set_value(std::move(result)); + }); + auto result4 = executePromise4.get_future().get(); + + result4->setSeq(1000); + + /// callback to precompiled + std::promise executePromise5; + executor->dmcExecuteTransaction(std::move(result4), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise5.set_value(std::move(result)); + }); + auto result5 = executePromise5.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result5->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result5; + }; + + ExecutionMessage::UniquePtr contractAvailable( + protocol::BlockNumber _number, Address const& _path, int _errorCode = 0) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("contractAvailable(address)", _path); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params1 = std::make_unique(); + params1->setTransactionHash(hash); + params1->setContextID(1000); + params1->setSeq(1000); + params1->setDepth(0); + params1->setFrom(sender); + params1->setTo(precompiled::AUTH_MANAGER_ADDRESS); + params1->setOrigin(sender); + params1->setStaticCall(false); + params1->setGasAvailable(gas); + params1->setData(std::move(in)); + params1->setType(NativeExecutionMessage::TXHASH); + + /// call precompiled + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params1), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + auto result = executePromise.get_future().get(); + + result->setSeq(1001); + + /// internal call get contract status + std::promise executePromise4; + executor->dmcExecuteTransaction(std::move(result), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise4.set_value(std::move(result)); + }); + auto result4 = executePromise4.get_future().get(); + + result4->setSeq(1000); + + /// callback to precompiled + std::promise executePromise5; + executor->dmcExecuteTransaction(std::move(result4), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise5.set_value(std::move(result)); + }); + auto result5 = executePromise5.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result5->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result5; + }; + + ExecutionMessage::UniquePtr getAdmin( + protocol::BlockNumber _number, int _contextId, Address const& _path, int _errorCode = 0) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("getAdmin(address)", _path); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + auto newSender = Address("0000000000000000000000000000000000010001"); + tx->forceSender(newSender.asBytes()); + auto hash = tx->hash(); + txpool->hash2Transaction[hash] = tx; + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto params1 = std::make_unique(); + params1->setTransactionHash(hash); + params1->setContextID(_contextId); + params1->setSeq(1000); + params1->setDepth(0); + params1->setFrom(sender); + params1->setTo(precompiled::AUTH_MANAGER_ADDRESS); + params1->setOrigin(sender); + params1->setStaticCall(false); + params1->setGasAvailable(gas); + params1->setData(std::move(in)); + params1->setType(NativeExecutionMessage::TXHASH); + + /// call precompiled + std::promise executePromise; + executor->dmcExecuteTransaction(std::move(params1), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + auto result1 = executePromise.get_future().get(); + + result1->setSeq(1001); + + /// internal call + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(result1), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + /// internal callback + result2->setSeq(1000); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result3->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result3; + }; + + ExecutionMessage::UniquePtr setDeployType( + protocol::BlockNumber _number, int _contextId, AuthType _authType, int _errorCode = 0) + { + nextBlock(_number); + uint8_t type = (_authType == AuthType::WHITE_LIST_MODE) ? 1 : 2; + auto t = toString32(h256(type)); + bytes in = codec->encodeWithSig("setDeployAuthType(uint8)", t); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + auto newSender = Address("0000000000000000000000000000000000010001"); + tx->forceSender(newSender.asBytes()); + auto hash = tx->hash(); + txpool->hash2Transaction[hash] = tx; + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(_contextId); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(precompiled::AUTH_MANAGER_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + // call precompiled + result2->setSeq(1001); + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result2; + }; + + ExecutionMessage::UniquePtr getDeployType( + protocol::BlockNumber _number, int _contextId, int _errorCode = 0) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("deployType()"); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + auto newSender = Address("0000000000000000000000000000000000010001"); + tx->forceSender(newSender.asBytes()); + auto hash = tx->hash(); + txpool->hash2Transaction[hash] = tx; + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(_contextId); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(precompiled::AUTH_MANAGER_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + // call precompiled + result2->setSeq(1001); + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result2; + }; + + ExecutionMessage::UniquePtr openDeployAuth( + protocol::BlockNumber _number, int _contextId, Address const& _address, int _errorCode = 0) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("openDeployAuth(address)", _address); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + auto newSender = Address("0000000000000000000000000000000000010001"); + tx->forceSender(newSender.asBytes()); + auto hash = tx->hash(); + txpool->hash2Transaction[hash] = tx; + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(_contextId); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(precompiled::AUTH_MANAGER_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + // call precompiled + result2->setSeq(1001); + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result2; + }; + + ExecutionMessage::UniquePtr closeDeployAuth( + protocol::BlockNumber _number, int _contextId, Address const& _address, int _errorCode = 0) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("closeDeployAuth(address)", _address); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + auto newSender = Address("0000000000000000000000000000000000010001"); + tx->forceSender(newSender.asBytes()); + auto hash = tx->hash(); + txpool->hash2Transaction[hash] = tx; + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(_contextId); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(precompiled::AUTH_MANAGER_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + // call precompiled + result2->setSeq(1001); + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result2; + }; + + ExecutionMessage::UniquePtr hasDeployAuth( + protocol::BlockNumber _number, int _contextId, Address const& _address, int _errorCode = 0) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("hasDeployAuth(address)", _address); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + auto newSender = Address("0000000000000000000000000000000000010001"); + tx->forceSender(newSender.asBytes()); + auto hash = tx->hash(); + txpool->hash2Transaction[hash] = tx; + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(_contextId); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(precompiled::AUTH_MANAGER_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + // call precompiled + result2->setSeq(1001); + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result2; + }; + + + std::string sender; + std::string helloAddress; + std::string hello2Address; + + // clang-format off + std::string helloBin = + "608060405234801561001057600080fd5b506040518060400160405280600d81526020017f48656c6c6f2c20576f726c6421000000000000000000000000000000000000008152506000908051906020019061005c929190610062565b50610107565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100a357805160ff19168380011785556100d1565b828001600101855582156100d1579182015b828111156100d05782518255916020019190600101906100b5565b5b5090506100de91906100e2565b5090565b61010491905b808211156101005760008160009055506001016100e8565b5090565b90565b610310806101166000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80634ed3885e1461003b5780636d4ce63c146100f6575b600080fd5b6100f46004803603602081101561005157600080fd5b810190808035906020019064010000000081111561006e57600080fd5b82018360208201111561008057600080fd5b803590602001918460018302840111640100000000831117156100a257600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610179565b005b6100fe610193565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561013e578082015181840152602081019050610123565b50505050905090810190601f16801561016b5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b806000908051906020019061018f929190610235565b5050565b606060008054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561022b5780601f106102005761010080835404028352916020019161022b565b820191906000526020600020905b81548152906001019060200180831161020e57829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061027657805160ff19168380011785556102a4565b828001600101855582156102a4579182015b828111156102a3578251825591602001919060010190610288565b5b5090506102b191906102b5565b5090565b6102d791905b808211156102d35760008160009055506001016102bb565b5090565b9056fea2646970667358221220bf4a4547462412a2d27d205b50ba5d4dba42f506f9ea3628eb3d0299c9c28d5664736f6c634300060a0033"; + std::string h2 = + "608060405234801561001057600080fd5b506040518060400160405280600d81526020017f48656c6c6f2c20576f726c6421000000000000000000000000000000000000008152506000908051906020019061005c9291906100cb565b506040516100699061014b565b604051809103906000f080158015610085573d6000803e3d6000fd5b50600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610175565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061010c57805160ff191683800117855561013a565b8280016001018555821561013a579182015b8281111561013957825182559160200191906001019061011e565b5b5090506101479190610158565b5090565b6104168061060f83390190565b5b80821115610171576000816000905550600101610159565b5090565b61048b806101846000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063a7ef43291461003b578063d2178b081461006f575b600080fd5b61004361015e565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610077610188565b604051808060200180602001838103835285818151815260200191508051906020019080838360005b838110156100bb5780820151818401526020810190506100a0565b50505050905090810190601f1680156100e85780820380516001836020036101000a031916815260200191505b50838103825284818151815260200191508051906020019080838360005b83811015610121578082015181840152602081019050610106565b50505050905090810190601f16801561014e5780820380516001836020036101000a031916815260200191505b5094505050505060405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606080600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636d4ce63c6040518163ffffffff1660e01b815260040160006040518083038186803b1580156101f357600080fd5b505afa158015610207573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250602081101561023157600080fd5b810190808051604051939291908464010000000082111561025157600080fd5b8382019150602082018581111561026757600080fd5b825186600182028301116401000000008211171561028457600080fd5b8083526020830192505050908051906020019080838360005b838110156102b857808201518184015260208101905061029d565b50505050905090810190601f1680156102e55780820380516001836020036101000a031916815260200191505b50604052505050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636d4ce63c6040518163ffffffff1660e01b815260040160006040518083038186803b15801561035457600080fd5b505afa158015610368573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250602081101561039257600080fd5b81019080805160405193929190846401000000008211156103b257600080fd5b838201915060208201858111156103c857600080fd5b82518660018202830111640100000000821117156103e557600080fd5b8083526020830192505050908051906020019080838360005b838110156104195780820151818401526020810190506103fe565b50505050905090810190601f1680156104465780820380516001836020036101000a031916815260200191505b5060405250505091509150909156fea2646970667358221220004a7724bbd7d9ee1d4c00b949283850a69ddc538be408fa1c79fce561994b0a64736f6c634300060c0033608060405234801561001057600080fd5b506040518060400160405280600d81526020017f48656c6c6f2c20576f726c6421000000000000000000000000000000000000008152506000908051906020019061005c929190610062565b506100ff565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100a357805160ff19168380011785556100d1565b828001600101855582156100d1579182015b828111156100d05782518255916020019190600101906100b5565b5b5090506100de91906100e2565b5090565b5b808211156100fb5760008160009055506001016100e3565b5090565b6103088061010e6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80634ed3885e1461003b5780636d4ce63c146100f6575b600080fd5b6100f46004803603602081101561005157600080fd5b810190808035906020019064010000000081111561006e57600080fd5b82018360208201111561008057600080fd5b803590602001918460018302840111640100000000831117156100a257600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610179565b005b6100fe610193565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561013e578082015181840152602081019050610123565b50505050905090810190601f16801561016b5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b806000908051906020019061018f929190610235565b5050565b606060008054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561022b5780601f106102005761010080835404028352916020019161022b565b820191906000526020600020905b81548152906001019060200180831161020e57829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061027657805160ff19168380011785556102a4565b828001600101855582156102a4579182015b828111156102a3578251825591602001919060010190610288565b5b5090506102b191906102b5565b5090565b5b808211156102ce5760008160009055506001016102b6565b509056fea26469706673582212200374a2bdb89cd0187ff6b1f551739ca53a44f456bd10ea4052e069475292e45b64736f6c634300060c0033"; + // clang-format on +}; + +BOOST_FIXTURE_TEST_SUITE(precompiledPermissionTest, PermissionPrecompiledFixture) + +BOOST_AUTO_TEST_CASE(testMethodWhiteList) +{ + deployHello(); + BlockNumber number = 2; + // simple get + { + auto result = helloGet(number++, 1000); + BOOST_CHECK(result->data().toBytes() == codec->encode(std::string("Hello, World!"))); + } + + // add method acl type + { + BlockNumber _number = 3; + + // not found + auto r1 = getMethodAuth(number++, Address(helloAddress), "set(string)"); + BOOST_CHECK( + r1->data().toBytes() == codec->encode((uint8_t)(0), std::vector({}), + std::vector({}))); + + auto r2 = getMethodAuth(number++, Address(helloAddress), "get()"); + BOOST_CHECK( + r2->data().toBytes() == codec->encode((uint8_t)(0), std::vector({}), + std::vector({}))); + + // set method acl type + { + auto result = setMethodType( + _number++, 1000, Address(helloAddress), "get()", AuthType::WHITE_LIST_MODE); + BOOST_CHECK(result->data().toBytes() == codec->encode(u256(0))); + + // row not found + auto result2 = getMethodAuth(number++, Address(helloAddress), "get()"); + BOOST_CHECK(result2->data().toBytes() == codec->encode((uint8_t)(1), + std::vector({}), + std::vector({}))); + } + + // can't get now, even if not set any acl + { + auto result = helloGet(_number++, 1000, 0); + BOOST_CHECK(result->status() == (int32_t)TransactionStatus::PermissionDenied); + BOOST_CHECK(result->type() == ExecutionMessage::REVERT); + } + + // can still set + { + auto result = helloSet(_number++, 1000, "test1"); + BOOST_CHECK(result->status() == (int32_t)TransactionStatus::None); + } + + // open white list, only 0x1234567890123456789012345678901234567890 address can use + { + auto result4 = modifyMethodAuth(_number++, 1000, + "openMethodAuth(address,bytes4,address)", Address(helloAddress), "get()", + Address("0x1234567890123456789012345678901234567890")); + BOOST_CHECK(result4->data().toBytes() == codec->encode(u256(0))); + + auto result2 = getMethodAuth(number++, Address(helloAddress), "get()"); + BOOST_CHECK(result2->data().toBytes() == + codec->encode((uint8_t)(AuthType::WHITE_LIST_MODE), + std::vector({"1234567890123456789012345678901234567890"}), + std::vector({}))); + } + + // get permission denied + { + auto result5 = helloGet(_number++, 1000); + BOOST_CHECK(result5->status() == (int32_t)TransactionStatus::PermissionDenied); + BOOST_CHECK(result5->type() == ExecutionMessage::REVERT); + } + + // can still set + { + auto result6 = helloSet(_number++, 1000, "test2"); + BOOST_CHECK(result6->status() == (int32_t)TransactionStatus::None); + } + + // use address 0x1234567890123456789012345678901234567890, success get + { + auto result7 = + helloGet(_number++, 1000, 0, Address("0x1234567890123456789012345678901234567890")); + BOOST_CHECK(result7->data().toBytes() == codec->encode(std::string("test2"))); + } + + // close white list, 0x1234567890123456789012345678901234567890 address can not use + { + auto result4 = modifyMethodAuth(_number++, 1000, + "closeMethodAuth(address,bytes4,address)", Address(helloAddress), "get()", + Address("0x1234567890123456789012345678901234567890")); + BOOST_CHECK(result4->data().toBytes() == codec->encode(u256(0))); + + auto result2 = getMethodAuth(number++, Address(helloAddress), "get()"); + BOOST_CHECK( + result2->data().toBytes() == + codec->encode((uint8_t)(AuthType::WHITE_LIST_MODE), std::vector({}), + std::vector({"1234567890123456789012345678901234567890"}))); + } + + // use address 0x1234567890123456789012345678901234567890 get permission denied + { + auto result5 = + helloGet(_number++, 1000, 0, Address("0x1234567890123456789012345678901234567890")); + BOOST_CHECK(result5->status() == (int32_t)TransactionStatus::PermissionDenied); + BOOST_CHECK(result5->type() == ExecutionMessage::REVERT); + + auto result2 = checkMethodAuth(number++, Address(helloAddress), "get()", + Address("0x1234567890123456789012345678901234567890")); + BOOST_CHECK(result2->data().toBytes() == codec->encode(bool(false))); + + auto result3 = checkMethodAuth(number++, Address(helloAddress), "set(string)", + Address("0x1234567890123456789012345678901234567890")); + BOOST_CHECK(result3->data().toBytes() == codec->encode(bool(true))); + } + + // use address 0x1234567890123456789012345678901234567890 still can set + { + auto result = helloSet( + _number++, 1000, "test2", 0, Address("0x1234567890123456789012345678901234567890")); + BOOST_CHECK(result->status() == (int32_t)TransactionStatus::None); + } + } +} + +BOOST_AUTO_TEST_CASE(testMethodBlackList) +{ + deployHello(); + // simple get + { + auto result = helloGet(3, 1000); + BOOST_CHECK(result->data().toBytes() == codec->encode(std::string("Hello, World!"))); + } + + // add method acl type + { + BlockNumber _number = 4; + // not found + auto r1 = getMethodAuth(_number++, Address(helloAddress), "set(string)"); + BOOST_CHECK( + r1->data().toBytes() == codec->encode((uint8_t)(0), std::vector({}), + std::vector({}))); + + auto r2 = getMethodAuth(_number++, Address(helloAddress), "get()"); + BOOST_CHECK( + r2->data().toBytes() == codec->encode((uint8_t)(0), std::vector({}), + std::vector({}))); + + // set method acl type + { + auto result = setMethodType( + _number++, 1000, Address(helloAddress), "get()", AuthType::BLACK_LIST_MODE); + BOOST_CHECK(result->data().toBytes() == codec->encode(u256(0))); + + auto result2 = getMethodAuth(_number++, Address(helloAddress), "get()"); + BOOST_CHECK(result2->data().toBytes() == codec->encode((uint8_t)(2), + std::vector({}), + std::vector({}))); + } + + // still can get now, even if not set any acl + { + auto result = helloGet(_number++, 1000, 0); + BOOST_CHECK(result->data().toBytes() == codec->encode(std::string("Hello, World!"))); + } + + // still can set, even if not set any acl + { + auto result = helloSet(_number++, 1000, "test1"); + BOOST_CHECK(result->status() == (int32_t)TransactionStatus::None); + } + + // still can get now, even if not set any acl + { + auto result = helloGet(_number++, 1000, 0); + BOOST_CHECK(result->data().toBytes() == codec->encode(std::string("test1"))); + } + + // open black list, block 0x1234567890123456789012345678901234567890 address usage + { + auto result = modifyMethodAuth(_number++, 1000, + "openMethodAuth(address,bytes4,address)", Address(helloAddress), "get()", + Address("0x1234567890123456789012345678901234567890")); + BOOST_CHECK(result->data().toBytes() == codec->encode(u256(0))); + + auto result2 = getMethodAuth(_number++, Address(helloAddress), "get()"); + BOOST_CHECK(result2->data().toBytes() == + codec->encode((uint8_t)(AuthType::BLACK_LIST_MODE), + std::vector({"1234567890123456789012345678901234567890"}), + std::vector({}))); + } + + // can still set + { + auto result = helloSet(_number++, 1000, "test2"); + BOOST_CHECK(result->status() == (int32_t)TransactionStatus::None); + } + + // can still get with default address + { + auto result = helloGet(_number++, 1000); + BOOST_CHECK(result->data().toBytes() == codec->encode(std::string("test2"))); + } + + // use address 0x1234567890123456789012345678901234567890, still can get + { + auto result = + helloGet(_number++, 1000, 0, Address("0x1234567890123456789012345678901234567890")); + BOOST_CHECK(result->data().toBytes() == codec->encode(std::string("test2"))); + } + + // close black list, 0x1234567890123456789012345678901234567890 address block + { + auto result4 = modifyMethodAuth(_number++, 1000, + "closeMethodAuth(address,bytes4,address)", Address(helloAddress), "get()", + Address("0x1234567890123456789012345678901234567890")); + BOOST_CHECK(result4->data().toBytes() == codec->encode(u256(0))); + + auto result2 = getMethodAuth(_number++, Address(helloAddress), "get()"); + BOOST_CHECK( + result2->data().toBytes() == + codec->encode((uint8_t)(AuthType::BLACK_LIST_MODE), std::vector({}), + std::vector({"1234567890123456789012345678901234567890"}))); + } + + // use address 0x1234567890123456789012345678901234567890, get success + { + auto result = + helloGet(_number++, 1000, 0, Address("0x1234567890123456789012345678901234567890")); + BOOST_CHECK(result->status() == (int32_t)TransactionStatus::PermissionDenied); + BOOST_CHECK(result->type() == ExecutionMessage::REVERT); + } + } +} + +BOOST_AUTO_TEST_CASE(testResetAdmin) +{ + deployHello(); + // simple get + { + auto result = helloGet(2, 1000); + BOOST_CHECK(result->data().toBytes() == codec->encode(std::string("Hello, World!"))); + } + + // add method acl type + { + BlockNumber _number = 3; + // get admin + { + auto result = getAdmin(_number++, 1000, Address(helloAddress)); + BOOST_CHECK(result->data().toBytes() == + codec->encode(Address("11ac3ca85a307ae2aff614e83949ab691ba019c5"))); + } + // get admin in wrong address + { + auto result = + getAdmin(_number++, 1000, Address("0x1234567890123456789012345678901234567890")); + BOOST_CHECK(result->status() == (int32_t)TransactionStatus::PrecompiledError); + } + // set method acl type + { + auto result = setMethodType( + _number++, 1000, Address(helloAddress), "get()", AuthType::BLACK_LIST_MODE); + BOOST_CHECK(result->data().toBytes() == codec->encode(u256(0))); + } + + // close black list, block 0x1234567890123456789012345678901234567890 address usage + { + auto result = modifyMethodAuth(_number++, 1000, + "closeMethodAuth(address,bytes4,address)", Address(helloAddress), "get()", + Address("0x1234567890123456789012345678901234567890")); + BOOST_CHECK(result->data().toBytes() == codec->encode(u256(0))); + } + + // can still set + { + auto result = helloSet(_number++, 1000, "test2"); + BOOST_CHECK(result->status() == (int32_t)TransactionStatus::None); + } + + // can still get with default address + { + auto result = helloGet(_number++, 1000); + BOOST_CHECK(result->data().toBytes() == codec->encode(std::string("test2"))); + } + + // use address 0x1234567890123456789012345678901234567890, get permission denied + { + auto result = + helloGet(_number++, 1000, 0, Address("0x1234567890123456789012345678901234567890")); + BOOST_CHECK(result->status() == (int32_t)TransactionStatus::PermissionDenied); + BOOST_CHECK(result->type() == ExecutionMessage::REVERT); + } + + // reset admin in error contract address + { + auto result = + resetAdmin(_number++, 1000, Address("0x1234567890123456789012345678901234567890"), + Address("0x1234567890123456789012345678901234567890")); + BOOST_CHECK(result->status() == (int32_t)TransactionStatus::PrecompiledError); + BOOST_CHECK(result->type() == ExecutionMessage::REVERT); + } + + // reset admin + { + auto result = resetAdmin(_number++, 1000, Address(helloAddress), + Address("0x1234567890123456789012345678901234567890")); + BOOST_CHECK(result->data().toBytes() == codec->encode(u256(0))); + } + + // reset admin with wrong sender + { + auto result = resetAdmin(_number++, 1000, Address(helloAddress), + Address("0x1234567890123456789012345678901234567890"), 0, true); + BOOST_CHECK(result->data().toBytes() == codec->encode(u256(CODE_NO_AUTHORIZED))); + } + + // get admin + { + auto result = getAdmin(_number++, 1000, Address(helloAddress)); + BOOST_CHECK(result->data().toBytes() == + codec->encode(Address("0x1234567890123456789012345678901234567890"))); + } + + // use address 0x1234567890123456789012345678901234567890, still permission denied + { + auto result = + helloGet(_number++, 1000, 0, Address("0x1234567890123456789012345678901234567890")); + BOOST_CHECK(result->status() == (int32_t)TransactionStatus::PermissionDenied); + BOOST_CHECK(result->type() == ExecutionMessage::REVERT); + } + } +} + +BOOST_AUTO_TEST_CASE(testDeployWhiteList) +{ + // simple deploy + deployHello(); + Address admin = Address("0x1234567890123456789012345678901234567890"); + + // add deploy acl type + { + BlockNumber _number = 3; + // set deploy acl type + { + auto result = setDeployType(_number++, 1000, AuthType::WHITE_LIST_MODE); + BOOST_CHECK(result->data().toBytes() == codec->encode(s256(0))); + } + // cannot deploy + { + auto result = deployHelloInAuthCheck( + "1234654b49838bd3e9466c85a4cc3428c9601235", _number++, admin, true); + BOOST_CHECK(result->status() == (int32_t)TransactionStatus::PermissionDenied); + BOOST_CHECK(result->type() == ExecutionMessage::REVERT); + } + // has auth? no + { + auto result = hasDeployAuth(_number++, 1000, admin); + BOOST_CHECK(result->data().toBytes() == codec->encode(false)); + } + // open deploy auth + { + auto result = openDeployAuth(_number++, 1000, admin); + BOOST_CHECK(result->data().toBytes() == codec->encode(s256(0))); + } + // has auth? yes + { + auto result = hasDeployAuth(_number++, 1000, admin); + BOOST_CHECK(result->data().toBytes() == codec->encode(true)); + } + // deploy ok + { + auto result = deployHelloInAuthCheck( + "1234654b49838bd3e9466c85a4cc3428c9605431", _number++, admin, false); + BOOST_CHECK( + result->newEVMContractAddress() == "1234654b49838bd3e9466c85a4cc3428c9605431"); + } + // close auth + { + auto result = closeDeployAuth(_number++, 1000, admin); + BOOST_CHECK(result->data().toBytes() == codec->encode(s256(0))); + } + // has auth? no + { + auto result = hasDeployAuth(_number++, 1000, admin); + BOOST_CHECK(result->data().toBytes() == codec->encode(false)); + } + // cannot deploy + { + auto result = deployHelloInAuthCheck( + "1234654b49838bd3e9466c85a4cc3428c9605430", _number++, admin, true); + BOOST_CHECK(result->status() == (int32_t)TransactionStatus::PermissionDenied); + BOOST_CHECK(result->type() == ExecutionMessage::REVERT); + } + // get deploy type + { + auto result = getDeployType(_number++, 1000); + BOOST_CHECK(result->data().toBytes() == codec->encode(u256(1))); + } + } +} + +BOOST_AUTO_TEST_CASE(testDeployBlackList) +{ + // simple deploy + deployHello(); + Address admin = Address("0x1234567890123456789012345678901234567890"); + + // add deploy acl type + { + BlockNumber _number = 3; + // set deploy acl type + { + auto result = setDeployType(_number++, 1000, AuthType::BLACK_LIST_MODE); + BOOST_CHECK(result->data().toBytes() == codec->encode(s256(0))); + } + // can still deploy + { + auto result = deployHelloInAuthCheck( + "1234654b49838bd3e9466c85a4cc3428c9601235", _number++, admin, false); + BOOST_CHECK( + result->newEVMContractAddress() == "1234654b49838bd3e9466c85a4cc3428c9601235"); + } + // has auth? yes + { + auto result = hasDeployAuth(_number++, 1000, admin); + BOOST_CHECK(result->data().toBytes() == codec->encode(true)); + } + // close deploy auth + { + auto result = closeDeployAuth(_number++, 1000, admin); + BOOST_CHECK(result->data().toBytes() == codec->encode(s256(0))); + } + // has auth? no + { + auto result = hasDeployAuth(_number++, 1000, admin); + BOOST_CHECK(result->data().toBytes() == codec->encode(false)); + } + // deploy permission denied + { + auto result = deployHelloInAuthCheck( + "1234654b49838bd3e9466c85a4cc3428c9605431", _number++, admin, true); + BOOST_CHECK(result->status() == (int32_t)TransactionStatus::PermissionDenied); + BOOST_CHECK(result->type() == ExecutionMessage::REVERT); + } + // open auth + { + auto result = openDeployAuth(_number++, 1000, admin); + BOOST_CHECK(result->data().toBytes() == codec->encode(s256(0))); + } + // has auth? yes + { + auto result = hasDeployAuth(_number++, 1000, admin); + BOOST_CHECK(result->data().toBytes() == codec->encode(true)); + } + // deploy ok + { + auto result = deployHelloInAuthCheck( + "1234654b49838bd3e9466c85a4cc3428c9605430", _number++, admin, false); + BOOST_CHECK( + result->newEVMContractAddress() == "1234654b49838bd3e9466c85a4cc3428c9605430"); + } + // get deploy type + { + auto result = getDeployType(_number++, 1000); + BOOST_CHECK(result->data().toBytes() == codec->encode(u256(2))); + } + } +} + +BOOST_AUTO_TEST_CASE(testDeployCommitteeManagerAndCall) +{ + // call CommitteeManager + { + nextBlock(2); + bytes in = codec->encodeWithSig("createUpdateGovernorProposal(address,uint32,uint256)", + admin, codec::toString32(h256(uint32_t(2))), u256(2)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + + Address _admin(admin); + tx->forceSender(_admin.asBytes()); + + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + // force cover write + txpool->hash2Transaction[hash] = tx; + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(100); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(precompiled::AUTH_COMMITTEE_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + // proposalManager.create + result2->setSeq(1001); + result2->setKeyLocks({}); + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + // + result3->setSeq(1000); + std::promise executePromise4; + executor->dmcExecuteTransaction(std::move(result3), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise4.set_value(std::move(result)); + }); + auto result4 = executePromise4.get_future().get(); + + commitBlock(2); + } +} + +BOOST_AUTO_TEST_CASE(testDeployAdmin) +{ + BlockNumber _number = 3; + Address admin = Address("0x1234567890123456789012345678901234567890"); + auto result = + deployHello2InAuthCheck("1234654b49838bd3e9466c85a4cc3428c9601235", _number++, admin); + // test deploy admin + { + auto result1 = + getAdmin(_number++, 1000, Address("1234654b49838bd3e9466c85a4cc3428c9601235")); + BOOST_CHECK(result1->data().toBytes() == codec->encode(admin)); + } + + // test external deploy admin + { + auto result1 = getAdmin(_number++, 1000, Address(hello2Address)); + std::cout << toHexStringWithPrefix(result1->data().toBytes()) << std::endl; + BOOST_CHECK(result1->data().toBytes() == codec->encode(admin)); + } +} + +BOOST_AUTO_TEST_CASE(testContractStatus) +{ + deployHello(); + BlockNumber _number = 3; + // frozen + { + auto r1 = setContractStatus(_number++, Address(helloAddress), true); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(0))); + + auto r2 = contractAvailable(_number++, Address(helloAddress)); + BOOST_CHECK(r2->data().toBytes() == codec->encode(false)); + } + // frozen, revert + { + auto result = helloGet(_number++, 1000); + BOOST_CHECK(result->status() == (int32_t)TransactionStatus::ContractFrozen); + + auto result2 = helloSet(_number++, 1000, ""); + BOOST_CHECK(result2->status() == (int32_t)TransactionStatus::ContractFrozen); + } + // switch normal + { + auto r1 = setContractStatus(_number++, Address(helloAddress), false); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(0))); + auto r2 = contractAvailable(_number++, Address(helloAddress)); + BOOST_CHECK(r2->data().toBytes() == codec->encode(true)); + } + // normal + { + auto result = helloGet(_number++, 1000); + BOOST_CHECK(result->data().toBytes() == codec->encode(std::string("Hello, World!"))); + + auto result2 = helloSet(_number++, 1000, "test"); + BOOST_CHECK(result2->status() == (int32_t)TransactionStatus::None); + + auto result3 = helloGet(_number++, 1000); + BOOST_CHECK(result3->data().toBytes() == codec->encode(std::string("test"))); + } + + // contract address not found + { + auto errorAddress = "123456"; + auto result2 = contractAvailable(_number++, Address(errorAddress)); + BOOST_CHECK(result2->status() == (int32_t)TransactionStatus::PrecompiledError); + } +} + +BOOST_AUTO_TEST_CASE(testContractStatusInKeyPage) +{ + setIsWasm(false, true, true); + + deployHello(); + BlockNumber _number = 3; + // frozen + { + auto r1 = setContractStatus(_number++, Address(helloAddress), true); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(0))); + + auto r2 = contractAvailable(_number++, Address(helloAddress)); + BOOST_CHECK(r2->data().toBytes() == codec->encode(false)); + } + // frozen, revert + { + auto result = helloGet(_number++, 1000); + BOOST_CHECK(result->status() == (int32_t)TransactionStatus::ContractFrozen); + + auto result2 = helloSet(_number++, 1000, ""); + BOOST_CHECK(result2->status() == (int32_t)TransactionStatus::ContractFrozen); + } + // switch normal + { + auto r1 = setContractStatus(_number++, Address(helloAddress), false); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(0))); + auto r2 = contractAvailable(_number++, Address(helloAddress)); + BOOST_CHECK(r2->data().toBytes() == codec->encode(true)); + } + // normal + { + auto result = helloGet(_number++, 1000); + BOOST_CHECK(result->data().toBytes() == codec->encode(std::string("Hello, World!"))); + + auto result2 = helloSet(_number++, 1000, "test"); + BOOST_CHECK(result2->status() == (int32_t)TransactionStatus::None); + + auto result3 = helloGet(_number++, 1000); + BOOST_CHECK(result3->data().toBytes() == codec->encode(std::string("test"))); + } + + // contract address not found + { + auto errorAddress = "123456"; + auto result2 = contractAvailable(_number++, Address(errorAddress)); + BOOST_CHECK(result2->status() == (int32_t)TransactionStatus::PrecompiledError); + } +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/ConfigPrecompiledTest.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/ConfigPrecompiledTest.cpp" new file mode 100644 index 00000000..8f0e1e9d --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/ConfigPrecompiledTest.cpp" @@ -0,0 +1,552 @@ +/** + * Copyright (C) 2021 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 ConfigPrecompiledTest.cpp + * @author: kyonRay + * @date 2021-06-22 + */ + +#include "libprecompiled/PreCompiledFixture.h" +#include "precompiled/ConsensusPrecompiled.h" +#include "precompiled/SystemConfigPrecompiled.h" +#include + +using namespace bcos; +using namespace bcos::precompiled; +using namespace bcos::executor; +using namespace bcos::storage; + +namespace bcos::test +{ +class ConfigPrecompiledFixture : public PrecompiledFixture +{ +public: + ConfigPrecompiledFixture() + { + codec = std::make_shared(hashImpl, false); + setIsWasm(false); + consTestAddress = Address("0x420f853b49838bd3e9466c85a4cc3428c960dde2").hex(); + sysTestAddress = Address("0x420f853b41234bd3e9466c85a4cc3428c960dde2").hex(); + paraTestAddress = Address("0x420f853b49838bd3e9412385a4cc3428c960dde2").hex(); + } + + ~ConfigPrecompiledFixture() override = default; + void deployTest(std::string _bin, std::string _address) + { + bytes input; + boost::algorithm::unhex(_bin, std::back_inserter(input)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setContextID(99); + params->setSeq(1000); + params->setDepth(0); + + params->setOrigin(sender); + params->setFrom(sender); + + // toChecksumAddress(addressString, hashImpl); + params->setTo(_address); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + nextBlock(1); + // -------------------------------- + // Create contract + // -------------------------------- + + std::promise executePromise; + executor->dmcExecuteTransaction( + std::move(params), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + BOOST_CHECK(result); + BOOST_CHECK_EQUAL(result->type(), ExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(result->contextID(), 99); + BOOST_CHECK_EQUAL(result->seq(), 1000); + BOOST_CHECK_EQUAL(result->create(), false); + BOOST_CHECK_EQUAL(result->newEVMContractAddress(), _address); + BOOST_CHECK_EQUAL(result->origin(), sender); + BOOST_CHECK_EQUAL(result->from(), _address); + BOOST_CHECK(result->to() == sender); + BOOST_CHECK_LT(result->gasAvailable(), gas); + commitBlock(1); + } + + std::string consTestBin = + "608060405234801561001057600080fd5b506110036000806101000a81548173ffffffffffffffffffffffffff" + "ffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610791806100" + "626000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000" + "000000000000000000900463ffffffff1680631216492d1461006757806325df91e6146100ee578063c0160bb5" + "1461016b578063edd56950146101f2575b600080fd5b34801561007357600080fd5b506100d860048036038101" + "9080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291" + "9081815260200183838082843782019150505050505091929192908035906020019092919050505061026f565b" + "6040518082815260200191505060405180910390f35b3480156100fa57600080fd5b5061015560048036038101" + "9080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291" + "9081815260200183838082843782019150505050505091929192905050506103b1565b60405180828152602001" + "91505060405180910390f35b34801561017757600080fd5b506101dc6004803603810190808035906020019082" + "01803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380" + "8284378201915050505050509192919290803590602001909291905050506104ea565b60405180828152602001" + "91505060405180910390f35b3480156101fe57600080fd5b506102596004803603810190808035906020019082" + "01803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380" + "828437820191505050505050919291929050505061062c565b6040518082815260200191505060405180910390" + "f35b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffff" + "ffffffffffffffffffffffffffff16633591685684846040518363ffffffff167c010000000000000000000000" + "000000000000000000000000000000000002815260040180806020018381526020018281038252848181518152" + "60200191508051906020019080838360005b83811015610321578082015181840152602081019050610306565b" + "50505050905090810190601f16801561034e5780820380516001836020036101000a031916815260200191505b" + "509350505050602060405180830381600087803b15801561036e57600080fd5b505af1158015610382573d6000" + "803e3d6000fd5b505050506040513d602081101561039857600080fd5b81019080805190602001909291905050" + "50905092915050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16" + "73ffffffffffffffffffffffffffffffffffffffff16632800efc0836040518263ffffffff167c010000000000" + "000000000000000000000000000000000000000000000002815260040180806020018281038252838181518152" + "60200191508051906020019080838360005b8381101561045c578082015181840152602081019050610441565b" + "50505050905090810190601f1680156104895780820380516001836020036101000a031916815260200191505b" + "5092505050602060405180830381600087803b1580156104a857600080fd5b505af11580156104bc573d600080" + "3e3d6000fd5b505050506040513d60208110156104d257600080fd5b8101908080519060200190929190505050" + "9050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ff" + "ffffffffffffffffffffffffffffffffffffff1663ce6fa5c584846040518363ffffffff167c01000000000000" + "000000000000000000000000000000000000000000000281526004018080602001838152602001828103825284" + "818151815260200191508051906020019080838360005b8381101561059c578082015181840152602081019050" + "610581565b50505050905090810190601f1680156105c95780820380516001836020036101000a031916815260" + "200191505b509350505050602060405180830381600087803b1580156105e957600080fd5b505af11580156105" + "fd573d6000803e3d6000fd5b505050506040513d602081101561061357600080fd5b8101908080519060200190" + "929190505050905092915050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffff" + "ffffffff1673ffffffffffffffffffffffffffffffffffffffff166380599e4b836040518263ffffffff167c01" + "000000000000000000000000000000000000000000000000000000000281526004018080602001828103825283" + "818151815260200191508051906020019080838360005b838110156106d7578082015181840152602081019050" + "6106bc565b50505050905090810190601f1680156107045780820380516001836020036101000a031916815260" + "200191505b5092505050602060405180830381600087803b15801561072357600080fd5b505af1158015610737" + "573d6000803e3d6000fd5b505050506040513d602081101561074d57600080fd5b810190808051906020019092" + "919050505090509190505600a165627a7a72305820508188017072d790559725e832a3ef9e1a851ab727e796b1" + "c1341248c03342440029"; + std::string sysTestBin = + "608060405234801561001057600080fd5b506110006000806101000a81548173ffffffffffffffffffffffffff" + "ffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610570806100" + "626000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000" + "000000000000000000900463ffffffff1680632d7fd05314610051578063ec9786001461013a575b600080fd5b" + "34801561005d57600080fd5b506100b8600480360381019080803590602001908201803590602001908080601f" + "016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050" + "91929192905050506101fd565b6040518080602001838152602001828103825284818151815260200191508051" + "906020019080838360005b838110156100fe5780820151818401526020810190506100e3565b50505050905090" + "810190601f16801561012b5780820380516001836020036101000a031916815260200191505b50935050505060" + "405180910390f35b34801561014657600080fd5b506101e7600480360381019080803590602001908201803590" + "602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782" + "01915050505050509192919290803590602001908201803590602001908080601f016020809104026020016040" + "519081016040528093929190818152602001838380828437820191505050505050919291929050505061039d56" + "5b6040518082815260200191505060405180910390f35b606060008060009054906101000a900473ffffffffff" + "ffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631258a93a8460" + "40518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401" + "8080602001828103825283818151815260200191508051906020019080838360005b838110156102aa57808201" + "518184015260208101905061028f565b50505050905090810190601f1680156102d75780820380516001836020" + "036101000a031916815260200191505b5092505050600060405180830381600087803b1580156102f657600080" + "fd5b505af115801561030a573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201" + "8060405250604081101561033457600080fd5b81019080805164010000000081111561034c57600080fd5b8281" + "019050602081018481111561036257600080fd5b815185600182028301116401000000008211171561037f5760" + "0080fd5b50509291906020018051906020019092919050505091509150915091565b6000806000905490610100" + "0a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffff" + "ff1663bd291aef84846040518363ffffffff167c01000000000000000000000000000000000000000000000000" + "000000000281526004018080602001806020018381038352858181518152602001915080519060200190808383" + "60005b8381101561044d578082015181840152602081019050610432565b50505050905090810190601f168015" + "61047a5780820380516001836020036101000a031916815260200191505b508381038252848181518152602001" + "91508051906020019080838360005b838110156104b3578082015181840152602081019050610498565b505050" + "50905090810190601f1680156104e05780820380516001836020036101000a031916815260200191505b509450" + "50505050602060405180830381600087803b15801561050157600080fd5b505af1158015610515573d6000803e" + "3d6000fd5b505050506040513d602081101561052b57600080fd5b810190808051906020019092919050505090" + "50929150505600a165627a7a72305820ba33b892d2d03143a7553d42fcb62b0b9067012df1de5ab763424bea55" + "85175e0029"; + std::string paraTestBin = + "608060405234801561001057600080fd5b506110066000806101000a81548173ffffffffffffffffffffffffff" + "ffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506104a6806100" + "626000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000" + "000000000000000000900463ffffffff1680630553904e1461005157806311e3f2af146100f8575b600080fd5b" + "34801561005d57600080fd5b506100e2600480360381019080803573ffffffffffffffffffffffffffffffffff" + "ffffff169060200190929190803590602001908201803590602001908080601f01602080910402602001604051" + "908101604052809392919081815260200183838082843782019150505050505091929192908035906020019092" + "9190505050610195565b6040518082815260200191505060405180910390f35b34801561010457600080fd5b50" + "61017f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080" + "3590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181" + "52602001838380828437820191505050505050919291929050505061030c565b60405180828152602001915050" + "60405180910390f35b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16" + "73ffffffffffffffffffffffffffffffffffffffff16630553904e8585856040518463ffffffff167c01000000" + "00000000000000000000000000000000000000000000000000028152600401808473ffffffffffffffffffffff" + "ffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018060200183815260" + "2001828103825284818151815260200191508051906020019080838360005b8381101561027a57808201518184" + "015260208101905061025f565b50505050905090810190601f1680156102a75780820380516001836020036101" + "000a031916815260200191505b50945050505050602060405180830381600087803b1580156102c857600080fd" + "5b505af11580156102dc573d6000803e3d6000fd5b505050506040513d60208110156102f257600080fd5b8101" + "90808051906020019092919050505090509392505050565b60008060009054906101000a900473ffffffffffff" + "ffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166311e3f2af848460" + "40518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401" + "808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff" + "16815260200180602001828103825283818151815260200191508051906020019080838360005b838110156103" + "ea5780820151818401526020810190506103cf565b50505050905090810190601f168015610417578082038051" + "6001836020036101000a031916815260200191505b509350505050602060405180830381600087803b15801561" + "043757600080fd5b505af115801561044b573d6000803e3d6000fd5b505050506040513d602081101561046157" + "600080fd5b81019080805190602001909291905050509050929150505600a165627a7a72305820fd4857231ba5" + "7cb17d47d43e38f1370285cfd965b622af793ee1bd9a3e490d270029"; + std::string consTestAddress; + std::string sysTestAddress; + std::string paraTestAddress; + std::string sender; + Address contractAddress = Address("0x420f853b49838bd3e9466c85a4cc3428c960dde2"); +}; +BOOST_FIXTURE_TEST_SUITE(precompiledConfigTest, ConfigPrecompiledFixture) + +BOOST_AUTO_TEST_CASE(sysConfig_test) +{ + deployTest(sysTestBin, sysTestAddress); + + auto simpleSetFunc = [&](protocol::BlockNumber _number, int _contextId, const std::string& _key, + const std::string& _v, + bcos::protocol::TransactionStatus _errorCode = + bcos::protocol::TransactionStatus::None) { + nextBlock(_number); + bytes in = codec->encodeWithSig("setValueByKeyTest(string,string)", _key, _v); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(_contextId); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(sysTestAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + // call precompiled setValueByKey + result2->setSeq(1001); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + // BOOST_CHECK(result3->data().toBytes() == codec->encode(s256(_errorCode))); + BOOST_CHECK(result3->status() == (int32_t)_errorCode); + commitBlock(_number); + }; + // simple set SYSTEM_KEY_TX_GAS_LIMIT + { + simpleSetFunc(2, 100, std::string{ledger::SYSTEM_KEY_TX_GAS_LIMIT}, std::string("1000000")); + } + + // simple get SYSTEM_KEY_TX_GAS_LIMIT + { + nextBlock(3); + bytes in = codec->encodeWithSig( + "getValueByKeyTest(string)", std::string(ledger::SYSTEM_KEY_TX_GAS_LIMIT)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(101); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(sysTestAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + // call precompiled setValueByKey + result2->setSeq(1001); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + BOOST_CHECK(result3->data().toBytes() == codec->encode(std::string("1000000"), u256(3))); + commitBlock(3); + } + + // simple set SYSTEM_KEY_TX_COUNT_LIMIT + { + simpleSetFunc(4, 102, std::string{ledger::SYSTEM_KEY_TX_COUNT_LIMIT}, std::string("1000")); + } + + // set SYSTEM_KEY_TX_COUNT_LIMIT error + { + simpleSetFunc(5, 103, std::string{ledger::SYSTEM_KEY_TX_COUNT_LIMIT}, std::string("error"), + bcos::protocol::TransactionStatus::PrecompiledError); + } + // set error key + { + simpleSetFunc(8, 106, std::string("errorKey"), std::string("1000"), + bcos::protocol::TransactionStatus::PrecompiledError); + } + + // get error key + { + nextBlock(9); + bytes in = codec->encodeWithSig("getValueByKeyTest(string)", std::string("error")); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(107); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(sysTestAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + // call precompiled setValueByKey + result2->setSeq(1001); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + BOOST_CHECK(result3->data().toBytes() == codec->encode(std::string(""), s256(-1))); + commitBlock(9); + } +} + +BOOST_AUTO_TEST_CASE(consensus_test) +{ + deployTest(consTestBin, consTestAddress); + + int64_t number = 2; + + std::stringstream nodeFactory; + nodeFactory << std::setfill('1') << std::setw(128) << 1; + std::string node1 = nodeFactory.str(); + std::stringstream().swap(nodeFactory); + + nodeFactory << std::setfill('2') << std::setw(128) << 2; + std::string node2 = nodeFactory.str(); + std::stringstream().swap(nodeFactory); + + nodeFactory << std::setfill('3') << std::setw(128) << 3; + std::string node3 = nodeFactory.str(); + std::stringstream().swap(nodeFactory); + + std::string errorNode = node1.substr(0, 127) + "s"; + + auto callFunc = [&](protocol::BlockNumber _number, const std::string& method, + const std::string& _nodeId, int _w = -1, int _errorCode = 0) { + BCOS_LOG(DEBUG) << LOG_BADGE("consensus_test") << LOG_KV("method", method) + << LOG_KV("_nodeId", _nodeId) << LOG_KV("_w", _w) + << LOG_KV("_errorCode", _errorCode); + nextBlock(_number); + bytes in = _w < 0 ? codec->encodeWithSig(method, _nodeId) : + codec->encodeWithSig(method, _nodeId, u256(_w)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(100); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(consTestAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + // call precompiled + result2->setSeq(1001); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + if (_errorCode != 0) + { + BOOST_CHECK(result3->data().toBytes() == codec->encode(int32_t(_errorCode))); + if (result3->data().toBytes() != codec->encode(int32_t(_errorCode))) + { + PRECOMPILED_LOG(TRACE) << "Mismatch result: " << toHex(result3->data().toBytes()) + << " expect: " << toHex(codec->encode(int32_t(_errorCode))); + } + } + commitBlock(_number); + return result3; + }; + + // node id too short + { + callFunc(number++, "addSealerTest(string,uint256)", std::string("111111"), 1, + CODE_INVALID_NODE_ID); + + callFunc( + number++, "addObserverTest(string)", std::string("111111"), -1, CODE_INVALID_NODE_ID); + callFunc(number++, "removeTest(string)", std::string("111111"), -1, CODE_INVALID_NODE_ID); + callFunc(number++, "setWeightTest(string,uint256)", std::string("111111"), 11, + CODE_INVALID_NODE_ID); + } + + // add sealer node1 + { + callFunc(number++, "addObserverTest(string)", node1, -1, 0); + callFunc(number++, "addSealerTest(string,uint256)", node1, 1, 0); + } + + // add observer node2 + { + callFunc(number++, "addObserverTest(string)", node2, -1, 0); + } + + // set weigh to observer + { + auto r = callFunc(number++, "setWeightTest(string,uint256)", node2, 123, 0); + BOOST_CHECK(r->status() == 15); + } + + // add errorNode + { + callFunc(number++, "addObserverTest(string)", errorNode, -1, CODE_INVALID_NODE_ID); + } + + // turn last sealer to observer + { + callFunc(number++, "addObserverTest(string)", node1, -1, CODE_LAST_SEALER); + } + + // add sealer node2 + { + callFunc(number++, "addSealerTest(string,uint256)", node2, 1, 0); + // removeTest sealer node2 + callFunc(number++, "removeTest(string)", node2, -1, 0); + // remove not exist sealer + callFunc(number++, "removeTest(string)", node2, -1, CODE_NODE_NOT_EXIST); + + // add observer node2 + callFunc(number++, "addObserverTest(string)", node2, -1, 0); + // add sealer again + callFunc(number++, "addSealerTest(string,uint256)", node2, 1, 0); + // add observer again + callFunc(number++, "addObserverTest(string)", node2, -1, 0); + } + + // removeTest last sealer + { + callFunc(number++, "removeTest(string)", node1, -1, CODE_LAST_SEALER); + } + + // set an invalid weight(0) to node + { + callFunc(number++, "setWeightTest(string,uint256)", node1, 0, CODE_INVALID_WEIGHT); + } + + // set a valid weight(2) to node1 + { + callFunc(number++, "setWeightTest(string,uint256)", node1, 2); + } + + // removeTest observer + { + callFunc(number++, "removeTest(string)", node2, -1, 0); + } + + // removeTest observer not exist + { + callFunc(number++, "removeTest(string)", node2, -1, CODE_NODE_NOT_EXIST); + } + + // set weigh to not exist node2 + { + callFunc(number++, "setWeightTest(string,uint256)", node2, 123, CODE_NODE_NOT_EXIST); + } + + // add node3 to sealer + { + callFunc(number++, "addSealerTest(string,uint256)", node3, 1, + CODE_ADD_SEALER_SHOULD_IN_OBSERVER); + callFunc(number++, "addObserverTest(string)", node3, -1, 0); + callFunc(number++, "addSealerTest(string,uint256)", node3, 1, 0); + } +} + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/CryptoPrecompiledTest.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/CryptoPrecompiledTest.cpp" new file mode 100644 index 00000000..c1882242 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/CryptoPrecompiledTest.cpp" @@ -0,0 +1,402 @@ +/** + * Copyright (C) 2021 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 CryptoPrecompiledTest.cpp + * @author: kyonRay + * @date 2021-07-05 + */ +#include "precompiled/CryptoPrecompiled.h" +#include "../mock/MockLedger.h" +#include "bcos-crypto/signature/codec/SignatureDataWithPub.h" +#include "bcos-executor/src/executive/LedgerCache.h" +#include "libprecompiled/PreCompiledFixture.h" +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::precompiled; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::ledger; +using namespace bcos::crypto; +using namespace bcos::codec; + +namespace bcos::test +{ +class CryptoPrecompiledFixture : public PrecompiledFixture +{ +public: + CryptoPrecompiledFixture() + { + codec = std::make_shared(hashImpl, false); + setIsWasm(false); + cryptoAddress = Address("0x420f853b49838bd3e9466c85a4cc3428c960dde2").hex(); + } + + virtual ~CryptoPrecompiledFixture() {} + + void deployTest() + { + bytes input; + boost::algorithm::unhex(cryptoBin, std::back_inserter(input)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setContextID(99); + params->setSeq(1000); + params->setDepth(0); + + params->setOrigin(sender); + params->setFrom(sender); + + // toChecksumAddress(addressString, hashImpl); + params->setTo(cryptoAddress); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + nextBlock(1); + // -------------------------------- + // Create contract + // -------------------------------- + + std::promise executePromise; + executor->dmcExecuteTransaction( + std::move(params), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + BOOST_CHECK(result); + BOOST_CHECK_EQUAL(result->type(), ExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(result->contextID(), 99); + BOOST_CHECK_EQUAL(result->seq(), 1000); + BOOST_CHECK_EQUAL(result->create(), false); + BOOST_CHECK_EQUAL(result->newEVMContractAddress(), cryptoAddress); + BOOST_CHECK_EQUAL(result->origin(), sender); + BOOST_CHECK_EQUAL(result->from(), cryptoAddress); + BOOST_CHECK(result->to() == sender); + BOOST_CHECK_LT(result->gasAvailable(), gas); + + // -------------------------------- + // Create contract twice to avoid address used in wasm + // -------------------------------- + + paramsBak.setSeq(1001); + std::promise executePromise2; + executor->dmcExecuteTransaction(std::make_unique(paramsBak), + [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + + auto result2 = executePromise2.get_future().get(); + BOOST_CHECK(result2); + BOOST_CHECK_EQUAL(result2->type(), ExecutionMessage::REVERT); + BOOST_CHECK_EQUAL( + result2->status(), (int32_t)TransactionStatus::ContractAddressAlreadyUsed); + + BOOST_CHECK_EQUAL(result2->contextID(), 99); + commitBlock(1); + } + + std::string sender; + std::string cryptoAddress; + std::string cryptoBin = + "608060405234801561001057600080fd5b5061100a6000806101000a81548173ffffffffffffffffffffffffff" + "ffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061090a806100" + "626000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80638b0537" + "581461005c57806393730bbe1461012b578063cbdb3a67146101fa578063eb90f45914610306578063fb34363c" + "146103d5575b600080fd5b6101156004803603602081101561007257600080fd5b810190808035906020019064" + "010000000081111561008f57600080fd5b8201836020820111156100a157600080fd5b80359060200191846001" + "8302840111640100000000831117156100c357600080fd5b91908080601f016020809104026020016040519081" + "016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050" + "50505091929192905050506104a4565b6040518082815260200191505060405180910390f35b6101e460048036" + "03602081101561014157600080fd5b810190808035906020019064010000000081111561015e57600080fd5b82" + "018360208201111561017057600080fd5b80359060200191846001830284011164010000000083111715610192" + "57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380" + "828437600081840152601f19601f82011690508083019250505050505050919291929050505061054b565b6040" + "518082815260200191505060405180910390f35b6102d16004803603608081101561021057600080fd5b810190" + "80803590602001909291908035906020019064010000000081111561023757600080fd5b820183602082011115" + "61024957600080fd5b8035906020019184600183028401116401000000008311171561026b57600080fd5b9190" + "8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000818401" + "52601f19601f820116905080830192505050505050509192919290803590602001909291908035906020019092" + "919050505061055c565b6040518083151581526020018273ffffffffffffffffffffffffffffffffffffffff16" + "81526020019250505060405180910390f35b6103bf6004803603602081101561031c57600080fd5b8101908080" + "35906020019064010000000081111561033957600080fd5b82018360208201111561034b57600080fd5b803590" + "6020019184600183028401116401000000008311171561036d57600080fd5b91908080601f0160208091040260" + "20016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080" + "83019250505050505050919291929050505061069e565b6040518082815260200191505060405180910390f35b" + "61048e600480360360208110156103eb57600080fd5b8101908080359060200190640100000000811115610408" + "57600080fd5b82018360208201111561041a57600080fd5b803590602001918460018302840111640100000000" + "8311171561043c57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181" + "52602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050" + "6107b9565b6040518082815260200191505060405180910390f35b600060028260405180828051906020019080" + "83835b602083106104dc57805182526020820191506020810190506020830392506104b9565b60018360200361" + "01000a038019825116818451168082178552505050505050905001915050602060405180830381855afa158015" + "61051e573d6000803e3d6000fd5b5050506040513d602081101561053357600080fd5b81019080805190602001" + "909291905050509050919050565b600081805190602001209050919050565b60008060008054906101000a9004" + "73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663" + "cbdb3a67878787876040518563ffffffff1660e01b815260040180858152602001806020018481526020018381" + "52602001828103825285818151815260200191508051906020019080838360005b838110156106005780820151" + "818401526020810190506105e5565b50505050905090810190601f16801561062d578082038051600183602003" + "6101000a031916815260200191505b5095505050505050604080518083038186803b15801561064c57600080fd" + "5b505afa158015610660573d6000803e3d6000fd5b505050506040513d604081101561067657600080fd5b8101" + "908080519060200190929190805190602001909291905050509150915094509492505050565b60008060009054" + "906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffff" + "ffffffffff1663eb90f459836040518263ffffffff1660e01b8152600401808060200182810382528381815181" + "5260200191508051906020019080838360005b8381101561072d57808201518184015260208101905061071256" + "5b50505050905090810190601f16801561075a5780820380516001836020036101000a03191681526020019150" + "5b509250505060206040518083038186803b15801561077757600080fd5b505afa15801561078b573d6000803e" + "3d6000fd5b505050506040513d60208110156107a157600080fd5b810190808051906020019092919050505090" + "50919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffff" + "ffffffffffffffffffffffffffffffffffff1663fb34363c836040518263ffffffff1660e01b81526004018080" + "602001828103825283818151815260200191508051906020019080838360005b83811015610848578082015181" + "84015260208101905061082d565b50505050905090810190601f16801561087557808203805160018360200361" + "01000a031916815260200191505b509250505060206040518083038186803b15801561089257600080fd5b505a" + "fa1580156108a6573d6000803e3d6000fd5b505050506040513d60208110156108bc57600080fd5b8101908080" + "519060200190929190505050905091905056fea2646970667358221220f90675a92f4cd30c9fb6c666bc2a0684" + "1325900bb12be808a30992090e509c9564736f6c634300060c0033"; +}; + +BOOST_FIXTURE_TEST_SUITE(precompiledCryptoTest, CryptoPrecompiledFixture) + +BOOST_AUTO_TEST_CASE(testSM3AndKeccak256) +{ + deployTest(); + + // sm3 + { + nextBlock(2); + std::string stringData = "abcd"; + bytesConstRef dataRef(stringData); + bytes encodedData = codec->encodeWithSig("sm3(bytes)", dataRef.toBytes()); + + auto tx = fakeTransaction(cryptoSuite, keyPair, "", encodedData, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto txHash = tx->hash(); + txpool->hash2Transaction.emplace(txHash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(txHash); + params2->setContextID(100); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(cryptoAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(encodedData)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + bytes out = result2->data().toBytes(); + string32 decodedHash; + codec->decode(bytesConstRef(&out), decodedHash); + HashType hash = + HashType("82ec580fe6d36ae4f81cae3c73f4a5b3b5a09c943172dc9053c69fd8e18dca1e"); + std::cout << "== testHash-sm3: decodedHash: " << codec::fromString32(decodedHash).hex() + << std::endl; + std::cout << "== testHash-sm3: hash:" << hash.hex() << std::endl; + BOOST_CHECK(hash == codec::fromString32(decodedHash)); + + commitBlock(2); + } + // keccak256Hash + { + nextBlock(3); + std::string stringData = "abcd"; + bytesConstRef dataRef(stringData); + bytes encodedData = codec->encodeWithSig("keccak256Hash(bytes)", dataRef.toBytes()); + + auto tx = fakeTransaction(cryptoSuite, keyPair, "", encodedData, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto txHash = tx->hash(); + txpool->hash2Transaction.emplace(txHash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(txHash); + params2->setContextID(101); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(cryptoAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(encodedData)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + bytes out = result2->data().toBytes(); + string32 decodedHash; + codec->decode(bytesConstRef(&out), decodedHash); + HashType hash = + HashType("48bed44d1bcd124a28c27f343a817e5f5243190d3c52bf347daf876de1dbbf77"); + std::cout << "== testHash-keccak256Hash: decodedHash: " + << codec::fromString32(decodedHash).hex() << std::endl; + std::cout << "== testHash-keccak256Hash: hash:" << hash.hex() << std::endl; + BOOST_CHECK(hash == codec::fromString32(decodedHash)); + commitBlock(3); + } +} + +class SM2VerifyPrecompiledFixture +{ +public: + SM2VerifyPrecompiledFixture() + { + clearName2SelectCache(); + m_cryptoSuite = std::make_shared( + std::make_shared(), std::make_shared(), nullptr); + m_cryptoPrecompiled = std::make_shared(m_cryptoSuite->hashImpl()); + m_blockContext = + std::make_shared(nullptr, m_ledgerCache, m_cryptoSuite->hashImpl(), 0, + h256(), utcTime(), (uint32_t)(bcos::protocol::BlockVersion::V3_0_VERSION), + FiscoBcosScheduleV4, false, false); + std::shared_ptr gasInjector = nullptr; + m_executive = std::make_shared( + std::weak_ptr(m_blockContext), "", 100, 0, gasInjector); + m_abi = std::make_shared(m_cryptoSuite->hashImpl()); + } + + ~SM2VerifyPrecompiledFixture() {} + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + BlockContext::Ptr m_blockContext; + TransactionExecutive::Ptr m_executive; + CryptoPrecompiled::Ptr m_cryptoPrecompiled; + std::string m_sm2VerifyFunction = "sm2Verify(bytes32,bytes,bytes32,bytes32)"; + std::shared_ptr m_abi; + LedgerCache::Ptr m_ledgerCache = std::make_shared(std::make_shared()); +}; + +BOOST_AUTO_TEST_CASE(testSM2Verify) +{ + // case Verify success + h256 fixedSec1("bcec428d5205abe0f0cc8a734083908d9eb8563e31f943d760786edf42ad67dd"); + auto sec1 = std::make_shared(fixedSec1.asBytes()); + auto keyFactory = std::make_shared(); + auto secCreated = keyFactory->createKey(fixedSec1.asBytes()); + + auto keyPair = std::make_shared(sec1); + HashType hash = HashType("82ec580fe6d36ae4f81cae3c73f4a5b3b5a09c943172dc9053c69fd8e18dca1e"); + auto signature = sm2Sign(*keyPair, hash, true); + h256 mismatchHash = h256("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); + SM2VerifyPrecompiledFixture fixture; + + // verify the signature + auto signatureStruct = std::make_shared(ref(*signature)); + bytes in = fixture.m_abi->abiIn(fixture.m_sm2VerifyFunction, codec::toString32(hash), + *signatureStruct->pub(), codec::toString32(signatureStruct->r()), + codec::toString32(signatureStruct->s())); + auto parameters = std::make_shared(); + parameters->m_input = bytesConstRef(in.data(), in.size()); + auto execResult = fixture.m_cryptoPrecompiled->call(fixture.m_executive, parameters); + auto out = execResult->execResult(); + bool verifySucc; + Address accountAddress; + fixture.m_abi->abiOut(bytesConstRef(&out), verifySucc, accountAddress); + BOOST_CHECK(verifySucc == true); + BOOST_CHECK(accountAddress.hex() == keyPair->address(smHashImpl).hex()); + + // mismatch case + in = fixture.m_abi->abiIn(fixture.m_sm2VerifyFunction, codec::toString32(mismatchHash), + *signatureStruct->pub(), codec::toString32(signatureStruct->r()), + codec::toString32(signatureStruct->s())); + parameters = std::make_shared(); + parameters->m_input = bytesConstRef(in.data(), in.size()); + execResult = fixture.m_cryptoPrecompiled->call(fixture.m_executive, parameters); + out = execResult->execResult(); + fixture.m_abi->abiOut(bytesConstRef(&out), verifySucc, accountAddress); + BOOST_CHECK(verifySucc == false); + BOOST_CHECK(accountAddress.hex() == Address().hex()); +} + +BOOST_AUTO_TEST_CASE(testEVMPrecompiled) +{ + deployTest(); + + // sha256 + { + nextBlock(2); + std::string stringData = "abcd"; + bytesConstRef dataRef(stringData); + bytes encodedData = codec->encodeWithSig("getSha256(bytes)", dataRef.toBytes()); + + auto tx = fakeTransaction(cryptoSuite, keyPair, "", encodedData, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto txHash = tx->hash(); + txpool->hash2Transaction.emplace(txHash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(txHash); + params2->setContextID(100); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(cryptoAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(encodedData)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + auto rData = result2->data().toBytes(); + string32 re; + codec->decode(ref(rData), re); + BOOST_CHECK_EQUAL("0x88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", + toHexStringWithPrefix(fromString32(re).asBytes())); + BOOST_CHECK_EQUAL(result2->status(), (int32_t)TransactionStatus::None); + commitBlock(2); + } +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/EVMStateContextTest.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/EVMStateContextTest.cpp" new file mode 100644 index 00000000..23a58d65 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/EVMStateContextTest.cpp" @@ -0,0 +1,467 @@ +/** + * 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 EVMStateContextTest.cpp + * @author: kyonGuo + * @date 2022/11/2 + */ +#include "bcos-crypto/signature/codec/SignatureDataWithPub.h" +#include "libprecompiled/PreCompiledFixture.h" +#include "precompiled/CryptoPrecompiled.h" +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::precompiled; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::ledger; +using namespace bcos::crypto; +using namespace bcos::codec; + +namespace bcos::test +{ +class EVMStateContextFixture : public PrecompiledFixture +{ +public: + EVMStateContextFixture() + { + codec = std::make_shared(hashImpl, false); + setIsWasm(false, false, true); + testAddress = Address("0x420f853b49838bd3e9466c85a4cc3428c960dde2").hex(); + origin = Address("0x1234567890123456789012345678901234567890").hex(); + } + + virtual ~EVMStateContextFixture() {} + + void deployTest() + { + bytes input; + boost::algorithm::unhex(testBin, std::back_inserter(input)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setContextID(99); + params->setSeq(1000); + params->setDepth(0); + + params->setOrigin(sender); + params->setFrom(sender); + + // toChecksumAddress(addressString, hashImpl); + params->setTo(testAddress); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + NativeExecutionMessage paramsBak = *params; + nextBlock(1); + // -------------------------------- + // Create contract + // -------------------------------- + + std::promise executePromise; + executor->dmcExecuteTransaction( + std::move(params), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + BOOST_CHECK(result); + BOOST_CHECK_EQUAL(result->type(), ExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(result->contextID(), 99); + BOOST_CHECK_EQUAL(result->seq(), 1000); + BOOST_CHECK_EQUAL(result->create(), false); + BOOST_CHECK_EQUAL(result->newEVMContractAddress(), testAddress); + BOOST_CHECK_EQUAL(result->origin(), sender); + BOOST_CHECK_EQUAL(result->from(), testAddress); + BOOST_CHECK(result->to() == sender); + BOOST_CHECK_LT(result->gasAvailable(), gas); + commitBlock(1); + } + + ExecutionMessage::UniquePtr callTest( + protocol::BlockNumber _number, bytesConstRef encodedData, std::string _origin = "") + { + nextBlock(_number); + auto tx = + fakeTransaction(cryptoSuite, keyPair, "", encodedData.toBytes(), 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + if (!_origin.empty()) + { + tx->forceSender(Address(_origin).asBytes()); + } + auto txHash = tx->hash(); + txpool->hash2Transaction.emplace(txHash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(txHash); + params2->setContextID(_number); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(testAddress); + params2->setOrigin(_origin.empty() ? sender : _origin); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(encodedData.toBytes()); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + commitBlock(_number); + return result2; + } + + std::string sender; + std::string origin; + std::string testAddress; + // clang-format off + std::string testBin = "608060405234801561001057600080fd5b50611b70806100206000396000f3fe608060405234801561001057600080fd5b50600436106101215760003560e01c8063b2acd509116100ad578063cbbab99c11610071578063cbbab99c14610914578063da1ce32314610997578063ec8b466a146109b5578063ff0732ec14610a33578063fffd016714610b0257610121565b8063b2acd50914610596578063b815849414610665578063c13be6ea146106bb578063c3e6b0181461078a578063caa26032146107e057610121565b806329470338116100f457806329470338146103e25780634849f27914610424578063653d710c146104ac5780636a4dc7f3146104f657806370a3872f1461054057610121565b8063086ecbfa146101265780630c6daa6e146101f55780631d1f490d146103355780631ecf4f2114610353575b600080fd5b6101df6004803603602081101561013c57600080fd5b810190808035906020019064010000000081111561015957600080fd5b82018360208201111561016b57600080fd5b8035906020019184600183028401116401000000008311171561018d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610b20565b6040518082815260200191505060405180910390f35b6102f7600480360361014081101561020c57600080fd5b81019080803563ffffffff16906020019092919080604001906002806020026040519081016040528092919082600260200280828437600081840152601f19601f820116905080830192505050505050919291929080608001906004806020026040519081016040528092919082600460200280828437600081840152601f19601f820116905080830192505050505050919291929080604001906002806020026040519081016040528092919082600260200280828437600081840152601f19601f8201169050808301925050505050509192919290803515159060200190929190505050610c8a565b6040518082600260200280838360005b83811015610322578082015181840152602081019050610307565b5050505090500191505060405180910390f35b61033d610efa565b6040518082815260200191505060405180910390f35b6103a06004803603608081101561036957600080fd5b8101908080359060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050610f02565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61040e600480360360208110156103f857600080fd5b8101908080359060200190929190505050611045565b6040518082815260200191505060405180910390f35b61046e6004803603608081101561043a57600080fd5b8101908080359060200190929190803590602001909291908035906020019092919080359060200190929190505050611050565b6040518082600260200280838360005b8381101561049957808201518184015260208101905061047e565b5050505090500191505060405180910390f35b6104b46111b6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6104fe6111be565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6105806004803603606081101561055657600080fd5b810190808035906020019092919080359060200190929190803590602001909291905050506111c6565b6040518082815260200191505060405180910390f35b61064f600480360360208110156105ac57600080fd5b81019080803590602001906401000000008111156105c957600080fd5b8201836020820111156105db57600080fd5b803590602001918460018302840111640100000000831117156105fd57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061127a565b6040518082815260200191505060405180910390f35b6106a56004803603606081101561067b57600080fd5b8101908080359060200190929190803590602001909291908035906020019092919050505061138d565b6040518082815260200191505060405180910390f35b610774600480360360208110156106d157600080fd5b81019080803590602001906401000000008111156106ee57600080fd5b82018360208201111561070057600080fd5b8035906020019184600183028401116401000000008311171561072257600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050611478565b6040518082815260200191505060405180910390f35b6107ca600480360360608110156107a057600080fd5b810190808035906020019092919080359060200190929190803590602001909291905050506115ee565b6040518082815260200191505060405180910390f35b610899600480360360208110156107f657600080fd5b810190808035906020019064010000000081111561081357600080fd5b82018360208201111561082557600080fd5b8035906020019184600183028401116401000000008311171561084757600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050611706565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156108d95780820151818401526020810190506108be565b50505050905090810190601f1680156109065780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61091c611849565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561095c578082015181840152602081019050610941565b50505050905090810190601f1680156109895780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61099f611896565b6040518082815260200191505060405180910390f35b6109f5600480360360608110156109cb57600080fd5b8101908080359060200190929190803590602001909291908035906020019092919050505061189e565b6040518082600260200280838360005b83811015610a20578082015181840152602081019050610a05565b5050505090500191505060405180910390f35b610aec60048036036020811015610a4957600080fd5b8101908080359060200190640100000000811115610a6657600080fd5b820183602082011115610a7857600080fd5b80359060200191846001830284011164010000000083111715610a9a57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506119ed565b6040518082815260200191505060405180910390f35b610b0a611acc565b6040518082815260200191505060405180910390f35b6000805a90506003836040518082805190602001908083835b60208310610b5c5780518252602082019150602081019050602083039250610b39565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa158015610b9e573d6000803e3d6000fd5b5050506040515160601b6bffffffffffffffffffffffff1916915060005a9050600081830390507fdfa34a4e767d8f6cbaddb2435b6adb60a8c66377dc9885145c3c4ccfaf58d6d2816040518082815260200191505060405180910390a16127108111158015610c1057506102588110155b610c82576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6761736c65667420636865636b206572726f720000000000000000000000000081525060200191505060405180910390fd5b505050919050565b610c92611ad4565b610c9a611ad4565b60608787600060028110610caa57fe5b602002015188600160028110610cbc57fe5b602002015188600060048110610cce57fe5b602002015189600160048110610ce057fe5b60200201518a600260048110610cf257fe5b60200201518b600360048110610d0457fe5b60200201518b600060028110610d1657fe5b60200201518c600160028110610d2857fe5b60200201518c604051602001808b63ffffffff1663ffffffff1660e01b81526004018a81526020018981526020018881526020018781526020018681526020018581526020018477ffffffffffffffffffffffffffffffffffffffffffffffff191677ffffffffffffffffffffffffffffffffffffffffffffffff191681526008018377ffffffffffffffffffffffffffffffffffffffffffffffff191677ffffffffffffffffffffffffffffffffffffffffffffffff19168152600801821515151560f81b81526001019a5050505050505050505050604051602081830303815290604052905060005a905060408360d5602085016009600019fa610e2d57600080fd5b60005a9050600081830390507fdfa34a4e767d8f6cbaddb2435b6adb60a8c66377dc9885145c3c4ccfaf58d6d2816040518082815260200191505060405180910390a1620186a0811115610ee9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6761736c65667420636865636b206572726f720000000000000000000000000081525060200191505060405180910390fd5b849550505050505095945050505050565b600042905090565b6000805a905060018686868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015610f65573d6000803e3d6000fd5b50505060206040510351915060005a9050600081830390507fdfa34a4e767d8f6cbaddb2435b6adb60a8c66377dc9885145c3c4ccfaf58d6d2816040518082815260200191505060405180910390a16127108111158015610fc85750610bb88110155b61103a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6761736c65667420636865636b206572726f720000000000000000000000000081525060200191505060405180910390fd5b505050949350505050565b600081409050919050565b611058611ad4565b611060611af6565b858160006004811061106e57fe5b602002018181525050848160016004811061108557fe5b602002018181525050838160026004811061109c57fe5b60200201818152505082816003600481106110b357fe5b60200201818152505060005a9050604083608084600060065af180600081146110db576110e0565b600080fd5b505060005a9050600081830390507fdfa34a4e767d8f6cbaddb2435b6adb60a8c66377dc9885145c3c4ccfaf58d6d2816040518082815260200191505060405180910390a16127108111158015611138575060968110155b6111aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6761736c65667420636865636b206572726f720000000000000000000000000081525060200191505060405180910390fd5b50505050949350505050565b600032905090565b600033905090565b6000805a9050600083806111d657fe5b858709905060005a90506000818403905061271081111580156111fa575060008110155b61126c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6761736c65667420636865636b206572726f720000000000000000000000000081525060200191505060405180910390fd5b829450505050509392505050565b60008082519050600060c0828161128d57fe5b061461129857600080fd5b60005a90506040516020818460208801600060085af180600081146112c057825195506112c5565b600080fd5b50505060005a9050600081830390507fdfa34a4e767d8f6cbaddb2435b6adb60a8c66377dc9885145c3c4ccfaf58d6d2816040518082815260200191505060405180910390a1620186a0811115611384576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6761736c65667420636865636b206572726f720000000000000000000000000081525060200191505060405180910390fd5b50505050919050565b6000805a90506000838061139d57fe5b858708905060005a9050600081840390507fdfa34a4e767d8f6cbaddb2435b6adb60a8c66377dc9885145c3c4ccfaf58d6d2816040518082815260200191505060405180910390a161271081111580156113f8575060008110155b61146a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6761736c65667420636865636b206572726f720000000000000000000000000081525060200191505060405180910390fd5b829450505050509392505050565b6000805a90506002836040518082805190602001908083835b602083106114b45780518252602082019150602081019050602083039250611491565b6001836020036101000a038019825116818451168082178552505050505050905001915050602060405180830381855afa1580156114f6573d6000803e3d6000fd5b5050506040513d602081101561150b57600080fd5b8101908080519060200190929190505050915060005a9050600081830390507fdfa34a4e767d8f6cbaddb2435b6adb60a8c66377dc9885145c3c4ccfaf58d6d2816040518082815260200191505060405180910390a161271081111580156115745750603c8110155b6115e6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6761736c65667420636865636b206572726f720000000000000000000000000081525060200191505060405180910390fd5b505050919050565b6000805a905060405160208152602080820152602060408201528560608201528460808201528360a082015260208160c083600060055af18060008114611638578251945061163d565b600080fd5b50505060005a9050600081830390507fdfa34a4e767d8f6cbaddb2435b6adb60a8c66377dc9885145c3c4ccfaf58d6d2816040518082815260200191505060405180910390a1620186a08111156116fc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6761736c65667420636865636b206572726f720000000000000000000000000081525060200191505060405180910390fd5b5050509392505050565b606080825167ffffffffffffffff8111801561172157600080fd5b506040519080825280601f01601f1916602001820160405280156117545781602001600182028036833780820191505090505b50905060005a9050835180602084018260208801600060045af161177457fe5b5060005a9050600081830390507fdfa34a4e767d8f6cbaddb2435b6adb60a8c66377dc9885145c3c4ccfaf58d6d2816040518082815260200191505060405180910390a161271081111580156117cb5750600f8110155b61183d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6761736c65667420636865636b206572726f720000000000000000000000000081525060200191505060405180910390fd5b83945050505050919050565b60606000368080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050905090565b600045905090565b6118a6611ad4565b6118ae611b18565b84816000600381106118bc57fe5b60200201818152505083816001600381106118d357fe5b60200201818152505082816002600381106118ea57fe5b60200201818152505060005a9050604083606084600060075af1806000811461191257611917565b600080fd5b505060005a9050600081830390507fdfa34a4e767d8f6cbaddb2435b6adb60a8c66377dc9885145c3c4ccfaf58d6d2816040518082815260200191505060405180910390a1612710811115801561197057506117708110155b6119e2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6761736c65667420636865636b206572726f720000000000000000000000000081525060200191505060405180910390fd5b505050509392505050565b6000805a90508280519060200120915060005a9050600081830390507fdfa34a4e767d8f6cbaddb2435b6adb60a8c66377dc9885145c3c4ccfaf58d6d2816040518082815260200191505060405180910390a16127108111158015611a525750600081115b611ac4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6761736c65667420636865636b206572726f720000000000000000000000000081525060200191505060405180910390fd5b505050919050565b600043905090565b6040518060400160405280600290602082028036833780820191505090505090565b6040518060800160405280600490602082028036833780820191505090505090565b604051806060016040528060039060208202803683378082019150509050509056fea2646970667358221220fcd3862acd2cfe62485e2ed162542e61e370b685319c69893f7b168021fbdd0f64736f6c634300060a0033"; + // clang-format on +}; + +BOOST_FIXTURE_TEST_SUITE(EVMStateContextTest, EVMStateContextFixture) + +BOOST_AUTO_TEST_CASE(testEVMPrecompiled) +{ + deployTest(); + BlockNumber number = 2; + // sha256 + { + std::string stringData = "abcd"; + bytesConstRef dataRef(stringData); + bytes encodedData = codec->encodeWithSig("sha256Test(bytes)", dataRef.toBytes()); + + auto result = callTest(number++, ref(encodedData)); + + string32 re; + codec->decode(result->data(), re); + uint64_t gasUsed; + auto logEntry = result->takeLogEntries(); + codec->decode(logEntry.at(0).data(), gasUsed); + std::cout << "sha256 gasUsed: " << gasUsed << std::endl; + + BOOST_CHECK_EQUAL("0x88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", + toHexStringWithPrefix(fromString32(re).asBytes())); + BOOST_CHECK_EQUAL(result->status(), (int32_t)TransactionStatus::None); + } + + // keccak256 + { + std::string stringData = "test123"; + bytesConstRef dataRef(stringData); + bytes encodedData = codec->encodeWithSig("keccak256Test(bytes)", dataRef.toBytes()); + + auto result = callTest(number++, ref(encodedData)); + + string32 re; + codec->decode(result->data(), re); + uint64_t gasUsed; + auto logEntry = result->takeLogEntries(); + codec->decode(logEntry.at(0).data(), gasUsed); + std::cout << "keccak256 gasUsed: " << gasUsed << std::endl; + + BOOST_CHECK_EQUAL("0xf81b517a242b218999ec8eec0ea6e2ddbef2a367a14e93f4a32a39e260f686ad", + toHexStringWithPrefix(fromString32(re).asBytes())); + BOOST_CHECK_EQUAL(result->status(), (int32_t)TransactionStatus::None); + } + + // ecRecover + { + std::string stringData = "test_ecRecover"; + HashType hash = hashImpl->hash(asBytes(stringData)); + + h256 fixedSec1("bcec428d5205abe0f0cc8a734083908d9eb8563e31f943d760786edf42ad67dd"); + auto sec1 = std::make_shared(fixedSec1.asBytes()); + auto keyFactory = std::make_shared(); + auto secCreated = keyFactory->createKey(fixedSec1.asBytes()); + auto keyPair = std::make_shared(sec1); + auto sign = secp256k1Sign(*keyPair, hash); + auto signWithV = std::make_shared(ref(*sign)); + + auto hash32 = toString32(hash); + string32 r = toString32(signWithV->r()); + string32 s = toString32(signWithV->s()); + uint8_t v = signWithV->v() + 27; + auto p = secp256k1Recover(hash, ref(*sign)); + bytes encodedData = + codec->encodeWithSig("ecRecoverTest(bytes32,uint8,bytes32,bytes32)", hash32, v, r, s); + auto result = callTest(number++, ref(encodedData)); + + Address pub; + codec->decode(result->data(), pub); + uint64_t gasUsed; + auto logEntry = result->takeLogEntries(); + codec->decode(logEntry.at(0).data(), gasUsed); + std::cout << "ecRecover gasUsed: " << gasUsed << std::endl; + + BOOST_CHECK_EQUAL(keyPair->address(hashImpl), pub); + BOOST_CHECK_EQUAL(result->status(), (int32_t)TransactionStatus::None); + } + + // ripemd160Test + { + std::string stringData = "test_ripemd160"; + bytesConstRef dataRef(stringData); + bytes encodedData = codec->encodeWithSig("ripemd160Test(bytes)", dataRef.toBytes()); + + auto result = callTest(number++, ref(encodedData)); + + string32 re; + codec->decode(result->data(), re); + uint64_t gasUsed; + auto logEntry = result->takeLogEntries(); + codec->decode(logEntry.at(0).data(), gasUsed); + std::cout << "ripemd160 gasUsed: " << gasUsed << std::endl; + BOOST_CHECK_EQUAL("0x983fcf500b356d411f44fac0c54b9156bac3a22d000000000000000000000000", + toHexStringWithPrefix(fromString32(re).asBytes())); + BOOST_CHECK_EQUAL(result->status(), (int32_t)TransactionStatus::None); + } + + // identityTest + { + std::string stringData = "test_ripemd160"; + bytesConstRef dataRef(stringData); + bytes encodedData = codec->encodeWithSig("callDatacopy(bytes)", dataRef.toBytes()); + + auto result = callTest(number++, ref(encodedData)); + + bytes re; + codec->decode(result->data(), re); + uint64_t gasUsed; + auto logEntry = result->takeLogEntries(); + codec->decode(logEntry.at(0).data(), gasUsed); + std::cout << "identityTest gasUsed: " << gasUsed << std::endl; + + BOOST_CHECK_EQUAL(toHexStringWithPrefix(asBytes(stringData)), toHexStringWithPrefix(re)); + BOOST_CHECK_EQUAL(result->status(), (int32_t)TransactionStatus::None); + } + + // modexp + { + string32 base = + toString32("0000000000000000000000000000000000000000000000000000000000000003"); + string32 exponent = + toString32("0000000000000000000000000000000000000000000000000000000000000003"); + string32 modulus = + toString32("0000000000000000000000000000000000000000000000000000000000000009"); + bytes encodedData = + codec->encodeWithSig("callBigModExp(bytes32,bytes32,bytes32)", base, exponent, modulus); + + auto result = callTest(number++, ref(encodedData)); + + string32 re; + codec->decode(result->data(), re); + uint64_t gasUsed; + auto logEntry = result->takeLogEntries(); + codec->decode(logEntry.at(0).data(), gasUsed); + std::cout << "modexp gasUsed: " << gasUsed << std::endl; + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000000000", + toHexStringWithPrefix(fromString32(re).asBytes())); + BOOST_CHECK_EQUAL(result->status(), (int32_t)TransactionStatus::None); + } + + // alt_bn128_G1_add + /* + { + string32 ax = + toString32("0000000000000000000000000000000000000000000000000000000000000003"); + string32 ay = + toString32("0000000000000000000000000000000000000000000000000000000000000003"); + string32 bx = + toString32("0000000000000000000000000000000000000000000000000000000000000009"); + string32 by = + toString32("0000000000000000000000000000000000000000000000000000000000000009"); + bytes encodedData = + codec->encodeWithSig("callBn256Add(bytes32,bytes32,bytes32,bytes32)", ax, ay, bx, by); + + auto result = callTest(number++, ref(encodedData)); + + std::vector re; + codec->decode(result->data(), re); + uint64_t gasUsed; + auto logEntry = result->takeLogEntries(); + codec->decode(logEntry.at(0).data(), gasUsed); + std::cout << "alt_bn128_G1_add gasUsed: " << gasUsed << std::endl; + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000000000", + toHexStringWithPrefix(fromString32(re[0]).asBytes())); + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000000000", + toHexStringWithPrefix(fromString32(re[1]).asBytes())); + BOOST_CHECK_EQUAL(result->status(), (int32_t)TransactionStatus::None); + } + + + // alt_bn128_G1_mul + { + string32 x = + toString32("0000000000000000000000000000000000000000000000000000000000000003"); + string32 y = + toString32("0000000000000000000000000000000000000000000000000000000000000003"); + string32 scalar = + toString32("0000000000000000000000000000000000000000000000000000000000000009"); + bytes encodedData = + codec->encodeWithSig("callBn256ScalarMul(bytes32,bytes32,bytes32)", x, y, scalar); + + auto result = callTest(number++, ref(encodedData)); + + std::vector re; + codec->decode(result->data(), re); + uint64_t gasUsed; + auto logEntry = result->takeLogEntries(); + codec->decode(logEntry.at(0).data(), gasUsed); + std::cout << "alt_bn128_G1_add gasUsed: " << gasUsed << std::endl; + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000000000", + toHexStringWithPrefix(fromString32(re[0]).asBytes())); + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000000000", + toHexStringWithPrefix(fromString32(re[1]).asBytes())); + BOOST_CHECK_EQUAL(result->status(), (int32_t)TransactionStatus::None); + } + */ + + // alt_bn128_pairing_product + // blake2 + + // addmod + { + u256 x = 100; + u256 y = 89; + u256 k = 99; + bytes encodedData = codec->encodeWithSig("addmodTest(uint256,uint256,uint256)", x, y, k); + + auto result = callTest(number++, ref(encodedData)); + + u256 re; + codec->decode(result->data(), re); + uint64_t gasUsed; + auto logEntry = result->takeLogEntries(); + codec->decode(logEntry.at(0).data(), gasUsed); + std::cout << "addmod gasUsed: " << gasUsed << std::endl; + BOOST_CHECK_EQUAL(re, 90); + BOOST_CHECK_EQUAL(result->status(), (int32_t)TransactionStatus::None); + } + + // mulmod + { + u256 x = 3; + u256 y = 89; + u256 k = 99; + bytes encodedData = codec->encodeWithSig("mulmodTest(uint256,uint256,uint256)", x, y, k); + + auto result = callTest(number++, ref(encodedData)); + + u256 re; + codec->decode(result->data(), re); + BOOST_CHECK_EQUAL(re, 69); + BOOST_CHECK_EQUAL(result->status(), (int32_t)TransactionStatus::None); + } + + // gasLimit + { + bytes encodedData = codec->encodeWithSig("gasLimitTest()"); + + auto result = callTest(number++, ref(encodedData)); + + u256 re; + codec->decode(result->data(), re); + BOOST_CHECK_EQUAL(re, gas); + BOOST_CHECK_EQUAL(result->status(), (int32_t)TransactionStatus::None); + } + + // number + { + bytes encodedData = codec->encodeWithSig("numberTest()"); + + number++; + auto _number = number; + auto result = callTest(_number, ref(encodedData)); + + u256 re; + codec->decode(result->data(), re); + BOOST_CHECK_EQUAL(re, _number); + BOOST_CHECK_EQUAL(result->status(), (int32_t)TransactionStatus::None); + } + + // timestamp + { + bytes encodedData = codec->encodeWithSig("timeStampTest()"); + + auto result = callTest(number++, ref(encodedData)); + + u256 re; + codec->decode(result->data(), re); + BOOST_CHECK_LT(re, utcTime()); + BOOST_CHECK_EQUAL(result->status(), (int32_t)TransactionStatus::None); + } + + // callData + { + bytes encodedData = codec->encodeWithSig("callDataTest()"); + + auto result = callTest(number++, ref(encodedData)); + + bytes re; + codec->decode(result->data(), re); + BOOST_CHECK_EQUAL(toHexStringWithPrefix(re), toHexStringWithPrefix(encodedData)); + BOOST_CHECK_EQUAL(result->status(), (int32_t)TransactionStatus::None); + } + + // sender + { + bytes encodedData = codec->encodeWithSig("senderTest()"); + + auto result = callTest(number++, ref(encodedData)); + + Address re; + codec->decode(result->data(), re); + BOOST_CHECK_EQUAL(re, Address(sender)); + BOOST_CHECK_EQUAL(result->status(), (int32_t)TransactionStatus::None); + } + + // origin + { + bytes encodedData = codec->encodeWithSig("originTest()"); + + auto result = callTest(number++, ref(encodedData), origin); + + Address re; + codec->decode(result->data(), re); + BOOST_CHECK_EQUAL(re, Address(origin)); + BOOST_CHECK_EQUAL(result->status(), (int32_t)TransactionStatus::None); + } + + // blockHash +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/FileSystemPrecompiledTest.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/FileSystemPrecompiledTest.cpp" new file mode 100644 index 00000000..af3def03 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/FileSystemPrecompiledTest.cpp" @@ -0,0 +1,1746 @@ +/** + * Copyright (C) 2021 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 FileSystemPrecompiledTest.cpp + * @author: kyonRay + * @date 2021-06-20 + */ + +#include "libprecompiled/PreCompiledFixture.h" +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::precompiled; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::ledger; + +namespace bcos::test +{ +class FileSystemPrecompiledFixture : public PrecompiledFixture +{ +public: + FileSystemPrecompiledFixture() = default; + + ~FileSystemPrecompiledFixture() override = default; + + void init(bool _isWasm, protocol::BlockVersion version = BlockVersion::V3_1_VERSION) + { + setIsWasm(_isWasm, false, true, version); + bfsAddress = _isWasm ? precompiled::BFS_NAME : BFS_ADDRESS; + tableAddress = _isWasm ? precompiled::KV_TABLE_NAME : KV_TABLE_ADDRESS; + tableTestAddress1 = Address("0x420f853b49838bd3e9466c85a4cc3428c960dde2").hex(); + tableTestAddress2 = Address("0x420f853b49838bd3e9466c85a4cc3428c9601234").hex(); + + if (_isWasm) + { + auto result1 = creatKVTable(1, "test1", "id", "item1", "/tables/test1"); + BOOST_CHECK(result1->data().toBytes() == codec->encode(int32_t(0))); + auto result2 = creatKVTable(2, "test2", "id", "item1", "/tables/test2"); + BOOST_CHECK(result2->data().toBytes() == codec->encode(int32_t(0))); + } + else + { + auto result1 = creatKVTable(1, "test1", "id", "item1", tableTestAddress1); + BOOST_CHECK(result1->data().toBytes() == codec->encode(int32_t(0))); + auto result2 = creatKVTable(2, "test2", "id", "item1", tableTestAddress2); + BOOST_CHECK(result2->data().toBytes() == codec->encode(int32_t(0))); + } + + h256 addressCreate("ff6f30856ad3bae00b1169808488502786a13e3c174d85682135ffd51310310e"); + addressString = addressCreate.hex().substr(0, 40); + } + + void deployHelloContract(protocol::BlockNumber _number, std::string const& address) + { + std::string helloBin = + "608060405234801561001057600080fd5b506040805190810160405280600d81526020017f48656c6c6f2c" + "20576f726c6421000000000000000000000000000000000000008152506000908051906020019061005c92" + "9190610062565b50610107565b828054600181600116156101000203166002900490600052602060002090" + "601f016020900481019282601f106100a357805160ff19168380011785556100d1565b8280016001018555" + "82156100d1579182015b828111156100d05782518255916020019190600101906100b5565b5b5090506100" + "de91906100e2565b5090565b61010491905b808211156101005760008160009055506001016100e8565b50" + "90565b90565b61047a806101166000396000f300608060405260043610610062576000357c010000000000" + "0000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100675780" + "634ed3885e146100f75780636d4ce63c14610160578063b8368615146101f0575b600080fd5b3480156100" + "7357600080fd5b5061007c610247565b604051808060200182810382528381815181526020019150805190" + "6020019080838360005b838110156100bc5780820151818401526020810190506100a1565b505050509050" + "90810190601f1680156100e95780820380516001836020036101000a031916815260200191505b50925050" + "5060405180910390f35b34801561010357600080fd5b5061015e6004803603810190808035906020019082" + "01803590602001908080601f01602080910402602001604051908101604052809392919081815260200183" + "838082843782019150505050505091929192905050506102e5565b005b34801561016c57600080fd5b5061" + "01756102ff565b604051808060200182810382528381815181526020019150805190602001908083836000" + "5b838110156101b557808201518184015260208101905061019a565b50505050905090810190601f168015" + "6101e25780820380516001836020036101000a031916815260200191505b509250505060405180910390f3" + "5b3480156101fc57600080fd5b506102056103a1565b604051808273ffffffffffffffffffffffffffffff" + "ffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390" + "f35b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160" + "405280929190818152602001828054600181600116156101000203166002900480156102dd5780601f1061" + "02b2576101008083540402835291602001916102dd565b820191906000526020600020905b815481529060" + "0101906020018083116102c057829003601f168201915b505050505081565b806000908051906020019061" + "02fb9291906103a9565b5050565b606060008054600181600116156101000203166002900480601f016020" + "80910402602001604051908101604052809291908181526020018280546001816001161561010002031660" + "02900480156103975780601f1061036c57610100808354040283529160200191610397565b820191906000" + "526020600020905b81548152906001019060200180831161037a57829003601f168201915b505050505090" + "5090565b600030905090565b82805460018160011615610100020316600290049060005260206000209060" + "1f016020900481019282601f106103ea57805160ff1916838001178555610418565b828001600101855582" + "15610418579182015b828111156104175782518255916020019190600101906103fc565b5b509050610425" + "9190610429565b5090565b61044b91905b8082111561044757600081600090555060010161042f565b5090" + "565b905600a165627a7a723058208c7b44898edb531f977931e72d1195b47424ff97a80e0d22932be8fab3" + "6bd9750029"; + bytes input; + boost::algorithm::unhex(helloBin, std::back_inserter(input)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", input, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + + auto params = std::make_unique(); + params->setContextID(99); + params->setSeq(1000); + params->setDepth(0); + + params->setOrigin(sender); + params->setFrom(sender); + + // toChecksumAddress(addressString, hashImpl); + params->setTo(address); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::TXHASH); + params->setTransactionHash(hash); + params->setCreate(true); + + nextBlock(_number); + // -------------------------------- + // Create contract HelloWorld + // -------------------------------- + + std::promise executePromise; + executor->dmcExecuteTransaction( + std::move(params), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + commitBlock(_number); + BOOST_CHECK(result); + BOOST_CHECK_EQUAL(result->type(), ExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(result->contextID(), 99); + BOOST_CHECK_EQUAL(result->seq(), 1000); + BOOST_CHECK_EQUAL(result->create(), false); + BOOST_CHECK_EQUAL(result->newEVMContractAddress(), address); + BOOST_CHECK_EQUAL(result->origin(), sender); + BOOST_CHECK_EQUAL(result->from(), address); + } + + ExecutionMessage::UniquePtr creatKVTable(protocol::BlockNumber _number, + const std::string& tableName, const std::string& key, const std::string& value, + const std::string& solidityAddress, int _errorCode = 0, bool errorInTableManager = false) + { + nextBlock(_number, m_blockVersion); + bytes in = + codec->encodeWithSig("createKVTable(string,string,string)", tableName, key, value); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 100, 10000, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(100); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(isWasm ? TABLE_MANAGER_NAME : TABLE_MANAGER_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + // call precompiled + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + if (errorInTableManager) + { + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode))); + } + commitBlock(_number); + return result2; + } + + // set new address + result2->setTo(solidityAddress); + // external create + result2->setSeq(1001); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + result3->setSeq(1002); + // external call bfs + std::promise executePromise4; + executor->dmcExecuteTransaction(std::move(result3), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise4.set_value(std::move(result)); + }); + auto result4 = executePromise4.get_future().get(); + + // call bfs success, callback to create + result4->setSeq(1001); + + std::promise executePromise5; + executor->dmcExecuteTransaction(std::move(result4), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise5.set_value(std::move(result)); + }); + auto result5 = executePromise5.get_future().get(); + + // create success, callback to precompiled + result5->setSeq(1000); + + std::promise executePromise6; + executor->dmcExecuteTransaction(std::move(result5), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise6.set_value(std::move(result)); + }); + auto result6 = executePromise6.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result6->data().toBytes() == codec->encode(s256(_errorCode))); + } + commitBlock(_number); + return result6; + }; + + ExecutionMessage::UniquePtr mkdir(protocol::BlockNumber _number, std::string const& path, + int _errorCode = 0, bool errorInPrecompiled = false) + { + bytes in = codec->encodeWithSig("mkdir(string)", path); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(bfsAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + nextBlock(_number, m_blockVersion); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + if (errorInPrecompiled) + { + commitBlock(_number); + return result2; + } + // call precompiled + result2->setSeq(1001); + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + result3->setSeq(1000); + std::promise executePromise4; + executor->dmcExecuteTransaction(std::move(result3), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise4.set_value(std::move(result)); + }); + auto result4 = executePromise4.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result4->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result4; + }; + + ExecutionMessage::UniquePtr link([[maybe_unused]] bool _isWasm, protocol::BlockNumber _number, + std::string const& name, std::string const& version, std::string const& address, + std::string const& abi, int _errorCode = 0, bool _isCover = false) + { + bytes in; + if (version.empty()) + { + in = codec->encodeWithSig("link(string,string,string)", name, address, abi); + } + else + { + in = codec->encodeWithSig( + "link(string,string,string,string)", name, version, address, abi); + } + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(bfsAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + nextBlock(_number, m_blockVersion); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + // if cover write link, then + // no need to touch new file external call + if (_isCover) + { + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result2; + } + + result2->setSeq(1001); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + result3->setSeq(1000); + + std::promise executePromise4; + executor->dmcExecuteTransaction(std::move(result3), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise4.set_value(std::move(result)); + }); + auto result4 = executePromise4.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result4->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result4; + }; + + ExecutionMessage::UniquePtr readlink( + protocol::BlockNumber _number, std::string const& _path, int _errorCode = 0) + { + bytes in = codec->encodeWithSig("readlink(string)", _path); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(bfsAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + nextBlock(_number, m_blockVersion); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result2; + }; + + ExecutionMessage::UniquePtr rebuildBfs( + protocol::BlockNumber _number, uint32_t from, uint32_t to, int _errorCode = 0) + { + bytes in = codec->encodeWithSig("rebuildBfs(uint256,uint256)", from, to); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + Address newSender = Address(isWasm ? std::string(precompiled::SYS_CONFIG_NAME) : + std::string(precompiled::SYS_CONFIG_ADDRESS)); + tx->forceSender(newSender.asBytes()); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(bfsAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + nextBlock(_number, m_blockVersion); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result2; + }; + + ExecutionMessage::UniquePtr rebuildBfsBySysConfig( + protocol::BlockNumber _number, std::string version, int _errorCode = 0) + { + bytes in = codec->encodeWithSig("setValueByKey(string,string)", + std::string(ledger::SYSTEM_KEY_COMPATIBILITY_VERSION), version); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + Address newSender = Address(std::string(precompiled::AUTH_COMMITTEE_ADDRESS)); + tx->forceSender(newSender.asBytes()); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(isWasm ? SYS_CONFIG_NAME : SYS_CONFIG_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + nextBlock(_number, m_blockVersion); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + // call to BFS + result2->setSeq(1001); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + // BFS callback to sys + result3->setSeq(1000); + + std::promise executePromise4; + executor->dmcExecuteTransaction(std::move(result3), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise4.set_value(std::move(result)); + }); + auto result4 = executePromise4.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result4->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result4; + }; + + ExecutionMessage::UniquePtr listPage(protocol::BlockNumber _number, std::string const& path, + uint32_t offset, uint32_t count, int _errorCode = 0) + { + bytes in = codec->encodeWithSig("list(string,uint256,uint256)", path, u256(offset), u256(count)); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(isWasm ? BFS_NAME : BFS_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + nextBlock(_number, m_blockVersion); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + if (_errorCode != 0) + { + std::vector empty; + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode), empty)); + } + + commitBlock(_number); + return result2; + }; + + std::string sender; + std::string addressString; + std::string bfsAddress; + std::string tableAddress; + std::string tableTestAddress1; + std::string tableTestAddress2; +}; +BOOST_FIXTURE_TEST_SUITE(precompiledFileSystemTest, FileSystemPrecompiledFixture) + +BOOST_AUTO_TEST_CASE(lsTest) +{ + init(false); + BlockNumber _number = 3; + + // ls dir + { + auto result = list(_number++, "/tables"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 2); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test1"); + BOOST_CHECK(std::get<0>(ls.at(1)) == "test2"); + } + + // ls regular + { + auto result = list(_number++, "/tables/test2"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 1); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test2"); + BOOST_CHECK(std::get<1>(ls.at(0)) == tool::FS_TYPE_LINK); + } + + // ls not exist + { + auto result = list(_number++, "/tables/test3", CODE_FILE_NOT_EXIST); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == s256((int)CODE_FILE_NOT_EXIST)); + BOOST_CHECK(ls.empty()); + } + + // ls invalid path + { + list(_number++, "", CODE_FILE_INVALID_PATH); + std::stringstream errorPath; + errorPath << std::setfill('0') << std::setw(56) << 1; + list(_number++, "/" + errorPath.str(), CODE_FILE_INVALID_PATH); + list(_number++, "/path/level/too/deep/not/over/six/", CODE_FILE_INVALID_PATH); + } + + // ls / + { + auto result = list(_number++, "/"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 4); + std::set lsSet; + for (const auto& item : ls | RANGES::views::transform([](auto&& bfs) -> std::string { + return std::get<0>(bfs); + })) + { + lsSet.insert(item); + } + + for (auto const& rootSub : tool::FS_ROOT_SUBS | RANGES::views::drop(1)) + { + BOOST_CHECK(lsSet.contains(std::string(rootSub.substr(1)))); + } + } + + // ls /sys + { + auto result = list(_number++, "/sys"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == precompiled::BFS_SYS_SUBS_COUNT); + std::set lsSet; + for (const auto& item : ls | RANGES::views::transform([](auto&& bfs) -> std::string { + return std::get<0>(bfs); + })) + { + lsSet.insert(item); + } + + for (auto const& sysSub : precompiled::BFS_SYS_SUBS | RANGES::views::drop(1)) + { + BOOST_CHECK(lsSet.contains(std::string(sysSub.substr(tool::FS_SYS_BIN.size() + 1)))); + } + } +} + +BOOST_AUTO_TEST_CASE(lsPageTest) +{ + init(false); + BlockNumber _number = 3; + + // ls dir + { + auto result = listPage(_number++, "/tables", 0, 500); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 2); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test1"); + BOOST_CHECK(std::get<0>(ls.at(1)) == "test2"); + + result = listPage(_number++, "/tables", 1, 2); + ls.clear(); + ls.shrink_to_fit(); + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 1); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test2"); + } + + // ls regular + { + auto result = listPage(_number++, "/tables/test2", 0, 500); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 1); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test2"); + BOOST_CHECK(std::get<1>(ls.at(0)) == tool::FS_TYPE_LINK); + } + + // ls not exist + { + auto result = listPage(_number++, "/tables/test3", 0, 500, CODE_FILE_NOT_EXIST); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == s256((int)CODE_FILE_NOT_EXIST)); + BOOST_CHECK(ls.empty()); + } + + // ls invalid path + { + listPage(_number++, "", 0, 500, CODE_FILE_INVALID_PATH); + std::stringstream errorPath; + errorPath << std::setfill('0') << std::setw(56) << 1; + listPage(_number++, "/" + errorPath.str(), 0, 500, CODE_FILE_INVALID_PATH); + listPage(_number++, "/path/level/too/deep/not/over/six/", 0, 500, CODE_FILE_INVALID_PATH); + } + + // ls / + { + auto result = listPage(_number++, "/", 0, 500); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 4); + std::set lsSet; + for (const auto& item : ls | RANGES::views::transform([](auto&& bfs) -> std::string { + return std::get<0>(bfs); + })) + { + lsSet.insert(item); + } + + for (auto const& rootSub : tool::FS_ROOT_SUBS | RANGES::views::drop(1)) + { + BOOST_CHECK(lsSet.contains(std::string(rootSub.substr(1)))); + } + } + + // ls /sys + { + auto result = listPage(_number++, "/sys", 0, 500); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == precompiled::BFS_SYS_SUBS_COUNT); + std::set lsSet; + for (const auto& item : ls | RANGES::views::transform([](auto&& bfs) -> std::string { + return std::get<0>(bfs); + })) + { + lsSet.insert(item); + } + + for (auto const& sysSub : precompiled::BFS_SYS_SUBS | RANGES::views::drop(1)) + { + BOOST_CHECK(lsSet.contains(std::string(sysSub.substr(tool::FS_SYS_BIN.size() + 1)))); + } + } +} + +BOOST_AUTO_TEST_CASE(lsPagWasmeTest) +{ + init(true); + BlockNumber _number = 3; + + // ls dir + { + auto result = listPage(_number++, "/tables", 0, 500); + s256 code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 2); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test1"); + BOOST_CHECK(std::get<0>(ls.at(1)) == "test2"); + + result = listPage(_number++, "/tables", 1, 2); + ls.clear(); + ls.shrink_to_fit(); + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 1); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test2"); + } + + // ls regular + { + auto result = listPage(_number++, "/tables/test2", 0, 500); + s256 code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 1); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test2"); + BOOST_CHECK(std::get<1>(ls.at(0)) == tool::FS_TYPE_CONTRACT); + } + + // ls not exist + { + auto result = listPage(_number++, "/tables/test3", 0, 500, CODE_FILE_NOT_EXIST); + s256 code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == s256((int)CODE_FILE_NOT_EXIST)); + BOOST_CHECK(ls.empty()); + } + + // ls invalid path + { + listPage(_number++, "", 0, 500, CODE_FILE_INVALID_PATH); + std::stringstream errorPath; + errorPath << std::setfill('0') << std::setw(56) << 1; + listPage(_number++, "/" + errorPath.str(), 0, 500, CODE_FILE_INVALID_PATH); + listPage(_number++, "/path/level/too/deep/not/over/six/", 0, 500, CODE_FILE_INVALID_PATH); + } + + // ls / + { + auto result = listPage(_number++, "/", 0, 500); + s256 code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 4); + std::set lsSet; + for (const auto& item : ls | RANGES::views::transform([](auto&& bfs) -> std::string { + return std::get<0>(bfs); + })) + { + lsSet.insert(item); + } + + for (auto const& rootSub : tool::FS_ROOT_SUBS | RANGES::views::drop(1)) + { + BOOST_CHECK(lsSet.contains(std::string(rootSub.substr(1)))); + } + } + + // ls /sys + { + auto result = listPage(_number++, "/sys", 0, 500); + s256 code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == precompiled::BFS_SYS_SUBS_COUNT); + std::set lsSet; + for (const auto& item : ls | RANGES::views::transform([](auto&& bfs) -> std::string { + return std::get<0>(bfs); + })) + { + lsSet.insert(item); + } + + for (auto const& sysSub : precompiled::BFS_SYS_SUBS | RANGES::views::drop(1)) + { + BOOST_CHECK(lsSet.contains(std::string(sysSub.substr(tool::FS_SYS_BIN.size() + 1)))); + } + } +} + +BOOST_AUTO_TEST_CASE(lsTest_3_0) +{ + init(false, BlockVersion::V3_0_VERSION); + m_blockVersion = BlockVersion::V3_0_VERSION; + BlockNumber _number = 3; + + // ls dir + { + auto result = list(_number++, "/tables"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 2); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test1"); + BOOST_CHECK(std::get<0>(ls.at(1)) == "test2"); + } + + // ls regular + { + auto result = list(_number++, "/tables/test2"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 1); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test2"); + BOOST_CHECK(std::get<1>(ls.at(0)) == tool::FS_TYPE_LINK); + } + + // ls not exist + { + auto result = list(_number++, "/tables/test3", CODE_FILE_NOT_EXIST); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == s256((int)CODE_FILE_NOT_EXIST)); + BOOST_CHECK(ls.empty()); + } + + // ls / + { + auto result = list(_number++, "/"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 3); // with '/' + } + + // mkdir invalid path + { + list(_number++, "", CODE_FILE_INVALID_PATH); + std::stringstream errorPath; + errorPath << std::setfill('0') << std::setw(56) << 1; + list(_number++, "/" + errorPath.str(), CODE_FILE_INVALID_PATH); + list(_number++, "/path/level/too/deep/not/over/six/", CODE_FILE_INVALID_PATH); + } +} + +BOOST_AUTO_TEST_CASE(lsTestWasm) +{ + init(true); + BlockNumber _number = 3; + // ls dir + { + auto result = list(_number++, "/tables"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 2); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test1"); + BOOST_CHECK(std::get<0>(ls.at(1)) == "test2"); + } + + // ls regular + { + auto result = list(_number++, "/tables/test2"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 1); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test2"); + BOOST_CHECK(std::get<1>(ls.at(0)) == executor::FS_TYPE_CONTRACT); + } + + // ls not exist + { + auto result = list(_number++, "/tables/test3", CODE_FILE_NOT_EXIST); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == s256((int)CODE_FILE_NOT_EXIST)); + BOOST_CHECK(ls.empty()); + } + + // ls / + { + auto result = list(_number++, "/"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 4); // with '/' + } + + // mkdir invalid path + { + list(_number++, "", CODE_FILE_INVALID_PATH); + std::stringstream errorPath; + errorPath << std::setfill('0') << std::setw(56) << 1; + list(_number++, "/" + errorPath.str(), CODE_FILE_INVALID_PATH); + list(_number++, "/path/level/too/deep/not/over/six/", CODE_FILE_INVALID_PATH); + } +} + +BOOST_AUTO_TEST_CASE(lsTestWasm_3_0) +{ + init(true, BlockVersion::V3_0_VERSION); + BlockNumber _number = 3; + // ls dir + { + auto result = list(_number++, "/tables"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 2); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test1"); + BOOST_CHECK(std::get<0>(ls.at(1)) == "test2"); + } + + // ls regular + { + auto result = list(_number++, "/tables/test2"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 1); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test2"); + BOOST_CHECK(std::get<1>(ls.at(0)) == executor::FS_TYPE_CONTRACT); + } + + // ls not exist + { + auto result = list(_number++, "/tables/test3", CODE_FILE_NOT_EXIST); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == s256((int)CODE_FILE_NOT_EXIST)); + BOOST_CHECK(ls.empty()); + } + + // ls / + { + auto result = list(_number++, "/"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 3); // with '/' + } + + // mkdir invalid path + { + list(_number++, "", CODE_FILE_INVALID_PATH); + std::stringstream errorPath; + errorPath << std::setfill('0') << std::setw(56) << 1; + list(_number++, "/" + errorPath.str(), CODE_FILE_INVALID_PATH); + list(_number++, "/path/level/too/deep/not/over/six/", CODE_FILE_INVALID_PATH); + } +} + +BOOST_AUTO_TEST_CASE(mkdirTest) +{ + init(false); + BlockNumber _number = 3; + // simple mkdir + { + auto result = mkdir(_number++, "/tables/temp/test"); + s256 m; + codec->decode(result->data(), m); + BOOST_TEST(m == 0u); + + auto lsResult = list(_number++, "/tables"); + std::vector ls; + s256 code; + codec->decode(lsResult->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 3); + + auto lsResult2 = list(_number++, "/tables/temp"); + std::vector ls2; + codec->decode(lsResult2->data(), code, ls2); + BOOST_CHECK(ls2.size() == 1); + BOOST_CHECK(std::get<0>(ls2[0]) == "test"); + BOOST_CHECK(std::get<1>(ls2[0]) == executor::FS_TYPE_DIR); + } + + // mkdir /tables/test1/test + { + auto result = mkdir(_number++, "/tables/test1/test", CODE_FILE_BUILD_DIR_FAILED); + } + + // mkdir /tables/test1 + { + auto result = mkdir(_number++, "/tables/test1", 0, true); + BOOST_CHECK(result->data().toBytes() == codec->encode(s256((int)CODE_FILE_ALREADY_EXIST))); + } + + // mkdir /tables + { + auto result = mkdir(_number++, "/tables", 0, true); + BOOST_CHECK(result->data().toBytes() == codec->encode(s256((int)CODE_FILE_ALREADY_EXIST))); + } + + // mkdir in wrong path + { + auto result = mkdir(_number++, "/sys/test1", CODE_FILE_INVALID_PATH); + auto result2 = mkdir(_number++, "/user/test1", CODE_FILE_INVALID_PATH); + auto result3 = mkdir(_number++, "/test1", CODE_FILE_INVALID_PATH); + } + + // mkdir invalid path + { + mkdir(_number++, "", CODE_FILE_INVALID_PATH); + std::stringstream errorPath; + errorPath << std::setfill('0') << std::setw(56) << 1; + mkdir(_number++, "/" + errorPath.str(), CODE_FILE_INVALID_PATH); + mkdir(_number++, "/path/level/too/deep/not/over/six/", CODE_FILE_INVALID_PATH); + } +} + +BOOST_AUTO_TEST_CASE(mkdirTest_3_0) +{ + init(false, protocol::BlockVersion::V3_0_VERSION); + BlockNumber _number = 3; + // simple mkdir + { + auto result = mkdir(_number++, "/tables/temp/test"); + s256 m; + codec->decode(result->data(), m); + BOOST_TEST(m == 0u); + + auto lsResult = list(_number++, "/tables"); + std::vector ls; + s256 code; + codec->decode(lsResult->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 3); + + auto lsResult2 = list(_number++, "/tables/temp"); + std::vector ls2; + codec->decode(lsResult2->data(), code, ls2); + BOOST_CHECK(ls2.size() == 1); + BOOST_CHECK(std::get<0>(ls2[0]) == "test"); + BOOST_CHECK(std::get<1>(ls2[0]) == executor::FS_TYPE_DIR); + } + + // mkdir /tables/test1/test + { + auto result = mkdir(_number++, "/tables/test1/test", CODE_FILE_BUILD_DIR_FAILED); + } + + // mkdir /tables/test1 + { + auto result = mkdir(_number++, "/tables/test1", 0, true); + BOOST_CHECK(result->data().toBytes() == codec->encode(s256((int)CODE_FILE_ALREADY_EXIST))); + } + + // mkdir /tables + { + auto result = mkdir(_number++, "/tables", 0, true); + BOOST_CHECK(result->data().toBytes() == codec->encode(s256((int)CODE_FILE_ALREADY_EXIST))); + } + + // mkdir in wrong path + { + auto result = mkdir(_number++, "/sys/test1", CODE_FILE_INVALID_PATH); + auto result2 = mkdir(_number++, "/user/test1", CODE_FILE_INVALID_PATH); + auto result3 = mkdir(_number++, "/test1", CODE_FILE_INVALID_PATH); + } + + // mkdir invalid path + { + mkdir(_number++, "", CODE_FILE_INVALID_PATH); + std::stringstream errorPath; + errorPath << std::setfill('0') << std::setw(56) << 1; + mkdir(_number++, "/" + errorPath.str(), CODE_FILE_INVALID_PATH); + mkdir(_number++, "/path/level/too/deep/not/over/six/", CODE_FILE_INVALID_PATH); + } +} + +BOOST_AUTO_TEST_CASE(linkTest) +{ + init(false); + BlockNumber number = 3; + deployHelloContract(number++, addressString); + + std::string contractName = "Hello"; + std::string contractVersion = "1.0"; + std::string contractAbi = + "[{\"constant\":false,\"inputs\":[{\"name\":" + "\"num\",\"type\":\"uint256\"}],\"name\":" + "\"trans\",\"outputs\":[],\"payable\":false," + "\"type\":\"function\"},{\"constant\":true," + "\"inputs\":[],\"name\":\"get\",\"outputs\":[{" + "\"name\":\"\",\"type\":\"uint256\"}]," + "\"payable\":false,\"type\":\"function\"},{" + "\"inputs\":[],\"payable\":false,\"type\":" + "\"constructor\"}]"; + + // link overflow + std::string overflowVersion130 = + "012345678901234567890123456789012345678901234567890123456789" + "0123456789012345678901234567890123456789012345678901234567890123456789"; + // simple link + { + link(false, number++, contractName, contractVersion, addressString, contractAbi); + auto result = list(number++, "/apps/Hello"); + s256 code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(ls.size() == 1); + BOOST_CHECK(std::get<0>(ls.at(0)) == contractVersion); + BOOST_CHECK(std::get<1>(ls.at(0)) == tool::FS_TYPE_LINK); + + auto result2 = list(number++, "/apps/Hello/1.0"); + std::vector ls2; + codec->decode(result2->data(), code, ls2); + BOOST_CHECK(ls2.size() == 1); + BOOST_CHECK(std::get<0>(ls2.at(0)) == contractVersion); + BOOST_CHECK(std::get<1>(ls2.at(0)) == tool::FS_TYPE_LINK); + BOOST_CHECK(std::get<2>(ls2.at(0)).at(0) == addressString); + BOOST_CHECK(std::get<2>(ls2.at(0)).at(1) == contractAbi); + + auto result3 = readlink(number++, "/apps/Hello/1.0"); + Address address; + codec->decode(result3->data(), address); + BOOST_CHECK_EQUAL(address.hex(), addressString); + } + + // overwrite link + { + auto latestVersion = "latest"; + link(false, number++, contractName, latestVersion, addressString, contractAbi); + auto result = list(number++, "/apps/Hello"); + s256 code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(ls.size() == 2); + BOOST_CHECK(std::get<0>(ls.at(0)) == contractVersion); + BOOST_CHECK(std::get<1>(ls.at(0)) == tool::FS_TYPE_LINK); + + auto result2 = list(number++, "/apps/Hello/latest"); + std::vector ls2; + codec->decode(result2->data(), code, ls2); + BOOST_CHECK(ls2.size() == 1); + BOOST_CHECK(std::get<2>(ls2.at(0)).at(0) == addressString); + BOOST_CHECK(std::get<2>(ls2.at(0)).at(1) == contractAbi); + + auto resultR1 = readlink(number++, "/apps/Hello/1.0"); + Address address; + codec->decode(resultR1->data(), address); + BOOST_CHECK_EQUAL(address.hex(), addressString); + + // cover write + auto newAddress = "420f853b49838bd3e9466c85a4cc3428c960dde1"; + deployHelloContract(number++, newAddress); + link(false, number++, contractName, latestVersion, newAddress, contractAbi, 0, true); + auto result3 = list(number++, "/apps/Hello/latest"); + std::vector ls3; + codec->decode(result3->data(), code, ls3); + BOOST_CHECK(ls3.size() == 1); + BOOST_CHECK(std::get<2>(ls3.at(0)).at(0) == newAddress); + BOOST_CHECK(std::get<2>(ls3.at(0)).at(1) == contractAbi); + + auto resultR2 = readlink(number++, "/apps/Hello/latest"); + Address address2; + codec->decode(resultR2->data(), address2); + BOOST_CHECK_EQUAL(address2.hex(), newAddress); + } + + // wrong version + { + auto errorVersion = "ver/tion"; + link(false, number++, contractName, errorVersion, addressString, contractAbi, + CODE_ADDRESS_OR_VERSION_ERROR, true); + } + + // wrong address + { + auto wrongAddress = addressString; + std::reverse(wrongAddress.begin(), wrongAddress.end()); + link(false, number++, contractName, contractVersion, wrongAddress, contractAbi, + CODE_ADDRESS_OR_VERSION_ERROR, true); + } + + // overflow version + { + std::stringstream errorVersion; + for (size_t i = 0; i < FS_PATH_MAX_LENGTH - contractName.size(); ++i) + { + errorVersion << "1"; + } + link(false, number++, contractName, errorVersion.str(), addressString, contractAbi, + CODE_FILE_INVALID_PATH, true); + } + + // simple link without version + { + std::string newAbsolutePath = "link_test"; + link(false, number++, newAbsolutePath, "", addressString, contractAbi); + auto result = list(number++, "/apps/link_test"); + s256 code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(ls.size() == 1); + BOOST_CHECK(std::get<0>(ls.at(0)) == "link_test"); + BOOST_CHECK(std::get<1>(ls.at(0)) == tool::FS_TYPE_LINK); + BOOST_CHECK(std::get<2>(ls.at(0)).at(0) == addressString); + BOOST_CHECK(std::get<2>(ls.at(0)).at(1) == contractAbi); + + auto result3 = readlink(number++, "/apps/link_test"); + Address address; + codec->decode(result3->data(), address); + BOOST_CHECK_EQUAL(address.hex(), addressString); + } +} + +BOOST_AUTO_TEST_CASE(linkTest_3_0) +{ + init(false, protocol::BlockVersion::V3_0_VERSION); + BlockNumber number = 3; + deployHelloContract(number++, addressString); + + std::string contractName = "Hello"; + std::string contractVersion = "1.0"; + std::string contractAbi = + "[{\"constant\":false,\"inputs\":[{\"name\":" + "\"num\",\"type\":\"uint256\"}],\"name\":" + "\"trans\",\"outputs\":[],\"payable\":false," + "\"type\":\"function\"},{\"constant\":true," + "\"inputs\":[],\"name\":\"get\",\"outputs\":[{" + "\"name\":\"\",\"type\":\"uint256\"}]," + "\"payable\":false,\"type\":\"function\"},{" + "\"inputs\":[],\"payable\":false,\"type\":" + "\"constructor\"}]"; + + // link overflow + std::string overflowVersion130 = + "012345678901234567890123456789012345678901234567890123456789" + "0123456789012345678901234567890123456789012345678901234567890123456789"; + // simple link + { + link(false, number++, contractName, contractVersion, addressString, contractAbi); + auto result = list(number++, "/apps/Hello"); + s256 code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(ls.size() == 1); + BOOST_CHECK(std::get<0>(ls.at(0)) == contractVersion); + BOOST_CHECK(std::get<1>(ls.at(0)) == tool::FS_TYPE_LINK); + + auto result2 = list(number++, "/apps/Hello/1.0"); + std::vector ls2; + codec->decode(result2->data(), code, ls2); + BOOST_CHECK(ls2.size() == 1); + BOOST_CHECK(std::get<0>(ls2.at(0)) == contractVersion); + BOOST_CHECK(std::get<1>(ls2.at(0)) == tool::FS_TYPE_LINK); + BOOST_CHECK(std::get<2>(ls2.at(0)).at(0) == addressString); + BOOST_CHECK(std::get<2>(ls2.at(0)).at(1) == contractAbi); + + auto result3 = readlink(number++, "/apps/Hello/1.0"); + Address address; + codec->decode(result3->data(), address); + BOOST_CHECK_EQUAL(address.hex(), addressString); + } + + // overwrite link + { + auto latestVersion = "latest"; + link(false, number++, contractName, latestVersion, addressString, contractAbi); + auto result = list(number++, "/apps/Hello"); + s256 code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(ls.size() == 2); + BOOST_CHECK(std::get<0>(ls.at(0)) == contractVersion); + BOOST_CHECK(std::get<1>(ls.at(0)) == tool::FS_TYPE_LINK); + + auto result2 = list(number++, "/apps/Hello/latest"); + std::vector ls2; + codec->decode(result2->data(), code, ls2); + BOOST_CHECK(ls2.size() == 1); + BOOST_CHECK(std::get<2>(ls2.at(0)).at(0) == addressString); + BOOST_CHECK(std::get<2>(ls2.at(0)).at(1) == contractAbi); + + auto resultR1 = readlink(number++, "/apps/Hello/1.0"); + Address address; + codec->decode(resultR1->data(), address); + BOOST_CHECK_EQUAL(address.hex(), addressString); + + // cover write + auto newAddress = "420f853b49838bd3e9466c85a4cc3428c960dde1"; + deployHelloContract(number++, newAddress); + link(false, number++, contractName, latestVersion, newAddress, contractAbi, 0, true); + auto result3 = list(number++, "/apps/Hello/latest"); + std::vector ls3; + codec->decode(result3->data(), code, ls3); + BOOST_CHECK(ls3.size() == 1); + BOOST_CHECK(std::get<2>(ls3.at(0)).at(0) == newAddress); + BOOST_CHECK(std::get<2>(ls3.at(0)).at(1) == contractAbi); + + auto resultR2 = readlink(number++, "/apps/Hello/latest"); + Address address2; + codec->decode(resultR2->data(), address2); + BOOST_CHECK_EQUAL(address2.hex(), newAddress); + } + + // wrong version + { + auto errorVersion = "ver/tion"; + link(false, number++, contractName, errorVersion, addressString, contractAbi, + CODE_ADDRESS_OR_VERSION_ERROR, true); + } + + // wrong address + { + auto wrongAddress = addressString; + std::reverse(wrongAddress.begin(), wrongAddress.end()); + link(false, number++, contractName, contractVersion, wrongAddress, contractAbi, + CODE_ADDRESS_OR_VERSION_ERROR, true); + } + + // overflow version + { + std::stringstream errorVersion; + for (size_t i = 0; i < FS_PATH_MAX_LENGTH - contractName.size(); ++i) + { + errorVersion << "1"; + } + link(false, number++, contractName, errorVersion.str(), addressString, contractAbi, + CODE_FILE_INVALID_PATH, true); + } +} + +BOOST_AUTO_TEST_CASE(rebuildBfsTest) +{ + init(false, protocol::BlockVersion::V3_0_VERSION); + BlockNumber _number = 3; + + // ls dir + { + auto result = list(_number++, "/tables"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 2); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test1"); + BOOST_CHECK(std::get<0>(ls.at(1)) == "test2"); + } + + // ls regular + { + auto result = list(_number++, "/tables/test2"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 1); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test2"); + BOOST_CHECK(std::get<1>(ls.at(0)) == tool::FS_TYPE_LINK); + } + + // ls / + { + auto result = list(_number++, "/"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 3); + } + + const int32_t mkdirCount = 1000; + mkdir(_number++, "/apps/temp/temp2/temp3/temp4"); + + boost::log::core::get()->set_logging_enabled(false); + std::promise>> temp4p; + storage->asyncOpenTable( + "/apps/temp/temp2/temp3/temp4", [&](Error::UniquePtr _e, std::optional
_t) { + temp4p.set_value({std::move(_e), std::move(_t)}); + }); + auto [error, temp4T] = temp4p.get_future().get(); + auto subEntry = temp4T->getRow(tool::FS_KEY_SUB); + std::map bfsInfo; + auto&& out = asBytes(std::string(subEntry->get())); + codec::scale::decode(bfsInfo, gsl::make_span(out)); + for (int i = 0; i < mkdirCount; ++i) + { + bfsInfo.insert({"test" + std::to_string(i), std::string(tool::FS_TYPE_DIR)}); + } + subEntry->importFields({asString(codec::scale::encode(bfsInfo))}); + temp4T->setRow(tool::FS_KEY_SUB, std::move(subEntry.value())); + boost::log::core::get()->set_logging_enabled(true); + + // ls /apps/temp/temp2/temp3/temp4 + { + auto result = list(_number++, "/apps/temp/temp2/temp3/temp4"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == mkdirCount); + } + + + // upgrade to v3.1.0 + m_blockVersion = protocol::BlockVersion::V3_1_VERSION; + + boost::log::core::get()->set_logging_enabled(false); + rebuildBfs(_number++, (uint32_t)protocol::BlockVersion::V3_0_VERSION, + (uint32_t)protocol::BlockVersion::V3_1_VERSION); + boost::log::core::get()->set_logging_enabled(true); + + std::promise>> p; + storage->asyncOpenTable(tool::FS_ROOT, [&](Error::UniquePtr _e, std::optional
_t) { + p.set_value({std::move(_e), std::move(_t)}); + }); + auto [e, t] = p.get_future().get(); + BOOST_CHECK(t->getRow(tool::FS_APPS.substr(1)).has_value()); + BOOST_CHECK(t->getRow(tool::FS_USER_TABLE.substr(1)).has_value()); + BOOST_CHECK(t->getRow(tool::FS_SYS_BIN.substr(1)).has_value()); + BOOST_CHECK(!t->getRow(tool::FS_KEY_SUB).has_value()); + + // ls dir + { + auto result = list(_number++, "/tables"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 2); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test1"); + BOOST_CHECK(std::get<0>(ls.at(1)) == "test2"); + } + + // ls regular + { + auto result = list(_number++, "/tables/test2"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 1); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test2"); + BOOST_CHECK(std::get<1>(ls.at(0)) == tool::FS_TYPE_LINK); + } + + // ls / + { + auto result = list(_number++, "/"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 3); + } + + // ls /sys + { + auto result = list(_number++, "/sys"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == precompiled::BFS_SYS_SUBS_COUNT); + } + + // ls /apps/temp/temp2/temp3/temp4 + { + auto result = list(_number++, "/apps/temp/temp2/temp3/temp4"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == USER_TABLE_MAX_LIMIT_COUNT); + } + + // ls /apps/temp/temp2/temp3/temp4 + { + auto result = listPage(_number++, "/apps/temp/temp2/temp3/temp4", 0, 10000); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == mkdirCount); + } + + // rebuild again + rebuildBfs(_number++, (uint32_t)protocol::BlockVersion::V3_0_VERSION, + (uint32_t)protocol::BlockVersion::V3_1_VERSION); +} + +BOOST_AUTO_TEST_CASE(rebuildBfsBySysTest) +{ + init(false, protocol::BlockVersion::V3_0_VERSION); + BlockNumber _number = 3; + + // ls dir + { + auto result = list(_number++, "/tables"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 2); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test1"); + BOOST_CHECK(std::get<0>(ls.at(1)) == "test2"); + } + + // ls regular + { + auto result = list(_number++, "/tables/test2"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 1); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test2"); + BOOST_CHECK(std::get<1>(ls.at(0)) == tool::FS_TYPE_LINK); + } + + // ls / + { + auto result = list(_number++, "/"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 3); + } + + const int32_t mkdirCount = 1000; + mkdir(_number++, "/apps/temp/temp2/temp3/temp4"); + + boost::log::core::get()->set_logging_enabled(false); + std::promise>> temp4p; + storage->asyncOpenTable( + "/apps/temp/temp2/temp3/temp4", [&](Error::UniquePtr _e, std::optional
_t) { + temp4p.set_value({std::move(_e), std::move(_t)}); + }); + auto [error, temp4T] = temp4p.get_future().get(); + auto subEntry = temp4T->getRow(tool::FS_KEY_SUB); + std::map bfsInfo; + auto&& out = asBytes(std::string(subEntry->get())); + codec::scale::decode(bfsInfo, gsl::make_span(out)); + for (int i = 0; i < mkdirCount; ++i) + { + bfsInfo.insert({"test" + std::to_string(i), std::string(tool::FS_TYPE_DIR)}); + } + subEntry->importFields({asString(codec::scale::encode(bfsInfo))}); + temp4T->setRow(tool::FS_KEY_SUB, std::move(subEntry.value())); + boost::log::core::get()->set_logging_enabled(true); + + // ls /apps/temp/temp2/temp3/temp4 + { + auto result = list(_number++, "/apps/temp/temp2/temp3/temp4"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == mkdirCount); + } + + // upgrade to v3.1.0 + // boost::log::core::get()->set_logging_enabled(false); + auto updateNumber = _number++; + rebuildBfsBySysConfig(_number++, V3_1_VERSION_STR); + // boost::log::core::get()->set_logging_enabled(true); + + std::promise>> p; + storage->asyncOpenTable(tool::FS_ROOT, [&](Error::UniquePtr _e, std::optional
_t) { + p.set_value({std::move(_e), std::move(_t)}); + }); + auto [e, t] = p.get_future().get(); + BOOST_CHECK(t->getRow(tool::FS_APPS.substr(1)).has_value()); + BOOST_CHECK(t->getRow(tool::FS_USER_TABLE.substr(1)).has_value()); + BOOST_CHECK(t->getRow(tool::FS_SYS_BIN.substr(1)).has_value()); + BOOST_CHECK(!t->getRow(tool::FS_KEY_SUB).has_value()); + + m_blockVersion = BlockVersion::V3_1_VERSION; + + // ls dir + { + auto result = list(_number++, "/tables"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 2); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test1"); + BOOST_CHECK(std::get<0>(ls.at(1)) == "test2"); + } + + // ls regular + { + auto result = list(_number++, "/tables/test2"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 1); + BOOST_CHECK(std::get<0>(ls.at(0)) == "test2"); + BOOST_CHECK(std::get<1>(ls.at(0)) == tool::FS_TYPE_LINK); + } + + // ls / + { + auto result = list(_number++, "/"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == 3); + } + + // ls /sys + { + auto result = list(_number++, "/sys"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == precompiled::BFS_SYS_SUBS_COUNT); + } + + // ls /apps/temp/temp2/temp3/temp4 + { + auto result = list(_number++, "/apps/temp/temp2/temp3/temp4"); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == USER_TABLE_MAX_LIMIT_COUNT); + } + + // ls /apps/temp/temp2/temp3/temp4 + { + auto result = listPage(_number++, "/apps/temp/temp2/temp3/temp4", 0, 10000); + int32_t code; + std::vector ls; + codec->decode(result->data(), code, ls); + BOOST_CHECK(code == (int)CODE_SUCCESS); + BOOST_CHECK(ls.size() == mkdirCount); + } +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/GroupSigPrecompiledTest.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/GroupSigPrecompiledTest.cpp" new file mode 100644 index 00000000..a0aadfc8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/GroupSigPrecompiledTest.cpp" @@ -0,0 +1,255 @@ +/** + * Copyright (C) 2021 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. + */ +#include "bcos-executor/src/precompiled/extension/GroupSigPrecompiled.h" +#include "../mock/MockLedger.h" +#include "bcos-codec/abi/ContractABICodec.h" +#include "bcos-executor/src/executive/BlockContext.h" +#include "bcos-executor/src/executive/TransactionExecutive.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::precompiled; +using namespace bcos::executor; +using namespace bcos::storage; +namespace bcos::test +{ +struct GroupSigPrecompiledFixture +{ + GroupSigPrecompiledFixture() + { + m_hashImpl = std::make_shared(); + m_groupSigPrecompiled = std::make_shared(m_hashImpl); + m_ledgerCache = std::make_shared(std::make_shared()); + m_blockContext = std::make_shared(nullptr, m_ledgerCache, m_hashImpl, 0, + h256(), utcTime(), 0, FiscoBcosScheduleV4, false, false); + std::shared_ptr gasInjector = nullptr; + m_executive = std::make_shared( + std::weak_ptr(m_blockContext), "", 100, 0, gasInjector); + } + + ~GroupSigPrecompiledFixture() {} + + LedgerCache::Ptr m_ledgerCache; + bcos::crypto::Hash::Ptr m_hashImpl; + BlockContext::Ptr m_blockContext; + TransactionExecutive::Ptr m_executive; + GroupSigPrecompiled::Ptr m_groupSigPrecompiled; +}; + +BOOST_FIXTURE_TEST_SUITE(test_GroupSigPrecompiled, GroupSigPrecompiledFixture) + +BOOST_AUTO_TEST_CASE(TestGroupSigVerify) +{ + std::string signature = + "eyJUMSI6ICIxY2ZmMmRiMDUzNGQ3OWJmMjFmMmI1ZWIxZTQ3ZjcxMjgzNTRhYmZjNDNiZDQyZTdiNDUwZTUwZTE1ZT" + "VkOWZmMjNiOWFiNGNiZTQwODhhZGYxZGE3MjFlNTFmMjc5NTFhNjUyYTVmNjQ1MDM2MjBhOThiYjZjYWIxMjg3MWUz" + "Njg1MzdjMjVmZGUyMWRhMTM3ZjM3Zjg1MGZlMWFlMjcwN2RlODZjNGE0MTliOGY4NzFmOWQ5Yzc0NDdhMGMxNDQ3OG" + "E0MThlYWQzMWM3NTYzY2ZlMmM3ZjY0ZGVjODJiY2JjOTUyNTZhOGNmMGY2ZTliN2I4N2RiYmFhNWM0OTU4MWU2MiIs" + "IlQyIjogIjBjZDlhYTAzYjgyYjJkYjcwOGRhYjEzZDcxZDA5YTBmYThkZWRjODA1MGFiMjA0ZGYyNzBmMGU4NTA4YT" + "hlMTg4NTJhYTVmMTUzNjBkYzdiMThjNWNkNjk4MjcyOGVmYzVjMTU3MjBjZTkwZjMzY2JmN2FlYjM2MzRlNWQ1NmQw" + "ZjczMzdjZjllYjM5ZWZiZTIzYWZhMjNjNTU0ODc4MGY2ZDUwMzk1NzhiNjE1YjdmOTBmOTdiNWExZThmN2Y3ZjE3Yz" + "QzNjZiMmRhYWMwODdlZjc4YjY3YmZhOWZjMDBjZGRhMDM0Y2I0OTIzMjEzYmE1NjMyNmUzZWUzOWZhYzM3NWM1Iiwi" + "VDMiOiAiMGEwYjJhNTc5NjNlMWZmMGNiZjZmZjQyMjQ2MDFmMTY4MzA2ZWI1ZmU1MTY1NDMzMmIxYjU5N2UxYjA1ZG" + "QxM2NjZjY2MmJkN2VhMWNjZTNjYmQ1MTM1MzBkNzVhY2ZmMWMwY2JhMzg3ODI2YTk1NDk0MDZlY2FmNjM0M2EwYmY2" + "OTI2MzkyZDUwMGE0NzRhZDQ3NzQwMWNhMjliZmZmODJhYjc5NDJkMjJlNzBkOGJmZTUzMGUxMzQ1MWY4YWRjMThhOT" + "QwOWI5NDIzZTg5NzgzOGZjNjk3MGQyMTYyYTg3OGU2YjkxYTlhZWM5Nzk3MWNlMmQxOTUxYTJiOTgzNzAwZTAiLCJj" + "IjogIjdiMmU3ZDlmYjY5ODBiODhjYjMxY2QxZjQ1N2IxZDVkODI5NTYwNjI3MDU1NDZlMDY5NTc4YTA4YzRjNWJmNz" + "MwMDdiMmU3ZDlmYjY5ODBiODhjYjMxY2QxZjQ1N2IxZDVkODI5NTYwNjI3MDU1NDZlMDY5NTc4YTA4YzRjNWJmIiwi" + "cmFscGhhIjogIjE1ZmUxZmI1NGRhYTBlMTY0YTc0YmE1YWYxOGYzOTdiYjk4NmViYmEzODg0YzI4MzA1Y2I5MmQ3NT" + "M4ODdmYWZmMjFhMWNkNTQ3OGQyMTBjMGU5ZmZkNDU5MDlkMjY1M2E4MjY0ZWM3ODllMjY3ZDk2NzA0MjIzN2I0Y2M4" + "MDBlIiwicmJldGEiOiAiMTE0NDliYzhhYTMxMmIwOTQ2ZmVhNjliOWUxMTljMjBlNDEzYzIwODlmYjVkMmRmZjExND" + "FkMDllMjE4M2UwMmI2YTUzZjY0ODY3ODY3MWQxMWU2ODdjMTk0ZGJkYThmNzM5OWRhMTAyM2I3NzAyNGYzNjlkMzg2" + "YTE1MTZhYjkiLCJyZGVsdGExIjogImY3ZGI5ZWM2ODIwNGJjNDUxMjk3ZjY0NWQ1ZTk5ZDk1NDY1ZWQ3ZjUwYTgwND" + "VmYjcyMDJkN2ZhYTUxMGUyOGFjMDk1NWIwOGZiZTY5ZWU4Yjg3MGY2NjRhNjI3OTMwM2ViZjU3MmM4MzAzOTYwYTkz" + "MjFlZTY3ZTExN2JlNTVkIiwicmRlbHRhMiI6ICJkMzRhNTQ3MGU2ZWI1ZjdjZmRhNjIzYmVmYjVkNjQyYzFlMDY1Nj" + "lhOTAzODI5MDc5ODVkZTQ2NGUzYmUxZWQyMDViMzZmZjllNjY2NGZlMjhiMjBjZjdkMTQxYThmMGIyNTg1YTFiNjUx" + "OGQ3ZmFmODFhZWFlNzAzYmVhMWRiNSIsInJ4IjogIjY2N2NmMDk0NjdmNWY1MTMyZWY1NjUyYmI5ZDRiMDdmN2UxND" + "VkZDdiMDU2OGY2ZjkyMmM4OGZjOGE3Njg0MTA3ZWRlYjVmYTk3ZTA5OTE5ZTc4ZjM3M2NkMDFiY2ZlYmQwZGNhYjZm" + "ZjYzMWQ4OGM5OTBiYmQwMGEzNmFiZTgwIn0="; + std::string message1 = "test groupSigVerify"; + std::string gpkInfo = + "eyJnMSI6ICIxZmNkZWQ1ZGU0Zjc3ZTQ2MWE4N2Q0ZmY4ZmY5YjI1ZDQ5MzkxYjY2Y2MyNGUxN2E2NjYxODRhZDBhYm" + "IzZDcwYTc4MDgwNTNjMjAxODhiZjA0NjBiZTA1YjQ0YzEyYmFhMzVhNjUxM2YwM2ZlZTVjNjU3ZmRlYzA1MDBlNGVj" + "YTY1MTcyYzI2MmNiNzExMGNhZGE5ZDcyZTNmYmFiZTcxYThjOWFjNDZiMTA5MTVjNGIzNGZmZjQyOWVjMGJiNjU0ZD" + "Y3YzVlNjA2NzI3YjIxM2ZhY2JkMDA1N2ZjYjE3ZWYyM2QwOTNkNGY2ZmUxZjk2NmMzYmFjMjk4NjI4ZTJjZGMwMyIs" + "ImcyIjogIjI2MDhlZDNlODU3ZmFlZTllZDRiNjVkZjY5MTQzMzdlOTg5N2Q1Mjc1NGJlODg2ZDc4ZDFiMWFiMGQxMT" + "czZTQyNjBlYWVlOTg5YjY3MjdmZDFhNDdmMGE1Y2M1M2IyNDY3NzBhYTcxOGIyOGI1NmJhODJjZDNhM2M2NzgxMzkw" + "YWQzMjU2NGVhMzU3MDFmZWViOTExZWQzNzA5YjQ1MmI2N2RiMjhiNmUxZmZmZWZiZTE5Y2M2NzBkOTRkN2NhMDZiMm" + "RlNmU5YzVmYWI2ZjMxMjE0Njk2NjRlYWZkNWNkZWNlNmZhMGU3ZTE0MjEyMTY3OTE5MDI1N2JjMWE4MDc5M2VjIiwi" + "aCI6ICIwOGU4M2U0MWM5MDJlNmM5NzQxYTQ3YTg5MzRkOTBjNTgyZDBiZTUyZjBiOTMyZTU0OWJhZDU1MWYzNDdlYz" + "FmYmFlMjkzOGNjOWNiNGI5NzA1NjgzNjI2YmRiYzNlYjBjMDk0NzYxNmI3NTNiOGJiMDRkZjg1ZmI0NmZiZGE5ZDQx" + "MmZkMTc5MjU4ZWVkODllMTRjNTczOGU4ZDIwOTAxOGI2NTQ1N2Q0ZmQ4YzRjZTY1NWJmYzY2MGNiOWI5ZWVmOTgwMT" + "M3MWM0MGY2ZTRlOGYyODUyZWM0MjVlOTlkNDhkNzFmMTA4YWJiMzA0OWE2YjAyODA5NTNmYWNmOWFlNWE2OSIsInBy" + "X2cxX2cyIjogIjM0MjFhN2FlMmUwNjE0MDQ4NGFmMTgzYzQ1MWZiYzAwYWU4ZDhmYzE0ZDBhOTlmMWM1OTI3MmQwOT" + "Y2ZDNiYzdjOTVkZjdjZDI3NTI1YmI0ZTcwNGNjYzM2NGNiNTAwMTdhZjUzOWRhNTgyNzFhZjgyODViZGNhYjc4NTg4" + "NzQ4ODMyYjZhODFlZjEzODEwYWJlMGU0M2ViMmRiN2UyODliODU0MGVjMmEzNmRiZDc3YWQ3OTgyNzc5YTJjZTYyZj" + "M2YWI4MjczMzcxYmQwODJmZTJjNzNiMjYwNjE1MDlkYmVjZDFmMjI0MjA2M2MyZDk1ZDdiZjZmNDRjMzIzODlhNGM1" + "IiwicHJfZzFfZzJfaW52IjogIjM0MjFhN2FlMmUwNjE0MDQ4NGFmMTgzYzQ1MWZiYzAwYWU4ZDhmYzE0ZDBhOTlmMW" + "M1OTI3MmQwOTY2ZDNiYzdjOTVkZjdjZDI3NTI1YmI0ZTcwNGNjYzM2NGNiNTAwMTdhZjUzOWRhNTgyNzFhZjgyODVi" + "ZGNhYjc4NTg4NzQ4ODMxMDk1N2UxMGVjN2VmNTQxZjFiYzE0ZDI0ODFkNzY0N2FiZjEzZDVjOTI0Mjg4NTI4NjdkOD" + "g2NWQzMTlkMGM5NTQ3ZDhjYzhlNDJmN2QwMWQzOGM0ZDlmOWVhZjYyNDEzMmUwZGRiZGY5YzNkMjZhMjg0MDkwYmIw" + "MGRjNzY1Yjc2IiwicHJfaF9nMiI6ICIwZjBjYmJmOTI4ZTI1YTQwODZhMGE3OWMwOTQzOGFiY2Q5NzhiNjkzZmYxNz" + "djMGNmZTYzZWNjM2IzOGQ1OWE0NDc5ZDhjNjc2NGEwZGMyMzdjZjViMTAxMzg0Y2E4NThjNWIzYjk2ZjdhZmYxMWJi" + "MDdlZjM3ZTI0YmExZTMzOGNmMmNkMWY5N2I4YWNjOWJjNTgwMGRlMDc4MmRiZTZhMDQ1MDJiZDMyNjY0NGU0MjJiNj" + "ZmZjFjMTgxODg0MzU1M2UwMzBmZGU1YjIxYzRiMjVlMWZiZjRjODdkOGRmNjI3OGY4NmExZjRjNDFmMTA0YjQ0MzNk" + "NGViYWMzNWI0YTI4YiIsInByX2hfdyI6ICIxOTFlNjFjZDQ1OWZiMWVkMGVjYmRjZTdiMjMwMTc4OTY4Y2IwZmFjZm" + "Y0MzA5Y2NiOGRjYTg2NGEyM2IwOTI4M2Y0NjgxZTQ3MTM3NzFiOTVhMmFkYWRhZWE5Yjc4MTVhNjhjNDk4NmI2MmE2" + "ZGY3YmNkMzI3NzRiYTI0OTJjM2U5MmViOGZhNmVkZDVmNWIxZjY5N2IxZGRmY2Q5OWIxYzg3MDU1MDM4YmJhYjkyYz" + "YwMDMxNGM1MmI5YzRkOGU0OGVjMmIzM2ZjYzk3Y2ViMzg3Mzk2ZWQ0NDA5MzZiYWNlMjFjZDUwYzg0NjllY2U1MDY4" + "NmE1NGFmYjE1M2QxYzIwMCIsInUiOiAiMjYwZDkwNWFmZDQ2ZjRlYzY5YjZhMTYwNDE0OTUzMDM5OTE0NmNkYWVkZW" + "QxNDdkNzY2YjgwZDNiNzBhYzliODlmYTZiM2RmZDM1ZGQxMTEzNTcxYTc2NjIzNjdmYTQxNWQwMjVhNzdlNTVkZTdi" + "YmI2YjhmNzYwN2E4ZDhlNGI0MjA2ZmY0YzM5MjgzNmRkMjZhZWFiZGYzMjg1OTQ5MmM4NjU0ODM5ZGQ3MThjZTBkYj" + "U3OTEyYTBmNTQzNGIwYjQ0MDY1MGZmNDY1ZjVjMGIyZjg0Y2M0ZDA5ZTlkYTMyY2VlZWU4YjgwNTMyYjM2ODkyYmRj" + "MWNhMjE2ZGJkNDYyODgiLCJ2IjogIjM4ZTZhNDZmNzYwZTA3OGM4OTYzODgwYjk1MTMyYjdmODQ1YjliM2M0OTU2Mm" + "FjZjRiZjY4NDIwNDI2YzdjYzVmYjhkMjQxYTc4ZjA4YWJjMWUxOTgzZDg0YTQ5OTY2ZjFhZjJkZmEzNGY3OTAwMjJl" + "NjU2MTM4MzI5ZWFmNmEzOGYyNGZjYjIyNzk0YTY4Y2Q0NDA1Zjk4YzIzZjYxNmEwZWRiYWY5MDYwOTZlNmRjNTEzMT" + "k4MzA5NmE1Y2FkOGY2NGI5OTdlMjM3ODU1NmRlM2UyNmFiYzdkY2UzNzQ1ZTg3NDEwM2I5MDgxNzgyMzA0NzE5OWQ4" + "MWJmMGE1YzY0OTRmIiwidyI6ICIzMWZkMWYzMThiZGFhNGI5ODA1MDk2ZDY2MGNlMDgzZjJkZDhlMjg0MmNmOWQ1Mj" + "IzMzU4M2MyY2NlMGRkNWNmZDFhYWNhZjcxM2RkYTljM2Y4MmI3ODkxODU4ZjcxZGE0YzM5YzQyODFmZjFmZTI1NWRj" + "ODIxNzZlMjdlMmQzMmQ4Mjc5MTEzODFlZTI4OWJhOGJiM2ZiMmM1N2ZjNmQ0OTI2ZjEyMDNjNDI4ZDQ3ZjE5ZjhmMz" + "ViOTlmN2Q1ODQxZWViNWU0Yjg5YjhhMTc3NGQ2Yzc2OThlNzNhZmJlMmE5ZGUwNWUwZWY1MTQxZmZhOTEwZjMwZGI3" + "ZDk1MmQ0NTRhMiJ9"; + std::string paramInfo = + "dHlwZSBhIHEgODA0NDY4NDc1Nzk2NTU1ODI1OTc0NDQxNDk5ODkyMzUwNzY3NjQ4NzYxOTQ5MjM1NTQzNjAyNjYzND" + "EzNjg2NjIzMzA1ODQxODA0NDEyODE4NjA4MTEyNDU3ODkwMDE0MjA1NjYxNDAxOTExNDkxMTg5MTYzMDUxMjI1MjMy" + "OTY4NzE2Nzk0MTk2Nzg2MDE4NjgyNjY3MDA4MDU5IGggNjAgciAxMzQwNzgwNzkyOTk0MjU5NzA5OTU3NDAyNDk5OD" + "IwNTg0NjEyNzQ3OTM2NTgyMDU5MjM5MzM3NzcyMzU2MTQ0MzcyMTc2NDAzMDA3MzU0Njk3NjgwMTg3NDI5ODE2Njkw" + "MzQyNzY5MDAzMTg1ODE4NjQ4NjA1MDg1Mzc1Mzg4MjgxMTk0NjU2OTk0NjQzMzY0NDcxMTExNjgwMSBleHAyIDUxMi" + "BleHAxIDMyIHNpZ24xIC0xIHNpZ24wIDE="; + + GroupSigPrecompiledFixture fixture; + auto hashImpl = fixture.m_hashImpl; + + bcos::codec::abi::ContractABICodec abi(hashImpl); + bytes in = abi.abiIn( + "groupSigVerify(string,string,string,string)", signature, message1, gpkInfo, paramInfo); + + + auto groupSigPrecompiled = fixture.m_groupSigPrecompiled; + auto parameters = std::make_shared(); + parameters->m_input = bytesConstRef(in.data(), in.size()); + auto execResult = groupSigPrecompiled->call(fixture.m_executive, parameters); + bytes out = execResult->execResult(); + + bool result; + int retCode; + abi.abiOut(bytesConstRef(&out), retCode, result); + BOOST_TEST(true == result); + BOOST_TEST(0 == retCode); + + // false + std::string message2 = "groupSigVerify"; + in = abi.abiIn( + "groupSigVerify(string,string,string,string)", signature, message2, gpkInfo, paramInfo); + parameters->m_input = bytesConstRef(in.data(), in.size()); + execResult = groupSigPrecompiled->call(fixture.m_executive, parameters); + out = execResult->execResult(); + + abi.abiOut(bytesConstRef(&out), retCode, result); + BOOST_TEST(false == result); + BOOST_TEST(retCode == VERIFY_GROUP_SIG_FAILED); +} + +BOOST_AUTO_TEST_CASE(ErrorFunc) +{ + GroupSigPrecompiledFixture fixture; + auto hashImpl = fixture.m_hashImpl; + auto executive = fixture.m_executive; + auto groupSigPrecompiled = fixture.m_groupSigPrecompiled; + + bcos::codec::abi::ContractABICodec abi(hashImpl); + bytes in = abi.abiIn("groupSigVerify(string)", std::string("2AE3FFE2")); + auto parameters = std::make_shared(); + parameters->m_input = bytesConstRef(in.data(), in.size()); + auto execResult = groupSigPrecompiled->call(executive, parameters); + auto out = execResult->execResult(); + bool result; + int retCode; + abi.abiOut(bytesConstRef(&out), retCode, result); + BOOST_TEST(false == result); + BOOST_TEST(retCode == CODE_UNKNOW_FUNCTION_CALL); +} + +BOOST_AUTO_TEST_CASE(InvalidInputs) +{ + GroupSigPrecompiledFixture fixture; + auto hashImpl = fixture.m_hashImpl; + auto executive = fixture.m_executive; + auto groupSigPrecompiled = fixture.m_groupSigPrecompiled; + + // situation1 + bcos::codec::abi::ContractABICodec abi(hashImpl); + bytes in = abi.abiIn("groupSigVerify(string,string,string,string)", std::string("2AE3FFE2"), + std::string("2AE3FFE2"), std::string("2AE3FFE2"), std::string("2AE3FFE2")); + auto parameters = std::make_shared(); + parameters->m_input = bytesConstRef(in.data(), in.size()); + + auto execResult = groupSigPrecompiled->call(executive, parameters); + bytes out = execResult->execResult(); + int retCode; + bool result; + abi.abiOut(bytesConstRef(&out), retCode, result); + BOOST_TEST(retCode == VERIFY_GROUP_SIG_FAILED); + BOOST_TEST(result == false); + + + // situation2 + std::string signature2 = + "eyJUMiI6ICIwY2Q5YWEwM2I4MmIyZGI3MDhkYWIxM2Q3MWQwOWEwZmE4ZGVkYzgwNTBhYjIwNGRmMjcwZjBlODUwOG" + "E4ZTE4ODUyYWE1ZjE1MzYwZGM3YjE4YzVjZDY5ODI3MjhlZmM1YzE1NzIwY2U5MGYzM2NiZjdhZWIzNjM0ZTVkNTZk" + "MGY3MzM3Y2Y5ZWIzOWVmYmUyM2FmYTIzYzU1NDg3ODBmNmQ1MDM5NTc4YjYxNWI3ZjkwZjk3YjVhMWU4ZjdmN2YxN2" + "M0MzY2YjJkYWFjMDg3ZWY3OGI2N2JmYTlmYzAwY2RkYTAzNGNiNDkyMzIxM2JhNTYzMjZlM2VlMzlmYWMzNzVjNSIs" + "IlQzIjogIjBhMGIyYTU3OTYzZTFmZjBjYmY2ZmY0MjI0NjAxZjE2ODMwNmViNWZlNTE2NTQzMzJiMWI1OTdlMWIwNW" + "RkMTNjY2Y2NjJiZDdlYTFjY2UzY2JkNTEzNTMwZDc1YWNmZjFjMGNiYTM4NzgyNmE5NTQ5NDA2ZWNhZjYzNDNhMGJm" + "NjkyNjM5MmQ1MDBhNDc0YWQ0Nzc0MDFjYTI5YmZmZjgyYWI3OTQyZDIyZTcwZDhiZmU1MzBlMTM0NTFmOGFkYzE4YT" + "k0MDliOTQyM2U4OTc4MzhmYzY5NzBkMjE2MmE4NzhlNmI5MWE5YWVjOTc5NzFjZTJkMTk1MWEyYjk4MzcwMGUwIn0" + "="; + std::string message2 = "ringSigVerify"; + std::string gpkInfo2 = + "eyJnMSI6ICIxZmNkZWQ1ZGU0Zjc3ZTQ2MWE4N2Q0ZmY4ZmY5YjI1ZDQ5MzkxYjY2Y2MyNGUxN2E2NjYxODRhZDBhYm" + "IzZDcwYTc4MDgwNTNjMjAxODhiZjA0NjBiZTA1YjQ0YzEyYmFhMzVhNjUxM2YwM2ZlZTVjNjU3ZmRlYzA1MDBlNGVj" + "YTY1MTcyYzI2MmNiNzExMGNhZGE5ZDcyZTNmYmFiZTcxYThjOWFjNDZiMTA5MTVjNGIzNGZmZjQyOWVjMGJiNjU0ZD" + "Y3YzVlNjA2NzI3YjIxM2ZhY2JkMDA1N2ZjYjE3ZWYyM2QwOTNkNGY2ZmUxZjk2NmMzYmFjMjk4NjI4ZTJjZGMwMyIs" + "ImcyIjogIjI2MDhlZDNlODU3ZmFlZTllZDRiNjVkZjY5MTQzMzdlOTg5N2Q1Mjc1NGJlODg2ZDc4ZDFiMWFiMGQxMT" + "czZTQyNjBlYWVlOTg5YjY3MjdmZDFhNDdmMGE1Y2M1M2IyNDY3NzBhYTcxOGIyOGI1NmJhODJjZDNhM2M2NzgxMzkw" + "YWQzMjU2NGVhMzU3MDFmZWViOTExZWQzNzA5YjQ1MmI2N2RiMjhiNmUxZmZmZWZiZTE5Y2M2NzBkOTRkN2NhMDZiMm" + "RlNmU5YzVmYWI2ZjMxMjE0Njk2NjRlYWZkNWNkZWNlNmZhMGU3ZTE0MjEyMTY3OTE5MDI1N2JjMWE4MDc5M2VjIn0" + "="; + std::string paramInfo2 = + "dHlwZSBhODA0NDY4NDc1Nzk2NTU1ODI1OTc0NDQxNDk5ODkyMzUwNzY3NjQ4NzYxOTQ5MjM1NTQzNjAyNjYzNDEzNj" + "g2NjIzMzA1ODQxODA0NDEyODE4NjA4MTEyNDU3ODkwMDE0MjA1NjYxNDAxOTExNDkxMTg5MTYzMDUxMjI1MjMyOTY4" + "NzE2Nzk0MTk2Nzg2MDE4NjgyNjY3MDA4MDU5IGggNjAgciAxMzQwNzgwNzkyOTk0MjU5NzA5OTU3NDAyNDk5ODIwNT" + "g0NjEyNzQ3OTM2NTgyMDU5MjM5MzM3NzcyMzU2MTQ0MzcyMTc2NDAzMDA3MzU0Njk3NjgwMTg3NDI5ODE2NjkwMzQy" + "NzY5MDAzMTg1ODE4NjQ4NjA1MDg1Mzc1Mzg4MjgxMTk0NjU2OTk0NjQzMzY0NDcxMTExNjgwMSBleHAyIDUxMiBleH" + "AxIDMyIHNpZ24xIC0xIHNpZ24wIDE="; + in = abi.abiIn( + "groupSigVerify(string,string,string,string)", signature2, message2, gpkInfo2, paramInfo2); + parameters->m_input = bytesConstRef(in.data(), in.size()); + + execResult = groupSigPrecompiled->call(executive, parameters); + out = execResult->execResult(); + + abi.abiOut(bytesConstRef(&out), retCode, result); + BOOST_TEST(retCode == VERIFY_GROUP_SIG_FAILED); + BOOST_TEST(result == false); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/KVTablePrecompiledTest.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/KVTablePrecompiledTest.cpp" new file mode 100644 index 00000000..c487d657 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/KVTablePrecompiledTest.cpp" @@ -0,0 +1,496 @@ +/** + * Copyright (C) 2021 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 KVTablePrecompiledTest.cpp + * @author: kyonRay + * @date 2021-12-01 + */ + +#include "precompiled/KVTablePrecompiled.h" +#include "bcos-framework/executor/PrecompiledTypeDef.h" +#include "libprecompiled/PreCompiledFixture.h" +#include + +using namespace bcos; +using namespace bcos::precompiled; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::ledger; + +namespace bcos::test +{ +class KVTableFactoryPrecompiledFixture : public PrecompiledFixture +{ +public: + KVTableFactoryPrecompiledFixture() { init(false); } + + void init(bool _isWasm) + { + codec = std::make_shared(hashImpl, _isWasm); + setIsWasm(_isWasm); + } + + virtual ~KVTableFactoryPrecompiledFixture() {} + + ExecutionMessage::UniquePtr creatKVTable(protocol::BlockNumber _number, + const std::string& tableName, const std::string& key, const std::string& value, + const std::string& solidityAddress, int _errorCode = 0, bool errorInTableManager = false) + { + nextBlock(_number); + bytes in = + codec->encodeWithSig("createKVTable(string,string,string)", tableName, key, value); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 100, 10000, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(100); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(isWasm ? TABLE_MANAGER_NAME : TABLE_MANAGER_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + // call precompiled + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + if (errorInTableManager) + { + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(int32_t(_errorCode))); + } + commitBlock(_number); + return result2; + } + + // set new address + result2->setTo(solidityAddress); + // external create + result2->setSeq(1001); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + result3->setSeq(1002); + // external call bfs + std::promise executePromise4; + executor->dmcExecuteTransaction(std::move(result3), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise4.set_value(std::move(result)); + }); + auto result4 = executePromise4.get_future().get(); + + // call bfs success, callback to create + result4->setSeq(1001); + + std::promise executePromise5; + executor->dmcExecuteTransaction(std::move(result4), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise5.set_value(std::move(result)); + }); + auto result5 = executePromise5.get_future().get(); + + // create success, callback to precompiled + result5->setSeq(1000); + + std::promise executePromise6; + executor->dmcExecuteTransaction(std::move(result5), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise6.set_value(std::move(result)); + }); + auto result6 = executePromise6.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result6->data().toBytes() == codec->encode(s256(_errorCode))); + } + commitBlock(_number); + return result6; + }; + + ExecutionMessage::UniquePtr desc(protocol::BlockNumber _number, std::string const& tableName) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("desc(string)", tableName); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(isWasm ? TABLE_MANAGER_NAME : TABLE_MANAGER_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + // call precompiled + commitBlock(_number); + return result2; + } + + ExecutionMessage::UniquePtr get( + protocol::BlockNumber _number, const std::string& callAddress, const std::string& key) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("get(string)", key); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(callAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + commitBlock(_number); + return result2; + } + + ExecutionMessage::UniquePtr set(protocol::BlockNumber _number, const std::string& callAddress, + const std::string& key, const std::string& value, int _errorCode = 1) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("set(string,string)", key, value); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(callAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + commitBlock(_number); + return result2; + } + + std::string sender; +}; + +BOOST_FIXTURE_TEST_SUITE(precompiledKVTableTest, KVTableFactoryPrecompiledFixture) + +BOOST_AUTO_TEST_CASE(createTableTest) +{ + int64_t number = 2; + // + { + creatKVTable( + number++, "t_test", "id", "item_name", "1234853b49838bd3e9466c85a4cc3428c960dde2"); + } + + // createTable exist + { + creatKVTable(number++, "t_test", "id", "item_name", + "2234853b49838bd3e9466c85a4cc3428c960dde2", CODE_TABLE_NAME_ALREADY_EXIST, true); + } + + // createTable build + { + auto response = creatKVTable(number++, "t_test/t_test2", "id", "item_name", + "3234853b49838bd3e9466c85a4cc3428c960dde2"); + BOOST_CHECK(response->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + std::string errorStr; + for (int i = 0; i <= SYS_TABLE_VALUE_FIELD_NAME_MAX_LENGTH; i++) + { + errorStr += std::to_string(0); + } + // createTable too long tableName, key and field + { + auto r1 = creatKVTable(number++, errorStr, "id", "item_name", + "1134853b49838bd3e9466c85a4cc3428c960dde1", 0, true); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + auto r2 = creatKVTable(number++, "t_test", errorStr, "item_name", + "2134853b49838bd3e9466c85a4cc3428c960dde1", 0, true); + BOOST_CHECK(r2->status() == (int32_t)TransactionStatus::PrecompiledError); + auto r3 = creatKVTable(number++, "t_test", "id", errorStr, + "3134853b49838bd3e9466c85a4cc3428c960dde1", 0, true); + BOOST_CHECK(r3->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + // createTable error key and field + std::string errorStr2 = "/test&"; + { + auto r1 = creatKVTable(number++, errorStr2, "id", "item_name,item_id", + "4134853b49838bd3e9466c85a4cc3428c960dde1", 0, true); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + auto r2 = creatKVTable(number++, "t_test", errorStr2, "item_name,item_id", + "5134853b49838bd3e9466c85a4cc3428c960dde1", 0, true); + BOOST_CHECK(r2->status() == (int32_t)TransactionStatus::PrecompiledError); + auto r3 = creatKVTable(number++, "t_test", "id", errorStr2, + "6134853b49838bd3e9466c85a4cc3428c960dde1", 0, true); + BOOST_CHECK(r3->status() == (int32_t)TransactionStatus::PrecompiledError); + } +} + +BOOST_AUTO_TEST_CASE(createTableWasmTest) +{ + init(true); + int64_t number = 2; + // + { + creatKVTable(number++, "t_test", "id", "item_name", "/tables/t_test"); + } + + // createTable exist + { + creatKVTable(number++, "t_test", "id", "item_name", "/tables/t_test", + CODE_TABLE_NAME_ALREADY_EXIST, true); + } + + // createTable build + { + auto response = + creatKVTable(number++, "t_test/t_test2", "id", "item_name", "/tables/t_test/t_test2"); + BOOST_CHECK(response->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + std::string errorStr; + for (int i = 0; i <= SYS_TABLE_VALUE_FIELD_NAME_MAX_LENGTH; i++) + { + errorStr += std::to_string(0); + } + // createTable too long tableName, key and field + { + auto r1 = creatKVTable(number++, errorStr, "id", "item_name", "/tables/t_test3", 0, true); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + auto r2 = + creatKVTable(number++, "t_test", errorStr, "item_name", "/tables/t_test4", 0, true); + BOOST_CHECK(r2->status() == (int32_t)TransactionStatus::PrecompiledError); + auto r3 = creatKVTable(number++, "t_test", "id", errorStr, "/tables/t_test5", 0, true); + BOOST_CHECK(r3->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + // createTable error key and field + std::string errorStr2 = "/test&"; + { + auto r1 = creatKVTable( + number++, errorStr2, "id", "item_name,item_id", "/tables/t_test6", 0, true); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + auto r2 = creatKVTable( + number++, "t_test", errorStr2, "item_name,item_id", "/tables/t_test7", 0, true); + BOOST_CHECK(r2->status() == (int32_t)TransactionStatus::PrecompiledError); + auto r3 = creatKVTable(number++, "t_test", "id", errorStr2, "/tables/t_test8", 0, true); + BOOST_CHECK(r3->status() == (int32_t)TransactionStatus::PrecompiledError); + } +} + +BOOST_AUTO_TEST_CASE(descTest) +{ + int64_t number = 2; + std::string address = "1234853b49838bd3e9466c85a4cc3428c960dde2"; + // create + { + creatKVTable(number++, "t_kv_test", "id", "item_name", address); + } + + auto result2 = desc(number++, "t_kv_test"); + TableInfoTuple tableInfo = {"id", {"item_name"}}; + BOOST_CHECK(result2->data().toBytes() == codec->encode(tableInfo)); +} + +BOOST_AUTO_TEST_CASE(descWasmTest) +{ + init(true); + int64_t number = 2; + std::string address = "/tables/t_kv_test"; + // create + { + auto result = creatKVTable(number++, "t_kv_test", "id", "item_name", address); + BOOST_CHECK(result->status() == (int32_t)TransactionStatus::None); + } + + auto result2 = desc(number++, "t_kv_test"); + TableInfoTuple tableInfo = {"id", {"item_name"}}; + BOOST_CHECK(result2->data().toBytes() == codec->encode(tableInfo)); +} + +BOOST_AUTO_TEST_CASE(setTest) +{ + int64_t number = 2; + std::string address = "1234853b49838bd3e9466c85a4cc3428c960dde2"; + // create + { + creatKVTable(number++, "t_kv_test", "id", "item_name", address); + } + + // simple set and get + { + auto result1 = set(number++, address, "id1", "test1"); + auto result2 = get(number++, address, "id1"); + BOOST_CHECK(result2->data().toBytes() == codec->encode(true, std::string("test1"))); + } + + // cover write and get + { + auto result3 = set(number++, address, "id1", "test2"); + auto result4 = get(number++, address, "id1"); + BOOST_CHECK(result4->data().toBytes() == codec->encode(true, std::string("test2"))); + } + + // get not exist + { + auto result4 = get(number++, address, "noExist"); + BOOST_CHECK(result4->data().toBytes() == codec->encode(false, std::string(""))); + } + + boost::log::core::get()->set_logging_enabled(false); + // set key overflow + { + std::string errorKey = "0"; + for (int j = 0; j < USER_TABLE_KEY_VALUE_MAX_LENGTH; ++j) + { + errorKey += "0"; + } + auto result3 = set(number++, address, errorKey, "test2"); + BOOST_CHECK(result3->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + // set value overflow + { + std::string errorValue = "0"; + for (int j = 0; j < USER_TABLE_FIELD_VALUE_MAX_LENGTH; ++j) + { + errorValue += "0"; + } + auto result3 = set(number++, address, "1", errorValue); + BOOST_CHECK(result3->status() == (int32_t)TransactionStatus::PrecompiledError); + } + boost::log::core::get()->set_logging_enabled(true); +} + +BOOST_AUTO_TEST_CASE(setWasmTest) +{ + init(true); + int64_t number = 2; + std::string address = "/tables/t_kv_test"; + // create + { + creatKVTable(number++, "t_kv_test", "id", "item_name", address); + } + + // simple set and get + { + auto result1 = set(number++, address, "id1", "test1"); + auto result2 = get(number++, address, "id1"); + BOOST_CHECK(result2->data().toBytes() == codec->encode(true, std::string("test1"))); + } + + // cover write and get + { + auto result3 = set(number++, address, "id1", "test2"); + auto result4 = get(number++, address, "id1"); + BOOST_CHECK(result4->data().toBytes() == codec->encode(true, std::string("test2"))); + } + + // get not exist + { + auto result4 = get(number++, address, "noExist"); + BOOST_CHECK(result4->data().toBytes() == codec->encode(false, std::string(""))); + } + + boost::log::core::get()->set_logging_enabled(false); + // set key overflow + { + std::string errorKey = "0"; + for (int j = 0; j < USER_TABLE_KEY_VALUE_MAX_LENGTH; ++j) + { + errorKey += "0"; + } + auto result3 = set(number++, address, errorKey, "test2"); + BOOST_CHECK(result3->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + // set value overflow + { + std::string errorValue = "0"; + for (int j = 0; j < USER_TABLE_FIELD_VALUE_MAX_LENGTH; ++j) + { + errorValue += "0"; + } + auto result3 = set(number++, address, "1", errorValue); + BOOST_CHECK(result3->status() == (int32_t)TransactionStatus::PrecompiledError); + } + boost::log::core::get()->set_logging_enabled(true); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/PreCompiledFixture.h" "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/PreCompiledFixture.h" new file mode 100644 index 00000000..80555acc --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/PreCompiledFixture.h" @@ -0,0 +1,701 @@ +/** + * Copyright (C) 2021 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 PreCompiledFixture.h + * @author: kyonRay + * @date 2021-06-19 + */ + +#pragma once +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include "bcos-framework/ledger/LedgerTypeDef.h" +#include "bcos-framework/protocol/Protocol.h" +#include "bcos-tars-protocol/protocol/BlockHeaderImpl.h" +#include "executive/BlockContext.h" +#include "executive/TransactionExecutive.h" +#include "executor/TransactionExecutorFactory.h" +#include "mock/MockKeyPageStorage.h" +#include "mock/MockLedger.h" +#include "mock/MockTransactionalStorage.h" +#include "mock/MockTxPool.h" +#include "precompiled/extension/UserPrecompiled.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::precompiled; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::ledger; +using namespace bcos::crypto; + +namespace bcos::test +{ +class PrecompiledFixture : public TestPromptFixture +{ +public: + PrecompiledFixture() + { + hashImpl = std::make_shared(); + assert(hashImpl); + smHashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto sm2Sign = std::make_shared(); + assert(signatureImpl); + cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + assert(cryptoSuite); + smCryptoSuite = std::make_shared(smHashImpl, sm2Sign, nullptr); + txpool = std::make_shared(); + assert(DEFAULT_PERMISSION_ADDRESS); + } + + virtual ~PrecompiledFixture() {} + + /// must set isWasm + void setIsWasm(bool _isWasm, bool _isCheckAuth = false, bool _isKeyPage = true, + protocol::BlockVersion version = BlockVersion::V3_1_VERSION) + { + isWasm = _isWasm; + if (_isKeyPage) + { + storage = std::make_shared(hashImpl); + } + else + { + storage = std::make_shared(hashImpl); + } + blockFactory = createBlockFactory(cryptoSuite); + auto header = blockFactory->blockHeaderFactory()->createBlockHeader(1); + header->setNumber(1); + ledger = std::make_shared(); + ledger->setBlockNumber(header->number() - 1); + + auto executionResultFactory = std::make_shared(); + + executor = bcos::executor::TransactionExecutorFactory::build(ledger, txpool, nullptr, + storage, executionResultFactory, hashImpl, _isWasm, _isCheckAuth, false); + + codec = std::make_shared(hashImpl, _isWasm); + keyPair = cryptoSuite->signatureImpl()->generateKeyPair(); + memcpy(keyPair->secretKey()->mutableData(), + fromHexString("ff6f30856ad3bae00b1169808488502786a13e3c174d85682135ffd51310310e") + ->data(), + 32); + memcpy(keyPair->publicKey()->mutableData(), + fromHexString( + "ccd8de502ac45462767e649b462b5f4ca7eadd69c7e1f1b410bdf754359be29b1b88ffd79744" + "03f56e250af52b25682014554f7b3297d6152401e85d426a06ae") + ->data(), + 64); + boost::log::core::get()->set_logging_enabled(false); + createSysTable(version); + boost::log::core::get()->set_logging_enabled(true); + if (_isCheckAuth) + { + boost::log::core::get()->set_logging_enabled(false); + deployAuthSolidity(1); + boost::log::core::get()->set_logging_enabled(true); + } + } + + void createSysTable(protocol::BlockVersion version) + { + // create sys table + { + std::promise> promise1; + storage->asyncCreateTable(std::string{ledger::SYS_CONFIG}, "value", + [&](Error::UniquePtr&& _error, std::optional
&& _table) { + BOOST_CHECK(!_error); + promise1.set_value(std::move(_table)); + }); + auto table = promise1.get_future().get(); + auto entry = table->newEntry(); + + entry.setObject(SystemConfigEntry{"3000000", 0}); + + table->setRow(SYSTEM_KEY_TX_GAS_LIMIT, std::move(entry)); + } + + m_blockVersion = version; + if (m_blockVersion >= protocol::BlockVersion::V3_1_VERSION) + { + initBfs(1); + } + else + { + // create / table + { + std::promise> promise2; + storage->asyncCreateTable( + "/", "value", [&](Error::UniquePtr&& _error, std::optional
&& _table) { + BOOST_CHECK(!_error); + promise2.set_value(std::move(_table)); + }); + auto rootTable = promise2.get_future().get(); + storage::Entry tEntry, newSubEntry, aclTypeEntry, aclWEntry, aclBEntry, extraEntry; + std::map newSubMap; + newSubMap.insert(std::make_pair("apps", executor::FS_TYPE_DIR)); + newSubMap.insert(std::make_pair("sys", executor::FS_TYPE_DIR)); + newSubMap.insert(std::make_pair("tables", executor::FS_TYPE_DIR)); + tEntry.importFields({executor::FS_TYPE_DIR}); + newSubEntry.importFields({asString(codec::scale::encode(newSubMap))}); + aclTypeEntry.importFields({"0"}); + aclWEntry.importFields({""}); + aclBEntry.importFields({""}); + extraEntry.importFields({""}); + rootTable->setRow(executor::FS_KEY_TYPE, std::move(tEntry)); + rootTable->setRow(executor::FS_KEY_SUB, std::move(newSubEntry)); + rootTable->setRow(executor::FS_ACL_TYPE, std::move(aclTypeEntry)); + rootTable->setRow(executor::FS_ACL_WHITE, std::move(aclWEntry)); + rootTable->setRow(executor::FS_ACL_BLACK, std::move(aclBEntry)); + rootTable->setRow(executor::FS_KEY_EXTRA, std::move(extraEntry)); + } + + // create /tables table + { + std::promise> promise3; + storage->asyncCreateTable("/tables", "value", + [&](Error::UniquePtr&& _error, std::optional
&& _table) { + BOOST_CHECK(!_error); + promise3.set_value(std::move(_table)); + }); + auto tablesTable = promise3.get_future().get(); + storage::Entry tEntry, newSubEntry, aclTypeEntry, aclWEntry, aclBEntry, extraEntry; + std::map newSubMap; + tEntry.importFields({executor::FS_TYPE_DIR}); + newSubEntry.importFields({asString(codec::scale::encode(newSubMap))}); + aclTypeEntry.importFields({"0"}); + aclWEntry.importFields({""}); + aclBEntry.importFields({""}); + extraEntry.importFields({""}); + tablesTable->setRow(executor::FS_KEY_TYPE, std::move(tEntry)); + tablesTable->setRow(executor::FS_KEY_SUB, std::move(newSubEntry)); + tablesTable->setRow(executor::FS_ACL_TYPE, std::move(aclTypeEntry)); + tablesTable->setRow(executor::FS_ACL_WHITE, std::move(aclWEntry)); + tablesTable->setRow(executor::FS_ACL_BLACK, std::move(aclBEntry)); + tablesTable->setRow(executor::FS_KEY_EXTRA, std::move(extraEntry)); + } + + // create /apps table + { + std::promise> promise4; + storage->asyncCreateTable("/apps", "value", + [&](Error::UniquePtr&& _error, std::optional
&& _table) { + BOOST_CHECK(!_error); + promise4.set_value(std::move(_table)); + }); + auto appsTable = promise4.get_future().get(); + storage::Entry tEntry, newSubEntry, aclTypeEntry, aclWEntry, aclBEntry, extraEntry; + std::map newSubMap; + tEntry.importFields({executor::FS_TYPE_DIR}); + newSubEntry.importFields({asString(codec::scale::encode(newSubMap))}); + aclTypeEntry.importFields({"0"}); + aclWEntry.importFields({""}); + aclBEntry.importFields({""}); + extraEntry.importFields({""}); + appsTable->setRow(executor::FS_KEY_TYPE, std::move(tEntry)); + appsTable->setRow(executor::FS_KEY_SUB, std::move(newSubEntry)); + appsTable->setRow(executor::FS_ACL_TYPE, std::move(aclTypeEntry)); + appsTable->setRow(executor::FS_ACL_WHITE, std::move(aclWEntry)); + appsTable->setRow(executor::FS_ACL_BLACK, std::move(aclBEntry)); + appsTable->setRow(executor::FS_KEY_EXTRA, std::move(extraEntry)); + } + + // create /usr table + { + std::promise> promise4; + storage->asyncCreateTable( + "/sys", "value", [&](Error::UniquePtr&& _error, std::optional
&& _table) { + BOOST_CHECK(!_error); + promise4.set_value(std::move(_table)); + }); + auto appsTable = promise4.get_future().get(); + storage::Entry tEntry, newSubEntry, aclTypeEntry, aclWEntry, aclBEntry, extraEntry; + std::map newSubMap; + tEntry.importFields({executor::FS_TYPE_DIR}); + newSubEntry.importFields({asString(codec::scale::encode(newSubMap))}); + aclTypeEntry.importFields({"0"}); + aclWEntry.importFields({""}); + aclBEntry.importFields({""}); + extraEntry.importFields({""}); + appsTable->setRow(executor::FS_KEY_TYPE, std::move(tEntry)); + appsTable->setRow(executor::FS_KEY_SUB, std::move(newSubEntry)); + appsTable->setRow(executor::FS_ACL_TYPE, std::move(aclTypeEntry)); + appsTable->setRow(executor::FS_ACL_WHITE, std::move(aclWEntry)); + appsTable->setRow(executor::FS_ACL_BLACK, std::move(aclBEntry)); + appsTable->setRow(executor::FS_KEY_EXTRA, std::move(extraEntry)); + } + } + } + + void nextBlock( + int64_t blockNumber, protocol::BlockVersion version = protocol::BlockVersion::V3_1_VERSION) + { + if (blockNumber < 0) [[unlikely]] + { + // for parallel test + return; + } + std::cout << "next block: " << blockNumber << std::endl; + auto blockHeader = std::make_shared(cryptoSuite, + [m_blockHeader = bcostars::BlockHeader()]() mutable { return &m_blockHeader; }); + blockHeader->setNumber(blockNumber); + blockHeader->setParentInfo({{blockNumber - 1, h256(blockNumber - 1)}}); + blockHeader->setVersion((uint32_t)version); + ledger->setBlockNumber(blockNumber - 1); + std::promise nextPromise; + executor->nextBlockHeader( + 0, blockHeader, [&](bcos::Error::Ptr&& error) { nextPromise.set_value(); }); + nextPromise.get_future().get(); + } + + void commitBlock(protocol::BlockNumber blockNumber) + { + if (blockNumber < 0) [[unlikely]] + { + // for parallel test + return; + } + std::cout << "commit block: " << blockNumber << std::endl; + TwoPCParams commitParams{}; + commitParams.number = blockNumber; + + std::promise preparePromise; + executor->prepare( + commitParams, [&](bcos::Error::Ptr&& error) { preparePromise.set_value(); }); + preparePromise.get_future().get(); + + std::promise commitPromise; + executor->commit( + commitParams, [&](bcos::Error::Ptr&& error) { commitPromise.set_value(); }); + commitPromise.get_future().get(); + } + + void deployAuthSolidity(protocol::BlockNumber blockNumber) + { + static const char* committeeBin = bcos::initializer::committeeBin; + + // deploy CommitteeManager + + // hex bin code to bytes + bytes code; + boost::algorithm::unhex(committeeBin, std::back_inserter(code)); + + // constructor (address[] initGovernors, = [authAdminAddress] + // uint32[] memory weights, = [0] + // uint8 participatesRate, = 0 + // uint8 winRate) = 0 + std::vector
initGovernors({Address(admin)}); + std::vector weights({bcos::codec::toString32(h256(uint8_t(1)))}); + // bytes code + abi encode constructor params + codec::abi::ContractABICodec abi(hashImpl); + bytes constructor = abi.abiIn("", initGovernors, weights, + codec::toString32(h256(uint8_t(0))), codec::toString32(h256(uint8_t(0)))); + bytes input = code + constructor; + + auto sender = admin; + + auto params = std::make_unique(); + params->setContextID(99); + params->setSeq(1000); + params->setDepth(0); + + params->setOrigin(sender); + params->setFrom(sender); + + // toChecksumAddress(addressString, hashImpl); + params->setTo(precompiled::AUTH_COMMITTEE_ADDRESS); + params->setStaticCall(false); + params->setGasAvailable(gas); + params->setData(input); + params->setType(NativeExecutionMessage::MESSAGE); + params->setCreate(true); + params->setData(input); + + nextBlock(blockNumber); + // -------------------------------- + // Create contract + // -------------------------------- + + std::promise executePromise; + executor->dmcExecuteTransaction( + std::move(params), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise.set_value(std::move(result)); + }); + + auto result = executePromise.get_future().get(); + + result->setSeq(1001); + /// call precompiled to get deploy auth + std::promise executePromise2; + executor->dmcExecuteTransaction( + std::move(result), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + + auto result2 = executePromise2.get_future().get(); + + result2->setSeq(1000); + /// callback get deploy committeeManager context + std::promise executePromise3; + executor->dmcExecuteTransaction( + std::move(result2), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + + auto result3 = executePromise3.get_future().get(); + + BOOST_CHECK_EQUAL(result3->type(), ExecutionMessage::MESSAGE); + BOOST_CHECK_EQUAL(result3->contextID(), 99); + BOOST_CHECK_EQUAL(result3->seq(), 1000); + BOOST_CHECK_EQUAL(result3->create(), true); + BOOST_CHECK_EQUAL(result3->origin(), sender); + BOOST_CHECK_EQUAL(result3->from(), precompiled::AUTH_COMMITTEE_ADDRESS); + + result3->setSeq(1002); + result3->setTo("1111111111111111111111111111111111111111"); + result3->setKeyLocks({}); + /// new committee + std::promise executePromise4; + executor->dmcExecuteTransaction( + std::move(result3), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise4.set_value(std::move(result)); + }); + + auto result4 = executePromise4.get_future().get(); + + result4->setSeq(1003); + /// call precompiled to get deploy auth + std::promise executePromise5; + executor->dmcExecuteTransaction( + std::move(result4), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise5.set_value(std::move(result)); + }); + + auto result5 = executePromise5.get_future().get(); + + result5->setSeq(1002); + /// callback get deploy committee context + std::promise executePromise6; + executor->dmcExecuteTransaction( + std::move(result5), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise6.set_value(std::move(result)); + }); + + auto result6 = executePromise6.get_future().get(); + + BOOST_CHECK_EQUAL(result6->type(), ExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(result6->contextID(), 99); + BOOST_CHECK_EQUAL(result6->seq(), 1002); + BOOST_CHECK_EQUAL(result6->create(), false); + BOOST_CHECK_EQUAL( + result6->newEVMContractAddress(), "1111111111111111111111111111111111111111"); + BOOST_CHECK_EQUAL(result6->origin(), sender); + BOOST_CHECK_EQUAL(result6->to(), precompiled::AUTH_COMMITTEE_ADDRESS); + + result6->setSeq(1000); + // new committee address => committeeManager + // new proposalManager + std::promise executePromise7; + executor->dmcExecuteTransaction( + std::move(result6), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise7.set_value(std::move(result)); + }); + + auto result7 = executePromise7.get_future().get(); + + BOOST_CHECK_EQUAL(result7->type(), ExecutionMessage::MESSAGE); + BOOST_CHECK_EQUAL(result7->contextID(), 99); + BOOST_CHECK_EQUAL(result7->seq(), 1000); + BOOST_CHECK_EQUAL(result7->create(), true); + BOOST_CHECK_EQUAL(result7->origin(), sender); + BOOST_CHECK_EQUAL(result7->from(), precompiled::AUTH_COMMITTEE_ADDRESS); + + result7->setSeq(1004); + result7->setTo("2222222222222222222222222222222222222222"); + result7->setKeyLocks({}); + + // new proposalManager + std::promise executePromise8; + executor->dmcExecuteTransaction( + std::move(result7), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise8.set_value(std::move(result)); + }); + + auto result8 = executePromise8.get_future().get(); + + result8->setSeq(1005); + /// call precompiled to get deploy auth + std::promise executePromise9; + executor->dmcExecuteTransaction( + std::move(result8), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise9.set_value(std::move(result)); + }); + + auto result9 = executePromise9.get_future().get(); + + result9->setSeq(1004); + /// callback get deploy committee context + std::promise executePromise10; + executor->dmcExecuteTransaction( + std::move(result9), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise10.set_value(std::move(result)); + }); + + auto result10 = executePromise10.get_future().get(); + + result10->setSeq(1006); + result10->setTo("3333333333333333333333333333333333333333"); + result10->setKeyLocks({}); + + /// new voteComputer + std::promise executePromise11; + executor->dmcExecuteTransaction( + std::move(result10), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise11.set_value(std::move(result)); + }); + + auto result11 = executePromise11.get_future().get(); + + result11->setSeq(1007); + + /// call precompiled to get deploy auth + std::promise executePromise12; + executor->dmcExecuteTransaction( + std::move(result11), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise12.set_value(std::move(result)); + }); + + auto result12 = executePromise12.get_future().get(); + + result12->setSeq(1006); + + /// callback get deploy proposalMgr context + std::promise executePromise13; + executor->dmcExecuteTransaction( + std::move(result12), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise13.set_value(std::move(result)); + }); + + auto result13 = executePromise13.get_future().get(); + + BOOST_CHECK_EQUAL(result13->type(), ExecutionMessage::MESSAGE); + BOOST_CHECK_EQUAL(result13->contextID(), 99); + BOOST_CHECK_EQUAL(result13->seq(), 1006); + BOOST_CHECK_EQUAL(result13->create(), false); + BOOST_CHECK_EQUAL(result13->origin(), sender); + BOOST_CHECK_EQUAL(result13->to(), "1111111111111111111111111111111111111111"); + + /// call to committee, getWeights + result13->setSeq(1008); + // to committee + std::promise executePromise14; + executor->dmcExecuteTransaction( + std::move(result13), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise14.set_value(std::move(result)); + }); + auto result14 = executePromise14.get_future().get(); + + result14->setSeq(1006); + + // committee callback to voteComputer + std::promise executePromise15; + executor->dmcExecuteTransaction( + std::move(result14), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise15.set_value(std::move(result)); + }); + auto result15 = executePromise15.get_future().get(); + + result15->setSeq(1004); + + // new voteCompute => new ProposalMgr context + std::promise executePromise16; + executor->dmcExecuteTransaction( + std::move(result15), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise16.set_value(std::move(result)); + }); + + auto result16 = executePromise16.get_future().get(); + + BOOST_CHECK_EQUAL(result16->type(), ExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(result16->contextID(), 99); + BOOST_CHECK_EQUAL(result16->seq(), 1004); + BOOST_CHECK_EQUAL(result16->create(), false); + BOOST_CHECK_EQUAL(result16->origin(), sender); + BOOST_CHECK_EQUAL( + result16->newEVMContractAddress(), "2222222222222222222222222222222222222222"); + BOOST_CHECK_EQUAL(result16->to(), AUTH_COMMITTEE_ADDRESS); + + result16->setSeq(1000); + // new proposalManager address => committeeManager + std::promise executePromise17; + executor->dmcExecuteTransaction( + std::move(result16), [&](bcos::Error::UniquePtr&& error, + bcos::protocol::ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise17.set_value(std::move(result)); + }); + + auto result17 = executePromise17.get_future().get(); + BOOST_CHECK_EQUAL(result17->type(), ExecutionMessage::FINISHED); + BOOST_CHECK_EQUAL(result17->contextID(), 99); + BOOST_CHECK_EQUAL(result17->seq(), 1000); + BOOST_CHECK_EQUAL(result17->create(), false); + BOOST_CHECK_EQUAL(result17->origin(), sender); + BOOST_CHECK_EQUAL(result17->from(), precompiled::AUTH_COMMITTEE_ADDRESS); + + commitBlock(blockNumber); + } + + ExecutionMessage::UniquePtr list( + protocol::BlockNumber _number, std::string const& path, int _errorCode = 0) + { + bytes in = codec->encodeWithSig("list(string)", path); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + auto sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(isWasm ? BFS_NAME : BFS_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + nextBlock(_number, m_blockVersion); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + if (_errorCode != 0) + { + std::vector empty; + BOOST_CHECK(result2->data().toBytes() == codec->encode(int32_t(_errorCode), empty)); + } + + commitBlock(_number); + return result2; + }; + + ExecutionMessage::UniquePtr initBfs(protocol::BlockNumber _number, int _errorCode = 0) + { + bytes in = codec->encodeWithSig("initBfs()"); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + auto sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(isWasm ? BFS_NAME : BFS_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + nextBlock(_number, m_blockVersion); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result2; + }; + +protected: + crypto::Hash::Ptr hashImpl; + crypto::Hash::Ptr smHashImpl; + protocol::BlockFactory::Ptr blockFactory; + CryptoSuite::Ptr cryptoSuite = nullptr; + CryptoSuite::Ptr smCryptoSuite = nullptr; + + TransactionalStorageInterface::Ptr storage; + TransactionExecutor::Ptr executor; + std::shared_ptr txpool; + std::shared_ptr ledger; + KeyPairInterface::Ptr keyPair; + + CodecWrapper::Ptr codec; + int64_t gas = MockLedger::TX_GAS_LIMIT; + bool isWasm = false; + std::string admin = "1111654b49838bd3e9466c85a4cc3428c9601111"; + protocol::BlockVersion m_blockVersion = protocol::BlockVersion::V3_1_VERSION; +}; +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/PrecompiledCallTest.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/PrecompiledCallTest.cpp" new file mode 100644 index 00000000..363140f8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/PrecompiledCallTest.cpp" @@ -0,0 +1,101 @@ +/** + * 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 PrecompiledCallTest.cpp + * @author: kyonGuo + * @date 2022/7/26 + */ + +#include "libprecompiled/PreCompiledFixture.h" +#include "precompiled/ConsensusPrecompiled.h" +#include "precompiled/SystemConfigPrecompiled.h" +#include + +using namespace bcos; +using namespace bcos::precompiled; +using namespace bcos::executor; +using namespace bcos::storage; + +namespace bcos::test +{ +class PrecompiledCallTestFixture : public PrecompiledFixture +{ +public: + PrecompiledCallTestFixture() + { + codec = std::make_shared(hashImpl, false); + setIsWasm(false); + contractAddress = Address("0x420f853b49838bd3e9466c85a4cc3428c960dde2").hex(); + } + + virtual ~PrecompiledCallTestFixture() = default; + + ExecutionMessage::UniquePtr openTable( + protocol::BlockNumber _number, const std::string& tableName, int _errorCode = 0) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("openTable(string)", tableName); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 100, 10000, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(100); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(isWasm ? TABLE_MANAGER_NAME : TABLE_MANAGER_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + // call precompiled + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode))); + } + commitBlock(_number); + return result2; + }; + + std::string sender; + std::string contractAddress; +}; + +BOOST_FIXTURE_TEST_SUITE(precompiledCallTest, PrecompiledCallTestFixture) + +BOOST_AUTO_TEST_CASE(precompiled_gas_test) +{ + // call precompiled gas overflow + gas = 1; + BlockNumber number = 1; + auto result = openTable(number++, "error_test"); + BOOST_CHECK(result->status() == (int32_t)TransactionStatus::OutOfGas); +} + + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/PrecompiledGas.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/PrecompiledGas.cpp" new file mode 100644 index 00000000..78367cd8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/PrecompiledGas.cpp" @@ -0,0 +1,109 @@ +/** + * Copyright (C) 2021 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 PrecompiledGas.cpp + * @author: kyonRay + * @date 2021-06-03 + */ + +#include "bcos-executor/src/precompiled/common/PrecompiledGas.h" +#include + +using namespace bcos; +using namespace bcos::precompiled; +using namespace bcos::executor; + +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(precompiledGasTest, TestPromptFixture) +void checkGasCost(GasMetrics::Ptr _metric, InterfaceOpcode const& _key, int64_t _value) +{ + BOOST_CHECK(_metric->OpCode2GasCost[_key] == _value); +} + +void checkBasicGasCost(GasMetrics::Ptr _metric) +{ + checkGasCost(_metric, InterfaceOpcode::EQ, 3); + checkGasCost(_metric, InterfaceOpcode::GE, 3); + checkGasCost(_metric, InterfaceOpcode::GT, 3); + checkGasCost(_metric, InterfaceOpcode::LE, 3); + checkGasCost(_metric, InterfaceOpcode::LT, 3); + checkGasCost(_metric, InterfaceOpcode::NE, 3); + checkGasCost(_metric, InterfaceOpcode::Limit, 3); + checkGasCost(_metric, InterfaceOpcode::GetInt, 3); + checkGasCost(_metric, InterfaceOpcode::GetAddr, 3); + checkGasCost(_metric, InterfaceOpcode::Set, 3); + checkGasCost(_metric, InterfaceOpcode::GetByte32, 96); + checkGasCost(_metric, InterfaceOpcode::GetByte64, 192); + checkGasCost(_metric, InterfaceOpcode::GetString, 3); + checkGasCost(_metric, InterfaceOpcode::PaillierAdd, 20000); + checkGasCost(_metric, InterfaceOpcode::GroupSigVerify, 20000); + checkGasCost(_metric, InterfaceOpcode::RingSigVerify, 20000); +} + +BOOST_AUTO_TEST_CASE(testPrecompiledGasFactory) +{ + auto precompiledGasFactory = std::make_shared(); + BOOST_CHECK(precompiledGasFactory->gasMetric() != nullptr); + auto metric = precompiledGasFactory->gasMetric(); + // check gas cost + checkBasicGasCost(metric); + checkGasCost(metric, InterfaceOpcode::CreateTable, 16000); + checkGasCost(metric, InterfaceOpcode::OpenTable, 200); + checkGasCost(metric, InterfaceOpcode::Select, 200); + checkGasCost(metric, InterfaceOpcode::Insert, 10000); + checkGasCost(metric, InterfaceOpcode::Update, 10000); + checkGasCost(metric, InterfaceOpcode::Remove, 2500); +} + +BOOST_AUTO_TEST_CASE(testPrecompiledGas) +{ + // disable freeStorage + auto precompiledGasFactory = std::make_shared(); + auto gasPricer = precompiledGasFactory->createPrecompiledGas(); + gasPricer->appendOperation(InterfaceOpcode::CreateTable); + BOOST_CHECK(gasPricer->calTotalGas() == 16000); + gasPricer->appendOperation(InterfaceOpcode::OpenTable); + BOOST_CHECK(gasPricer->calTotalGas() == 16200); + gasPricer->appendOperation(InterfaceOpcode::Insert); + BOOST_CHECK(gasPricer->calTotalGas() == 26200); + gasPricer->appendOperation(InterfaceOpcode::Select); + BOOST_CHECK(gasPricer->calTotalGas() == 26400); + gasPricer->appendOperation(InterfaceOpcode::Update); + BOOST_CHECK(gasPricer->calTotalGas() == 36400); + gasPricer->setMemUsed(256); + BOOST_CHECK(gasPricer->calTotalGas() == 36424); + gasPricer->updateMemUsed(15000); + BOOST_CHECK(gasPricer->calTotalGas() == 38236); + gasPricer->appendOperation(InterfaceOpcode::LE); + BOOST_CHECK(gasPricer->calTotalGas() == 38239); + + // with bad instructions + auto metric = precompiledGasFactory->gasMetric(); + metric->OpCode2GasCost.clear(); + gasPricer->appendOperation(InterfaceOpcode::LT); + gasPricer->appendOperation(InterfaceOpcode::GetString); + // only calculate the memory gas for bad instructions + BOOST_CHECK(gasPricer->calTotalGas() == 1836); + metric->init(); + gasPricer->appendOperation(InterfaceOpcode::CreateTable); + gasPricer->appendOperation(InterfaceOpcode::OpenTable); + BOOST_CHECK(gasPricer->calTotalGas() == 54445); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/RingSigPrecompiledTest.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/RingSigPrecompiledTest.cpp" new file mode 100644 index 00000000..83f777e1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/RingSigPrecompiledTest.cpp" @@ -0,0 +1,208 @@ +/** + * Copyright (C) 2021 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. + */ +#include "bcos-executor/src/precompiled/extension/RingSigPrecompiled.h" +#include "../mock/MockLedger.h" +#include "bcos-codec/abi/ContractABICodec.h" +#include "bcos-executor/src/executive/BlockContext.h" +#include "bcos-executor/src/executive/TransactionExecutive.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::precompiled; +using namespace bcos::executor; +using namespace bcos::storage; +namespace bcos::test +{ +struct RingSigPrecompiledFixture +{ + RingSigPrecompiledFixture() + { + m_hashImpl = std::make_shared(); + m_ringSigPrecompiled = std::make_shared(m_hashImpl); + m_ledgerCache = std::make_shared(std::make_shared()); + m_blockContext = std::make_shared(nullptr, m_ledgerCache, m_hashImpl, 0, + h256(), utcTime(), 0, FiscoBcosScheduleV4, false, false); + std::shared_ptr gasInjector = nullptr; + m_executive = std::make_shared( + std::weak_ptr(m_blockContext), "", 100, 0, gasInjector); + } + + ~RingSigPrecompiledFixture() {} + + LedgerCache::Ptr m_ledgerCache; + bcos::crypto::Hash::Ptr m_hashImpl; + BlockContext::Ptr m_blockContext; + TransactionExecutive::Ptr m_executive; + RingSigPrecompiled::Ptr m_ringSigPrecompiled; +}; + +BOOST_FIXTURE_TEST_SUITE(test_RingSigPrecompiled, RingSigPrecompiledFixture) + +BOOST_AUTO_TEST_CASE(TestRingSigVerify) +{ + // true + std::string signature = + "eyJDIjogIjU4MDk3MjI0NzExMDQwOTI0NzQzMDI1OTQ4MTM3MTUzNzA0MzM5MjIwODE2NTY5MjYzNDM1NTEyOTUwNj" + "Y5NTIxMDA0MTAxNTQ2MzIzLiIsIlkiOiAiMTcxOTc4NjM0MzM1MzM5NjU4OTUxNzI5Mzg0MDM3ODU5MDk3NTc5MDA4" + "NzU3MTAxMjI3MTY3NzE2MDEzMzM3MjEzNzcwMjUxMzA2MTY2NjE5NDEzMzEyMzMyMDIxMDA4ODI5NjQ2MjA5OTY1Nj" + "gwNzU1NzgxNjgyNjc2OTg1NTA3MDYyNDM0NTAzNjM3OTIzMDQzNTMxNzk2MzExMjIxOTY5NDU4NTM1NTY0OTg4MDI4" + "MTYxMDAzNjc5NzI2NTQ1Mjg3ODY1NzE3MjEyNTQyOTU4MDc0NjIxMDE5MTc1OTI3NzcxNjc0NDA5ODA1MTc2NTcyMT" + "gzOTQ4MzM2NjMyNjA1NDI2ODEzMzExMTc4MDIzNjY0MTg1MzkxMTgyMTg2NjI1ODYxODYyNjkxNzk2OTA2MTY1LiIs" + "Im51bSI6ICIyIiwicGswIjogIjYxNjQ5MTE2MzI3Mjk5MjUyMTMyNDc4NjY2Njc1MDE1MzY0NDE1OTM2NzE0ODk1Nj" + "czMjE1MzU2NTA5MTg1MjY5OTcxODc1NTMyOTMzNjg3MTk4ODIxNjcxMTk1MDExOTMzMTg3ODQ2MTA3ODE4MjQ5Mzc3" + "NDAwMTc3MzIwNjQzMDYwMjAwMzUwMjM5MDM4NDA4MjczNTA1NTMyMzE0NTc3NjkyNjczNzQyNjM2NDI3MTc2MjYyMz" + "E1OTg4NDEyMDkwNjMyNDI5ODE0MDgxNjk4MTAwODcyMTI1NjQ5MjY5OTU1NDQ0NTYyMjM3NDY1NzI0NjAxNzU3MTQ2" + "ODA1MTg4MDk5MTUxNTQwMTE1NDQ4MjM2ODcyMTQ5MTk1MjQwNzUzNzU5MzI5NDA1NDAxMDIwNjg4LiIsInBrMSI6IC" + "IxMzk2OTMyMzA0NjMyOTMyMDE2MTYyNzk5ODc0ODMwNzExMzU5OTY5NzYxNTUyNDUwOTk3MTEwMDM0NDEwMzE1NTY2" + "MDE2NTA2MDQwOTU0NjA4OTAxMTE1OTc2NDUyMDI2MDMyOTcxNTQzNDUyNzgyMjUzNDc3NjU4NzQ4NjI2MjUzMjY0MD" + "I0MzM4OTE1MjM4NTg0Njc5NDI1Mjk2NTUxOTgyOTY1OTY3NjYyNzUxMjA2NDQ0NzU1ODg4NjU3MzA4MjgzNjA1MzE4" + "MjU1NzY2NTIxMTE3MTc5NTczNjU0Nzc4Njc3NDY1OTAxMTA5NTg0Mzg2ODgzNzA0NjU4OTQ5MzM0Nzg3MTE0NjAwNz" + "UyMTg0NTk0NDgzNjk1NTk1MDYzMDY4NzQ4ODk4OTU1MTAxOTkyNDYuIiwiczAiOiAiMjAxMDcyNTE3OTI3MzQwMDE1" + "NzY2NzM0NTE3NzQ1MjgzMjc4NDAyODM1MjQ0OTAzNzAzNDA0MTkwODE4NDI2NDUwMDkzODQ2Nzc0MDQ5NTMwMjY1ND" + "M0MDMxNjY5MzA5NDc5MzU4MzIxNDE0OTAzNzEyNzk0MTI5NzQ1MTYxNDA3Mjc0OTI5NTYyNjk0ODk3MDYwNTAyNjcx" + "Njk0NDIyNzY4OTkyMjI2MTAwMDg1NDcyNTg0ODE3Nzg2ODIzODk4Mzc2MjIxMzA5MjU4NzcxODUwODUzMjU2Nzc2Nj" + "U2MzI4Nzg0NzE3MTUxNzg0NDI4NTQ1Njg5MDA4MzIwMzA1ODYzMTAyMTQyNDY1MjkzODgwNTczNjAxNDYwMDM1MzIw" + "MDgzMzIyMTcyNDUyMzEwNDIxOTcuIiwiczEiOiAiNjkyNTY3NzY0NTc4MjY4Mjk0MTYwMTEzMjY5MjM1NTQwMzM5ND" + "ExMjgzMjY0MjM4NDIwMTc4Njk3ODM4OTE1MDg4NTcyNzQ1MjAxMjA5NTcyNDQzNDIyNDY0MzA4MTA2MTUwMTg4MTk5" + "MjM4MjE1MDg5MzgxODY1NDYzNTUyNDkxOTY0OTg2MzA0NDU4MTQ4ODMzOTEzNzk3MDc0OTQyNTMxMjEzNDU3MDE0Mj" + "YzNDc4ODY1Mjc3MzMwNjYwMjM5OTE2NzQxMDY5MzE0ODg3MzA4NjUxODkyMjA5MTY2OTkyNDk0NjY2OTc0NDI1OTAz" + "ODc5NDgzMTA0NzUyNjQ3MTg0NDYzODQyNzUzMDUwNDAyODg2NjMyNzIwNzE4MDEwMTkxNzU3ODI0MTI0ODI3MTM4Nj" + "UuIn0="; + std::string message1 = "test ringSigVerify"; + std::string paramInfo = + "eyJnIjogIjMuIiwicCI6ICIxNzc4NzYxMzUxNzk5MTA2NjkyODQwMjg1MDQ1NDQ5NzMxNDM2MTI0NDI1NTE0MTk4Nj" + "U0ODQ4MzA5MzE2NjYxOTQzMjkwNDg5ODIyMzgzNTgxMzQzMDk3NTc3MTQyMjU4NDg2NzI3NTkyNzU2NzQxMzc4MjI0" + "NjQ0Nzk5NjEwNjAxMDY3MzQ4NzgwNjA0MjQ5NTMyOTQwODQxNTEyOTE3Nzg0NTQxODIwOTI4OTk2NjE0MDIwNzgxNj" + "kzODU4NTcwMjM2NzYwMTI3OTc5ODQxNTU3MzAxMzM0NzQ4MTY1OTg4NDExMDAyMzczNjMxNjg2NjY3MDUzMjk5Mjk3" + "NjA4MjU2OTI3Mzk2NzQ1MTkzMzU2Nzg3NzA5NjQ1MzQ0MzA5MDUxNjkyNzk0MTIxOTA2MDY4MTkuIiwicSI6ICI4OD" + "kzODA2NzU4OTk1NTMzNDY0MjAxNDI1MjI3MjQ4NjU3MTgwNjIyMTI3NTcwOTkzMjc0MjQxNTQ2NTgzMzA5NzE2NDUy" + "NDQ5MTExOTE3OTA2NzE1NDg3ODg1NzExMjkyNDMzNjM3OTYzNzgzNzA2ODkxMTIzMjIzOTk4MDUzMDA1MzM2NzQzOT" + "AzMDIxMjQ3NjY0NzA0MjA3NTY0NTg4OTIyNzA5MTA0NjQ0OTgzMDcwMTAzOTA4NDY5MjkyODUxMTgzODAwNjM5ODk5" + "MjA3Nzg2NTA2NjczNzQwODI5OTQyMDU1MDExODY4MTU4NDMzMzM1MjY2NDk2NDg4MDQxMjg0NjM2OTgzNzI1OTY2Nz" + "gzOTM4NTQ4MjI2NzIxNTQ1MjU4NDYzOTcwNjA5NTMwMzQwOS4ifQ=="; + + RingSigPrecompiledFixture fixture; + auto hashImpl = fixture.m_hashImpl; + + bcos::codec::abi::ContractABICodec abi(hashImpl); + bytes in = abi.abiIn("ringSigVerify(string,string,string)", signature, message1, paramInfo); + + + auto ringSigPrecompiled = fixture.m_ringSigPrecompiled; + auto parameters = std::make_shared(); + parameters->m_input = bytesConstRef(in.data(), in.size()); + auto execResult = ringSigPrecompiled->call(fixture.m_executive, parameters); + bytes out = execResult->execResult(); + + bool result; + int retCode; + abi.abiOut(bytesConstRef(&out), retCode, result); + BOOST_TEST(true == result); + BOOST_TEST(0 == retCode); + + // false + std::string message2 = "ringSigVerify"; + in = abi.abiIn("ringSigVerify(string,string,string)", signature, message2, paramInfo); + parameters->m_input = bytesConstRef(in.data(), in.size()); + execResult = ringSigPrecompiled->call(fixture.m_executive, parameters); + out = execResult->execResult(); + + abi.abiOut(bytesConstRef(&out), retCode, result); + BOOST_TEST(false == result); + BOOST_TEST(retCode == VERIFY_RING_SIG_FAILED); +} + +BOOST_AUTO_TEST_CASE(ErrorFunc) +{ + RingSigPrecompiledFixture fixture; + auto hashImpl = fixture.m_hashImpl; + auto executive = fixture.m_executive; + auto ringSigPrecompiled = fixture.m_ringSigPrecompiled; + + bcos::codec::abi::ContractABICodec abi(hashImpl); + bytes in = abi.abiIn("ringSigVerify(string)", std::string("2AE3FFE2")); + auto parameters = std::make_shared(); + parameters->m_input = bytesConstRef(in.data(), in.size()); + auto execResult = ringSigPrecompiled->call(executive, parameters); + auto out = execResult->execResult(); + bool result; + int retCode; + abi.abiOut(bytesConstRef(&out), retCode, result); + BOOST_TEST(false == result); + BOOST_TEST(retCode == CODE_UNKNOW_FUNCTION_CALL); +} + +BOOST_AUTO_TEST_CASE(InvalidInputs) +{ + RingSigPrecompiledFixture fixture; + auto hashImpl = fixture.m_hashImpl; + auto executive = fixture.m_executive; + auto ringSigPrecompiled = fixture.m_ringSigPrecompiled; + + // situation1 + bcos::codec::abi::ContractABICodec abi(hashImpl); + bytes in = abi.abiIn("ringSigVerify(string,string,string)", std::string("2AE3FFE2"), + std::string("2AE3FFE2"), std::string("2AE3FFE2")); + auto parameters = std::make_shared(); + parameters->m_input = bytesConstRef(in.data(), in.size()); + + auto execResult = ringSigPrecompiled->call(executive, parameters); + bytes out = execResult->execResult(); + int retCode; + bool result; + abi.abiOut(bytesConstRef(&out), retCode, result); + BOOST_TEST(retCode == VERIFY_RING_SIG_FAILED); + BOOST_TEST(result == false); + + + // situation2 + std::string signature2 = + "eyJDIjogIjU4MDk3MjI0NzExMDQwOTI0NzQzMDI1OTQ4MTM3MTUzNzA0MzM5MjIwODE2NTY5MjYzNDM1NTEyOTUwNj" + "Y5NTIxMDA0MTAxNTQ2MzIzLiIsIlkiOiAiMTcxOTc4NjM0MzM1MzM5NjU4OTUxNzI5Mzg0MDM3ODU5MDk3NTc5MDA4" + "NzU3MTAxMjI3MTY3NzE2MDEzMzM3MjEzNzcwMjUxMzA2MTY2NjE5NDEzMzEyMzMyMDIxMDA4ODI5NjQ2MjA5OTY1Nj" + "gwNzU1NzgxNjgyNjc2OTg1NTA3MDYyNDM0NTAzNjM3OTIzMDQzNTMxNzk2MzExMjIxOTY5NDU4NTM1NTY0OTg4MDI4" + "MTYxMDAzNjc5NzI2NTQ1Mjg3ODY1NzE3MjEyNTQyOTU4MDc0NjIxMDE5MTc1OTI3NzcxNjc0NDA5ODA1MTc2NTcyMT" + "gzOTQ4MzM2NjMyNjA1NDI2ODEzMzExMTc4MDIzNjY0MTg1MzkxMTgyMTg2NjI1ODYxODYyNjkxNzk2OTA2MTY1LiIs" + "Im51bSI6ICIyIiwicGswIjogIjYxNjQ5MTE2MzI3Mjk5MjUyMTMyNDc4NjY2Njc1MDE1MzY0NDE1OTM2NzE0ODk1Nj" + "czMjE1MzU2NTA5MTg1MjY5OTcxODc1NTMyOTMzNjg3MTk4ODIxNjcxMTk1MDExOTMzMTg3ODQ2MTA3ODE4MjQ5Mzc3" + "NDAwMTc3MzIwNjQzMDYwMjAwMzUwMjM5MDM4NDA4MjczNTA1NTMyMzE0NTc3NjkyNjczNzQyNjM2NDI3MTc2MjYyMz" + "E1OTg4NDEyMDkwNjMyNDI5ODE0MDgxNjk4MTAwODcyMTI1NjQ5MjY5OTU1NDQ0NTYyMjM3NDY1NzI0NjAxNzU3MTQ2" + "ODA1MTg4MDk5MTUxNTQwMTE1NDQ4MjM2ODcyMTQ5MTk1MjQwNzUzNzU5MzI5NDA1NDAxMDIwNjg4LiJ9"; + std::string message2 = "ringSigVerify"; + std::string paramInfo2 = + "dHlwZSBhIHEgODA0NDY4NDc1Nzk2NTU1ODI1OTc0NDQxNDk5ODkyMzUwNzY3NjQ4NzYxOTQ5MjM1NTQzNjAyNjYzND" + "EzNjg2NjIzMzA1ODQxODA0NDEyODE4NjA4MTEyNDU3ODkwMDE0MjA1NjYxNDAxOTExNDkxMTg5MTYzMDUxMjI1MjMy" + "OTY4NzE2Nzk0MTk2Nzg2MDE4NjgyNjY3MDA4MDU5IGggNjAgciAxMzQwNzgwNzkyOTk0MjU5NzA5OTU3NDAyNDk5OD" + "IwNTg0NjEyNzQ3OTM2NTgyMDU5MjM5MzM3NzcyMzU2MTQ0MzcyMTc2NDAzMDA3MzU0Njk3NjgwMTg3NDI5ODE2Njkw" + "MzQyNzY5MDAzMTg1ODE4NjQ4NjA1MDg1Mzc1Mzg4MjgxMTk0NjU2OTk0NjQzMzY0NDcxMTExNjgwMSBleHAyIDUxMi" + "BleHAxIDMyIHNpZ24xIC0xIHNpZ24wIDE="; + in = abi.abiIn("ringSigVerify(string,string,string)", signature2, message2, paramInfo2); + parameters->m_input = bytesConstRef(in.data(), in.size()); + + execResult = ringSigPrecompiled->call(executive, parameters); + out = execResult->execResult(); + + abi.abiOut(bytesConstRef(&out), retCode, result); + BOOST_TEST(retCode == VERIFY_RING_SIG_FAILED); + BOOST_TEST(result == false); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/TablePrecompiledTest.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/TablePrecompiledTest.cpp" new file mode 100644 index 00000000..82af6b64 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/TablePrecompiledTest.cpp" @@ -0,0 +1,1474 @@ +/** + * Copyright (C) 2021 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 TablePrecompiledTest.cpp + * @author: kyonRay + * @date 2022-04-13 + */ + +#include "libprecompiled/PreCompiledFixture.h" +#include "precompiled/TableManagerPrecompiled.h" +#include + +using namespace bcos; +using namespace bcos::precompiled; +using namespace bcos::executor; +using namespace bcos::storage; +using namespace bcos::ledger; + +namespace bcos::test +{ +class TableFactoryPrecompiledFixture : public PrecompiledFixture +{ +public: + TableFactoryPrecompiledFixture() { init(false); } + + void init(bool isWasm) + { + setIsWasm(isWasm, false, true); + tableTestAddress = isWasm ? "/tables/t_test" : "420f853b49838bd3e9466c85a4cc3428c960dde2"; + } + + virtual ~TableFactoryPrecompiledFixture() = default; + + ExecutionMessage::UniquePtr creatTable(protocol::BlockNumber _number, + const std::string& tableName, const std::string& key, const std::vector& value, + const std::string& callAddress, int _errorCode = 0, bool errorInTableManager = false) + { + nextBlock(_number); + TableInfoTuple tableInfoTuple = std::make_tuple(key, value); + bytes in = codec->encodeWithSig( + "createTable(string,(string,string[]))", tableName, tableInfoTuple); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 100, 10000, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(100); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(isWasm ? TABLE_MANAGER_NAME : TABLE_MANAGER_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + // call precompiled + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + if (errorInTableManager) + { + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(int32_t(_errorCode))); + } + commitBlock(_number); + return result2; + } + + // set new address + result2->setTo(callAddress); + // external create + result2->setSeq(1001); + + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + result3->setSeq(1002); + // external call bfs + std::promise executePromise4; + executor->dmcExecuteTransaction(std::move(result3), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise4.set_value(std::move(result)); + }); + auto result4 = executePromise4.get_future().get(); + + // call bfs success, callback to create + result4->setSeq(1001); + + std::promise executePromise5; + executor->dmcExecuteTransaction(std::move(result4), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise5.set_value(std::move(result)); + }); + auto result5 = executePromise5.get_future().get(); + + // create success, callback to precompiled + result5->setSeq(1000); + + std::promise executePromise6; + executor->dmcExecuteTransaction(std::move(result5), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise6.set_value(std::move(result)); + }); + auto result6 = executePromise6.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result6->data().toBytes() == codec->encode(s256(_errorCode))); + } + commitBlock(_number); + return result6; + }; + + ExecutionMessage::UniquePtr appendColumns(protocol::BlockNumber _number, + const std::string& tableName, const std::vector& values, int _errorCode = 0) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("appendColumns(string,string[])", tableName, values); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 100, 10000, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(100); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(isWasm ? TABLE_MANAGER_NAME : TABLE_MANAGER_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + // call precompiled + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode))); + } + commitBlock(_number); + return result2; + }; + + ExecutionMessage::UniquePtr openTable( + protocol::BlockNumber _number, std::string const& _path, int _errorCode = 0) + { + bytes in = codec->encodeWithSig("openTable(string)", _path); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(isWasm ? TABLE_MANAGER_NAME : TABLE_MANAGER_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + nextBlock(_number); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + if (_errorCode != 0) + { + BOOST_CHECK(result2->data().toBytes() == codec->encode(s256(_errorCode))); + } + + commitBlock(_number); + return result2; + }; + + ExecutionMessage::UniquePtr desc(protocol::BlockNumber _number, std::string const& tableName) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("desc(string)", tableName); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(isWasm ? TABLE_MANAGER_NAME : TABLE_MANAGER_ADDRESS); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + // call precompiled + commitBlock(_number); + return result2; + } + + ExecutionMessage::UniquePtr insert(protocol::BlockNumber _number, const std::string& key, + const std::vector& values, const std::string& callAddress) + { + nextBlock(_number); + EntryTuple entryTuple = {key, values}; + bytes in = codec->encodeWithSig("insert((string,string[]))", entryTuple); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(callAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + result2->setSeq(1001); + + // get desc + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + result3->setSeq(1000); + + // get desc callback + std::promise executePromise4; + executor->dmcExecuteTransaction(std::move(result3), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + executePromise4.set_value(std::move(result)); + }); + auto result4 = executePromise4.get_future().get(); + + // call precompiled + commitBlock(_number); + return result4; + } + + ExecutionMessage::UniquePtr selectByKey( + protocol::BlockNumber _number, const std::string& key, const std::string& callAddress) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("select(string)", key); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(callAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + // call precompiled + commitBlock(_number); + return result2; + } + + ExecutionMessage::UniquePtr selectByCondition(protocol::BlockNumber _number, + const std::vector& keyCond, const LimitTuple& limit, + const std::string& callAddress) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("select((uint8,string)[],(uint32,uint32))", keyCond, limit); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(callAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + // call precompiled + commitBlock(_number); + return result2; + } + + ExecutionMessage::UniquePtr count(protocol::BlockNumber _number, + const std::vector& keyCond, const std::string& callAddress) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("count((uint8,string)[])", keyCond); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(callAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + // call precompiled + commitBlock(_number); + return result2; + } + + ExecutionMessage::UniquePtr updateByKey(protocol::BlockNumber _number, const std::string& key, + const std::vector& _updateFields, + const std::string& callAddress, bool _isErrorInTable = false) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("update(string,(string,string)[])", key, _updateFields); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(callAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + if (_isErrorInTable) + { + // call precompiled + commitBlock(_number); + return result2; + } + + result2->setSeq(1001); + + // get desc + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + result3->setSeq(1000); + + // get desc callback + std::promise executePromise4; + executor->dmcExecuteTransaction(std::move(result3), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + executePromise4.set_value(std::move(result)); + }); + auto result4 = executePromise4.get_future().get(); + + // call precompiled + commitBlock(_number); + return result4; + } + + ExecutionMessage::UniquePtr updateByCondition(protocol::BlockNumber _number, + const std::vector& conditions, const LimitTuple& _limit, + const std::vector& _updateFields, + const std::string& callAddress) + { + nextBlock(_number); + bytes in = + codec->encodeWithSig("update((uint8,string)[],(uint32,uint32),(string,string)[])", + conditions, _limit, _updateFields); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(callAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + result2->setSeq(1001); + + if (result2->status() != (int32_t)TransactionStatus::None) + { + commitBlock(_number); + return result2; + } + + // get desc + std::promise executePromise3; + executor->dmcExecuteTransaction(std::move(result2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + executePromise3.set_value(std::move(result)); + }); + auto result3 = executePromise3.get_future().get(); + + result3->setSeq(1000); + + // get desc callback + std::promise executePromise4; + executor->dmcExecuteTransaction(std::move(result3), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + executePromise4.set_value(std::move(result)); + }); + auto result4 = executePromise4.get_future().get(); + + // call precompiled + commitBlock(_number); + return result4; + } + + ExecutionMessage::UniquePtr removeByKey( + protocol::BlockNumber _number, const std::string& key, const std::string& callAddress) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("remove(string)", key); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(callAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + // call precompiled + commitBlock(_number); + return result2; + } + + ExecutionMessage::UniquePtr removeByCondition(protocol::BlockNumber _number, + const std::vector& keyCond, const LimitTuple& limit, + const std::string& callAddress) + { + nextBlock(_number); + bytes in = codec->encodeWithSig("remove((uint8,string)[],(uint32,uint32))", keyCond, limit); + auto tx = fakeTransaction(cryptoSuite, keyPair, "", in, 101, 100001, "1", "1"); + sender = boost::algorithm::hex_lower(std::string(tx->sender())); + auto hash = tx->hash(); + txpool->hash2Transaction.emplace(hash, tx); + auto params2 = std::make_unique(); + params2->setTransactionHash(hash); + params2->setContextID(1000); + params2->setSeq(1000); + params2->setDepth(0); + params2->setFrom(sender); + params2->setTo(callAddress); + params2->setOrigin(sender); + params2->setStaticCall(false); + params2->setGasAvailable(gas); + params2->setData(std::move(in)); + params2->setType(NativeExecutionMessage::TXHASH); + + std::promise executePromise2; + executor->dmcExecuteTransaction(std::move(params2), + [&](bcos::Error::UniquePtr&& error, ExecutionMessage::UniquePtr&& result) { + BOOST_CHECK(!error); + executePromise2.set_value(std::move(result)); + }); + auto result2 = executePromise2.get_future().get(); + + // call precompiled + commitBlock(_number); + return result2; + } + + std::string tableTestAddress; + std::string sender; +}; + +BOOST_FIXTURE_TEST_SUITE(precompiledTableTest, TableFactoryPrecompiledFixture) + +BOOST_AUTO_TEST_CASE(createTableTest) +{ + auto callAddress = tableTestAddress; + BlockNumber number = 1; + { + creatTable(number++, "t_test", "id", {"item_name", "item_id"}, callAddress); + } + + // open table not exist + { + auto response1 = openTable(number++, "t_test2"); + BOOST_CHECK(response1->data().toBytes() == codec->encode(Address())); + + auto response2 = openTable(number++, tableTestAddress); + BOOST_CHECK(response2->data().toBytes() == codec->encode(Address())); + } + + // check create + { + auto r1 = openTable(number++, "t_test"); + BOOST_CHECK(r1->data().toBytes() == codec->encode(Address(tableTestAddress))); + + auto response1 = openTable(number++, "/tables/t_test"); + Address address; + codec->decode(response1->data(), address); + BOOST_CHECK(address.hex() == tableTestAddress); + auto response2 = list(number++, "/tables"); + int32_t ret; + std::vector bfsInfos; + codec->decode(response2->data(), ret, bfsInfos); + BOOST_CHECK(ret == 0); + BOOST_CHECK(bfsInfos.size() == 1); + auto fileInfo = bfsInfos[0]; + BOOST_CHECK(std::get<0>(fileInfo) == "t_test"); + BOOST_CHECK(std::get<1>(fileInfo) == tool::FS_TYPE_LINK); + } + + // createTable exist + { + creatTable(number++, "t_test", "id", {"item_name", "item_id"}, callAddress, + CODE_TABLE_NAME_ALREADY_EXIST, true); + } + + // createTable too long tableName, key and field + std::string errorStr; + for (int i = 0; i <= SYS_TABLE_VALUE_FIELD_NAME_MAX_LENGTH; i++) + { + errorStr += std::to_string(1); + } + { + auto r1 = + creatTable(number++, errorStr, "id", {"item_name", "item_id"}, callAddress, 0, true); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + auto r2 = creatTable( + number++, "t_test", errorStr, {"item_name", "item_id"}, callAddress, 0, true); + BOOST_CHECK(r2->status() == (int32_t)TransactionStatus::PrecompiledError); + auto r3 = creatTable(number++, "t_test", "id", {errorStr}, callAddress, 0, true); + BOOST_CHECK(r3->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + // createTable error key and field + std::string errorStr2 = "/test&"; + { + auto r1 = + creatTable(number++, errorStr2, "id", {"item_name", "item_id"}, callAddress, 0, true); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + auto r2 = creatTable( + number++, "t_test", errorStr2, {"item_name", "item_id"}, callAddress, 0, true); + BOOST_CHECK(r2->status() == (int32_t)TransactionStatus::PrecompiledError); + auto r3 = creatTable(number++, "t_test", "id", {errorStr2}, callAddress, 0, true); + BOOST_CHECK(r3->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + // check trim create table params + { + auto r1 = creatTable(number++, "t_test123", " id ", {" item_name ", " item_id "}, + "420f853b49838bd3e9466c85a4cc3428c960dde3"); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(0))); + auto r2 = desc(number++, "t_test123"); + TableInfoTuple t = {"id", {"item_name", "item_id"}}; + BOOST_CHECK(r2->data().toBytes() == codec->encode(t)); + } +} + +BOOST_AUTO_TEST_CASE(createTableWasmsTest) +{ + init(true); + + auto callAddress = tableTestAddress; + BlockNumber number = 1; + { + creatTable(number++, "t_test", "id", {"item_name", "item_id"}, callAddress); + } + + // check create + { + auto response2 = list(number++, "/tables"); + int32_t ret; + std::vector bfsInfos; + codec->decode(response2->data(), ret, bfsInfos); + BOOST_CHECK(ret == 0); + BOOST_CHECK(bfsInfos.size() == 1); + auto fileInfo = bfsInfos[0]; + BOOST_CHECK(std::get<0>(fileInfo) == "t_test"); + BOOST_CHECK(std::get<1>(fileInfo) == executor::FS_TYPE_CONTRACT); + } + + // createTable exist + { + creatTable(number++, "t_test", "id", {"item_name", "item_id"}, callAddress, + CODE_TABLE_NAME_ALREADY_EXIST, true); + } + + // createTable too long tableName, key and field + std::string errorStr; + for (int i = 0; i <= SYS_TABLE_VALUE_FIELD_NAME_MAX_LENGTH; i++) + { + errorStr += std::to_string(1); + } + { + auto r1 = + creatTable(number++, errorStr, "id", {"item_name", "item_id"}, callAddress, 0, true); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + auto r2 = creatTable( + number++, "t_test", errorStr, {"item_name", "item_id"}, callAddress, 0, true); + BOOST_CHECK(r2->status() == (int32_t)TransactionStatus::PrecompiledError); + auto r3 = creatTable(number++, "t_test", "id", {errorStr}, callAddress, 0, true); + BOOST_CHECK(r3->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + // createTable error key and field + std::string errorStr2 = "/test&"; + { + auto r1 = + creatTable(number++, errorStr2, "id", {"item_name", "item_id"}, callAddress, 0, true); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + auto r2 = creatTable( + number++, "t_test", errorStr2, {"item_name", "item_id"}, callAddress, 0, true); + BOOST_CHECK(r2->status() == (int32_t)TransactionStatus::PrecompiledError); + auto r3 = creatTable(number++, "t_test", "id", {errorStr2}, callAddress, 0, true); + BOOST_CHECK(r3->status() == (int32_t)TransactionStatus::PrecompiledError); + } +} + +BOOST_AUTO_TEST_CASE(appendColumnsTest) +{ + auto callAddress = tableTestAddress; + BlockNumber number = 1; + { + creatTable(number++, "t_test", "id", {"item_name", "item_id"}, callAddress); + } + + // check create + { + auto response1 = openTable(number++, "/tables/t_test"); + Address address; + codec->decode(response1->data(), address); + BOOST_CHECK(address.hex() == tableTestAddress); + auto response2 = list(number++, "/tables"); + int32_t ret; + std::vector bfsInfos; + codec->decode(response2->data(), ret, bfsInfos); + BOOST_CHECK(ret == 0); + BOOST_CHECK(bfsInfos.size() == 1); + auto fileInfo = bfsInfos[0]; + BOOST_CHECK(std::get<0>(fileInfo) == "t_test"); + BOOST_CHECK(std::get<1>(fileInfo) == tool::FS_TYPE_LINK); + } + // simple append + { + auto r1 = appendColumns(number++, "t_test", {"v1", "v2"}); + auto r2 = desc(number++, "t_test"); + TableInfoTuple tableInfo = {"id", {"item_name", "item_id", "v1", "v2"}}; + BOOST_CHECK(r2->data().toBytes() == codec->encode(tableInfo)); + } + // append not exist table + { + auto r1 = appendColumns(number++, "t_test2", {"v1", "v2"}); + BOOST_CHECK(r1->data().toBytes() == codec->encode((int32_t)CODE_TABLE_NOT_EXIST)); + } + // append duplicate field + { + auto r1 = appendColumns(number++, "t_test", {"v1", "v3"}); + BOOST_CHECK(r1->data().toBytes() == codec->encode((int32_t)CODE_TABLE_DUPLICATE_FIELD)); + } + + // append too long field + { + std::string longField = "0"; + for (int j = 0; j < USER_TABLE_FIELD_NAME_MAX_LENGTH; ++j) + { + longField += "0"; + } + auto r1 = appendColumns(number++, "t_test", {"v3", longField}); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + } +} + +BOOST_AUTO_TEST_CASE(appendColumnsWasmTest) +{ + init(true); + auto callAddress = tableTestAddress; + BlockNumber number = 1; + { + creatTable(number++, "t_test", "id", {"item_name", "item_id"}, callAddress); + } + + // check create + { + auto response2 = list(number++, "/tables"); + int32_t ret; + std::vector bfsInfos; + codec->decode(response2->data(), ret, bfsInfos); + BOOST_CHECK(ret == 0); + BOOST_CHECK(bfsInfos.size() == 1); + auto fileInfo = bfsInfos[0]; + BOOST_CHECK(std::get<0>(fileInfo) == "t_test"); + BOOST_CHECK(std::get<1>(fileInfo) == executor::FS_TYPE_CONTRACT); + } + // simple append + { + auto r1 = appendColumns(number++, "t_test", {"v1", "v2"}); + auto r2 = desc(number++, "t_test"); + TableInfoTuple tableInfo = {"id", {"item_name", "item_id", "v1", "v2"}}; + BOOST_CHECK(r2->data().toBytes() == codec->encode(tableInfo)); + } + // append not exist table + { + auto r1 = appendColumns(number++, "t_test2", {"v1", "v2"}); + BOOST_CHECK(r1->data().toBytes() == codec->encode((int32_t)CODE_TABLE_NOT_EXIST)); + } + // append duplicate field + { + auto r1 = appendColumns(number++, "t_test", {"v1", "v3"}); + BOOST_CHECK(r1->data().toBytes() == codec->encode((int32_t)CODE_TABLE_DUPLICATE_FIELD)); + } + + // append too long field + { + std::string longField = "0"; + for (int j = 0; j < USER_TABLE_FIELD_NAME_MAX_LENGTH; ++j) + { + longField += "0"; + } + auto r1 = appendColumns(number++, "t_test", {"v3", longField}); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + } +} + +BOOST_AUTO_TEST_CASE(insertTest) +{ + auto callAddress = tableTestAddress; + BlockNumber number = 1; + { + creatTable(number++, "t_test", "id", {"item_name", "item_id"}, callAddress); + } + + // simple insert + { + auto r1 = insert(number++, "id1", {"test1", "test2"}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(1))); + } + + // insert exist + { + auto r1 = insert(number++, "id1", {"test1", "test2"}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(CODE_INSERT_KEY_EXIST))); + } + + // insert too much value + { + auto r1 = insert(number++, "id2", {"test1", "test2", "test3"}, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + // insert not enough value + { + auto r1 = insert(number++, "id3", {"test1"}, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + // insert too long key + { + boost::log::core::get()->set_logging_enabled(false); + std::string longKey = "0"; + for (int j = 0; j < USER_TABLE_KEY_VALUE_MAX_LENGTH; ++j) + { + longKey += "0"; + } + auto r1 = insert(number++, longKey, {"test1", "test2"}, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + boost::log::core::get()->set_logging_enabled(true); + } + + // insert too long key + { + boost::log::core::get()->set_logging_enabled(false); + std::string longValue = "0"; + for (int j = 0; j < USER_TABLE_FIELD_VALUE_MAX_LENGTH; ++j) + { + longValue += "0"; + } + auto r1 = insert(number++, "id111", {"test1", longValue}, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + boost::log::core::get()->set_logging_enabled(true); + } + + // insert after append + { + auto r1 = appendColumns(number++, "t_test", {"v1", "v2"}); + auto r2 = desc(number++, "t_test"); + TableInfoTuple tableInfo = {"id", {"item_name", "item_id", "v1", "v2"}}; + BOOST_CHECK(r2->data().toBytes() == codec->encode(tableInfo)); + + // good + auto r3 = insert(number++, "id4", {"test1", "test2", "test3", "test4"}, callAddress); + BOOST_CHECK(r3->data().toBytes() == codec->encode(int32_t(1))); + + // insert too much value + auto r4 = + insert(number++, "id5", {"test1", "test2", "test3", "test4", "test5"}, callAddress); + BOOST_CHECK(r4->status() == (int32_t)TransactionStatus::PrecompiledError); + + // insert not enough value + auto r5 = insert(number++, "id3", {"test1", "test2"}, callAddress); + BOOST_CHECK(r5->status() == (int32_t)TransactionStatus::PrecompiledError); + } +} + +BOOST_AUTO_TEST_CASE(insertWasmTest) +{ + init(true); + auto callAddress = tableTestAddress; + BlockNumber number = 1; + { + creatTable(number++, "t_test", "id", {"item_name", "item_id"}, callAddress); + } + + // simple insert + { + auto r1 = insert(number++, "id1", {"test1", "test2"}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(1))); + } + + // insert exist + { + auto r1 = insert(number++, "id1", {"test1", "test2"}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(CODE_INSERT_KEY_EXIST))); + } + + // insert too much value + { + auto r1 = insert(number++, "id1", {"test1", "test2", "test3"}, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + // insert not enough value + { + auto r1 = insert(number++, "id1", {"test1"}, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + // insert too long key + { + boost::log::core::get()->set_logging_enabled(false); + std::string longKey = "0"; + for (int j = 0; j < USER_TABLE_KEY_VALUE_MAX_LENGTH; ++j) + { + longKey += "0"; + } + auto r1 = insert(number++, longKey, {"test1", "test2"}, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + boost::log::core::get()->set_logging_enabled(true); + } + + // insert too long key + { + boost::log::core::get()->set_logging_enabled(false); + std::string longValue = "0"; + for (int j = 0; j < USER_TABLE_FIELD_VALUE_MAX_LENGTH; ++j) + { + longValue += "0"; + } + auto r1 = insert(number++, "id1", {"test1", longValue}, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + boost::log::core::get()->set_logging_enabled(true); + } +} + +BOOST_AUTO_TEST_CASE(selectTest) +{ + /// INSERT_COUNT should > 100 + const int INSERT_COUNT = 10000; + auto fillZero = [](int _num) -> std::string { + std::stringstream stream; + stream << std::setfill('0') << std::setw(40) << std::right << _num; + return stream.str(); + }; + auto callAddress = tableTestAddress; + BlockNumber number = 1; + { + creatTable(number++, "t_test", "id", {"item_name", "item_id"}, callAddress); + } + + // simple insert + { + auto r1 = insert(number++, fillZero(1), {"test1", "test2"}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(1))); + } + + // simple select by key + { + auto r1 = selectByKey(number++, fillZero(1), callAddress); + EntryTuple entryTuple = {fillZero(1), {"test1", "test2"}}; + BOOST_CHECK(r1->data().toBytes() == codec->encode(entryTuple)); + } + + // select by key not exist + { + auto r1 = selectByKey(number++, fillZero(2), callAddress); + EntryTuple entryTuple = {}; + BOOST_CHECK(r1->data().toBytes() == codec->encode(entryTuple)); + } + for (int j = 3; j < INSERT_COUNT; ++j) + { + boost::log::core::get()->set_logging_enabled(false); + std::string index = std::to_string(j); + insert(number++, fillZero(j), {"test" + index, "test" + index}, callAddress); + boost::log::core::get()->set_logging_enabled(true); + } + + // simple select by condition + { + uint32_t limitCount = 10; + // lexicographical order, 1~INSERT_COUNT + ConditionTuple cond1 = {0, fillZero(1)}; + LimitTuple limit = {0, limitCount}; + auto r1 = selectByCondition(number++, {cond1}, limit, callAddress); + std::vector entries; + codec->decode(r1->data(), entries); + BOOST_CHECK(entries.size() == limitCount); + } + + { + // lexicographical order, 100~INSERT_COUNT + uint32_t geNumber = 100; + ConditionTuple cond1 = {1, fillZero(geNumber)}; + auto r1 = count(number++, {cond1}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(uint32_t(INSERT_COUNT - geNumber))); + } + + // select by condition, empty condition + { + LimitTuple limit = {0, 10}; + auto r1 = selectByCondition(number++, {}, limit, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + + auto r2 = count(number++, {}, callAddress); + BOOST_CHECK(r2->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + // select by condition, condition with undefined cmp + { + ConditionTuple cond1 = {5, "90"}; + LimitTuple limit = {0, 10}; + auto r1 = selectByCondition(number++, {cond1}, limit, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + + auto r2 = count(number++, {cond1}, callAddress); + BOOST_CHECK(r2->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + // select by condition, limit overflow + { + ConditionTuple cond1 = {0, "90"}; + LimitTuple limit = {0, 10000}; + auto r1 = selectByCondition(number++, {cond1}, limit, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + } +} + +BOOST_AUTO_TEST_CASE(selectWasmTest) +{ + init(true); + /// INSERT_COUNT should > 100 + const int INSERT_COUNT = 1000; + auto fillZero = [](int _num) -> std::string { + std::stringstream stream; + stream << std::setfill('0') << std::setw(40) << std::right << _num; + return stream.str(); + }; + auto callAddress = tableTestAddress; + BlockNumber number = 1; + { + creatTable(number++, "t_test", "id", {"item_name", "item_id"}, callAddress); + } + + // simple insert + { + auto r1 = insert(number++, fillZero(1), {"test1", "test2"}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(1))); + } + + // simple select by key + { + auto r1 = selectByKey(number++, fillZero(1), callAddress); + EntryTuple entryTuple = {fillZero(1), {"test1", "test2"}}; + BOOST_CHECK(r1->data().toBytes() == codec->encode(entryTuple)); + } + + // select by key not exist + { + auto r1 = selectByKey(number++, fillZero(2), callAddress); + EntryTuple entryTuple = {}; + BOOST_CHECK(r1->data().toBytes() == codec->encode(entryTuple)); + } + for (int j = 3; j < INSERT_COUNT; ++j) + { + boost::log::core::get()->set_logging_enabled(false); + std::string index = std::to_string(j); + insert(number++, fillZero(j), {"test" + index, "test" + index}, callAddress); + boost::log::core::get()->set_logging_enabled(true); + } + + // simple select by condition + { + uint32_t limitCount = 10; + // lexicographical order, 1~INSERT_COUNT + ConditionTuple cond1 = {0, fillZero(1)}; + LimitTuple limit = {0, limitCount}; + auto r1 = selectByCondition(number++, {cond1}, limit, callAddress); + std::vector entries; + codec->decode(r1->data(), entries); + BOOST_CHECK(entries.size() == limitCount); + } + + { + // lexicographical order, 100~INSERT_COUNT + uint32_t geNumber = 100; + ConditionTuple cond1 = {1, fillZero(geNumber)}; + auto r1 = count(number++, {cond1}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(uint32_t(INSERT_COUNT - geNumber))); + } + + // select by condition, empty condition + { + LimitTuple limit = {0, 10}; + auto r1 = selectByCondition(number++, {}, limit, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + + auto r2 = count(number++, {}, callAddress); + BOOST_CHECK(r2->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + // select by condition, condition with undefined cmp + { + ConditionTuple cond1 = {5, "90"}; + LimitTuple limit = {0, 10}; + auto r1 = selectByCondition(number++, {cond1}, limit, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + + auto r2 = count(number++, {cond1}, callAddress); + BOOST_CHECK(r2->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + // select by condition, limit overflow + { + ConditionTuple cond1 = {0, "90"}; + LimitTuple limit = {0, 10000}; + auto r1 = selectByCondition(number++, {cond1}, limit, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + } +} + +/// TODO: check limit +BOOST_AUTO_TEST_CASE(updateTest) +{ + auto callAddress = tableTestAddress; + BlockNumber number = 1; + { + creatTable(number++, "t_test", "id", {"item_name", "item_id"}, callAddress); + } + + // simple insert + { + auto r1 = insert(number++, "1", {"test1", "test2"}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(1))); + } + + // simple update by key, modify 1 column + { + UpdateFieldTuple updateFieldTuple1 = {"item_name", "update"}; + auto r1 = updateByKey(number++, "1", {updateFieldTuple1}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(1))); + auto r2 = selectByKey(number++, "1", callAddress); + EntryTuple entryTuple = {"1", {"update", "test2"}}; + BOOST_CHECK(r2->data().toBytes() == codec->encode(entryTuple)); + } + + // simple update by key, modify 2 columns + { + UpdateFieldTuple updateFieldTuple1 = {"item_name", "update1"}; + UpdateFieldTuple updateFieldTuple2 = {"item_id", "update2"}; + auto r1 = updateByKey(number++, "1", {updateFieldTuple2, updateFieldTuple1}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(1))); + auto r2 = selectByKey(number++, "1", callAddress); + EntryTuple entryTuple = {"1", {"update1", "update2"}}; + BOOST_CHECK(r2->data().toBytes() == codec->encode(entryTuple)); + } + + // update by key not exist + { + UpdateFieldTuple updateFieldTuple1 = {"item_name", "update1"}; + auto r1 = updateByKey(number++, "2", {updateFieldTuple1}, callAddress, true); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(CODE_UPDATE_KEY_NOT_EXIST))); + } + + // update by key, index overflow + { + UpdateFieldTuple updateFieldTuple1 = {"errorField", "update1"}; + auto r1 = updateByKey(number++, "1", {updateFieldTuple1}, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + std::string longValue = "0"; + for (int j = 0; j < USER_TABLE_FIELD_VALUE_MAX_LENGTH; ++j) + { + longValue += "0"; + } + + // update by key, value overflow + { + boost::log::core::get()->set_logging_enabled(false); + UpdateFieldTuple updateFieldTuple1 = {"item_name", longValue}; + auto r1 = updateByKey(number++, "1", {updateFieldTuple1}, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + boost::log::core::get()->set_logging_enabled(true); + } + + for (int j = 2; j < 100; ++j) + { + boost::log::core::get()->set_logging_enabled(false); + std::string index = std::to_string(j); + insert(number++, index, {"test" + index, "test" + index}, callAddress); + boost::log::core::get()->set_logging_enabled(true); + } + + // update by empty condition + { + LimitTuple limit = {0, 10}; + UpdateFieldTuple updateFieldTuple1 = {"item_name", "update1"}; + auto r1 = updateByCondition(number++, {}, limit, {updateFieldTuple1}, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + // simple update by condition + { + // lexicographical order, 90~99 + ConditionTuple cond1 = {0, "90"}; + LimitTuple limit = {0, 10}; + UpdateFieldTuple updateFieldTuple1 = {"item_name", "update1"}; + auto r1 = updateByCondition(number++, {cond1}, limit, {updateFieldTuple1}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(9))); + + // select + auto r2 = selectByKey(number++, "98", callAddress); + EntryTuple entryTuple = {"98", {"update1", "test98"}}; + BOOST_CHECK(r2->data().toBytes() == codec->encode(entryTuple)); + + // update second column + UpdateFieldTuple updateFieldTuple2 = {"item_id", "update2"}; + auto r3 = updateByCondition( + number++, {cond1}, limit, {updateFieldTuple1, updateFieldTuple2}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(9))); + + // select + auto r4 = selectByKey(number++, "96", callAddress); + EntryTuple entryTuple2 = {"96", {"update1", "update2"}}; + BOOST_CHECK(r4->data().toBytes() == codec->encode(entryTuple2)); + } +} + +/// TODO: check limit +BOOST_AUTO_TEST_CASE(updateWasmTest) +{ + init(true); + auto callAddress = tableTestAddress; + BlockNumber number = 1; + { + creatTable(number++, "t_test", "id", {"item_name", "item_id"}, callAddress); + } + + // simple insert + { + auto r1 = insert(number++, "1", {"test1", "test2"}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(1))); + } + + // simple update by key, modify 1 column + { + UpdateFieldTuple updateFieldTuple1 = {"item_name", "update"}; + auto r1 = updateByKey(number++, "1", {updateFieldTuple1}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(1))); + auto r2 = selectByKey(number++, "1", callAddress); + EntryTuple entryTuple = {"1", {"update", "test2"}}; + BOOST_CHECK(r2->data().toBytes() == codec->encode(entryTuple)); + } + + // simple update by key, modify 2 columns + { + UpdateFieldTuple updateFieldTuple1 = {"item_name", "update1"}; + UpdateFieldTuple updateFieldTuple2 = {"item_id", "update2"}; + auto r1 = updateByKey(number++, "1", {updateFieldTuple2, updateFieldTuple1}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(1))); + auto r2 = selectByKey(number++, "1", callAddress); + EntryTuple entryTuple = {"1", {"update1", "update2"}}; + BOOST_CHECK(r2->data().toBytes() == codec->encode(entryTuple)); + } + + // update by key not exist + { + UpdateFieldTuple updateFieldTuple1 = {"item_name", "update1"}; + auto r1 = updateByKey(number++, "2", {updateFieldTuple1}, callAddress, true); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(CODE_UPDATE_KEY_NOT_EXIST))); + } + + // update by key, index overflow + { + UpdateFieldTuple updateFieldTuple1 = {"errorField", "update1"}; + auto r1 = updateByKey(number++, "1", {updateFieldTuple1}, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + std::string longValue = "0"; + for (int j = 0; j < USER_TABLE_FIELD_VALUE_MAX_LENGTH; ++j) + { + longValue += "0"; + } + + // update by key, value overflow + { + boost::log::core::get()->set_logging_enabled(false); + UpdateFieldTuple updateFieldTuple1 = {"item_name", longValue}; + auto r1 = updateByKey(number++, "1", {updateFieldTuple1}, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + boost::log::core::get()->set_logging_enabled(true); + } + + for (int j = 2; j < 100; ++j) + { + boost::log::core::get()->set_logging_enabled(false); + std::string index = std::to_string(j); + insert(number++, index, {"test" + index, "test" + index}, callAddress); + boost::log::core::get()->set_logging_enabled(true); + } + + // simple update by condition + { + // lexicographical order, 90~99 + ConditionTuple cond1 = {0, "90"}; + LimitTuple limit = {0, 10}; + UpdateFieldTuple updateFieldTuple1 = {"item_name", "update1"}; + auto r1 = updateByCondition(number++, {cond1}, limit, {updateFieldTuple1}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(9))); + + // select + auto r2 = selectByKey(number++, "98", callAddress); + EntryTuple entryTuple = {"98", {"update1", "test98"}}; + BOOST_CHECK(r2->data().toBytes() == codec->encode(entryTuple)); + + // update second column + UpdateFieldTuple updateFieldTuple2 = {"item_id", "update2"}; + auto r3 = updateByCondition( + number++, {cond1}, limit, {updateFieldTuple1, updateFieldTuple2}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(9))); + + // select + auto r4 = selectByKey(number++, "96", callAddress); + EntryTuple entryTuple2 = {"96", {"update1", "update2"}}; + BOOST_CHECK(r4->data().toBytes() == codec->encode(entryTuple2)); + } +} + +/// TODO: check limit +BOOST_AUTO_TEST_CASE(removeTest) +{ + auto callAddress = tableTestAddress; + BlockNumber number = 1; + { + creatTable(number++, "t_test", "id", {"item_name", "item_id"}, callAddress); + } + + // simple insert + { + auto r1 = insert(number++, "1", {"test1", "test2"}, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(1))); + } + + // simple remove by key + { + auto r1 = removeByKey(number++, "1", callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(1))); + auto r2 = selectByKey(number++, "1", callAddress); + // empty + EntryTuple entryTuple = {}; + BOOST_CHECK(r2->data().toBytes() == codec->encode(entryTuple)); + } + + // remove by key not exist + { + auto r1 = removeByKey(number++, "1", callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(CODE_REMOVE_KEY_NOT_EXIST))); + } + + for (int j = 1; j < 100; ++j) + { + boost::log::core::get()->set_logging_enabled(false); + std::string index = std::to_string(j); + insert(number++, index, {"test" + index, "test" + index}, callAddress); + boost::log::core::get()->set_logging_enabled(true); + } + + // remove by empty condition + { + LimitTuple limit = {0, 10}; + auto r1 = removeByCondition(number++, {}, limit, callAddress); + BOOST_CHECK(r1->status() == (int32_t)TransactionStatus::PrecompiledError); + } + + // simple remove by condition + { + // lexicographical order, 90~99 + ConditionTuple cond1 = {0, "90"}; + LimitTuple limit = {0, 10}; + auto r1 = removeByCondition(number++, {cond1}, limit, callAddress); + BOOST_CHECK(r1->data().toBytes() == codec->encode(int32_t(9))); + + // select + auto r2 = selectByKey(number++, "98", callAddress); + EntryTuple entryTuple = {}; + BOOST_CHECK(r2->data().toBytes() == codec->encode(entryTuple)); + + // select + auto r3 = selectByKey(number++, "89", callAddress); + EntryTuple entryTuple2 = {"89", {"test89", "test89"}}; + BOOST_CHECK(r3->data().toBytes() == codec->encode(entryTuple2)); + + // remove again + auto r4 = removeByCondition(number++, {cond1}, limit, callAddress); + BOOST_CHECK(r4->data().toBytes() == codec->encode(int32_t(0))); + } +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/VRFPrecompiledTest.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/VRFPrecompiledTest.cpp" new file mode 100644 index 00000000..abd4c1b7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libprecompiled/VRFPrecompiledTest.cpp" @@ -0,0 +1,171 @@ +/** + * Copyright (C) 2021 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. + */ +#include "../mock/MockLedger.h" +#include "bcos-codec/abi/ContractABICodec.h" +#include "bcos-executor/src/executive/BlockContext.h" +#include "bcos-executor/src/executive/TransactionExecutive.h" +#include "bcos-executor/src/precompiled/CryptoPrecompiled.h" +#include "bcos-executor/src/precompiled/common/Common.h" +#include "bcos-executor/src/precompiled/common/Utilities.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::crypto; +using namespace bcos::precompiled; +using namespace bcos::executor; +using namespace bcos::storage; +namespace bcos::test +{ +class VRFPrecompiledFixture +{ +public: + VRFPrecompiledFixture(bool _useSM, uint32_t _blockVersion) + { + clearName2SelectCache(); + if (!_useSM) + { + m_cryptoSuite = std::make_shared( + std::make_shared(), std::make_shared(), nullptr); + } + else + { + m_cryptoSuite = std::make_shared( + std::make_shared(), std::make_shared(), nullptr); + } + m_cryptoPrecompiled = std::make_shared(m_cryptoSuite->hashImpl()); + m_ledgerCache = std::make_shared(std::make_shared()); + m_blockContext = + std::make_shared(nullptr, m_ledgerCache, m_cryptoSuite->hashImpl(), 0, + h256(), utcTime(), _blockVersion, FiscoBcosScheduleV4, false, false); + std::shared_ptr gasInjector = nullptr; + m_executive = std::make_shared( + std::weak_ptr(m_blockContext), "", 100, 0, gasInjector); + m_abi = std::make_shared(m_cryptoSuite->hashImpl()); + } + + ~VRFPrecompiledFixture() {} + + LedgerCache::Ptr m_ledgerCache; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + BlockContext::Ptr m_blockContext; + TransactionExecutive::Ptr m_executive; + CryptoPrecompiled::Ptr m_cryptoPrecompiled; + std::string m_vrfVerifyFunction = "curve25519VRFVerify(bytes,bytes,bytes)"; + std::shared_ptr m_abi; +}; + +BOOST_FIXTURE_TEST_SUITE(test_VRFPrecompiled, TestPromptFixture) + +void testVRFVerify(VRFPrecompiledFixture _fixture) +{ + auto keyPair = _fixture.m_cryptoSuite->signatureImpl()->generateKeyPair(); + // generate vrfProof + std::string input = "abcd"; + bytes vrfPublicKey; + vrfPublicKey.resize(32); + CInputBuffer privateKey{ + (const char*)keyPair->secretKey()->data().data(), keyPair->secretKey()->size()}; + COutputBuffer publicKey{(char*)vrfPublicKey.data(), vrfPublicKey.size()}; + // derive the public key + std::cout << "try to wedpr_curve25519_vrf_derive_public_key" << std::endl; + auto ret = wedpr_curve25519_vrf_derive_public_key(&privateKey, &publicKey); + BOOST_CHECK_EQUAL(ret, WEDPR_SUCCESS); + std::cout << "try to wedpr_curve25519_vrf_derive_public_key success" << std::endl; + + // generate proof + bytes inputBytes = bytes(input.begin(), input.end()); + CInputBuffer inputMsg{(const char*)inputBytes.data(), inputBytes.size()}; + bytes vrfProof; + size_t proofSize = 96; + vrfProof.resize(proofSize); + COutputBuffer proof{(char*)vrfProof.data(), proofSize}; + std::cout << "try to wedpr_curve25519_vrf_prove_utf8" << std::endl; + ret = wedpr_curve25519_vrf_prove_utf8(&privateKey, &inputMsg, &proof); + BOOST_CHECK_EQUAL(ret, WEDPR_SUCCESS); + std::cout << "try to wedpr_curve25519_vrf_prove_utf8 success" << std::endl; + + // case1: verify success + u256 lastRandomValue; + bool verifySucc; + u256 randomValue; + std::cout << "### inputBytes: " << *toHexString(inputBytes) << std::endl; + std::cout << "### vrfPublicKey: " << *toHexString(vrfPublicKey) << std::endl; + std::cout << "### vrfProof: " << *toHexString(vrfProof) << std::endl; + for (int i = 0; i < 10; i++) + { + bytes in = + _fixture.m_abi->abiIn(_fixture.m_vrfVerifyFunction, inputBytes, vrfPublicKey, vrfProof); + auto parameters = std::make_shared(); + parameters->m_input = bytesConstRef(in.data(), in.size()); + auto execResult = _fixture.m_cryptoPrecompiled->call(_fixture.m_executive, parameters); + auto out = execResult->execResult(); + + _fixture.m_abi->abiOut(bytesConstRef(&out), verifySucc, randomValue); + BOOST_CHECK(verifySucc == true); + if (i > 0) + { + BOOST_CHECK(lastRandomValue == randomValue); + } + lastRandomValue = randomValue; + } + + // case2: mismatch public key + std::string fakeData = "mismatchVRFPublicKey"; + auto mismatchVRFPublicKey = bytes(fakeData.begin(), fakeData.end()); + auto in = _fixture.m_abi->abiIn( + _fixture.m_vrfVerifyFunction, inputBytes, mismatchVRFPublicKey, vrfProof); + auto parameters = std::make_shared(); + parameters->m_input = bytesConstRef(in.data(), in.size()); + auto execResult = _fixture.m_cryptoPrecompiled->call(_fixture.m_executive, parameters); + auto out = execResult->execResult(); + _fixture.m_abi->abiOut(bytesConstRef(&out), verifySucc, randomValue); + BOOST_CHECK(verifySucc == false); + BOOST_CHECK(randomValue == 0); + + // case3: mismatch input data + fakeData = "abc^5%@@bc$"; + bytes mismatchInput = bytes(fakeData.begin(), fakeData.end()); + in = _fixture.m_abi->abiIn(_fixture.m_vrfVerifyFunction, mismatchInput, vrfPublicKey, vrfProof); + parameters = std::make_shared(); + parameters->m_input = bytesConstRef(in.data(), in.size()); + execResult = _fixture.m_cryptoPrecompiled->call(_fixture.m_executive, parameters); + out = execResult->execResult(); + _fixture.m_abi->abiOut(bytesConstRef(&out), verifySucc, randomValue); + BOOST_CHECK(verifySucc == false); + BOOST_CHECK(randomValue == 0); +} + +BOOST_AUTO_TEST_CASE(testCurve25519VRFVerify) +{ + VRFPrecompiledFixture fixture(false, (uint32_t)(bcos::protocol::BlockVersion::V3_0_VERSION)); + testVRFVerify(fixture); +} +BOOST_AUTO_TEST_CASE(testSMCurve25519VRFVerify) +{ + VRFPrecompiledFixture fixture(true, (uint32_t)(bcos::protocol::BlockVersion::V3_0_VERSION)); + testVRFVerify(fixture); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libvm/.gitignore" "b/BFPL\345\243\271/bcos-executor/test/unittest/libvm/.gitignore" new file mode 100644 index 00000000..528c1e87 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libvm/.gitignore" @@ -0,0 +1 @@ +WasmPath.h diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libvm/TestTransactionExecutive.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/libvm/TestTransactionExecutive.cpp" new file mode 100644 index 00000000..318eae17 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libvm/TestTransactionExecutive.cpp" @@ -0,0 +1,21 @@ +#include "../../src/executive/TransactionExecutive.h" +#include + +namespace bcos::test +{ +class TransactionExecutiveFixture +{ +public: + TransactionExecutiveFixture() {} +}; + +BOOST_FIXTURE_TEST_SUITE(testTransactionExecutive, TransactionExecutiveFixture) + +BOOST_AUTO_TEST_CASE(test) +{ + BOOST_CHECK(true); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/libvm/WasmPath.h.in" "b/BFPL\345\243\271/bcos-executor/test/unittest/libvm/WasmPath.h.in" new file mode 100644 index 00000000..256b3bec --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/libvm/WasmPath.h.in" @@ -0,0 +1,7 @@ +#include + +#pragma once + +char originBinary[] = "@CMAKE_CURRENT_SOURCE_DIR@/../wasm/infinit_loop.wasm"; +char useGasBinary[] = "@CMAKE_CURRENT_SOURCE_DIR@/../wasm/metric_infinit_loop_useGas.wasm"; +char globalGasBinary[] = "@CMAKE_CURRENT_SOURCE_DIR@/../wasm/metric_infinit_loop_global_gas.wasm"; diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/main.cpp" "b/BFPL\345\243\271/bcos-executor/test/unittest/main.cpp" new file mode 100644 index 00000000..7e74d52e --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/main.cpp" @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2021 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 main.cpp + * @author: xingqiangbai + * @date 2021-05-17 + */ +#define BOOST_TEST_MODULE FISCO_BCOS_Tests +#define BOOST_TEST_MAIN +#define BOOST_TEST_STATIC_LINK + +#include +#include diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockBlock.h" "b/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockBlock.h" new file mode 100644 index 00000000..29fbf998 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockBlock.h" @@ -0,0 +1,55 @@ +#pragma once +#include "MockBlockHeader.h" +#include + +namespace bcos::test +{ +class MockBlock : public bcos::protocol::Block +{ +public: + MockBlock() : bcos::protocol::Block(nullptr, nullptr) {} + ~MockBlock() override {} + + void setBlockHeader(protocol::BlockHeader::Ptr blockHeader) override + { + m_blockHeader = blockHeader; + } + void decode(bytesConstRef _data, bool _calculateHash, bool _checkSig) override {} + void encode(bytes& _encodeData) const override {} + crypto::HashType calculateTransactionRoot() const override { return bcos::crypto::HashType(); } + crypto::HashType calculateReceiptRoot() const override { return bcos::crypto::HashType(); } + int32_t version() const override { return m_blockHeader->version(); } + void setVersion(int32_t _version) override { m_blockHeader->setVersion(_version); } + protocol::BlockType blockType() const override { return protocol::WithTransactionsHash; } + protocol::BlockHeader::ConstPtr blockHeaderConst() const override { return m_blockHeader; } + protocol::BlockHeader::Ptr blockHeader() override { return m_blockHeader; } + protocol::Transaction::ConstPtr transaction(uint64_t _index) const override { return {}; } + protocol::TransactionReceipt::ConstPtr receipt(uint64_t _index) const override { return {}; } + protocol::TransactionMetaData::ConstPtr transactionMetaData(uint64_t _index) const override + { + return {}; + } + crypto::HashType transactionHash(uint64_t _index) const override + { + return Block::transactionHash(_index); + } + void setBlockType(protocol::BlockType _blockType) override {} + void setTransaction(uint64_t _index, protocol::Transaction::Ptr _transaction) override {} + void appendTransaction(protocol::Transaction::Ptr _transaction) override {} + void setReceipt(uint64_t _index, protocol::TransactionReceipt::Ptr _receipt) override {} + void appendReceipt(protocol::TransactionReceipt::Ptr _receipt) override {} + void appendTransactionMetaData(protocol::TransactionMetaData::Ptr _txMetaData) override {} + protocol::NonceListPtr nonces() const override { return {}; } + uint64_t transactionsSize() const override { return 0; } + uint64_t transactionsMetaDataSize() const override { return 0; } + uint64_t transactionsHashSize() const override { return Block::transactionsHashSize(); } + uint64_t receiptsSize() const override { return 0; } + void setNonceList(const protocol::NonceList& _nonceList) override {} + void setNonceList(protocol::NonceList&& _nonceList) override {} + const protocol::NonceList& nonceList() const override { return m_nodelist; } + +private: + protocol::BlockHeader::Ptr m_blockHeader = std::make_shared(1); + protocol::NonceList m_nodelist; +}; +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockBlockHeader.h" "b/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockBlockHeader.h" new file mode 100644 index 00000000..543c2862 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockBlockHeader.h" @@ -0,0 +1,59 @@ +#pragma once +#include + +namespace bcos::test +{ +class MockBlockHeader : public bcos::protocol::BlockHeader +{ +public: + MockBlockHeader(protocol::BlockNumber _number) : BlockHeader({}), m_blockNumber(_number) {} + virtual ~MockBlockHeader() {} + + void decode(bytesConstRef _data) override {} + void encode(bytes& _encodeData) const override {} + void clear() override {} + uint32_t version() const override { return 0; } + gsl::span parentInfo() const override + { + return gsl::span(); + } + crypto::HashType txsRoot() const override { return bcos::crypto::HashType(); } + crypto::HashType receiptsRoot() const override { return bcos::crypto::HashType(); } + crypto::HashType stateRoot() const override { return bcos::crypto::HashType(); } + protocol::BlockNumber number() const override { return m_blockNumber; } + u256 gasUsed() const override { return bcos::u256(); } + int64_t timestamp() const override { return 0; } + int64_t sealer() const override { return 0; } + gsl::span sealerList() const override { return gsl::span(); } + bytesConstRef extraData() const override { return bcos::bytesConstRef(); } + gsl::span signatureList() const override + { + return gsl::span(); + } + gsl::span consensusWeights() const override + { + return gsl::span(); + } + void setVersion(uint32_t _version) override {} + void setParentInfo(const gsl::span& _parentInfo) override {} + void setParentInfo(protocol::ParentInfoList&& _parentInfo) override {} + void setTxsRoot(bcos::crypto::HashType _txsRoot) override {} + void setReceiptsRoot(bcos::crypto::HashType _receiptsRoot) override {} + void setStateRoot(bcos::crypto::HashType _stateRoot) override {} + void setNumber(protocol::BlockNumber _blockNumber) override { m_blockNumber = _blockNumber; } + void setGasUsed(u256 _gasUsed) override {} + void setTimestamp(int64_t _timestamp) override {} + void setSealer(int64_t _sealerId) override {} + void setSealerList(const gsl::span& _sealerList) override {} + void setSealerList(std::vector&& _sealerList) override {} + void setConsensusWeights(const gsl::span& _weightList) override {} + void setConsensusWeights(std::vector&& _weightList) override {} + void setExtraData(const bytes& _extraData) override {} + void setExtraData(bytes&& _extraData) override {} + void setSignatureList(const gsl::span& _signatureList) override {} + void setSignatureList(protocol::SignatureList&& _signatureList) override {} + +private: + protocol::BlockNumber m_blockNumber; +}; +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockExecutiveFactory.h" "b/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockExecutiveFactory.h" new file mode 100644 index 00000000..7828848d --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockExecutiveFactory.h" @@ -0,0 +1,46 @@ +#pragma once +#include "../../../src/Common.h" +#include "../../../src/executive/BlockContext.h" +#include "../../../src/executive/ExecutiveFactory.h" +#include "../../../src/executive/TransactionExecutive.h" +#include "../../../src/vm/gas_meter/GasInjector.h" +#include "MockLedger.h" +#include "MockTransactionExecutive.h" +#include + +using namespace bcos; +using namespace bcos::executor; +namespace bcos::test +{ +class MockExecutiveFactory : public bcos::executor::ExecutiveFactory +{ +public: + using Ptr = std::shared_ptr; + MockExecutiveFactory(std::shared_ptr blockContext, + std::shared_ptr>> + precompiledContract, + std::shared_ptr>> + constantPrecompiled, + std::shared_ptr> builtInPrecompiled, + std::shared_ptr gasInjector) + : ExecutiveFactory(std::move(blockContext), precompiledContract, constantPrecompiled, + builtInPrecompiled, gasInjector) + {} + virtual ~MockExecutiveFactory() {} + + + std::shared_ptr build(const std::string&, int64_t, int64_t, bool) override + { + auto ledgerCache = std::make_shared(std::make_shared()); + std::shared_ptr blockContext = std::make_shared( + nullptr, ledgerCache, nullptr, 0, h256(), 0, 0, FiscoBcosScheduleV4, false, false); + auto executive = + std::make_shared(blockContext, "0x00", 0, 0, instruction); + return executive; + } + + + std::shared_ptr instruction = + std::make_shared(wasm::GetInstructionTable()); +}; +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockExecutiveFlow.h" "b/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockExecutiveFlow.h" new file mode 100644 index 00000000..2e1e8833 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockExecutiveFlow.h" @@ -0,0 +1,35 @@ +#pragma once +#include "../../../src/CallParameters.h" +#include "../../../src/executive/ExecutiveFlowInterface.h" +#include +#include + +using namespace bcos; +using namespace std; +using namespace bcos::executor; + +namespace bcos::test +{ +class MockExecutiveFlow : public bcos::executor::ExecutiveFlowInterface +{ +public: + using Ptr = std::shared_ptr; + MockExecutiveFlow(std::string& name) : m_name(name) {} + virtual ~MockExecutiveFlow() {} + + + void submit(CallParameters::UniquePtr txInput) override {} + void submit(std::shared_ptr> txInputs) override {} + void asyncRun( + // onTxReturn(output) + std::function onTxReturn, + + // onFinished(success, errorMessage) + std::function onFinished) override{}; + std::string& name() const { return m_name; } + +private: + std::string& m_name; +}; + +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockKeyPageStorage.h" "b/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockKeyPageStorage.h" new file mode 100644 index 00000000..b0eac423 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockKeyPageStorage.h" @@ -0,0 +1,132 @@ +/** + * 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 MockKeyPageStorage.h + * @author: kyonGuo + * @date 2022/6/28 + */ + +#pragma once +#include "../../src/Common.h" +#include "MockTransactionalStorage.h" +#include +#include +#include +#include +#include + + +using namespace bcos::protocol; + +namespace bcos::test +{ +class MockKeyPageStorage : public bcos::storage::TransactionalStorageInterface +{ +public: + MockKeyPageStorage(bcos::crypto::Hash::Ptr hashImpl) : m_hashImpl(std::move(hashImpl)) + { + auto pre = std::make_shared(hashImpl); + m_inner = std::make_shared(std::move(pre)); + } + + void asyncGetPrimaryKeys(std::string_view table, + const std::optional& _condition, + std::function)> _callback) noexcept override + { + m_inner->asyncGetPrimaryKeys(table, _condition, std::move(_callback)); + } + + void asyncGetRow(std::string_view table, std::string_view _key, + std::function)> _callback) noexcept + override + { + m_inner->asyncGetRow(table, _key, std::move(_callback)); + } + + void asyncGetRows(std::string_view table, + const std::variant, + const gsl::span>& _keys, + std::function>)> + _callback) noexcept override + { + m_inner->asyncGetRows(table, _keys, std::move(_callback)); + } + + void asyncSetRow(std::string_view table, std::string_view key, storage::Entry entry, + std::function callback) noexcept override + { + m_inner->asyncSetRow(table, key, std::move(entry), std::move(callback)); + } + + void asyncOpenTable(std::string_view tableName, + std::function)> callback) noexcept + override + { + m_inner->asyncOpenTable(tableName, std::move(callback)); + } + + void asyncPrepare(const TwoPCParams& params, + const bcos::storage::TraverseStorageInterface& storage, + std::function callback) noexcept override + { + BOOST_CHECK_GT(params.number, 0); + + std::mutex mutex; + storage.parallelTraverse( + true, [&](const std::string_view& table, const std::string_view& key, + const storage::Entry& entry) { + std::unique_lock lock(mutex); + + auto keyHex = boost::algorithm::hex_lower(std::string(key)); + // EXECUTOR_LOG(TRACE) << "Merge data" << LOG_KV("table", table) + // << LOG_KV("key", keyHex) << LOG_KV("fields", fields); + + auto myTable = m_inner->openTable(table); + if (!myTable) + { + m_inner->createTable(std::string(table), executor::STORAGE_VALUE); + myTable = m_inner->openTable(std::string(table)); + } + myTable->setRow(key, entry); + + return true; + }); + + callback(nullptr, 0, ""); + } + + void asyncCommit(const TwoPCParams& params, + std::function callback) noexcept override + { + BOOST_CHECK_GT(params.number, 0); + callback(nullptr, 0); + } + + void asyncRollback( + const TwoPCParams& params, std::function callback) noexcept override + { + BOOST_CHECK_GT(params.number, 0); + callback(nullptr); + } + + std::pair count(const std::string_view& table) + { + return m_inner->count(table); + } + + bcos::storage::KeyPageStorage::Ptr m_inner; + bcos::crypto::Hash::Ptr m_hashImpl; +}; +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockLedger.h" "b/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockLedger.h" new file mode 100644 index 00000000..13ffa0bf --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockLedger.h" @@ -0,0 +1,143 @@ +#pragma once + +#include "MockBlock.h" +#include +#include +#include +#include + +namespace bcos::test +{ +class MockLedger : public bcos::ledger::LedgerInterface +{ +public: + using Ptr = std::shared_ptr; + static const uint32_t TX_GAS_LIMIT = 3000000666; + + void asyncPrewriteBlock(bcos::storage::StorageInterface::Ptr storage, + bcos::protocol::TransactionsPtr _blockTxs, bcos::protocol::Block::ConstPtr block, + std::function callback) override + { + BOOST_CHECK(false); // Need implementations + }; + void asyncPreStoreBlockTxs(bcos::protocol::TransactionsPtr, bcos::protocol::Block::ConstPtr, + std::function _callback) override + { + if (!_callback) + { + return; + } + _callback(nullptr); + } + + void asyncStoreTransactions(std::shared_ptr> _txToStore, + crypto::HashListPtr _txHashList, std::function _onTxStored) override + { + BOOST_CHECK(false); // Need implementations + }; + + protocol::BlockHeader::Ptr m_blockHeader; + void setBlockHeader(protocol::BlockHeader::Ptr blockHeader) + { + if (blockHeader) + { + m_blockHeader = blockHeader; + m_blockNumber = blockHeader->number(); + } + } + void asyncGetBlockDataByNumber(protocol::BlockNumber _blockNumber, int32_t _blockFlag, + std::function _onGetBlock) override + { + auto block = std::make_shared(); + if (m_blockHeader) + { + block->setBlockHeader(m_blockHeader); + } + block->blockHeader()->setNumber(m_blockNumber); + _onGetBlock(nullptr, block); + }; + + protocol::BlockNumber m_blockNumber = 0; + void setBlockNumber(protocol::BlockNumber blockNumber) { m_blockNumber = blockNumber; } + + void asyncGetBlockNumber( + std::function _onGetBlock) override + { + _onGetBlock(nullptr, m_blockNumber); + }; + + void asyncGetBlockHashByNumber(protocol::BlockNumber _blockNumber, + std::function _onGetBlock) override + { + _onGetBlock(nullptr, crypto::HashType(_blockNumber)); + }; + + void asyncGetBlockNumberByHash(crypto::HashType const& _blockHash, + std::function _onGetBlock) override + { + BOOST_CHECK(false); // Need implementations + }; + + void asyncGetBatchTxsByHashList(crypto::HashListPtr _txHashList, bool _withProof, + std::function>)> + _onGetTx) override + { + BOOST_CHECK(false); // Need implementations + }; + + + void asyncGetTransactionReceiptByHash(crypto::HashType const& _txHash, bool _withProof, + std::function + _onGetTx) override + { + BOOST_CHECK(false); // Need implementations + }; + + + void asyncGetTotalTransactionCount(std::function + _callback) override + { + BOOST_CHECK(false); // Need implementations + }; + + + void asyncGetSystemConfigByKey(std::string_view const& _key, + std::function _onGetConfig) override + { + if (std::string(bcos::ledger::SYSTEM_KEY_COMPATIBILITY_VERSION) == std::string(_key)) + { + std::stringstream ss; + ss << bcos::protocol::BlockVersion::MAX_VERSION; + + _onGetConfig(nullptr, ss.str(), m_blockNumber); + return; + } + else if (std::string(bcos::ledger::SYSTEM_KEY_TX_GAS_LIMIT) == std::string(_key)) + { + _onGetConfig(nullptr, std::to_string(MockLedger::TX_GAS_LIMIT), m_blockNumber); + return; + } + + + BOOST_CHECK(false); // Need implementations + }; + + + void asyncGetNodeListByType(std::string_view const& _type, + std::function _onGetConfig) override + { + BOOST_CHECK(false); // Need implementations + }; + + void asyncGetNonceList(protocol::BlockNumber _startNumber, int64_t _offset, + std::function>)> + _onGetList) override + { + BOOST_CHECK(false); // Need implementations + }; +}; +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockTransactionExecutive.h" "b/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockTransactionExecutive.h" new file mode 100644 index 00000000..18d736b0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockTransactionExecutive.h" @@ -0,0 +1,52 @@ +#pragma once +#include "../../../src/executive/BlockContext.h" +#include "../../src/Common.h" +#include "../../src/executive/TransactionExecutive.h" +#include "bcos-executor/src/executive/BlockContext.h" +#include "bcos-executor/src/executor/TransactionExecutor.h" +#include "bcos-framework/protocol/BlockHeader.h" +#include + + +using namespace bcos; +using namespace bcos::executor; +namespace bcos::test +{ +class MockTransactionExecutive : public bcos::executor::CoroutineTransactionExecutive +{ +public: + using Ptr = std::shared_ptr; + MockTransactionExecutive(std::weak_ptr blockContext, + std::string contractAddress, int64_t contextID, int64_t seq, + std::shared_ptr& gasInjector) + : CoroutineTransactionExecutive( + std::move(blockContext), contractAddress, contextID, seq, gasInjector) + {} + + virtual ~MockTransactionExecutive() {} + + CallParameters::UniquePtr start(CallParameters::UniquePtr input) override { return input; } + CallParameters::UniquePtr resume() override + { + auto callParameters = std::make_unique(CallParameters::Type::MESSAGE); + callParameters->staticCall = false; + callParameters->codeAddress = "aabbccddee"; + callParameters->contextID = 1; + callParameters->seq = 1; + return callParameters; + } + + void setExchangeMessage(CallParameters::UniquePtr callParameters) override + { + m_exchangeMessage = std::move(callParameters); + } + void appendResumeKeyLocks(std::vector keyLocks) override + { + std::copy( + keyLocks.begin(), keyLocks.end(), std::back_inserter(m_exchangeMessage->keyLocks)); + } + +private: + CallParameters::UniquePtr m_exchangeMessage = nullptr; +}; +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockTransactionalStorage.h" "b/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockTransactionalStorage.h" new file mode 100644 index 00000000..5c4f5d73 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockTransactionalStorage.h" @@ -0,0 +1,106 @@ +#pragma once + +#include "../../src/Common.h" +#include +#include +#include +#include +#include + +using namespace bcos::protocol; + +namespace bcos::test +{ +class MockTransactionalStorage : public bcos::storage::TransactionalStorageInterface +{ +public: + MockTransactionalStorage(bcos::crypto::Hash::Ptr hashImpl) : m_hashImpl(std::move(hashImpl)) + { + m_inner = std::make_shared(nullptr); + m_inner->setEnableTraverse(true); + } + + void asyncGetPrimaryKeys(std::string_view table, + const std::optional& _condition, + std::function)> _callback) noexcept override + { + m_inner->asyncGetPrimaryKeys(table, _condition, std::move(_callback)); + } + + void asyncGetRow(std::string_view table, std::string_view _key, + std::function)> _callback) noexcept + override + { + m_inner->asyncGetRow(table, _key, std::move(_callback)); + } + + void asyncGetRows(std::string_view table, + const std::variant, + const gsl::span>& _keys, + std::function>)> + _callback) noexcept override + { + m_inner->asyncGetRows(table, _keys, std::move(_callback)); + } + + void asyncSetRow(std::string_view table, std::string_view key, storage::Entry entry, + std::function callback) noexcept override + { + m_inner->asyncSetRow(table, key, std::move(entry), std::move(callback)); + } + + void asyncOpenTable(std::string_view tableName, + std::function)> callback) noexcept + override + { + m_inner->asyncOpenTable(tableName, std::move(callback)); + } + + void asyncPrepare(const TwoPCParams& params, + const bcos::storage::TraverseStorageInterface& storage, + std::function callback) noexcept override + { + BOOST_CHECK_GT(params.number, 0); + + std::mutex mutex; + storage.parallelTraverse( + true, [&](const std::string_view& table, const std::string_view& key, + const storage::Entry& entry) { + std::unique_lock lock(mutex); + + auto keyHex = boost::algorithm::hex_lower(std::string(key)); + // EXECUTOR_LOG(TRACE) << "Merge data" << LOG_KV("table", table) + // << LOG_KV("key", keyHex) << LOG_KV("fields", fields); + + auto myTable = m_inner->openTable(table); + if (!myTable) + { + m_inner->createTable(std::string(table), executor::STORAGE_VALUE); + myTable = m_inner->openTable(std::string(table)); + } + myTable->setRow(key, entry); + + return true; + }); + + callback(nullptr, 0, ""); + } + + void asyncCommit(const TwoPCParams& params, + std::function callback) noexcept override + { + BOOST_CHECK_GT(params.number, 0); + callback(nullptr, 0); + } + + void asyncRollback( + const TwoPCParams& params, std::function callback) noexcept override + { + BOOST_CHECK_GT(params.number, 0); + callback(nullptr); + } + + bcos::storage::StateStorage::Ptr m_inner; + bcos::crypto::Hash::Ptr m_hashImpl; +}; +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockTxPool.h" "b/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockTxPool.h" new file mode 100644 index 00000000..560afaa6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/test/unittest/mock/MockTxPool.h" @@ -0,0 +1,70 @@ +#pragma once + +#include +#include + +namespace bcos::test +{ +class MockTxPool : public txpool::TxPoolInterface +{ +public: + void start() override {} + void stop() override {} + task::Task submitTransaction( + protocol::Transaction::Ptr transaction) override + { + co_return nullptr; + } + void asyncSealTxs(uint64_t, bcos::txpool::TxsHashSetPtr, + std::function) + override + {} + void asyncMarkTxs(bcos::crypto::HashListPtr, bool, bcos::protocol::BlockNumber, + bcos::crypto::HashType const&, std::function) override + {} + void asyncVerifyBlock(bcos::crypto::PublicPtr, bytesConstRef const&, + std::function) override + {} + void asyncNotifyBlockResult(bcos::protocol::BlockNumber, + bcos::protocol::TransactionSubmitResultsPtr, std::function) override + {} + void asyncNotifyTxsSyncMessage(bcos::Error::Ptr, std::string const&, bcos::crypto::NodeIDPtr, + bytesConstRef, std::function) override + {} + void notifyConsensusNodeList( + bcos::consensus::ConsensusNodeList const&, std::function) override + {} + void notifyObserverNodeList( + bcos::consensus::ConsensusNodeList const&, std::function) override + {} + void asyncGetPendingTransactionSize(std::function) override {} + void asyncResetTxPool(std::function) override {} + + void asyncFillBlock(bcos::crypto::HashListPtr _txsHash, + std::function _onBlockFilled) override + { + BOOST_CHECK_GT(_txsHash->size(), 0); + auto transactions = std::make_shared(); + for (auto& hash : *_txsHash) + { + auto it = hash2Transaction.find(hash); + if (it != hash2Transaction.end()) + { + transactions->push_back(it->second); + } + else + { + transactions->push_back(nullptr); + } + } + + _onBlockFilled(nullptr, std::move(transactions)); + } + + void notifyConnectedNodes( + const bcos::crypto::NodeIDSet&, std::function)>) override + {} + + std::map hash2Transaction; +}; +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-executor/test/wasm/infinit_loop.wasm" "b/BFPL\345\243\271/bcos-executor/test/wasm/infinit_loop.wasm" new file mode 100644 index 0000000000000000000000000000000000000000..ab6a517f73091e06e56932ca3d45ab82110d93e0 GIT binary patch literal 267 zcmYL^O=^{v36#>OK?&;lm1MiCn^6B|vQqvA%YU(T55OxHqESe9lqODS}|7e+{;uJ)L$m<-Rv$~P_@OZ}2**@>?16H-U vJ|OXtbEcMB$rsqnze|L`LFEbQ3~Rata0dxUI?0316k4bKgP@{;%8=?mi-99p literal 0 HcmV?d00001 diff --git "a/BFPL\345\243\271/bcos-executor/test/wasm/metric_infinit_loop_useGas.wasm" "b/BFPL\345\243\271/bcos-executor/test/wasm/metric_infinit_loop_useGas.wasm" new file mode 100644 index 0000000000000000000000000000000000000000..d291f148c1d7ff79c93e16742c651d4cd159990e GIT binary patch literal 145 zcmW;Cu@V6>6ouh)?~Rz*FhZ%K@d#?tcmOYuAY&LSMpmJbSC`#We8rg`A-4p8X5s;_ zN*z4?z-v~gbIMJQ-LXsWaE+kh(VrX-A%jI27zu&LbJmymdZaH{wc+-JG+W*<6)LiG a3rj2P>0aR;saQC+G=VYGA#=KqmgWzOdm2Xo literal 0 HcmV?d00001 diff --git "a/BFPL\345\243\271/bcos-executor/tools/CMakeLists.txt" "b/BFPL\345\243\271/bcos-executor/tools/CMakeLists.txt" new file mode 100644 index 00000000..f56e091d --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/tools/CMakeLists.txt" @@ -0,0 +1,5 @@ + + +add_executable(injector inject_meter.cpp) +target_include_directories(injector PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../src/) +target_link_libraries(injector PUBLIC ${EXECUTOR_TARGET} ${TOOL_TARGET} wabt) diff --git "a/BFPL\345\243\271/bcos-executor/tools/inject_meter.cpp" "b/BFPL\345\243\271/bcos-executor/tools/inject_meter.cpp" new file mode 100644 index 00000000..2e3be9e3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-executor/tools/inject_meter.cpp" @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2021 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. + * + * @brief the tool of gas injector + * @file inject_meter.cpp + * @author: xingqiangbai + * @date: 2021-11-01 + */ + +#include "src/binary-reader-ir.h" +#include "src/binary-reader.h" +#include "src/error-formatter.h" +#include "src/ir.h" +#include "src/option-parser.h" +#include "src/stream.h" +#include "src/validator.h" +#include "src/wast-lexer.h" +#include "vm/gas_meter/GasInjector.h" +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace wabt; + +static std::unique_ptr s_log_stream; + +int main(int argc, char* argv[]) +{ + if (argc != 2) + { + cerr << "The number of parameters not equal to 2" << endl; + return 0; + } + std::vector file_data; + auto result = wabt::ReadFile(argv[1], &file_data); + filesystem::path p(argv[1]); + if (Succeeded(result)) + { + wasm::GasInjector injector(wasm::GetInstructionTable()); + auto ret = injector.InjectMeter(file_data); + if (ret.status == wasm::GasInjector::Status::Success) + { + Errors errors; + Module module; + const bool kStopOnFirstError = true; + // s_log_stream = FileStream::CreateStdout(); + auto defaultFeature = Features(); + ReadBinaryOptions options( + defaultFeature, s_log_stream.get(), true, kStopOnFirstError, true); + result = ReadBinaryIr(p.filename().generic_string().c_str(), + (const char*)ret.byteCode->data(), ret.byteCode->size(), options, &errors, &module); + if (Succeeded(result)) + { + ValidateOptions voptions(defaultFeature); + result = ValidateModule(&module, &errors, voptions); + if (result == Result::Ok) + { + ofstream out("metric_" + p.filename().generic_string(), + std::ofstream::out | std::ofstream::binary | std::ofstream::trunc); + out.write((const char*)ret.byteCode->data(), ret.byteCode->size()); + out.close(); + cout << "InjectMeter success" << endl; + return 0; + } + } + cout << "validate failed" << endl; + FormatErrorsToFile(errors, Location::Type::Binary); + } + cerr << "InjectMeter failed, reason:" + << (ret.status == wasm::GasInjector::Status::InvalidFormat ? "invalid format" : + "bad instruction") + << endl; + } + cerr << "Read file failed" << endl; + return -1; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/CMakeLists.txt" "b/BFPL\345\243\271/bcos-framework/CMakeLists.txt" new file mode 100644 index 00000000..66435a9d --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/CMakeLists.txt" @@ -0,0 +1,49 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for bcos-framework +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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. +# ------------------------------------------------------------------------------ + +cmake_minimum_required(VERSION 3.12) +set(CMAKE_OSX_DEPLOYMENT_TARGET "11.3" CACHE STRING "Minimum OS X deployment version") + +project(bcos-framework VERSION ${VERSION}) + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/bcos-framework) + +find_package(Microsoft.GSL CONFIG REQUIRED) + +add_library(bcos-framework INTERFACE) +target_include_directories(bcos-framework INTERFACE + $ + $) +target_link_libraries(bcos-framework INTERFACE bcos-crypto bcos-task Microsoft.GSL::GSL) + +if (TESTS) + enable_testing() + set(CTEST_OUTPUT_ON_FAILURE TRUE) + add_subdirectory(test) +endif() + +# for code coverage +if (COVERAGE) + include(Coverage) + config_coverage("framework-cov" "'/usr*' '${CMAKE_CURRENT_SOURCE_DIR}/bcos-cmake-scripts*' '${CMAKE_CURRENT_SOURCE_DIR}/test/bcos-test*'") +endif () + +include(GNUInstallDirs) +install(TARGETS bcos-framework EXPORT fiscobcosTargets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") +install(DIRECTORY "bcos-framework" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" FILES_MATCHING PATTERN "*.h") \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/Common.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/Common.h" new file mode 100644 index 00000000..b23fbd2d --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/Common.h" @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 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: yujiechen + */ +#pragma once +#include +#include +#include + +#define METRIC LOG_BADGE("METRIC") \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/consensus/ConsensusInterface.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/consensus/ConsensusInterface.h" new file mode 100644 index 00000000..8666092f --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/consensus/ConsensusInterface.h" @@ -0,0 +1,86 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for Consensus module + * @file ConsensusInterface.h + * @author: yujiechen + * @date 2021-04-08 + */ +#pragma once +#include "../ledger/LedgerConfig.h" +#include "../protocol/Block.h" +#include "../protocol/Protocol.h" +#include "../protocol/ProtocolTypeDef.h" +#include "ConsensusTypeDef.h" +#include +#include +#include + + +namespace bcos::consensus +{ +// ConsensusInterface is the interface of consensus exposed to other modules +class ConsensusInterface +{ +public: + using Ptr = std::shared_ptr; + ConsensusInterface() = default; + virtual ~ConsensusInterface() = default; + + virtual void start() = 0; + virtual void stop() = 0; + + virtual void asyncSubmitProposal(bool _containSysTxs, bytesConstRef _proposalData, + bcos::protocol::BlockNumber _proposalIndex, bcos::crypto::HashType const& _proposalHash, + std::function _onProposalSubmitted) = 0; + + virtual void asyncGetPBFTView(std::function _onGetView) = 0; + + // the sync module calls this interface to check block + virtual void asyncCheckBlock(bcos::protocol::Block::Ptr _block, + std::function _onVerifyFinish) = 0; + // the sync module calls this interface to notify new block + virtual void asyncNotifyNewBlock( + bcos::ledger::LedgerConfig::Ptr _ledgerConfig, std::function _onRecv) = 0; + + // called by frontService to dispatch message + virtual void asyncNotifyConsensusMessage(bcos::Error::Ptr _error, std::string const& _id, + bcos::crypto::NodeIDPtr _nodeID, bytesConstRef _data, + std::function _onRecv) = 0; + + + // for the sync module to notify the syncing number + virtual void notifyHighestSyncingNumber(bcos::protocol::BlockNumber _number) = 0; + + virtual void asyncNoteUnSealedTxsSize( + uint64_t _unsealedTxsSize, std::function _onRecvResponse) = 0; + + // get the consensusNodeList + // Note: if separate sealer with the PBFT module, should implement with notify + virtual ConsensusNodeList consensusNodeList() const { return ConsensusNodeList(); } + virtual uint64_t nodeIndex() const { return 0; } + virtual uint32_t compatibilityVersion() const + { + return (uint32_t)(bcos::protocol::DEFAULT_VERSION); + } + + virtual void asyncGetConsensusStatus( + std::function _onGetConsensusStatus) = 0; + virtual void notifyConnectedNodes(bcos::crypto::NodeIDSet const& _connectedNodes, + std::function _onResponse) = 0; + + virtual void clearExceptionProposalState(bcos::protocol::BlockNumber) {} +}; +} // namespace bcos::consensus diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/consensus/ConsensusNode.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/consensus/ConsensusNode.h" new file mode 100644 index 00000000..43beb995 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/consensus/ConsensusNode.h" @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2021 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. + * + * @brief the information of the consensus node with weight + * @file ConsensusNode.h + * @author: yujiechen + * @date 2021-04-12 + */ +#pragma once +#include "ConsensusNodeInterface.h" +namespace bcos +{ +namespace consensus +{ +class ConsensusNode : public ConsensusNodeInterface +{ +public: + using Ptr = std::shared_ptr; + explicit ConsensusNode(bcos::crypto::PublicPtr _nodeID) : m_nodeID(_nodeID) {} + + ConsensusNode(bcos::crypto::PublicPtr _nodeID, uint64_t _weight) + : m_nodeID(_nodeID), m_weight(_weight) + {} + + ~ConsensusNode() override {} + + bcos::crypto::PublicPtr nodeID() const override { return m_nodeID; } + uint64_t weight() const override { return m_weight; } + +private: + bcos::crypto::PublicPtr m_nodeID; + uint64_t m_weight = 100; +}; +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/consensus/ConsensusNodeInterface.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/consensus/ConsensusNodeInterface.h" new file mode 100644 index 00000000..108320f1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/consensus/ConsensusNodeInterface.h" @@ -0,0 +1,66 @@ +/** + * Copyright (C) 2021 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. + * + * @brief the information of the consensus node + * @file ConsensusNodeInterface.h + * @author: yujiechen + * @date 2021-04-09 + */ +#pragma once +#include +#include +namespace bcos +{ +namespace consensus +{ +class ConsensusNodeInterface +{ +public: + using Ptr = std::shared_ptr; + ConsensusNodeInterface() = default; + virtual ~ConsensusNodeInterface() = default; + + // the nodeID of the consensus node + [[nodiscard]] virtual bcos::crypto::PublicPtr nodeID() const = 0; + + [[nodiscard]] virtual uint64_t weight() const { return 100; } +}; +using ConsensusNodeList = std::vector; +using ConsensusNodeListPtr = std::shared_ptr; + +struct ConsensusNodeComparator +{ + bool operator()( + const ConsensusNodeInterface::Ptr& _left, const ConsensusNodeInterface::Ptr& _right) const + { + if (_left->nodeID()->data() == _right->nodeID()->data()) + { + return _left->weight() < _right->weight(); + } + return (_left->nodeID()->data() < _right->nodeID()->data()); + } +}; + +inline std::string decsConsensusNodeList(ConsensusNodeList const& _nodeList) +{ + std::ostringstream stringstream; + for (const auto& node : _nodeList) + { + stringstream << LOG_KV(node->nodeID()->shortHex(), std::to_string(node->weight())); + } + return stringstream.str(); +} +} // namespace consensus +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/consensus/ConsensusTypeDef.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/consensus/ConsensusTypeDef.h" new file mode 100644 index 00000000..860cccfe --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/consensus/ConsensusTypeDef.h" @@ -0,0 +1,28 @@ +/** + * Copyright (C) 2021 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. + * + * @brief data type for consensus module + * @file ConsensusTypeDef.h + * @author: yujiechen + * @date 2021-04-09 + */ +#pragma once +#include +namespace bcos::consensus +{ +using IndexType = uint64_t; +using ViewType = uint64_t; +const ViewType MaxView = std::numeric_limits::max() / 2; +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/dispatcher/SchedulerInterface.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/dispatcher/SchedulerInterface.h" new file mode 100644 index 00000000..6de799c5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/dispatcher/SchedulerInterface.h" @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2021 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. + * + * @brief interface of Scheduler + * @file SchedulerInterface.h + * @author: ancelmo + * @date: 2021-07-27 + */ + +#pragma once +#include "../executor/ParallelTransactionExecutorInterface.h" +#include "../ledger/LedgerConfig.h" +#include "../protocol/Block.h" +#include "../protocol/ProtocolTypeDef.h" +#include +#include +#include +#include +#include + +namespace bcos::scheduler +{ +class SchedulerInterface +{ +public: + using Ptr = std::shared_ptr; + SchedulerInterface() = default; + virtual ~SchedulerInterface() {} + + // by pbft & sync + virtual void executeBlock(bcos::protocol::Block::Ptr block, bool verify, + std::function + callback) = 0; + + // by pbft & sync + virtual void commitBlock(bcos::protocol::BlockHeader::Ptr header, + std::function callback) = 0; + + // by console, query committed committing executing + virtual void status( + std::function callback) = 0; + + // by rpc + virtual void call(protocol::Transaction::Ptr tx, + std::function) = 0; + + // by executor + virtual void registerExecutor(std::string name, + bcos::executor::ParallelTransactionExecutorInterface::Ptr executor, + std::function callback) = 0; + + virtual void unregisterExecutor( + const std::string& name, std::function callback) = 0; + + // clear all status + virtual void reset(std::function callback) = 0; + virtual void getCode( + std::string_view contract, std::function callback) = 0; + + virtual void getABI( + std::string_view contract, std::function callback) = 0; + + // for performance, do the things before executing block in executor. + virtual void preExecuteBlock(bcos::protocol::Block::Ptr block, bool verify, + std::function callback) = 0; + + virtual void stop() {}; +}; +} // namespace bcos::scheduler diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/dispatcher/SchedulerTypeDef.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/dispatcher/SchedulerTypeDef.h" new file mode 100644 index 00000000..f9529def --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/dispatcher/SchedulerTypeDef.h" @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 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. + * + * @brief typedef for scheduler module + * @file SchedulerTypeDef.h + * @author: yujiechen + * @date: 2021-04-9 + */ +#pragma once +#include +namespace bcos +{ +namespace scheduler +{ +enum SchedulerError +{ + UnknownError = -70000, + InvalidStatus, + InvalidBlockNumber, + InvalidBlocks, + NextBlockError, + PrewriteBlockError, + CommitError, + RollbackError, + UnexpectedKeyLockError, + BatchError, + SerialExecuteError, + DMCError, + DAGError, + CallError, + ExecutorNotEstablishedError, + fetchGasLimitError, + Stopped, + InvalidBlockVersion, + BlockIsCommitting, +}; +} // namespace scheduler +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/election/FailOverTypeDef.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/election/FailOverTypeDef.h" new file mode 100644 index 00000000..2c9f4914 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/election/FailOverTypeDef.h" @@ -0,0 +1,28 @@ +/** + * 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 FailOverTypeDef.h + * @author: yujiechen + * @date 2022-04-29 + */ +#pragma once +#include +namespace bcos +{ +namespace election +{ +const char* const CONSENSUS_LEADER_DIR = "/consensus/"; +} +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/election/LeaderElectionInterface.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/election/LeaderElectionInterface.h" new file mode 100644 index 00000000..c7f01988 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/election/LeaderElectionInterface.h" @@ -0,0 +1,65 @@ +/** + * 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. + * + * @brief the interface for LeaderElection + * @file LeaderElectionInterface.h + * @author: yujiechen + * @date 2022-04-26 + */ +#pragma once +#include "bcos-framework/protocol/MemberInterface.h" +#include +namespace bcos +{ +namespace election +{ +class LeaderElectionInterface +{ +public: + using Ptr = std::shared_ptr; + LeaderElectionInterface() = default; + virtual ~LeaderElectionInterface() {} + + virtual void start() = 0; + virtual void stop() = 0; + virtual void updateSelfConfig(bcos::protocol::MemberInterface::Ptr _self) = 0; + virtual bool electionClusterOk() const = 0; + + // called when campaign success, this logic should start to work when campaign success + virtual void registerOnCampaignHandler( + std::function _onCampaignHandler) = 0; + // called when keep-alive exception + virtual void registerKeepAliveExceptionHandler( + std::function _handler) = 0; + // handler called when the election cluster down + virtual void registerOnElectionClusterException(std::function _handler) = 0; + // handler called when the election cluster recover + virtual void registerOnElectionClusterRecover(std::function _handler) = 0; +}; + +class LeaderElectionFactoryInterface +{ +public: + using Ptr = std::shared_ptr; + LeaderElectionFactoryInterface() = default; + virtual ~LeaderElectionFactoryInterface() {} + virtual LeaderElectionInterface::Ptr createLeaderElection(std::string const& _memberID, + std::string const& _memberConfig, std::string const& _etcdEndPoint, + std::string const& _leaderKey, std::string const& _purpose, unsigned _leaseTTL, + const std::string& _caPath, const std::string& _certPath, const std::string& _keyPath) = 0; +}; + +} // namespace election +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/election/LeaderEntryPointInterface.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/election/LeaderEntryPointInterface.h" new file mode 100644 index 00000000..b7ac50bb --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/election/LeaderEntryPointInterface.h" @@ -0,0 +1,58 @@ +/** + * 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. + * + * @brief the leader entry-point + * @file LeaderEntryPointInterface.h + * @author: yujiechen + * @date 2022-04-26 + */ +#pragma once +#include "bcos-framework/protocol/MemberInterface.h" +#include +namespace bcos +{ +namespace election +{ +class LeaderEntryPointInterface +{ +public: + using Ptr = std::shared_ptr; + LeaderEntryPointInterface() = default; + virtual ~LeaderEntryPointInterface() {} + + virtual bcos::protocol::MemberInterface::Ptr getLeaderByKey(std::string const& _leaderKey) = 0; + virtual std::map getAllLeaders() = 0; + + virtual void addMemberChangeNotificationHandler( + std::function) = 0; + virtual void addMemberDeleteNotificationHandler( + std::function _handler) = 0; + virtual void start() = 0; + virtual void stop() = 0; +}; + +class LeaderEntryPointFactory +{ +public: + using Ptr = std::shared_ptr(); + LeaderEntryPointFactory() = default; + virtual ~LeaderEntryPointFactory() {} + + virtual LeaderEntryPointInterface::Ptr createLeaderEntryPoint(std::string const& _etcdEndPoint, + std::string const& _watchDir, std::string const& _purpose, const std::string& _caPath, + const std::string& _certPath, const std::string& _keyPath) = 0; +}; +} // namespace election +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/executor/ExecuteError.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/executor/ExecuteError.h" new file mode 100644 index 00000000..bae34b93 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/executor/ExecuteError.h" @@ -0,0 +1,25 @@ +#pragma once + +namespace bcos +{ +namespace executor +{ +enum ExecuteError : int32_t +{ + SUCCESS = -80000, + INVALID_BLOCKNUMBER, + GETHASH_ERROR, + CALL_ERROR, + EXECUTE_ERROR, + PREPARE_ERROR, + COMMIT_ERROR, + ROLLBACK_ERROR, + DAG_ERROR, + DEAD_LOCK, + TABLE_NOT_FOUND, + STOPPED, + SCHEDULER_TERM_ID_ERROR, // to notify switch + INTERNAL_ERROR +}; +} +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/executor/ExecutionMessage.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/executor/ExecutionMessage.h" new file mode 100644 index 00000000..df8010ac --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/executor/ExecutionMessage.h" @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2021 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. + * + * @brief interface of ExecutionParams + * @file ExecutionMessage.h + * @author: ancelmo + * @date: 2021-09-22 + */ + +#pragma once +#include "../protocol/LogEntry.h" +#include "../protocol/ProtocolTypeDef.h" +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace protocol +{ + +static const std::string SERIAL_EXECUTIVE_FLOW_ADDRESS = + std::string("serial_executive_flow_address"); + +const bcos::bytes GET_CODE_INPUT_BYTES = asBytes(std::string("getCode")); + +class ExecutionMessage +{ +public: + using UniquePtr = std::unique_ptr; + using UniqueConstPtr = std::unique_ptr; + + virtual ~ExecutionMessage() = default; + + enum Type : int8_t + { + TXHASH = 0, // Received an new transaction from scheduler + MESSAGE, // Send/Receive an external call to/from another contract + FINISHED, // Send a finish to another contract + KEY_LOCK, // Send a wait key lock to scheduler, or release key lock + SEND_BACK, // Send a dag refuse to scheduler + REVERT, // Send/Receive a revert to/from previous external call + }; + + static std::string getTypeName(Type type) + { + switch (type) + { + case TXHASH: + return "TXHASH"; + case MESSAGE: + return "MESSAGE"; + case FINISHED: + return "FINISHED"; + case KEY_LOCK: + return "KEY_LOCK"; + case SEND_BACK: + return "SEND_BACK"; + case REVERT: + return "REVERT"; + } + return "Unknown"; + } + + std::string toString() + { + std::stringstream ss; + ss << "[" << (staticCall() ? "call" : "tx") << "|" << contextID() << "|" << seq() << "|" + << getTypeName(type()) << "|" << from() << "->" << to() << "|" << gasAvailable() << "|" + << toHex(keyLockAcquired()) << "|" << keyLocks().size() << ":"; + for (auto& lock : keyLocks()) + { + ss << toHex(lock) << "."; + } + ss << "]"; + + if (delegateCall()) + { + ss << "(delegateCall|" << delegateCallSender() << "|" << from() << "->" + << delegateCallAddress() << "|code.size=" << delegateCallCode().size() << ")"; + } + return ss.str(); + } + + // ----------------------------------------------- + // Request fields + // ----------------------------------------------- + virtual Type type() const = 0; + virtual void setType(Type type) = 0; + + virtual crypto::HashType transactionHash() const = 0; + virtual void setTransactionHash(crypto::HashType hash) = 0; + + virtual int64_t contextID() const = 0; + virtual void setContextID(int64_t contextID) = 0; + + virtual int64_t seq() const = 0; + virtual void setSeq(int64_t seq) = 0; + + virtual std::string_view origin() const = 0; // readable format + virtual void setOrigin(std::string origin) = 0; + + virtual std::string_view from() const = 0; // readable format + virtual void setFrom(std::string from) = 0; + + virtual std::string_view to() const = 0; // readable format + virtual void setTo(std::string to) = 0; + + virtual std::string_view abi() const = 0; // readable format + virtual void setABI(std::string to) = 0; + + virtual int32_t depth() const = 0; + virtual void setDepth(int32_t depth) = 0; + + virtual bool create() const = 0; + virtual void setCreate(bool create) = 0; + + virtual bool internalCreate() const = 0; + virtual void setInternalCreate(bool internalCreate) = 0; + + virtual bool internalCall() const = 0; + virtual void setInternalCall(bool internalCall) = 0; + + // ----------------------------------------------- + // Request / Response common fields + // ----------------------------------------------- + virtual int64_t gasAvailable() const = 0; + virtual void setGasAvailable(int64_t gasAvailable) = 0; + + virtual bcos::bytesConstRef data() const = 0; + virtual bytes takeData() = 0; + virtual void setData(bcos::bytes data) = 0; + + virtual bool staticCall() const = 0; + virtual void setStaticCall(bool staticCall) = 0; + + // for evm + virtual std::optional createSalt() const = 0; + virtual void setCreateSalt(u256 createSalt) = 0; + + // ----------------------------------------------- + // Response fields + // ----------------------------------------------- + virtual int32_t status() const = 0; + virtual void setStatus(int32_t status) = 0; + + virtual int32_t evmStatus() const = 0; + virtual void setEvmStatus(int32_t evmstatus) = 0; + + virtual std::string_view message() const = 0; + virtual void setMessage(std::string message) = 0; + + virtual gsl::span const logEntries() const = 0; + virtual std::vector takeLogEntries() = 0; + virtual void setLogEntries(std::vector logEntries) = 0; + + // for evm + virtual std::string_view newEVMContractAddress() const = 0; + virtual void setNewEVMContractAddress(std::string newEVMContractAddress) = 0; + + // ----------------------------------------------- + // Key locks + // ----------------------------------------------- + virtual gsl::span keyLocks() const = 0; + virtual std::vector takeKeyLocks() = 0; + virtual void setKeyLocks(std::vector keyLocks) = 0; + + virtual std::string_view keyLockAcquired() const = 0; + virtual void setKeyLockAcquired(std::string keyLock) = 0; + + virtual bool delegateCall() const = 0; + virtual void setDelegateCall(bool delegateCall) = 0; + + virtual std::string_view delegateCallAddress() const = 0; + virtual void setDelegateCallAddress(std::string delegateCallAddress) = 0; + + virtual bcos::bytesConstRef delegateCallCode() const = 0; + virtual bytes takeDelegateCallCode() = 0; + virtual void setDelegateCallCode(bcos::bytes delegateCallCode) = 0; + + virtual std::string_view delegateCallSender() const = 0; + virtual void setDelegateCallSender(std::string delegateCallSender) = 0; +}; + +class ExecutionMessageFactory +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + virtual ~ExecutionMessageFactory() = default; + + virtual bcos::protocol::ExecutionMessage::UniquePtr createExecutionMessage() = 0; +}; +} // namespace protocol +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/executor/ExecutorStatus.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/executor/ExecutorStatus.h" new file mode 100644 index 00000000..cb7ab3fe --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/executor/ExecutorStatus.h" @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2021 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. + * + * @brief interface of ExecutorStatus + * @file ExecutorStatus.h + * @author: jimmyshi + * @date: 2021-07-03 + */ + +#pragma once +#include "../protocol/LogEntry.h" +#include "../protocol/ProtocolTypeDef.h" +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace protocol +{ +class ExecutorStatus +{ +public: + using UniquePtr = std::unique_ptr; + using UniqueConstPtr = std::unique_ptr; + + virtual ~ExecutorStatus() = default; + + int64_t seq() const { return m_seq; } + void setSeq(int64_t seq) { m_seq = seq; } + +private: + int64_t m_seq; +}; + + +} // namespace protocol +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/executor/NativeExecutionMessage.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/executor/NativeExecutionMessage.h" new file mode 100644 index 00000000..9cf28b52 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/executor/NativeExecutionMessage.h" @@ -0,0 +1,177 @@ +#pragma once + +#include "bcos-framework/executor/ExecutionMessage.h" +#include +#include +#include +#include +#include + +namespace bcos::executor +{ +class NativeExecutionMessage : public protocol::ExecutionMessage +{ +public: + NativeExecutionMessage() = default; + virtual ~NativeExecutionMessage() {} + + Type type() const override { return m_type; } + void setType(Type type) override { m_type = type; } + + crypto::HashType transactionHash() const override { return m_transactionHash; } + void setTransactionHash(crypto::HashType hash) override { m_transactionHash = hash; } + + int64_t contextID() const override { return m_contextID; } + void setContextID(int64_t contextID) override { m_contextID = contextID; } + + int64_t seq() const override { return m_seq; } + void setSeq(int64_t seq) override { m_seq = seq; } + + std::string_view origin() const override { return m_origin; } + void setOrigin(std::string origin) override { m_origin = std::move(origin); } + + std::string_view from() const override { return m_from; } + void setFrom(std::string from) override { m_from = std::move(from); } + + std::string_view to() const override { return m_to; } + void setTo(std::string to) override { m_to = std::move(to); } + + std::string_view abi() const override { return m_abi; } + void setABI(std::string abi) override { m_abi = std::move(abi); } + + int32_t depth() const override { return m_depth; } + void setDepth(int32_t depth) override { m_depth = depth; } + + bool create() const override { return m_create; } + void setCreate(bool create) override { m_create = create; } + + bool internalCreate() const override { return m_internalCreate; } + void setInternalCreate(bool internalCreate) override { m_internalCreate = internalCreate; } + + bool internalCall() const override { return m_internalCall; } + void setInternalCall(bool internalCall) override { m_internalCall = internalCall; } + + int64_t gasAvailable() const override { return m_gasAvailable; } + void setGasAvailable(int64_t gasAvailable) override { m_gasAvailable = gasAvailable; } + + bcos::bytesConstRef data() const override { return ref(m_data); } + bcos::bytes takeData() override { return std::move(m_data); } + void setData(bcos::bytes input) override { m_data = std::move(input); } + + bool staticCall() const override { return m_staticCall; } + void setStaticCall(bool staticCall) override { m_staticCall = staticCall; } + + std::optional createSalt() const override { return m_createSalt; } + void setCreateSalt(u256 createSalt) override { m_createSalt = createSalt; } + + int32_t status() const override { return m_status; } + void setStatus(int32_t status) override { m_status = status; } + + int32_t evmStatus() const override { return m_evmStatus; } + void setEvmStatus(int32_t evmStatus) override { m_evmStatus = evmStatus; } + + std::string_view message() const override { return m_message; } + void setMessage(std::string message) override { m_message = std::move(message); } + + gsl::span const logEntries() const override + { + return m_logEntries; + } + std::vector takeLogEntries() override + { + return std::move(m_logEntries); + } + void setLogEntries(std::vector logEntries) override + { + m_logEntries = std::move(logEntries); + } + + std::string_view newEVMContractAddress() const override { return m_newEVMContractAddress; } + void setNewEVMContractAddress(std::string newEVMContractAddress) override + { + m_newEVMContractAddress = std::move(newEVMContractAddress); + } + + std::string_view toStringView(const std::string& it) const { return std::string_view(it); } + + gsl::span keyLocks() const override { return m_keyLocks; } + + std::vector takeKeyLocks() override { return std::move(m_keyLocks); } + + void setKeyLocks(std::vector keyLocks) override + { + m_keyLocks = std::move(keyLocks); + } + + std::string_view keyLockAcquired() const override { return m_keyLockAcquired; } + void setKeyLockAcquired(std::string keyLock) override { m_keyLockAcquired = keyLock; } + + + bool delegateCall() const override { return m_delegateCall; } + void setDelegateCall(bool delegateCall) override { m_delegateCall = delegateCall; } + + std::string_view delegateCallAddress() const override { return m_delegateCallAddress; } + void setDelegateCallAddress(std::string delegateCallAddress) override + { + m_delegateCallAddress = std::move(delegateCallAddress); + } + + bcos::bytesConstRef delegateCallCode() const override { return ref(m_delegateCallCode); } + bcos::bytes takeDelegateCallCode() override { return std::move(m_delegateCallCode); } + void setDelegateCallCode(bcos::bytes delegateCallCode) override + { + m_delegateCallCode = std::move(delegateCallCode); + } + + std::string_view delegateCallSender() const override { return m_delegateCallSender; } + void setDelegateCallSender(std::string delegateCallSender) override + { + m_delegateCallSender = std::move(delegateCallSender); + } + + bcos::crypto::HashType m_transactionHash; + int64_t m_contextID = 0; + int64_t m_seq = 0; + + std::string m_origin; + std::string m_from; + std::string m_to; + std::string m_abi; + + int64_t m_gasAvailable = 0; + bcos::bytes m_data; + + std::optional m_createSalt; + + std::string m_message; + std::vector m_logEntries; + std::string m_newEVMContractAddress; + + std::vector m_keyLocks; + std::string m_keyLockAcquired; + + int32_t m_status = 0; + int32_t m_depth = 0; + int32_t m_evmStatus = 0; + Type m_type = TXHASH; + bool m_create = false; + bool m_staticCall = false; + bool m_internalCreate = false; + bool m_internalCall = false; + + // for delegateCall + bool m_delegateCall = false; + std::string m_delegateCallAddress; + bcos::bytes m_delegateCallCode; + std::string m_delegateCallSender; +}; + +class NativeExecutionMessageFactory : public protocol::ExecutionMessageFactory +{ +public: + protocol::ExecutionMessage::UniquePtr createExecutionMessage() override + { + return std::make_unique(); + } +}; +} // namespace bcos::executor \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/executor/ParallelTransactionExecutorInterface.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/executor/ParallelTransactionExecutorInterface.h" new file mode 100644 index 00000000..59e7b719 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/executor/ParallelTransactionExecutorInterface.h" @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2021 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. + * + * @brief interface of Executor + * @file ParallelExecutorInterface.h + * @author: ancelmo + * @date: 2021-07-27 + */ + +#pragma once + +#include "../protocol/BlockHeader.h" +#include "../protocol/ProtocolTypeDef.h" +#include "../protocol/Transaction.h" +#include "../protocol/TransactionReceipt.h" +#include "ExecutionMessage.h" +#include "ExecutorStatus.h" +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace executor +{ +class ParallelTransactionExecutorInterface +{ +public: + using Ptr = std::shared_ptr; + ParallelTransactionExecutorInterface() = default; + virtual ~ParallelTransactionExecutorInterface() = default; + + virtual void status( + std::function + callback) + { + // TODO: use pure virtual function + auto status = std::make_unique(); + status->setSeq(m_seq); + callback(nullptr, std::move(status)); + }; + + virtual void nextBlockHeader(int64_t schedulerTermId, + const bcos::protocol::BlockHeader::ConstPtr& blockHeader, + std::function callback) = 0; + + virtual void executeTransaction(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) = 0; + + virtual void call(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) = 0; + + virtual void executeTransactions(std::string contractAddress, + gsl::span inputs, + std::function)> + callback) = 0; + + virtual void dmcExecuteTransactions(std::string contractAddress, + gsl::span inputs, + + // called every time at all tx stop( pause or finish) + std::function)> + callback) = 0; + + virtual void dagExecuteTransactions( + gsl::span inputs, + std::function)> + callback) = 0; + + + virtual void dmcExecuteTransaction( + [[maybe_unused]] bcos::protocol::ExecutionMessage::UniquePtr input, + [[maybe_unused]] std::function + callback) + { + BCOS_LOG(FATAL) << "dmcExecuteTransaction is not available"; + assert(false); + }; + + virtual void dmcCall(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) = 0; + + virtual void getHash(bcos::protocol::BlockNumber number, + std::function callback) = 0; + + /* ----- XA Transaction interface Start ----- */ + + // Write data to storage uncommitted + virtual void prepare(const bcos::protocol::TwoPCParams& params, + std::function callback) = 0; + + // Commit uncommitted data + virtual void commit(const bcos::protocol::TwoPCParams& params, + std::function callback) = 0; + + // Rollback the changes + virtual void rollback(const bcos::protocol::TwoPCParams& params, + std::function callback) = 0; + + /* ----- XA Transaction interface End ----- */ + + // drop all status + virtual void reset(std::function callback) = 0; + + virtual void getCode( + std::string_view contract, std::function callback) = 0; + + virtual void getABI( + std::string_view contract, std::function callback) = 0; + + virtual void start(){}; + + virtual void stop(){}; + +protected: + int64_t m_seq = utcTime(); +}; +} // namespace executor +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/executor/PrecompiledTypeDef.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/executor/PrecompiledTypeDef.h" new file mode 100644 index 00000000..172967ef --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/executor/PrecompiledTypeDef.h" @@ -0,0 +1,120 @@ +/** + * Copyright (C) 2021 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 PrecompiledTypeDef.h + * @author: kyonRay + * @date 2021-06-22 + */ + +#pragma once +#include + +namespace bcos +{ +/// System contract +constexpr const int SYS_CONTRACT_DEPLOY_NUMBER = 0; +inline bool isSysContractDeploy(protocol::BlockNumber _number) +{ + return _number == SYS_CONTRACT_DEPLOY_NUMBER; +} + +namespace precompiled +{ +/// precompiled contract path for wasm +constexpr const char* const SYS_CONFIG_NAME = "/sys/status"; +constexpr const char* const TABLE_NAME = "/sys/table_storage"; +constexpr const char* const TABLE_MANAGER_NAME = "/sys/table_manager"; +constexpr const char* const CONSENSUS_NAME = "/sys/consensus"; +constexpr const char* const AUTH_MANAGER_NAME = "/sys/auth"; +constexpr const char* const KV_TABLE_NAME = "/sys/kv_storage"; +constexpr const char* const CRYPTO_NAME = "/sys/crypto_tools"; +constexpr const char* const DAG_TRANSFER_NAME = "/sys/dag_test"; +constexpr const char* const BFS_NAME = "/sys/bfs"; +constexpr const char* const GROUP_SIG_NAME = "/sys/group_sig"; +constexpr const char* const RING_SIG_NAME = "/sys/ring_sig"; +constexpr const char* const DISCRETE_ZKP_NAME = "/sys/discrete_zkp"; +constexpr const char* const ACCOUNT_MANAGER_NAME = "/sys/account_manager"; +constexpr static const uint8_t BFS_SYS_SUBS_COUNT = 13; +constexpr static const std::array BFS_SYS_SUBS = { + SYS_CONFIG_NAME, TABLE_NAME, TABLE_MANAGER_NAME, CONSENSUS_NAME, AUTH_MANAGER_NAME, + KV_TABLE_NAME, CRYPTO_NAME, DAG_TRANSFER_NAME, BFS_NAME, GROUP_SIG_NAME, RING_SIG_NAME, + DISCRETE_ZKP_NAME, ACCOUNT_MANAGER_NAME}; + +/// precompiled contract for solidity +/// precompiled address should range in [0x1000, 0x20000) +constexpr const char* const SYS_CONFIG_ADDRESS = "0000000000000000000000000000000000001000"; +constexpr const char* const TABLE_ADDRESS = "0000000000000000000000000000000000001001"; +constexpr const char* const TABLE_MANAGER_ADDRESS = "0000000000000000000000000000000000001002"; +constexpr const char* const CONSENSUS_ADDRESS = "0000000000000000000000000000000000001003"; +constexpr const char* const AUTH_MANAGER_ADDRESS = "0000000000000000000000000000000000001005"; +constexpr const char* const KV_TABLE_ADDRESS = "0000000000000000000000000000000000001009"; +constexpr const char* const CRYPTO_ADDRESS = "000000000000000000000000000000000000100a"; +constexpr const char* const WORKING_SEALER_MGR_ADDRESS = "000000000000000000000000000000000000100b"; +constexpr const char* const DAG_TRANSFER_ADDRESS = "000000000000000000000000000000000000100c"; +constexpr const char* const BFS_ADDRESS = "000000000000000000000000000000000000100e"; +constexpr const char* const SYS_ADDRESS_PREFIX = "00000000000000000000000000000000000"; + +// Contract address related to privacy computing +constexpr const char* const GROUP_SIG_ADDRESS = "0000000000000000000000000000000000005004"; +constexpr const char* const RING_SIG_ADDRESS = "0000000000000000000000000000000000005005"; +// for zkp +constexpr const char* const DISCRETE_ZKP_ADDRESS = "0000000000000000000000000000000000005100"; + + +/// auth system contract for solidity +constexpr const char* const AUTH_INTERCEPT_ADDRESS = "0000000000000000000000000000000000010000"; +constexpr const char* const AUTH_COMMITTEE_ADDRESS = "0000000000000000000000000000000000010001"; +constexpr const char* const AUTH_CONTRACT_MGR_ADDRESS = "0000000000000000000000000000000000010002"; +constexpr const char* const ACCOUNT_MGR_ADDRESS = "0000000000000000000000000000000000010003"; +constexpr const char* const ACCOUNT_ADDRESS = "0000000000000000000000000000000000010004"; + +// clang-format off +/// name to address, only for create sys table +constexpr static const std::array, BFS_SYS_SUBS_COUNT> + SYS_NAME_ADDRESS_MAP ={ + std::pair{SYS_CONFIG_NAME, SYS_CONFIG_ADDRESS}, + {TABLE_NAME, TABLE_ADDRESS}, + {TABLE_MANAGER_NAME, TABLE_MANAGER_ADDRESS}, + {CONSENSUS_NAME, CONSENSUS_ADDRESS}, + {AUTH_MANAGER_NAME, AUTH_MANAGER_ADDRESS}, + {KV_TABLE_NAME, KV_TABLE_ADDRESS}, + {CRYPTO_NAME, CRYPTO_ADDRESS}, + {DAG_TRANSFER_NAME, DAG_TRANSFER_ADDRESS}, + {BFS_NAME, BFS_ADDRESS}, + {GROUP_SIG_NAME, GROUP_SIG_ADDRESS}, + {RING_SIG_NAME, RING_SIG_ADDRESS}, + {DISCRETE_ZKP_NAME, DISCRETE_ZKP_ADDRESS}, + {ACCOUNT_MANAGER_NAME, ACCOUNT_MGR_ADDRESS} +}; +// clang-format on + +const std::set c_systemTxsAddress = {bcos::precompiled::SYS_CONFIG_ADDRESS, + bcos::precompiled::CONSENSUS_ADDRESS, bcos::precompiled::WORKING_SEALER_MGR_ADDRESS, + bcos::precompiled::SYS_CONFIG_NAME, bcos::precompiled::CONSENSUS_NAME, + bcos::precompiled::AUTH_COMMITTEE_ADDRESS, bcos::precompiled::AUTH_MANAGER_ADDRESS, + bcos::precompiled::ACCOUNT_ADDRESS, bcos::precompiled::ACCOUNT_MGR_ADDRESS, + bcos::precompiled::ACCOUNT_MANAGER_NAME}; + +/// for testing +// CpuHeavy test: 0x5200 ~ (0x5200 + 128) +const char* const CPU_HEAVY_START_ADDRESS = "0x5200"; +const int CPU_HEAVY_CONTRACT_NUM = 128; + +// Smallbank test: 0x6200 ~ (0x6200 + 128) +const char* const SMALLBANK_START_ADDRESS = "0x6200"; +const int SMALLBANK_CONTRACT_NUM = 128; + +} // namespace precompiled +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/executor/SerialTransactionExecutor.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/executor/SerialTransactionExecutor.h" new file mode 100644 index 00000000..8fe00254 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/executor/SerialTransactionExecutor.h" @@ -0,0 +1,60 @@ +#pragma once +#include "../protocol/LogEntry.h" +#include +#include +#include +#include +#include + +namespace bcos::executor::serial +{ +template +concept CommonFields = requires(MessageType message) +{ + std::unsigned_integral; + std::convertible_to>; + std::integral; +}; + +template +concept RequestMessage = requires(MessageType message) +{ + CommonFields; + std::convertible_to; + std::convertible_to; + std::convertible_to; + std::convertible_to; +}; + +template +concept ResponseMessage = requires(MessageType message) +{ + CommonFields; + std::integral; + std::convertible_to; + std::convertible_to; + requires RANGES::range&& std::convertible_to< + RANGES::range_value_t, bcos::protocol::LogEntry>; +}; + +template +concept Executor = requires(ExecutorType executor, + typename boost::function_traits::arg1_type executeArg1) +{ + { + executeArg1 + } + ->RequestMessage; + { + executor.execute(executeArg1) + } + ->ResponseMessage; +}; + +// class SerialTransactionExecutor +// { +// public: +// bcos::protocol::ExecutionMessage::UniquePtr executeTransaction( +// bcos::protocol::ExecutionMessage::UniquePtr input) = 0; +// }; +} // namespace bcos::executor::serial \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/front/FrontServiceInterface.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/front/FrontServiceInterface.h" new file mode 100644 index 00000000..bfce181b --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/front/FrontServiceInterface.h" @@ -0,0 +1,137 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for front service module + * @file FrontInterface.h + * @author: octopus + * @date 2021-04-19 + */ +#pragma once +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace front +{ +using GetGroupNodeInfoFunc = + std::function; +using ReceiveMsgFunc = std::function; +using ResponseFunc = std::function; +using CallbackFunc = std::function; + +/** + * @brief: the interface provided by the front service + */ +class FrontServiceInterface +{ +public: + using Ptr = std::shared_ptr; + FrontServiceInterface() = default; + virtual ~FrontServiceInterface() {} + +public: + /** + * @brief: start/stop service + */ + virtual void start() = 0; + virtual void stop() = 0; + +public: + /** + * @brief: get groupNodeInfo from the gateway + * @param _getGroupNodeInfoFunc: get groupNodeInfo callback + * @return void + */ + virtual void asyncGetGroupNodeInfo(GetGroupNodeInfoFunc _onGetGroupNodeInfo) = 0; + /** + * @brief: receive nodeIDs from gateway, call by gateway + * @param _groupID: groupID + * @param _groupNodeInfo: the groupNodeInfo + * @return void + */ + virtual void onReceiveGroupNodeInfo(const std::string& _groupID, + bcos::gateway::GroupNodeInfo::Ptr _groupNodeInfo, ReceiveMsgFunc _receiveMsgCallback) = 0; + + /** + * @brief: receive message from gateway, call by gateway + * @param _groupID: groupID + * @param _nodeID: the node send this message + * @param _data: received message data + * @return void + */ + virtual void onReceiveMessage(const std::string& _groupID, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, ReceiveMsgFunc _receiveMsgCallback) = 0; + + /** + * @brief: receive broadcast message from gateway, call by gateway + * @param _groupID: groupID + * @param _nodeID: the node send this message + * @param _data: received message data + * @return void + */ + virtual void onReceiveBroadcastMessage(const std::string& _groupID, + bcos::crypto::NodeIDPtr _nodeID, bytesConstRef _data, + ReceiveMsgFunc _receiveMsgCallback) = 0; + + /** + * @brief: send message to node + * @param _moduleID: moduleID + * @param _nodeID: the receiver nodeID + * @param _data: message + * @param _timeout: the timeout value of async function, in milliseconds. + * @param _callback: callback + * @return void + */ + virtual void asyncSendMessageByNodeID(int _moduleID, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, uint32_t _timeout, CallbackFunc _callback) = 0; + + /** + * @brief: send response + * @param _id: the request id + * @param _moduleID: moduleID + * @param _nodeID: the receiver nodeID + * @param _data: message + * @return void + */ + virtual void asyncSendResponse(const std::string& _id, int _moduleID, + bcos::crypto::NodeIDPtr _nodeID, bytesConstRef _data, + ReceiveMsgFunc _receiveMsgCallback) = 0; + + /** + * @brief: send messages to multiple nodes + * @param _moduleID: moduleID + * @param _nodeIDs: the receiver nodeIDs + * @param _data: message + * @return void + */ + virtual void asyncSendMessageByNodeIDs(int _moduleID, + const std::vector& _nodeIDs, bytesConstRef _data) = 0; + + /** + * @brief: send broadcast message + * @param _moduleID: moduleID + * @param _data: message + * @return void + */ + virtual void asyncSendBroadcastMessage(uint16_t _type, int _moduleID, bytesConstRef _data) = 0; +}; + +} // namespace front +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/gateway/GatewayInterface.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/gateway/GatewayInterface.h" new file mode 100644 index 00000000..3c0775ec --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/gateway/GatewayInterface.h" @@ -0,0 +1,142 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for Gateway module + * @file GatewayInterface.h + * @author: octopus + * @date 2021-04-19 + */ +#pragma once +#include "GatewayTypeDef.h" +#include "bcos-framework/front/FrontServiceInterface.h" +#include "bcos-framework/multigroup/GroupInfo.h" +#include "bcos-framework/protocol/Protocol.h" +#include "bcos-framework/protocol/ProtocolInfo.h" +#include +#include +#include + +namespace bcos +{ +namespace gateway +{ +using ErrorRespFunc = std::function; +using PeerRespFunc = std::function; +using GetGroupNodeInfoFunc = + std::function; + +/** + * @brief: A list of interfaces provided by the gateway which are called by the front service. + */ +class GatewayInterface +{ +public: + using Ptr = std::shared_ptr; + GatewayInterface() = default; + virtual ~GatewayInterface() {} + + /** + * @brief: start/stop service + */ + virtual void start() = 0; + virtual void stop() = 0; + +public: + /** + * @brief: get nodeIDs from gateway + * @param: _groupID + * @param _getGroupNodeInfoFunc: get nodeIDs callback + * @return void + */ + virtual void asyncGetGroupNodeInfo( + const std::string& _groupID, GetGroupNodeInfoFunc _getGroupNodeInfoFunc) = 0; + /** + * @brief: get connected peers + * @param _callback: + * @return void + */ + virtual void asyncGetPeers( + std::function _callback) = 0; + /** + * @brief: send message to a single node + * @param _groupID: groupID + * @param _moduleID: moduleID + * @param _srcNodeID: the sender nodeID + * @param _dstNodeID: the receiver nodeID + * @param _payload: message content + * @return void + */ + virtual void asyncSendMessageByNodeID(const std::string& _groupID, int _moduleID, + bcos::crypto::NodeIDPtr _srcNodeID, bcos::crypto::NodeIDPtr _dstNodeID, + bytesConstRef _payload, ErrorRespFunc _errorRespFunc) = 0; + + /** + * @brief: send message to multiple nodes + * @param _groupID: groupID + * @param _moduleID: moduleID + * @param _srcNodeID: the sender nodeID + * @param _nodeIDs: the receiver nodeIDs + * @param _payload: message content + * @param _errorRespFunc: error func + * @return void + */ + virtual void asyncSendMessageByNodeIDs(const std::string& _groupID, int _moduleID, + bcos::crypto::NodeIDPtr _srcNodeID, const bcos::crypto::NodeIDs& _dstNodeIDs, + bytesConstRef _payload) = 0; + + /** + * @brief: send message to all nodes + * @param _type: type + * @param _groupID: groupID + * @param _moduleID: moduleID + * @param _srcNodeID: the sender nodeID + * @param _payload: message content + * @return void + */ + virtual void asyncSendBroadcastMessage(uint16_t _type, const std::string& _groupID, + int _moduleID, bcos::crypto::NodeIDPtr _srcNodeID, bytesConstRef _payload) = 0; + + /// multi-group related interfaces + + /** + * @brief receive the latest group information notification from the GroupManagerInterface + * + * @param _groupInfo the latest group information + */ + virtual void asyncNotifyGroupInfo( + bcos::group::GroupInfo::Ptr _groupInfo, std::function) = 0; + + /// for AMOP + virtual void asyncSendMessageByTopic(const std::string& _topic, bcos::bytesConstRef _data, + std::function _respFunc) = 0; + virtual void asyncSendBroadcastMessageByTopic( + const std::string& _topic, bcos::bytesConstRef _data) = 0; + + virtual void asyncSubscribeTopic(std::string const& _clientID, std::string const& _topicInfo, + std::function _callback) = 0; + virtual void asyncRemoveTopic(std::string const& _clientID, + std::vector const& _topicList, + std::function _callback) = 0; + + // for the air-mode node + virtual bool registerNode(const std::string&, bcos::crypto::NodeIDPtr, bcos::protocol::NodeType, + bcos::front::FrontServiceInterface::Ptr, bcos::protocol::ProtocolInfo::ConstPtr) + { + return true; + } +}; + +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/gateway/GatewayTypeDef.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/gateway/GatewayTypeDef.h" new file mode 100644 index 00000000..4c0559a6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/gateway/GatewayTypeDef.h" @@ -0,0 +1,160 @@ +/** + * Copyright (C) 2021 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. + * + * @brief typedef for gateway + * @file GatewayTypeDef.h + * @author: octopus + * @date 2021-04-19 + */ +#pragma once +#include +#include +#include +#include +namespace bcos +{ +namespace gateway +{ +// Message type definition +enum GatewayMessageType : int16_t +{ + Heartbeat = 0x1, + Handshake = 0x2, + RequestNodeStatus = 0x3, // for request the gateway nodeinfo + ResponseNodeStatus = 0x4, + PeerToPeerMessage = 0x5, + BroadcastMessage = 0x6, + AMOPMessageType = 0x7, + WSMessageType = 0x8, + SyncNodeSeq = 0x9, + RouterTableSyncSeq = 0xa, + RouterTableResponse = 0xb, + RouterTableRequest = 0xc, + ForwardMessage = 0xd, +}; +/** + * @brief client end endpoint. Node will connect to NodeIPEndpoint. + */ +struct NodeIPEndpoint +{ + using Ptr = std::shared_ptr; + NodeIPEndpoint() = default; + NodeIPEndpoint(std::string const& _host, uint16_t _port) : m_host(_host), m_port(_port) {} + NodeIPEndpoint(const NodeIPEndpoint& _nodeIPEndpoint) = default; + NodeIPEndpoint(boost::asio::ip::address _addr, uint16_t _port) + : m_host(_addr.to_string()), m_port(_port), m_ipv6(_addr.is_v6()) + {} + + virtual ~NodeIPEndpoint() = default; + NodeIPEndpoint(const boost::asio::ip::tcp::endpoint& _endpoint) + { + m_host = _endpoint.address().to_string(); + m_port = _endpoint.port(); + m_ipv6 = _endpoint.address().is_v6(); + } + bool operator<(const NodeIPEndpoint& rhs) const + { + if (m_host + std::to_string(m_port) < rhs.m_host + std::to_string(rhs.m_port)) + { + return true; + } + return false; + } + bool operator==(const NodeIPEndpoint& rhs) const + { + return (m_host + std::to_string(m_port) == rhs.m_host + std::to_string(rhs.m_port)); + } + operator boost::asio::ip::tcp::endpoint() const + { + return boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(m_host), m_port); + } + + // Get the port associated with the endpoint. + uint16_t port() const { return m_port; }; + + // Get the IP address associated with the endpoint. + std::string address() const { return m_host; }; + bool isIPv6() const { return m_ipv6; } + + std::string m_host; + uint16_t m_port; + bool m_ipv6 = false; +}; + +inline std::ostream& operator<<(std::ostream& _out, NodeIPEndpoint const& _endpoint) +{ + _out << _endpoint.address() << ":" << _endpoint.port(); + return _out; +} + +/// node info obtained from the certificate +struct P2PInfo +{ + using Ptr = std::shared_ptr; + P2PInfo() = default; + ~P2PInfo() {} + std::string p2pID; + std::string agencyName; + std::string nodeName; + NodeIPEndpoint nodeIPEndpoint; +}; +using P2PInfos = std::vector; + +class GatewayInfo +{ +public: + using Ptr = std::shared_ptr; + // groupID=>nodeList + using NodeIDInfoType = std::map, std::less<>>; + GatewayInfo() + : m_p2pInfo(std::make_shared()), m_nodeIDInfo(std::make_shared()) + {} + explicit GatewayInfo(P2PInfo const& _p2pInfo) : GatewayInfo() { *m_p2pInfo = _p2pInfo; } + + virtual ~GatewayInfo() {} + void setNodeIDInfo(NodeIDInfoType&& _nodeIDInfo) + { + WriteGuard l(x_nodeIDInfo); + *m_nodeIDInfo = std::move(_nodeIDInfo); + } + void setP2PInfo(P2PInfo&& _p2pInfo) + { + WriteGuard l(x_p2pInfo); + *m_p2pInfo = std::move(_p2pInfo); + } + // Note: copy here to ensure thread-safe, use std::move out to ensure performance + NodeIDInfoType nodeIDInfo() + { + ReadGuard l(x_nodeIDInfo); + return *m_nodeIDInfo; + } + // Note: copy here to ensure thread-safe, use std::move out to ensure performance + P2PInfo p2pInfo() + { + ReadGuard l(x_p2pInfo); + return *m_p2pInfo; + } + +private: + std::shared_ptr m_p2pInfo; + SharedMutex x_p2pInfo; + + std::shared_ptr m_nodeIDInfo; + SharedMutex x_nodeIDInfo; +}; +using GatewayInfos = std::vector; +using GatewayInfosPtr = std::shared_ptr; +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/gateway/GroupNodeInfo.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/gateway/GroupNodeInfo.h" new file mode 100644 index 00000000..e6fc9d1a --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/gateway/GroupNodeInfo.h" @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2021 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 GroupNodeInfo.h + * @author: yujiechen + * @date 2022-3-8 + */ +#pragma once +#include "bcos-crypto/interfaces/crypto/KeyInterface.h" +#include +#include +#include +#include +namespace bcos +{ +namespace gateway +{ +class GroupNodeInfo +{ +public: + using Ptr = std::shared_ptr; + GroupNodeInfo() = default; + virtual ~GroupNodeInfo() {} + // the groupID + virtual void setGroupID(std::string const& _groupID) = 0; + // the nodeIDList + virtual void setNodeIDList(std::vector&& _nodeIDList) = 0; + virtual void appendNodeID(std::string const& _nodeID) = 0; + virtual void appendProtocol(bcos::protocol::ProtocolInfo::ConstPtr _protocol) = 0; + // the groupType + virtual void setType(uint16_t _type) = 0; + + virtual std::string const& groupID() const = 0; + // Note: externally ensure thread safety + virtual std::vector const& nodeIDList() const = 0; + virtual int type() const = 0; + + virtual void setNodeProtocolList( + std::vector&& _protocolList) = 0; + virtual std::vector const& nodeProtocolList() const = 0; + virtual bcos::protocol::ProtocolInfo::ConstPtr protocol(uint64_t _index) const = 0; +}; +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/ledger/GenesisConfig.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/ledger/GenesisConfig.h" new file mode 100644 index 00000000..1d582c5a --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/ledger/GenesisConfig.h" @@ -0,0 +1,103 @@ +/** + * 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 genesisData.h + * @author: wenlinli + * @date 2022-10-24 + */ + +#pragma once + +#include "LedgerConfig.h" +#include "bcos-framework/consensus/ConsensusNodeInterface.h" +#include "bcos-ledger/src/libledger/Ledger.h" +#include +#include + + +namespace bcos +{ +namespace ledger +{ +class GenesisConfig +{ +public: + using Ptr = std::shared_ptr; + GenesisConfig(bool smcrypto, std::string chainID, std::string groupID, + std::string consensusType, uint64_t blockLimit, uint64_t leaderSwitchPeriod, + std::string compatibilityVersion, uint64_t txGasLimit, bool isWasm, bool isAuthCheck, + std::string authAdminAccount, bool isSerialExecute) + : m_smCrypto(smcrypto), + m_chainID(chainID), + m_groupID(groupID), + m_consensusType(consensusType), + m_blockLimit(blockLimit), + m_leaderSwitchPeriod(leaderSwitchPeriod), + m_compatibilityVersion(compatibilityVersion), + m_txGasLimit(txGasLimit), + m_isWasm(isWasm), + m_isAuthCheck(isAuthCheck), + m_authAdminAccount(authAdminAccount), + m_isSerialExecute(isSerialExecute){}; + virtual ~GenesisConfig() {} + + std::string genesisDataOutPut() + { + std::stringstream ss; + ss << "[chain]" << std::endl + << "sm_crypto:" << m_smCrypto << std::endl + << "chainID: " << m_chainID << std::endl + << "grouID: " << m_groupID << std::endl + << "[consensys]" << std::endl + << "consensus_type: " << m_consensusType << std::endl + << "block_tx_count_limit:" << m_blockLimit << std::endl + << "leader_period:" << m_leaderSwitchPeriod << std::endl + << "[version]" << std::endl + << "compatibility_version:" << m_compatibilityVersion << std::endl + << "[tx]" << std::endl + << "gaslimit:" << m_txGasLimit << std::endl + << "[executor]" << std::endl + << "iswasm: " << m_isWasm << std::endl + << "isAuthCheck:" << m_isAuthCheck << std::endl + << "authAdminAccount:" << m_authAdminAccount << std::endl + << "isSerialExecute:" << m_isSerialExecute << std::endl; + return ss.str(); + } + + +private: + // chain config + bool m_smCrypto; + std::string m_chainID; + std::string m_groupID; + + // consensus config + std::string m_consensusType; + uint64_t m_blockLimit; + uint64_t m_leaderSwitchPeriod = 1; + + // version config + std::string m_compatibilityVersion; + + // tx config + uint64_t m_txGasLimit = 3000000000; + // executorConfig + bool m_isWasm; + bool m_isAuthCheck; + std::string m_authAdminAccount; + bool m_isSerialExecute; +}; // namespace genesisConfig +} // namespace ledger +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/ledger/LedgerConfig.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/ledger/LedgerConfig.h" new file mode 100644 index 00000000..9414fedc --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/ledger/LedgerConfig.h" @@ -0,0 +1,118 @@ +/** + * Copyright (C) 2021 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. + * + * @brief + * @file LedgerConfig.h + * @author: yujiechen + * @date 2021-05-06 + */ +#pragma once +#include "../consensus/ConsensusNodeInterface.h" +#include "../protocol/ProtocolTypeDef.h" + +namespace bcos +{ +namespace ledger +{ +class LedgerConfig +{ +public: + using Ptr = std::shared_ptr; + LedgerConfig() + : m_consensusNodeList(std::make_shared()), + m_observerNodeList(std::make_shared()) + {} + virtual ~LedgerConfig() {} + + virtual void setConsensusNodeList(bcos::consensus::ConsensusNodeList const& _consensusNodeList) + { + *m_consensusNodeList = _consensusNodeList; + } + virtual void setObserverNodeList(bcos::consensus::ConsensusNodeList const& _observerNodeList) + { + *m_observerNodeList = _observerNodeList; + } + virtual void setHash(bcos::crypto::HashType const& _hash) { m_hash = _hash; } + virtual void setBlockNumber(bcos::protocol::BlockNumber _blockNumber) + { + m_blockNumber = _blockNumber; + } + virtual void setBlockTxCountLimit(uint64_t _blockTxCountLimit) + { + m_blockTxCountLimit = _blockTxCountLimit; + } + + virtual bcos::consensus::ConsensusNodeList const& consensusNodeList() const + { + return *m_consensusNodeList; + } + + virtual bcos::consensus::ConsensusNodeList& mutableConsensusNodeList() + { + return *m_consensusNodeList; + } + + virtual bcos::consensus::ConsensusNodeList const& observerNodeList() const + { + return *m_observerNodeList; + } + bcos::crypto::HashType const& hash() const { return m_hash; } + bcos::protocol::BlockNumber blockNumber() const { return m_blockNumber; } + + uint64_t blockTxCountLimit() const { return m_blockTxCountLimit; } + + bcos::consensus::ConsensusNodeListPtr mutableConsensusList() { return m_consensusNodeList; } + bcos::consensus::ConsensusNodeListPtr mutableObserverList() { return m_observerNodeList; } + + uint64_t leaderSwitchPeriod() const { return m_leaderSwitchPeriod; } + void setLeaderSwitchPeriod(uint64_t _leaderSwitchPeriod) + { + m_leaderSwitchPeriod = _leaderSwitchPeriod; + } + + std::tuple const& gasLimit() const { return m_gasLimit; } + void setGasLimit(std::tuple mGasLimit) + { + m_gasLimit = std::move(mGasLimit); + } + + // Not enforce to set this field, in memory data + void setSealerId(int64_t _sealerId) { m_sealerId = _sealerId; } + int64_t sealerId() const { return m_sealerId; } + + // Not enforce to set this field, in memory data + void setTxsSize(int64_t _txsSize) { m_txsSize = _txsSize; } + int64_t txsSize() const { return m_txsSize; } + + void setCompatibilityVersion(uint32_t _version) { m_compatibilityVersion = _version; } + uint32_t compatibilityVersion() const { return m_compatibilityVersion; } + +protected: + bcos::consensus::ConsensusNodeListPtr m_consensusNodeList; + bcos::consensus::ConsensusNodeListPtr m_observerNodeList; + bcos::crypto::HashType m_hash; + bcos::protocol::BlockNumber m_blockNumber; + uint64_t m_blockTxCountLimit; + uint64_t m_leaderSwitchPeriod = 1; + std::tuple m_gasLimit = {3000000000, 0}; + // the compatibilityVersion + // the system version, can only be upgraded manually + uint32_t m_compatibilityVersion; + // no need to store, in memory data + int64_t m_sealerId = -1; + int64_t m_txsSize = -1; +}; +} // namespace ledger +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/ledger/LedgerInterface.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/ledger/LedgerInterface.h" new file mode 100644 index 00000000..4b915276 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/ledger/LedgerInterface.h" @@ -0,0 +1,164 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for Ledger + * @file LedgerInterface.h + * @author: kyonRay + * @date: 2021-04-07 + */ + +#pragma once + +#include "../protocol/Block.h" +#include "../protocol/BlockHeader.h" +#include "../protocol/Transaction.h" +#include "../protocol/TransactionReceipt.h" +#include "../storage/StorageInterface.h" +#include "LedgerConfig.h" +#include "LedgerTypeDef.h" +#include +#include +#include +#include + + +namespace bcos::ledger +{ +class LedgerInterface +{ +public: + using Ptr = std::shared_ptr; + LedgerInterface() = default; + virtual ~LedgerInterface() {} + + /** + * @brief async prewrite a block in scheduler module + * @param block the block to commit + * @param callback trigger this callback when write is finished + */ + virtual void asyncPrewriteBlock(bcos::storage::StorageInterface::Ptr storage, + bcos::protocol::TransactionsPtr _blockTxs, bcos::protocol::Block::ConstPtr block, + std::function callback) = 0; + + /** + * @brief async store txs in block when tx pool verify + * @param _txToStore tx bytes data list + * @param _txHashList tx hash list + * @param _onTxsStored callback + */ + virtual void asyncStoreTransactions(std::shared_ptr> _txToStore, + crypto::HashListPtr _txHashList, std::function _onTxStored) = 0; + + /** + * @brief async get block by blockNumber + * @param _blockNumber number of block + * @param _blockFlag flag bit of what the block be callback contains, + * you can checkout all flags in LedgerTypeDef.h + * @param _onGetBlock + * + * @example + * asyncGetBlockDataByNumber(10, HEADER|TRANSACTIONS, [](error, block){ doSomething(); }); + */ + virtual void asyncGetBlockDataByNumber(protocol::BlockNumber _blockNumber, int32_t _blockFlag, + std::function _onGetBlock) = 0; + + /** + * @brief async get latest block number + * @param _onGetBlock + */ + virtual void asyncGetBlockNumber( + std::function _onGetBlock) = 0; + + /** + * @brief async get block hash by block number + * @param _blockNumber the number of block to get + * @param _onGetBlock + */ + virtual void asyncGetBlockHashByNumber(protocol::BlockNumber _blockNumber, + std::function _onGetBlock) = 0; + + /** + * @brief async get block number by block hash + * @param _blockHash the hash of block to get + * @param _onGetBlock + */ + virtual void asyncGetBlockNumberByHash(crypto::HashType const& _blockHash, + std::function _onGetBlock) = 0; + + /** + * @brief async get a batch of transaction by transaction hash list + * @param _txHashList transaction hash list, hash should be hex + * @param _withProof if true then it will callback MerkleProofPtr map in _onGetTx + * if false then MerkleProofPtr map will be nullptr + * @param _onGetTx return + */ + virtual void asyncGetBatchTxsByHashList(crypto::HashListPtr _txHashList, bool _withProof, + std::function>)> + _onGetTx) = 0; + + /** + * @brief async get a transaction receipt by tx hash + * @param _txHash hash of transaction + * @param _withProof if true then it will callback MerkleProofPtr in _onGetTx + * if false then MerkleProofPtr will be nullptr + * @param _onGetTx + */ + virtual void asyncGetTransactionReceiptByHash(crypto::HashType const& _txHash, bool _withProof, + std::function + _onGetTx) = 0; + + /** + * @brief async get total transaction count and latest block number + * @param _callback callback totalTxCount, totalFailedTxCount, and latest block number + */ + virtual void asyncGetTotalTransactionCount(std::function + _callback) = 0; + + /** + * @brief async get system config by table key + * @param _key the key of row, you can checkout all key in LedgerTypeDef.h + * @param _onGetConfig callback when get config, + */ + virtual void asyncGetSystemConfigByKey(std::string_view const& _key, + std::function _onGetConfig) = 0; + + /** + * @brief async get node list by type, can be sealer or observer + * @param _type the type of node, CONSENSUS_SEALER or CONSENSUS_OBSERVER + * @param _onGetConfig + */ + virtual void asyncGetNodeListByType(std::string_view const& _type, + std::function _onGetConfig) = 0; + + /** + * @brief async get a batch of nonce lists in blocks + * @param _startNumber start block number + * @param _offset batch offset, if batch is 0, then callback nonce list in start block number; + * if (_startNumber + _offset) > latest block number, then callback nonce lists in + * [_startNumber, latest number] + * @param _onGetList + */ + virtual void asyncGetNonceList(protocol::BlockNumber _startNumber, int64_t _offset, + std::function>)> + _onGetList) = 0; + + virtual void asyncPreStoreBlockTxs(bcos::protocol::TransactionsPtr _blockTxs, + bcos::protocol::Block::ConstPtr block, + std::function _callback) = 0; +}; +} // namespace bcos::ledger diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/ledger/LedgerTypeDef.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/ledger/LedgerTypeDef.h" new file mode 100644 index 00000000..abcfe582 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/ledger/LedgerTypeDef.h" @@ -0,0 +1,73 @@ +/** + * Copyright (C) 2021 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 LedgerTypeDef.h + * @author: kyonRay + * @date 2021-04-30 + */ + +#pragma once +#include "../protocol/ProtocolTypeDef.h" +#include + +namespace bcos::ledger +{ +using MerkleProof = std::vector, std::vector > >; +using MerkleProofPtr = std::shared_ptr; + +// get block flag +constexpr static int32_t FULL_BLOCK = 0xFFFF; +constexpr static int32_t HEADER = 0x0008; +constexpr static int32_t TRANSACTIONS = 0x0004; +constexpr static int32_t RECEIPTS = 0x0002; + +// get system config key +constexpr static std::string_view SYSTEM_KEY_TX_GAS_LIMIT = "tx_gas_limit"; +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"; +// for compatibility +constexpr static std::string_view SYSTEM_KEY_COMPATIBILITY_VERSION = "compatibility_version"; + +// system config struct +using SystemConfigEntry = std::tuple; + +const unsigned TX_GAS_LIMIT_MIN = 100000; +// get consensus node list type +constexpr static std::string_view CONSENSUS_SEALER = "consensus_sealer"; +constexpr static std::string_view CONSENSUS_OBSERVER = "consensus_observer"; +constexpr static std::string_view CONSENSUS_WORKING_SEALER = "consensus_working_sealer"; + +// get current state key +constexpr static std::string_view SYS_KEY_CURRENT_NUMBER = "current_number"; +constexpr static std::string_view SYS_KEY_TOTAL_TRANSACTION_COUNT = "total_transaction_count"; +constexpr static std::string_view SYS_KEY_TOTAL_FAILED_TRANSACTION = + "total_failed_transaction_count"; + +// sys table name +constexpr static std::string_view SYS_CONSENSUS{"s_consensus"}; +constexpr static std::string_view SYS_CONFIG{"s_config"}; +constexpr static std::string_view SYS_CURRENT_STATE{"s_current_state"}; +constexpr static std::string_view SYS_HASH_2_NUMBER{"s_hash_2_number"}; +constexpr static std::string_view SYS_NUMBER_2_HASH{"s_number_2_hash"}; +constexpr static std::string_view SYS_BLOCK_NUMBER_2_NONCES{"s_block_number_2_nonces"}; +constexpr static std::string_view SYS_NUMBER_2_BLOCK_HEADER{"s_number_2_header"}; +constexpr static std::string_view SYS_NUMBER_2_TXS{"s_number_2_txs"}; +constexpr static std::string_view SYS_HASH_2_TX{"s_hash_2_tx"}; +constexpr static std::string_view SYS_HASH_2_RECEIPT{"s_hash_2_receipt"}; +constexpr static std::string_view DAG_TRANSFER{"/tables/dag_transfer"}; +constexpr static std::string_view SMALLBANK_TRANSFER{"/tables/smallbank_transfer"}; +constexpr static std::string_view SYS_CODE_BINARY{"s_code_binary"}; +constexpr static std::string_view SYS_CONTRACT_ABI{"s_contract_abi"}; +} // namespace bcos::ledger diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/ChainNodeInfo.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/ChainNodeInfo.h" new file mode 100644 index 00000000..d810bb79 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/ChainNodeInfo.h" @@ -0,0 +1,160 @@ +/** + * Copyright (C) 2021 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. + * + * @brief the information used to deploy new node + * @file NodeInfo.h + * @author: yujiechen + * @date 2021-09-08 + */ +#pragma once +#include "GroupTypeDef.h" +#include "bcos-framework/protocol/Protocol.h" +#include "bcos-framework/protocol/ProtocolInfo.h" +#include "bcos-framework/protocol/ServiceDesc.h" +#include +#include +namespace bcos +{ +namespace group +{ +enum NodeCryptoType : uint32_t +{ + NON_SM_NODE = 0, + SM_NODE = 1, +}; + +class ChainNodeInfo +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + using ServicesInfo = std::map; + ChainNodeInfo() : m_nodeProtocol(std::make_shared()) {} + ChainNodeInfo(std::string const& _nodeName, int32_t _type) : ChainNodeInfo() + { + m_nodeName = _nodeName; + m_nodeCryptoType = (NodeCryptoType)_type; + } + + virtual ~ChainNodeInfo() {} + + virtual std::string const& nodeName() const { return m_nodeName; } + virtual NodeCryptoType const& nodeCryptoType() const { return m_nodeCryptoType; } + virtual std::string const& serviceName(bcos::protocol::ServiceType _type) const + { + if (!m_servicesInfo.count(_type)) + { + return c_emptyServiceName; + } + return m_servicesInfo.at(_type); + } + + virtual void setNodeName(std::string const& _nodeName) { m_nodeName = _nodeName; } + virtual void setNodeCryptoType(NodeCryptoType const& _nodeType) + { + m_nodeCryptoType = _nodeType; + } + virtual void appendServiceInfo( + bcos::protocol::ServiceType _type, std::string const& _serviceName) + { + m_servicesInfo[_type] = _serviceName; + + BCOS_LOG(TRACE) << LOG_BADGE("ChainNodeInfo") << LOG_DESC("appendServiceInfo") + << LOG_KV("type", _type) << LOG_KV("name", getServiceNameByType(_type)) + << LOG_KV("serviceName", _serviceName); + } + + virtual ServicesInfo const& serviceInfo() const { return m_servicesInfo; } + + virtual void setServicesInfo(ServicesInfo&& _servicesInfo) + { + m_servicesInfo = std::move(_servicesInfo); + } + + virtual void setIniConfig(std::string const& _iniConfig) { m_iniConfig = _iniConfig; } + virtual std::string const& iniConfig() const { return m_iniConfig; } + virtual std::string const& nodeID() const { return m_nodeID; } + virtual void setNodeID(std::string const& _nodeID) { m_nodeID = _nodeID; } + + void setMicroService(bool _microService) { m_microService = _microService; } + bool microService() const { return m_microService; } + void setNodeType(bcos::protocol::NodeType _type) { m_nodeType = _type; } + bcos::protocol::NodeType nodeType() const { return m_nodeType; } + + bcos::protocol::ProtocolInfo::ConstPtr nodeProtocol() const { return m_nodeProtocol; } + void setNodeProtocol(bcos::protocol::ProtocolInfo&& _protocol) + { + *m_nodeProtocol = std::move(_protocol); + } + + void setNodeProtocol(bcos::protocol::ProtocolInfo const& _protocol) + { + *m_nodeProtocol = _protocol; + } + + virtual void setWasm(bool _wasm) { m_wasm = _wasm; } + virtual void setSmCryptoType(bool _smCryptoType) { m_smCryptoType = _smCryptoType; } + + bool wasm() const { return m_wasm; } + bool smCryptoType() const { return m_smCryptoType; } + + void setCompatibilityVersion(uint32_t _version) { m_compatibilityVersion = _version; } + uint32_t compatibilityVersion() const { return m_compatibilityVersion; } + +protected: + bool m_microService = false; + // the node name + std::string m_nodeName; + NodeCryptoType m_nodeCryptoType; + // the nodeType + bcos::protocol::NodeType m_nodeType; + // the nodeID + std::string m_nodeID; + + // mapping of service to deployed machine + ServicesInfo m_servicesInfo; + // the ini config maintained by the node, use the iniConfig of the node if empty + std::string m_iniConfig = ""; + std::string c_emptyServiceName = ""; + + // the node protocol + bcos::protocol::ProtocolInfo::Ptr m_nodeProtocol; + + // the system version + uint32_t m_compatibilityVersion; + + bool m_wasm{false}; + bool m_smCryptoType{false}; +}; +inline std::string printNodeInfo(ChainNodeInfo::Ptr _nodeInfo) +{ + if (!_nodeInfo) + { + return ""; + } + std::stringstream oss; + oss << LOG_KV("name", _nodeInfo->nodeName()) + << LOG_KV("cryptoType", std::to_string((int32_t)_nodeInfo->nodeCryptoType())) + << LOG_KV("nodeType", _nodeInfo->nodeType()); + auto const& serviceInfos = _nodeInfo->serviceInfo(); + oss << ", serviceInfos: "; + for (auto const& info : serviceInfos) + { + oss << info.second << ","; + } + return oss.str(); +} +} // namespace group +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/ChainNodeInfoFactory.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/ChainNodeInfoFactory.h" new file mode 100644 index 00000000..b1e23a86 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/ChainNodeInfoFactory.h" @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2021 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. + * + * @brief factory to build the ChainNodeInfo + * @file ChainNodeInfoFactory.h + * @author: yujiechen + * @date 2021-09-18 + */ +#pragma once +#include "ChainNodeInfo.h" +namespace bcos +{ +namespace group +{ +class ChainNodeInfoFactory +{ +public: + using Ptr = std::shared_ptr; + ChainNodeInfoFactory() = default; + virtual ~ChainNodeInfoFactory() {} + virtual ChainNodeInfo::Ptr createNodeInfo() { return std::make_shared(); } + virtual ChainNodeInfo::Ptr createNodeInfo(std::string const& _nodeName, int32_t _type) + { + return std::make_shared(_nodeName, _type); + } +}; +} // namespace group +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/GroupInfo.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/GroupInfo.h" new file mode 100644 index 00000000..ee8dac7f --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/GroupInfo.h" @@ -0,0 +1,151 @@ +/** + * Copyright (C) 2021 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. + * + * @brief the information used to manager group + * @file GroupInfo.h + * @author: yujiechen + * @date 2021-09-08 + */ +#pragma once +#include "ChainNodeInfoFactory.h" +#include "GroupTypeDef.h" +namespace bcos +{ +namespace group +{ +class GroupInfo +{ +public: + using Ptr = std::shared_ptr; + GroupInfo() = default; + GroupInfo(std::string const& _chainID, std::string const& _groupID) + : m_chainID(_chainID), m_groupID(_groupID) + {} + virtual ~GroupInfo() {} + + virtual std::string const& genesisConfig() const { return m_genesisConfig; } + virtual std::string const& iniConfig() const { return m_iniConfig; } + virtual ChainNodeInfo::Ptr nodeInfo(std::string_view _nodeName) const + { + ReadGuard l(x_nodeInfos); + auto it = m_nodeInfos.find(_nodeName); + if (it == m_nodeInfos.end()) + { + return nullptr; + } + return it->second; + } + + std::string const& groupID() const { return m_groupID; } + std::string const& chainID() const { return m_chainID; } + + virtual void setGenesisConfig(std::string const& _genesisConfig) + { + m_genesisConfig = _genesisConfig; + } + virtual void setIniConfig(std::string const& _iniConfig) { m_iniConfig = _iniConfig; } + virtual bool appendNodeInfo(ChainNodeInfo::Ptr _nodeInfo) + { + UpgradableGuard l(x_nodeInfos); + auto const& nodeName = _nodeInfo->nodeName(); + if (m_nodeInfos.count(nodeName)) + { + return false; + } + UpgradeGuard ul(l); + m_nodeInfos[nodeName] = _nodeInfo; + return true; + } + + virtual void updateNodeInfo(ChainNodeInfo::Ptr _nodeInfo) + { + WriteGuard l(x_nodeInfos); + auto const& nodeName = _nodeInfo->nodeName(); + if (m_nodeInfos.count(nodeName)) + { + *(m_nodeInfos[nodeName]) = *_nodeInfo; + return; + } + m_nodeInfos[nodeName] = _nodeInfo; + } + + virtual bool removeNodeInfo(std::string const& _nodeName) + { + UpgradableGuard l(x_nodeInfos); + if (!m_nodeInfos.count(_nodeName)) + { + return false; + } + UpgradeGuard ul(l); + m_nodeInfos.erase(_nodeName); + return true; + } + + virtual void setGroupID(std::string const& _groupID) { m_groupID = _groupID; } + virtual void setChainID(std::string const& _chainID) { m_chainID = _chainID; } + virtual int64_t nodesNum() const + { + ReadGuard l(x_nodeInfos); + return m_nodeInfos.size(); + } + + // return copied nodeInfos to ensure thread-safe + auto nodeInfos() { return m_nodeInfos; } + + bcos::group::ChainNodeInfoFactory::Ptr chainNodeInfoFactory() const + { + return m_chainNodeInfoFactory; + } + + void setChainNodeInfoFactory(bcos::group::ChainNodeInfoFactory::Ptr _chainNodeInfoFactory) + { + m_chainNodeInfoFactory = _chainNodeInfoFactory; + } + + bool wasm() const { return m_wasm; } + bool smCryptoType() const { return m_smCryptoType; } + virtual void setWasm(bool _wasm) { m_wasm = _wasm; } + virtual void setSmCryptoType(bool _smCryptoType) { m_smCryptoType = _smCryptoType; } + +protected: + bool m_wasm{false}; + bool m_smCryptoType{false}; + + ChainNodeInfoFactory::Ptr m_chainNodeInfoFactory; + + std::string m_chainID; + std::string m_groupID; + // the genesis config for the group + std::string m_genesisConfig; + // the iniConfig for the group + std::string m_iniConfig; + // node name to node deployment information mapping + std::map> m_nodeInfos; + mutable SharedMutex x_nodeInfos; +}; + +inline std::string printGroupInfo(GroupInfo::Ptr _groupInfo) +{ + if (!_groupInfo) + { + return ""; + } + std::stringstream oss; + oss << LOG_KV("group", _groupInfo->groupID()) << LOG_KV("chain", _groupInfo->chainID()) + << LOG_KV("nodeSize", _groupInfo->nodesNum()); + return oss.str(); +} +} // namespace group +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/GroupInfoCodec.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/GroupInfoCodec.h" new file mode 100644 index 00000000..393f481b --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/GroupInfoCodec.h" @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2021 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. + * + * @brief the information used to deploy new node + * @file GroupInfoCodec.h + * @author: yujiechen + * @date 2022-03-29 + */ +#pragma once +#include "GroupInfo.h" +#include +#include + +namespace bcos +{ +namespace group +{ +class GroupInfoCodec +{ +public: + using Ptr = std::shared_ptr; + GroupInfoCodec() = default; + virtual ~GroupInfoCodec() {} + + virtual GroupInfo::Ptr deserialize(const std::string& _encodedData) = 0; + virtual void serialize(std::string& _encodedData, GroupInfo::Ptr _groupInfo) = 0; + virtual Json::Value serialize(GroupInfo::Ptr _groupInfo) = 0; +}; +} // namespace group +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/GroupInfoFactory.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/GroupInfoFactory.h" new file mode 100644 index 00000000..f192be58 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/GroupInfoFactory.h" @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2021 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. + * + * @brief factory to build the GroupInfo + * @file GroupInfoFactory.h + * @author: yujiechen + * @date 2021-09-18 + */ +#pragma once +#include "ChainNodeInfoFactory.h" +#include "GroupInfo.h" +namespace bcos +{ +namespace group +{ +class GroupInfoFactory +{ +public: + using Ptr = std::shared_ptr; + GroupInfoFactory() = default; + virtual ~GroupInfoFactory() {} + virtual GroupInfo::Ptr createGroupInfo() { return std::make_shared(); } + virtual GroupInfo::Ptr createGroupInfo(std::string const& _chainID, std::string const& _groupID) + { + return std::make_shared(_chainID, _groupID); + } +}; +} // namespace group +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/GroupTypeDef.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/GroupTypeDef.h" new file mode 100644 index 00000000..27faf8c1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/multigroup/GroupTypeDef.h" @@ -0,0 +1,35 @@ +/** + * Copyright (C) 2021 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. + * + * @brief define the basic type of the GroupManager + * @file GroupTypeDef.h + * @author: yujiechen + * @date 2021-09-16 + */ +#pragma once +#include +#include +#include + +#define GROUP_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("GROUP") + +namespace bcos +{ +namespace group +{ +DERIVE_BCOS_EXCEPTION(InvalidGroupInfo); +DERIVE_BCOS_EXCEPTION(InvalidChainNodeInfo); +} // namespace group +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/AMOPRequest.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/AMOPRequest.h" new file mode 100644 index 00000000..b0eb486a --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/AMOPRequest.h" @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2021 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 AMOPRequest.h + * @author: octopus + * @date 2021-08-23 + */ +#pragma once +#include +#include + +// check offset length overflow when decode message +#define OFFSET_CHECK(offset, step, length) \ + do \ + { \ + if (offset + step > length) \ + { \ + throw std::runtime_error("offset overflow, offset: " + std::to_string(offset) + \ + ",step: " + std::to_string(step) + \ + " ,length: " + std::to_string(length)); \ + } \ + } while (0) + +namespace bcos +{ +namespace protocol +{ +class AMOPRequest +{ +public: + using Ptr = std::shared_ptr; + AMOPRequest() = default; + AMOPRequest(bcos::bytesConstRef _data) { decode(_data); } + virtual ~AMOPRequest() {} + + // topic field length + const static size_t TOPIC_MAX_LENGTH = 65535; + const static size_t MESSAGE_MIN_LENGTH = 2; + + + std::string topic() const { return m_topic; } + void setTopic(const std::string& _topic) { m_topic = _topic; } + void setData(bcos::bytesConstRef _data) { m_data = _data; } + bcos::bytesConstRef data() const { return m_data; } + + virtual bool encode(bcos::bytes& _buffer) + { + if (m_topic.size() > TOPIC_MAX_LENGTH) + { + return false; + } + // the version + auto version = boost::asio::detail::socket_ops::host_to_network_long(m_version); + auto versionLen = sizeof(m_version) / sizeof(uint8_t); + _buffer.insert(_buffer.end(), (byte*)&version, (byte*)&version + versionLen); + // the topic length + uint16_t length = + boost::asio::detail::socket_ops::host_to_network_short((uint16_t)m_topic.size()); + _buffer.insert(_buffer.end(), (byte*)&length, (byte*)&length + 2); + // the topic data + _buffer.insert(_buffer.end(), m_topic.begin(), m_topic.end()); + // the data + _buffer.insert(_buffer.end(), m_data.begin(), m_data.end()); + return true; + } + virtual int64_t decode(bcos::bytesConstRef _data) + { + if (_data.size() < MESSAGE_MIN_LENGTH) + { + return -1; + } + + try + { + std::size_t length = _data.size(); + std::size_t offset = 0; + // decode version + auto versionLen = sizeof(m_version) / sizeof(uint8_t); + OFFSET_CHECK(offset, versionLen, length); + offset += versionLen; + m_version = + boost::asio::detail::socket_ops::network_to_host_long(*((uint32_t*)_data.data())); + + // decode topicLength + OFFSET_CHECK(offset, 2, length); + uint16_t topicLen = boost::asio::detail::socket_ops::network_to_host_short( + *((uint16_t*)(_data.data() + versionLen))); + offset += 2; + + // decode topic + OFFSET_CHECK(offset, topicLen, length); + m_topic = std::string(_data.data() + offset, _data.data() + offset + topicLen); + offset += topicLen; + + // decode data + m_data = _data.getCroppedData(offset); + return _data.size(); + } + catch (const std::string&) + { + return -1; + } + } + + virtual uint32_t version() const { return m_version; } + virtual void setVersion(uint32_t _version) { m_version = _version; } + +private: + std::string m_topic; + bcos::bytesConstRef m_data = bcos::bytesConstRef(); + uint32_t m_version = 0; +}; + +class AMOPRequestFactory +{ +public: + using Ptr = std::shared_ptr; + AMOPRequestFactory() = default; + virtual ~AMOPRequestFactory() {} + + std::shared_ptr buildRequest() { return std::make_shared(); } + std::shared_ptr buildRequest(bcos::bytesConstRef _data) + { + return std::make_shared(_data); + } +}; + +} // namespace protocol +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/Block.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/Block.h" new file mode 100644 index 00000000..8d625f9b --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/Block.h" @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2021 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. + * + * @brief interface for Block + * @file Block.h + * @author: yujiechen + * @date: 2021-03-23 + */ +#pragma once +#include "BlockHeader.h" +#include "Transaction.h" +#include "TransactionFactory.h" +#include "TransactionMetaData.h" +#include "TransactionReceipt.h" +#include "TransactionReceiptFactory.h" + +namespace bcos +{ +namespace protocol +{ +using HashList = std::vector; +using HashListPtr = std::shared_ptr; +using HashListConstPtr = std::shared_ptr; + +using NonceList = std::vector; +using NonceListPtr = std::shared_ptr; + +enum BlockType : int32_t +{ + CompleteBlock = 1, + WithTransactionsHash = 2, +}; + +class Block +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + Block( + TransactionFactory::Ptr _transactionFactory, TransactionReceiptFactory::Ptr _receiptFactory) + : m_transactionFactory(_transactionFactory), m_receiptFactory(_receiptFactory) + {} + + virtual ~Block() {} + + virtual void decode(bytesConstRef _data, bool _calculateHash, bool _checkSig) = 0; + virtual void encode(bytes& _encodeData) const = 0; + + virtual bcos::crypto::HashType calculateTransactionRoot() const = 0; + virtual bcos::crypto::HashType calculateReceiptRoot() const = 0; + + virtual int32_t version() const = 0; + virtual void setVersion(int32_t _version) = 0; + virtual BlockType blockType() const = 0; + // blockHeader gets blockHeader + virtual BlockHeader::ConstPtr blockHeaderConst() const = 0; + virtual BlockHeader::Ptr blockHeader() = 0; + // get transactions + virtual Transaction::ConstPtr transaction(uint64_t _index) const = 0; + // get receipts + virtual TransactionReceipt::ConstPtr receipt(uint64_t _index) const = 0; + // get transaction metaData + virtual TransactionMetaData::ConstPtr transactionMetaData(uint64_t _index) const = 0; + // get transaction hash + virtual bcos::crypto::HashType transactionHash(uint64_t _index) const + { + auto txMetaData = transactionMetaData(_index); + if (txMetaData) + { + return txMetaData->hash(); + } + return bcos::crypto::HashType(); + } + + virtual void setBlockType(BlockType _blockType) = 0; + // setBlockHeader sets blockHeader + virtual void setBlockHeader(BlockHeader::Ptr _blockHeader) = 0; + // set transactions + virtual void setTransaction(uint64_t _index, Transaction::Ptr _transaction) = 0; + virtual void appendTransaction(Transaction::Ptr _transaction) = 0; + // set receipts + virtual void setReceipt(uint64_t _index, TransactionReceipt::Ptr _receipt) = 0; + virtual void appendReceipt(TransactionReceipt::Ptr _receipt) = 0; + // set transaction metaData + virtual void appendTransactionMetaData(TransactionMetaData::Ptr _txMetaData) = 0; + + virtual NonceListPtr nonces() const + { + auto nonceList = std::make_shared(); + if (transactionsSize() == 0) + { + return nonceList; + } + for (uint64_t i = 0; i < transactionsSize(); ++i) + { + nonceList->push_back(transaction(i)->nonce()); + } + return nonceList; + } + + // get transactions size + virtual uint64_t transactionsSize() const = 0; + virtual uint64_t transactionsMetaDataSize() const = 0; + virtual uint64_t transactionsHashSize() const { return transactionsMetaDataSize(); } + + // get receipts size + virtual uint64_t receiptsSize() const = 0; + + // for nonceList + virtual void setNonceList(NonceList const& _nonceList) = 0; + virtual void setNonceList(NonceList&& _nonceList) = 0; + virtual NonceList const& nonceList() const = 0; + +protected: + TransactionFactory::Ptr m_transactionFactory; + TransactionReceiptFactory::Ptr m_receiptFactory; +}; +using Blocks = std::vector; +using BlocksPtr = std::shared_ptr; +} // namespace protocol +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/BlockFactory.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/BlockFactory.h" new file mode 100644 index 00000000..eacdde29 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/BlockFactory.h" @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 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 BlockFactory.h + * @author: yujiechen + * @date: 2021-03-23 + */ +#pragma once +#include "Block.h" +#include "BlockHeaderFactory.h" +#include "TransactionFactory.h" +#include "TransactionMetaData.h" +#include "TransactionReceiptFactory.h" +#include + +namespace bcos +{ +namespace protocol +{ + +class BlockFactory +{ +public: + using Ptr = std::shared_ptr; + BlockFactory() = default; + virtual ~BlockFactory() {} + virtual Block::Ptr createBlock() = 0; + virtual Block::Ptr createBlock( + bytes const& _data, bool _calculateHash = true, bool _checkSig = true) = 0; + virtual Block::Ptr createBlock( + bytesConstRef _data, bool _calculateHash = true, bool _checkSig = true) = 0; + + virtual TransactionMetaData::Ptr createTransactionMetaData() = 0; + virtual TransactionMetaData::Ptr createTransactionMetaData( + bcos::crypto::HashType const _hash, std::string const& _to) = 0; + + virtual bcos::crypto::CryptoSuite::Ptr cryptoSuite() = 0; + virtual BlockHeaderFactory::Ptr blockHeaderFactory() = 0; + virtual TransactionFactory::Ptr transactionFactory() = 0; + virtual TransactionReceiptFactory::Ptr receiptFactory() = 0; +}; +} // namespace protocol +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/BlockHeader.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/BlockHeader.h" new file mode 100644 index 00000000..31547785 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/BlockHeader.h" @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2021 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. + * + * @brief interface for BlockHeader + * @file BlockHeader.h + * @author: yujiechen + * @date: 2021-03-22 + */ +#pragma once +#include "Exceptions.h" +#include "ProtocolTypeDef.h" +#include +#include +#include + +namespace bcos +{ +namespace protocol +{ +class BlockHeader +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + using BlockHeadersPtr = std::shared_ptr >; + explicit BlockHeader(bcos::crypto::CryptoSuite::Ptr _cryptoSuite) : m_cryptoSuite(_cryptoSuite) + {} + + virtual ~BlockHeader() {} + + virtual void decode(bytesConstRef _data) = 0; + virtual void encode(bytes& _encodeData) const = 0; + + virtual bcos::crypto::HashType hash() const { return {}; } + + virtual void populateFromParents(BlockHeadersPtr _parents, BlockNumber _number) + { + // set parentInfo + ParentInfoList parentInfoList; + for (auto parentHeader : *_parents) + { + ParentInfo parentInfo; + parentInfo.blockNumber = parentHeader->number(); + parentInfo.blockHash = parentHeader->hash(); + parentInfoList.emplace_back(parentInfo); + } + setParentInfo(std::move(parentInfoList)); + setNumber(_number); + } + + virtual void clear() = 0; + + // verifySignatureList verifys the signatureList + virtual void verifySignatureList() const + { + auto signatures = signatureList(); + auto sealers = sealerList(); + if (signatures.size() < sealers.size()) + { + BOOST_THROW_EXCEPTION(InvalidBlockHeader() << errinfo_comment( + "Invalid blockHeader for the size of sealerList " + "is smaller than the size of signatureList")); + } + for (auto signature : signatures) + { + auto sealerIndex = signature.index; + auto signatureData = signature.signature; + if (!m_cryptoSuite->signatureImpl()->verify( + std::shared_ptr(&((sealers)[sealerIndex]), [](const bytes*) {}), + hash(), bytesConstRef(signatureData.data(), signatureData.size()))) + { + BOOST_THROW_EXCEPTION( + InvalidSignatureList() + << errinfo_comment("Invalid signatureList for verify failed, signatureData:" + + *toHexString(signatureData))); + } + } + } + virtual void populateEmptyBlock( + BlockNumber _number, int64_t _sealerId, int64_t _timestamp = utcTime()) + { + setNumber(_number); + setSealer(_sealerId); + setTimestamp(_timestamp); + } + + // version returns the version of the blockHeader + virtual uint32_t version() const = 0; + // parentInfo returns the parent information, including (parentBlockNumber, parentHash) + virtual gsl::span parentInfo() const = 0; + // txsRoot returns the txsRoot of the current block + virtual bcos::crypto::HashType txsRoot() const = 0; + // receiptsRoot returns the receiptsRoot of the current block + virtual bcos::crypto::HashType receiptsRoot() const = 0; + // stateRoot returns the stateRoot of the current block + virtual bcos::crypto::HashType stateRoot() const = 0; + // number returns the number of the current block + virtual BlockNumber number() const = 0; + virtual u256 gasUsed() const = 0; + virtual int64_t timestamp() const = 0; + // sealer returns the sealer that generate this block + virtual int64_t sealer() const = 0; + // sealerList returns the current sealer list + virtual gsl::span sealerList() const = 0; + virtual bytesConstRef extraData() const = 0; + virtual gsl::span signatureList() const = 0; + virtual gsl::span consensusWeights() const = 0; + + virtual void setVersion(uint32_t _version) = 0; + virtual void setParentInfo(gsl::span const& _parentInfo) = 0; + virtual void setParentInfo(ParentInfoList&& _parentInfo) = 0; + + virtual void setTxsRoot(bcos::crypto::HashType _txsRoot) = 0; + virtual void setReceiptsRoot(bcos::crypto::HashType _receiptsRoot) = 0; + virtual void setStateRoot(bcos::crypto::HashType _stateRoot) = 0; + virtual void setNumber(BlockNumber _blockNumber) = 0; + virtual void setGasUsed(u256 _gasUsed) = 0; + virtual void setTimestamp(int64_t _timestamp) = 0; + virtual void setSealer(int64_t _sealerId) = 0; + + virtual void setSealerList(gsl::span const& _sealerList) = 0; + virtual void setSealerList(std::vector&& _sealerList) = 0; + + virtual void setConsensusWeights(gsl::span const& _weightList) = 0; + virtual void setConsensusWeights(std::vector&& _weightList) = 0; + + virtual void setExtraData(bytes const& _extraData) = 0; + virtual void setExtraData(bytes&& _extraData) = 0; + + virtual void setSignatureList(gsl::span const& _signatureList) = 0; + virtual void setSignatureList(SignatureList&& _signatureList) = 0; + virtual bcos::crypto::CryptoSuite::Ptr cryptoSuite() { return m_cryptoSuite; } + +protected: + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; +}; +} // namespace protocol +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/BlockHeaderFactory.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/BlockHeaderFactory.h" new file mode 100644 index 00000000..2468d73f --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/BlockHeaderFactory.h" @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021 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. + * + * @brief factory for BlockHeader + * @file BlockHeaderFactory.h + * @author: yujiechen + * @date: 2021-03-23 + */ +#pragma once +#include "BlockHeader.h" +namespace bcos +{ +namespace protocol +{ +class BlockHeaderFactory +{ +public: + using Ptr = std::shared_ptr; + BlockHeaderFactory() = default; + virtual ~BlockHeaderFactory() {} + virtual BlockHeader::Ptr createBlockHeader() = 0; + virtual BlockHeader::Ptr createBlockHeader(bytes const& _data) = 0; + virtual BlockHeader::Ptr createBlockHeader(bytesConstRef _data) = 0; + virtual BlockHeader::Ptr createBlockHeader(BlockNumber _number) = 0; + + virtual BlockHeader::Ptr populateBlockHeader(BlockHeader::Ptr _blockHeader) + { + auto header = createBlockHeader(); + header->setVersion(_blockHeader->version()); + header->setTxsRoot(_blockHeader->txsRoot()); + header->setReceiptsRoot(_blockHeader->receiptsRoot()); + header->setStateRoot(_blockHeader->stateRoot()); + header->setNumber(_blockHeader->number()); + header->setGasUsed(_blockHeader->gasUsed()); + header->setTimestamp(_blockHeader->timestamp()); + header->setSealer(_blockHeader->sealer()); + header->setSealerList(_blockHeader->sealerList()); + header->setSignatureList(_blockHeader->signatureList()); + header->setConsensusWeights(_blockHeader->consensusWeights()); + header->setParentInfo(_blockHeader->parentInfo()); + auto extraData = _blockHeader->extraData().toBytes(); + header->setExtraData(std::move(extraData)); + return header; + } +}; +} // namespace protocol +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/CommonError.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/CommonError.h" new file mode 100644 index 00000000..22701a0e --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/CommonError.h" @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2021 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. + * + * @brief define common error for all the modules + * @file CommonError.h + * @author: yujiechen + * @date 2021-04-22 + */ +#pragma once +#include +namespace bcos +{ +namespace protocol +{ +enum CommonError : int32_t +{ + SUCCESS = 0, + TIMEOUT = 1000, // for gateway + NotFoundFrontServiceSendMsg = 1001, + NotFoundFrontServiceDispatchMsg = 1002, + GatewaySendMsgFailed = 1003, + NetworkBandwidthOverFlow = 1004, + TransactionsMissing = 2000, // for transaction sync + InconsistentTransactions = 2001, + TxsSignatureVerifyFailed = 2002, + FetchTransactionsFailed = 2003, + NotFoundPeerByTopicSendMsg = 3001, + NotFoundClientByTopicDispatchMsg = 3002, + AMOPSendMsgFailed = 3003, + UnSupportedPacketType = 3004, +}; + +} // namespace protocol +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/Exceptions.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/Exceptions.h" new file mode 100644 index 00000000..489ff79c --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/Exceptions.h" @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2021 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. + * + * @brief exceptions for protocol + * @file Exceptions.h + * @author: yujiechen + * @date: 2021-03-16 + */ +#pragma once +#include +namespace bcos +{ +namespace protocol +{ +DERIVE_BCOS_EXCEPTION(InvalidBlockHeader); +DERIVE_BCOS_EXCEPTION(InvalidSignatureList); +// transaction exceptions +DERIVE_BCOS_EXCEPTION(OutOfGasLimit); +DERIVE_BCOS_EXCEPTION(NotEnoughCash); +DERIVE_BCOS_EXCEPTION(BadInstruction); +DERIVE_BCOS_EXCEPTION(BadJumpDestination); +DERIVE_BCOS_EXCEPTION(OutOfGas); +DERIVE_BCOS_EXCEPTION(OutOfStack); +DERIVE_BCOS_EXCEPTION(StackUnderflow); +DERIVE_BCOS_EXCEPTION(ContractAddressAlreadyUsed); +DERIVE_BCOS_EXCEPTION(RevertInstruction); +DERIVE_BCOS_EXCEPTION(PermissionDenied); +DERIVE_BCOS_EXCEPTION(CallAddressError); +DERIVE_BCOS_EXCEPTION(GasOverflow); +DERIVE_BCOS_EXCEPTION(ContractFrozen); +DERIVE_BCOS_EXCEPTION(AccountFrozen); +class PrecompiledError : public Exception +{ +public: + PrecompiledError() : Exception() {} + PrecompiledError(std::string const& _msg) : Exception(_msg) {} +}; +} // namespace protocol +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/GlobalConfig.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/GlobalConfig.h" new file mode 100644 index 00000000..e1141128 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/GlobalConfig.h" @@ -0,0 +1,92 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for ProtocolInfo + * @file ProtocolInfo.h + * @author: yujiechen + * @date 2022-03-08 + */ +#pragma once +#include "Protocol.h" +#include "ProtocolInfo.h" +#include "ProtocolInfoCodec.h" +#include +#include + +namespace bcos +{ +namespace protocol +{ +class GlobalConfig +{ +public: + static GlobalConfig& instance() + { + static GlobalConfig ins; + return ins; + } + GlobalConfig() + { + // nodeService + c_supportedProtocols.insert({ProtocolModuleID::NodeService, + std::make_shared( + ProtocolModuleID::NodeService, ProtocolVersion::V0, ProtocolVersion::V1)}); + // gatewayService + c_supportedProtocols.insert({ProtocolModuleID::GatewayService, + std::make_shared( + ProtocolModuleID::GatewayService, ProtocolVersion::V0, ProtocolVersion::V2)}); + // rpcService && SDK + c_supportedProtocols.insert({ProtocolModuleID::RpcService, + std::make_shared( + ProtocolModuleID::RpcService, ProtocolVersion::V0, ProtocolVersion::V1)}); + // executorService + c_supportedProtocols.insert({ProtocolModuleID::ExecutorService, + std::make_shared( + ProtocolModuleID::ExecutorService, ProtocolVersion::V0, ProtocolVersion::V1)}); + } + virtual ~GlobalConfig() {} + + ProtocolInfo::ConstPtr protocolInfo(ProtocolModuleID _moduleID) const + { + if (!c_supportedProtocols.count(_moduleID)) + { + return nullptr; + } + return c_supportedProtocols.at(_moduleID); + } + + std::map const& supportedProtocols() const + { + return c_supportedProtocols; + } + // Note: must set the protocolInfo codec when init + virtual void setCodec(ProtocolInfoCodec::Ptr _codec) { m_codec = _codec; } + virtual ProtocolInfoCodec::Ptr codec() const { return m_codec; } + + BlockVersion minSupportedVersion() const { return m_minSupportedVersion; } + BlockVersion maxSupportedVersion() const { return m_maxSupportedVersion; } + +private: + std::map c_supportedProtocols; + // the minimum supported version + BlockVersion m_minSupportedVersion = BlockVersion::MIN_VERSION; + BlockVersion m_maxSupportedVersion = BlockVersion::MAX_VERSION; + + ProtocolInfoCodec::Ptr m_codec; + mutable bcos::SharedMutex x_version; +}; +} // namespace protocol +} // namespace bcos +#define g_BCOSConfig bcos::protocol::GlobalConfig::instance() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/LogEntry.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/LogEntry.h" new file mode 100644 index 00000000..18738c04 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/LogEntry.h" @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2021 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 LogEntry.h + * @author: yujiechen + * @date: 2021-03-18 + */ +#pragma once +#include +#include +#include + +namespace bcos +{ +namespace protocol +{ +class LogEntry +{ +public: + using Ptr = std::shared_ptr; + LogEntry() = default; + LogEntry(bytes const& _address, h256s _topics, bytes _data) + : m_address(_address), m_topics(std::move(_topics)), m_data(std::move(_data)) + {} + + ~LogEntry() {} + + std::string_view address() const + { + return std::string_view((char*)m_address.data(), m_address.size()); + } + gsl::span topics() const { return gsl::span(m_topics.data(), m_topics.size()); } + bytesConstRef data() const { return ref(m_data); } + // Define the scale decode method, which cannot be modified at will + template > + friend Stream& operator>>(Stream& _stream, LogEntry& _logEntry) + { + return _stream >> _logEntry.m_address >> _logEntry.m_topics >> _logEntry.m_data; + } + + // Define the scale encode method, which cannot be modified at will + template > + friend Stream& operator<<(Stream& _stream, LogEntry const& _logEntry) + { + return _stream << _logEntry.m_address << _logEntry.m_topics << _logEntry.m_data; + } + +private: + bcos::bytes m_address; + bcos::h256s m_topics; + bytes m_data; +}; + +using LogEntries = std::vector; +using LogEntriesPtr = std::shared_ptr>; +} // namespace protocol +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/MemberInterface.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/MemberInterface.h" new file mode 100644 index 00000000..73baf720 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/MemberInterface.h" @@ -0,0 +1,68 @@ +/** + * 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. + * + * @brief the member information + * @file MemberInterface.h + * @author: yujiechen + * @date 2022-04-26 + */ +#pragma once +#include +namespace bcos +{ +namespace protocol +{ +class MemberInterface +{ +public: + using Ptr = std::shared_ptr; + MemberInterface() = default; + virtual ~MemberInterface() {} + + // the memberID of different service, should be unique + virtual void setMemberID(std::string const& _memberID) = 0; + // the memberConfig of different service + virtual void setMemberConfig(std::string const& _config) = 0; + // no-need to store or transfer in the network + virtual void setSeq(int64_t _seq) { m_seq = _seq; } + virtual void setLeaseID(int64_t _leaseID) { m_leaseID = _leaseID; } + + virtual std::string const& memberID() const = 0; + virtual std::string const& memberConfig() const = 0; + virtual int64_t seq() const { return m_seq; } + virtual int64_t leaseID() const { return m_leaseID; } + + // encode the member into string + virtual void encode(std::string& _encodedData) = 0; + // decode the member info + virtual void decode(std::string const& _memberData) = 0; + +protected: + int64_t m_seq; + int64_t m_leaseID; +}; + +class MemberFactoryInterface +{ +public: + using Ptr = std::shared_ptr; + MemberFactoryInterface() = default; + virtual ~MemberFactoryInterface() {} + + virtual MemberInterface::Ptr createMember() = 0; + virtual MemberInterface::Ptr createMember(std::string const& _memberData) = 0; +}; +} // namespace protocol +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/Protocol.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/Protocol.h" new file mode 100644 index 00000000..6c85fa16 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/Protocol.h" @@ -0,0 +1,241 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Protocol for all the modules + * @file Protocol.h + * @author: yujiechen + * @date 2021-04-21 + */ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace protocol +{ +// Note: both MessageExtFieldFlag and NodeType occupy the ext fields +enum MessageExtFieldFlag : uint32_t +{ + Response = 0x0001, + Compress = 0x0010, +}; +enum NodeType : uint32_t +{ + None = 0x0, + CONSENSUS_NODE = 0x2, + OBSERVER_NODE = 0x4, + NODE_OUTSIDE_GROUP = 0x8, +}; + +enum NodeArchitectureType +{ + AIR = 0, + PRO = 1, + MAX = 2, + LIGHT +}; + +enum MessageType +{ + HANDESHAKE = 0x100, // 256 + BLOCK_NOTIFY = 0x101, // 257 + RPC_REQUEST = 0x102, // 258 + GROUP_NOTIFY = 0x103, // 259 + EVENT_SUBSCRIBE = 0x120, // 288 + EVENT_UNSUBSCRIBE = 0x121, // 289 + EVENT_LOG_PUSH = 0x122, // 290 +}; + +enum ModuleID +{ + PBFT = 1000, + Raft = 1001, + BlockSync = 2000, + TxsSync = 2001, + ConsTxsSync = 2002, + AMOP = 3000, + + LIGHTNODE_GET_BLOCK = 4000, + LIGHTNODE_GET_TRANSACTIONS = 4001, + LIGHTNODE_GET_RECEIPTS = 4002, + LIGHTNODE_GET_STATUS = 4003, + LIGHTNODE_SEND_TRANSACTION = 4004, + LIGHTNODE_CALL = 4005, + LIGHTNODE_END = 4999, + + SYNC_PUSH_TRANSACTION = 5000, + SYNC_GET_TRANSACTIONS = 5001, + SYNC_END = 5999 +}; +enum ProtocolModuleID : uint32_t +{ + NodeService = 0x0, + GatewayService = 0x1, + RpcService = 0x2, + ExecutorService = 0x3, + MAX_PROTOCOL_MODULE = ExecutorService, +}; +enum ProtocolVersion : uint32_t +{ + V0 = 0, + V1 = 1, + V2 = 2, +}; + +// BlockVersion only present the data version with format major.minor.patch of 3 bytes, data should +// be compatible with the same major.minor version, the patch version should always be compatible, +// the last byte is reserved, so 3.1.0 is 0x03010000 and is compatible with 3.1.1 which is 0x03010100 + +enum class BlockVersion : uint32_t +{ + V3_1_VERSION = 0x03010000, + V3_0_VERSION = 0x03000000, + RC4_VERSION = 4, + MIN_VERSION = RC4_VERSION, + MAX_VERSION = V3_1_VERSION, +}; +const std::string RC4_VERSION_STR = "3.0.0-rc4"; +const std::string V3_0_VERSION_STR = "3.0.0"; +const std::string V3_1_VERSION_STR = "3.1.0"; + +const std::string RC_VERSION_PREFIX = "3.0.0-rc"; + +const BlockVersion DEFAULT_VERSION = bcos::protocol::BlockVersion::V3_1_VERSION; +const uint8_t MAX_MAJOR_VERSION = std::numeric_limits::max(); +const uint8_t MIN_MAJOR_VERSION = 3; + +inline int versionCompareTo(std::variant _v1, BlockVersion const& _v2) +{ + int flag = 0; + std::visit( + [&_v2, &flag](auto&& arg) { + auto ver1 = static_cast(arg); + auto ver2 = static_cast(_v2); + flag = ver1 > ver2 ? 1 : -1; + flag = (ver1 == ver2) ? 0 : flag; + }, + _v1); + return flag; +} +inline std::ostream& operator<<(std::ostream& _out, bcos::protocol::BlockVersion const& _version) +{ + switch (_version) + { + case bcos::protocol::BlockVersion::RC4_VERSION: + _out << RC4_VERSION_STR; + break; + case bcos::protocol::BlockVersion::V3_0_VERSION: + _out << V3_0_VERSION_STR; + break; + case bcos::protocol::BlockVersion::V3_1_VERSION: + _out << V3_1_VERSION_STR; + break; + default: + _out << "Unknown"; + break; + } + return _out; +} +inline std::ostream& operator<<(std::ostream& _out, NodeType const& _nodeType) +{ + switch (_nodeType) + { + case NodeType::None: + _out << "None"; + break; + case NodeType::CONSENSUS_NODE: + _out << "CONSENSUS_NODE"; + break; + case NodeType::OBSERVER_NODE: + _out << "OBSERVER_NODE"; + break; + case NodeType::NODE_OUTSIDE_GROUP: + _out << "NODE_OUTSIDE_GROUP"; + break; + default: + _out << "Unknown"; + break; + } + return _out; +} + +inline std::optional stringToModuleID(const std::string& _moduleName) +{ + if (boost::iequals(_moduleName, "raft")) + { + return bcos::protocol::ModuleID::Raft; + } + else if (boost::iequals(_moduleName, "pbft")) + { + return bcos::protocol::ModuleID::PBFT; + } + else if (boost::iequals(_moduleName, "amop")) + { + return bcos::protocol::ModuleID::AMOP; + } + else if (boost::iequals(_moduleName, "block_sync")) + { + return bcos::protocol::ModuleID::BlockSync; + } + else if (boost::iequals(_moduleName, "txs_sync")) + { + return bcos::protocol::ModuleID::TxsSync; + } + else if (boost::iequals(_moduleName, "cons_txs_sync")) + { + return bcos::protocol::ModuleID::ConsTxsSync; + } + else if (boost::iequals(_moduleName, "light_node")) + { + return bcos::protocol::ModuleID::LIGHTNODE_GET_BLOCK; + } + else + { + return std::nullopt; + } +} + +inline std::string moduleIDToString(ModuleID _moduleID) +{ + switch (_moduleID) + { + case ModuleID::PBFT: + return "pbft"; + case ModuleID::Raft: + return "raft"; + case ModuleID::BlockSync: + return "block_sync"; + case ModuleID::TxsSync: + return "txs_sync"; + case ModuleID::ConsTxsSync: + return "cons_txs_sync"; + case ModuleID::AMOP: + return "amop"; + case ModuleID::LIGHTNODE_GET_BLOCK: + return "light_node"; + default: + return "unrecognized module"; + }; +} + + +} // namespace protocol +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/ProtocolInfo.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/ProtocolInfo.h" new file mode 100644 index 00000000..2a071694 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/ProtocolInfo.h" @@ -0,0 +1,65 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for ProtocolInfo + * @file ProtocolInfo.h + * @author: yujiechen + * @date 2022-03-08 + */ +#pragma once +#include "Protocol.h" +#include +namespace bcos +{ +namespace protocol +{ +class ProtocolInfo +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + ProtocolInfo() : m_protocolModuleID{}, m_minVersion{}, m_maxVersion{}, m_version{} {} + ProtocolInfo(ProtocolModuleID _moduleID, uint32_t _minVersion, uint32_t _maxVersion) + : m_protocolModuleID(_moduleID), + m_minVersion(_minVersion), + m_maxVersion(_maxVersion), + m_version(m_minVersion) + {} + virtual ~ProtocolInfo() {} + virtual void setProtocolModuleID(ProtocolModuleID _moduleID) { m_protocolModuleID = _moduleID; } + virtual void setMinVersion(uint32_t _minVersion) { m_minVersion = _minVersion; } + virtual void setMaxVersion(uint32_t _maxVersion) { m_maxVersion = _maxVersion; } + // set the negotiated version + virtual void setVersion(uint32_t _version) { m_version = _version; } + + // the moduleID + virtual ProtocolModuleID protocolModuleID() const { return m_protocolModuleID; } + // the minimum supported version number + virtual uint32_t minVersion() const { return m_minVersion; } + // the maximum supported version number + virtual uint32_t maxVersion() const { return m_maxVersion; } + + // the negotiated version + virtual uint32_t version() const { return m_version; } + +protected: + ProtocolModuleID m_protocolModuleID; + // Note: here can't use enum Version type in case of setVersion failed for no-defined Version + uint32_t m_minVersion; + uint32_t m_maxVersion; + uint32_t m_version; +}; +} // namespace protocol +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/ProtocolInfoCodec.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/ProtocolInfoCodec.h" new file mode 100644 index 00000000..807ea182 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/ProtocolInfoCodec.h" @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for ProtocolInfoCodec + * @file ProtocolInfoCodec.h + * @author: yujiechen + * @date 2022-03-22 + */ +#pragma once +#include "bcos-framework/protocol/ProtocolInfo.h" +#include +#include +namespace bcos +{ +namespace protocol +{ +class ProtocolInfoCodec +{ +public: + using ConstPtr = std::shared_ptr; + using Ptr = std::shared_ptr; + ProtocolInfoCodec() = default; + virtual ~ProtocolInfoCodec() {} + + virtual void encode(ProtocolInfo::ConstPtr _protocol, bcos::bytes& _encodeData) const = 0; + virtual ProtocolInfo::Ptr decode(bcos::bytesConstRef _data) const = 0; +}; +} // namespace protocol +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/ProtocolTypeDef.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/ProtocolTypeDef.h" new file mode 100644 index 00000000..da338517 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/ProtocolTypeDef.h" @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2021 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. + * + * @brief typedef for protocol module + * @file ProtocolTypeDef.h + * @author: yujiechen + * @date: 2021-04-9 + */ +#pragma once +#include +namespace bcos::protocol +{ +using BlockNumber = int64_t; +using NonceType = u256; +using NonceList = std::vector; +using NonceListPtr = std::shared_ptr; +using BytesList = std::vector>; +using BytesListPtr = std::shared_ptr; + +struct ParentInfo +{ + BlockNumber blockNumber; + bcos::crypto::HashType blockHash; + + bool operator==(const ParentInfo& rhs) const + { + return this->blockNumber == rhs.blockNumber && this->blockHash == rhs.blockHash; + } + + template > + friend Stream& operator>>(Stream& _stream, ParentInfo& parentInfo) + { + return _stream >> parentInfo.blockNumber >> parentInfo.blockHash; + } + + template > + friend Stream& operator<<(Stream& _stream, ParentInfo const& parentInfo) + { + return _stream << parentInfo.blockNumber << parentInfo.blockHash; + } +}; +using ParentInfoList = std::vector; +using ParentInfoListPtr = std::shared_ptr; + +struct Signature +{ + int64_t index; + bytes signature; + + template > + friend Stream& operator>>(Stream& _stream, Signature& _signature) + { + return _stream >> _signature.index >> _signature.signature; + } + + template > + friend Stream& operator<<(Stream& _stream, Signature const& _signature) + { + return _stream << _signature.index << _signature.signature; + } +}; +using SignatureList = std::vector; +using SignatureListPtr = std::shared_ptr; + +// the weight list +using WeightList = std::vector; +using WeightListPtr = std::shared_ptr; + +int64_t constexpr InvalidSealerIndex = INT64_MAX; + +struct Session +{ + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + enum Status + { + STARTED = 0, + DIRTY, + COMMITTED, + ROLLBACKED + }; + + long sessionID; + Status status; + bcos::protocol::BlockNumber beginNumber; // [ + bcos::protocol::BlockNumber endNumber; // ) +}; + +struct TwoPCParams +{ + BlockNumber number = 0; + std::string primaryKey; + uint64_t timestamp = 0; +}; +} // namespace bcos::protocol \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/ServiceDesc.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/ServiceDesc.h" new file mode 100644 index 00000000..0fddb2ab --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/ServiceDesc.h" @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2021 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. + * + * @brief define the common service information + * @file ServiceDesc.h + * @author: yujiechen + * @date: 2021-10-11 + */ +#pragma once +#include +#include +#include +namespace bcos +{ +namespace protocol +{ +enum ServiceType : int32_t +{ + NATIVE = 0, + EXECUTOR = 1, + LEDGER = 2, + SCHEDULER = 3, + FRONT = 4, + CONSENSUS = 5, + GATEWAY = 6, + RPC = 7, + TXPOOL = 8, +}; +const std::string NATIVE_NAME = "Native"; +const std::string NATIVE_SERVANT_NAME = "NativeNodeObj"; + +const std::string LEDGER_NAME = "Ledger"; +const std::string LEDGER_SERVANT_NAME = "LedgerServiceObj"; + +const std::string EXECUTOR_NAME = "Executor"; +const std::string EXECUTOR_SERVANT_NAME = "ExecutorServiceObj"; +const std::string EXECUTOR_SERVICE_NAME = "ExecutorService"; + +const std::string SCHEDULER_NAME = "Scheduler"; +const std::string SCHEDULER_SERVANT_NAME = "SchedulerServiceObj"; +const std::string SCHEDULER_SERVICE_NAME = "SchedulerService"; + +const std::string FRONT_NAME = "Front"; +const std::string FRONT_SERVANT_NAME = "FrontServiceObj"; +const std::string FRONT_SERVICE_NAME = "FrontService"; + +const std::string GATEWAY_NAME = "Gateway"; +const std::string GATEWAY_SERVANT_NAME = "GatewayServiceObj"; + +const std::string TXPOOL_NAME = "TxPool"; +const std::string TXPOOL_SERVANT_NAME = "TxPoolServiceObj"; +const std::string TXPOOL_SERVICE_NAME = "TxPoolService"; + +const std::string CONSENSUS_NAME = "PBFT"; +const std::string CONSENSUS_SERVANT_NAME = "PBFTServiceObj"; +const std::string CONSENSUS_SERVICE_NAME = "PBFTService"; + +const std::string RPC_NAME = "Rpc"; +const std::string RPC_SERVANT_NAME = "RpcServiceObj"; + +const std::string UNKNOWN_SERVANT = "Unknown"; + +inline std::string getPrxDesc(std::string const& _serviceName, std::string const& _objName) +{ + return _serviceName + "." + _objName; +} + +inline std::string getServiceObjByType(ServiceType _type) +{ + switch (_type) + { + case NATIVE: + return NATIVE_SERVANT_NAME; + case EXECUTOR: + return EXECUTOR_SERVANT_NAME; + case LEDGER: + return LEDGER_SERVANT_NAME; + case SCHEDULER: + return SCHEDULER_SERVANT_NAME; + case FRONT: + return FRONT_SERVANT_NAME; + case CONSENSUS: + return CONSENSUS_SERVANT_NAME; + case GATEWAY: + return GATEWAY_SERVANT_NAME; + case RPC: + return RPC_SERVANT_NAME; + case TXPOOL: + return TXPOOL_SERVANT_NAME; + default: + // undefined Comparator + break; + } + return UNKNOWN_SERVANT; +} + +inline std::string getServiceNameByType(ServiceType _type) +{ + switch (_type) + { + case NATIVE: + return NATIVE_NAME; + case EXECUTOR: + return EXECUTOR_NAME; + case LEDGER: + return LEDGER_NAME; + case SCHEDULER: + return SCHEDULER_NAME; + case FRONT: + return FRONT_NAME; + case CONSENSUS: + return CONSENSUS_NAME; + case GATEWAY: + return GATEWAY_NAME; + case RPC: + return RPC_SERVANT_NAME; + case TXPOOL: + return TXPOOL_NAME; + default: + // undefined Comparator + break; + } + return UNKNOWN_SERVANT; +} +} // namespace protocol +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/Transaction.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/Transaction.h" new file mode 100644 index 00000000..361e371d --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/Transaction.h" @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2021 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. + * + * @brief interfaces for transaction + * @file Transaction.h + */ +#pragma once +#include "TransactionSubmitResult.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::protocol +{ +enum TransactionType +{ + NullTransaction = 0, + ContractCreation, + MessageCall, +}; + +using TxSubmitCallback = + std::function; +class Transaction +{ +public: + enum Attribute : uint32_t + { + EVM_ABI_CODEC = 0x1, + LIQUID_SCALE_CODEC = 0x2, + DAG = 0x4, + LIQUID_CREATE = 0x8, + }; + + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + explicit Transaction(bcos::crypto::CryptoSuite::Ptr _cryptoSuite) : m_cryptoSuite(_cryptoSuite) + {} + + virtual ~Transaction() {} + + virtual void decode(bytesConstRef _txData) = 0; + virtual void encode(bcos::bytes& txData) const = 0; + virtual bcos::crypto::HashType hash(bool _useCache = true) const = 0; + + virtual void verify() const + { + // The tx has already been verified + if (!sender().empty()) + { + return; + } + + // check the hash when verify + auto hashResult = hash(false); + if (hashResult != this->hash()) + { + throw bcos::Exception("Hash mismatch!"); + } + + // check the signatures + auto signature = signatureData(); + auto publicKey = m_cryptoSuite->signatureImpl()->recover(this->hash(), signature); + // recover the sender + forceSender(m_cryptoSuite->calculateAddress(publicKey).asBytes()); + } + + virtual int32_t version() const = 0; + virtual std::string_view chainId() const = 0; + virtual std::string_view groupId() const = 0; + virtual int64_t blockLimit() const = 0; + virtual u256 nonce() const = 0; + virtual std::string_view to() const = 0; + virtual std::string_view abi() const = 0; + virtual std::string_view source() const = 0; + virtual void setSource(std::string const& _source) = 0; + + virtual std::string_view sender() const + { + return std::string_view((char*)m_sender.data(), m_sender.size()); + } + + virtual bytesConstRef input() const = 0; + virtual int64_t importTime() const = 0; + virtual void setImportTime(int64_t _importTime) = 0; + virtual TransactionType type() const + { + if (to().size() > 0) + { + return TransactionType::MessageCall; + } + return TransactionType::ContractCreation; + } + virtual void forceSender(bytes _sender) const { m_sender = std::move(_sender); } + virtual bytesConstRef signatureData() const = 0; + + virtual uint32_t attribute() const = 0; + virtual void setAttribute(uint32_t attribute) = 0; + + TxSubmitCallback takeSubmitCallback() { return std::move(m_submitCallback); } + TxSubmitCallback const& submitCallback() const { return m_submitCallback; } + void setSubmitCallback(TxSubmitCallback _submitCallback) { m_submitCallback = _submitCallback; } + bool synced() const { return m_synced; } + void setSynced(bool _synced) const { m_synced = _synced; } + + bool sealed() const { return m_sealed; } + void setSealed(bool _sealed) const { m_sealed = _sealed; } + + bool invalid() const { return m_invalid; } + void setInvalid(bool _invalid) const { m_invalid = _invalid; } + + bcos::crypto::CryptoSuite::Ptr cryptoSuite() { return m_cryptoSuite; } + + void appendKnownNode(bcos::crypto::NodeIDPtr _node) const + { + std::unique_lock l(x_knownNodeList); + m_knownNodeList.insert(_node); + } + + bool isKnownBy(bcos::crypto::NodeIDPtr _node) const + { + std::shared_lock l(x_knownNodeList); + return m_knownNodeList.count(_node); + } + + void setSystemTx(bool _systemTx) const { m_systemTx = _systemTx; } + bool systemTx() const { return m_systemTx; } + + void setBatchId(bcos::protocol::BlockNumber _batchId) const { m_batchId = _batchId; } + bcos::protocol::BlockNumber batchId() const { return m_batchId; } + + void setBatchHash(bcos::crypto::HashType const& _hash) const { m_batchHash = _hash; } + bcos::crypto::HashType const& batchHash() const { return m_batchHash; } + + bool storeToBackend() const { return m_storeToBackend; } + void setStoreToBackend(bool _storeToBackend) const { m_storeToBackend = _storeToBackend; } + +protected: + mutable bcos::bytes m_sender; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + + TxSubmitCallback m_submitCallback; + // the tx has been synced or not + + // the hash of the proposal that the tx batched into + mutable bcos::crypto::HashType m_batchHash; + + // Record the list of nodes containing the transaction and provide related query interfaces. + mutable std::shared_mutex x_knownNodeList; + // Record the node where the transaction exists + mutable bcos::crypto::NodeIDSet m_knownNodeList; + // the number of proposal that the tx batched into + mutable bcos::protocol::BlockNumber m_batchId = {-1}; + + mutable std::atomic_bool m_synced = {false}; + // the tx has been sealed by the leader of not + mutable std::atomic_bool m_sealed = {false}; + // the tx is invalid for verify failed + mutable std::atomic_bool m_invalid = {false}; + // the transaction is the system transaction or not + mutable std::atomic_bool m_systemTx = {false}; + // the transaction has been stored to the storage or not + mutable std::atomic_bool m_storeToBackend = {false}; +}; + +using Transactions = std::vector; +using TransactionsPtr = std::shared_ptr; +using TransactionsConstPtr = std::shared_ptr; +using ConstTransactions = std::vector; +using ConstTransactionsPtr = std::shared_ptr; + +} // namespace bcos::protocol \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionFactory.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionFactory.h" new file mode 100644 index 00000000..5eea9f0b --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionFactory.h" @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2021 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. + * + * @brief factory interface of Transaction + * @file TransactionFactory.h + * @author: yujiechen + * @date: 2021-03-23 + */ +#pragma once +#include "Transaction.h" +#include +namespace bcos +{ +namespace protocol +{ +class TransactionFactory +{ +public: + using Ptr = std::shared_ptr; + TransactionFactory() = default; + virtual ~TransactionFactory() {} + virtual Transaction::Ptr createTransaction(bytesConstRef _txData, bool _checkSig = true) = 0; + virtual Transaction::Ptr createTransaction(bytes const& _txData, bool _checkSig = true) = 0; + virtual Transaction::Ptr createTransaction(int32_t _version, const std::string_view& _to, + bytes const& _input, u256 const& _nonce, int64_t blockLimit, std::string const& _chainId, + std::string const& _groupId, int64_t _importTime) = 0; + virtual Transaction::Ptr createTransaction(int32_t _version, const std::string_view& _to, + bytes const& _input, u256 const& _nonce, int64_t _blockLimit, std::string const& _chainId, + std::string const& _groupId, int64_t _importTime, + bcos::crypto::KeyPairInterface::Ptr keyPair) = 0; + virtual bcos::crypto::CryptoSuite::Ptr cryptoSuite() = 0; +}; +} // namespace protocol +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionMetaData.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionMetaData.h" new file mode 100644 index 00000000..fe0104ab --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionMetaData.h" @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 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. + * + * @brief interface for TransactionMetaData + * @file TransactionMetaData.h + * @author: yujiechen + * @date: 2021-08-20 + */ +#pragma once +#include "Transaction.h" +#include "TransactionSubmitResult.h" +#include +#include + +namespace bcos +{ +namespace protocol +{ +class TransactionMetaData +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + virtual ~TransactionMetaData() = default; + + virtual bcos::crypto::HashType hash() const = 0; + virtual void setHash(bcos::crypto::HashType _hash) = 0; + + virtual std::string_view to() const = 0; + virtual void setTo(std::string _to) = 0; + + virtual uint32_t attribute() const = 0; + virtual void setAttribute(uint32_t attribute) = 0; + + virtual std::string_view source() const = 0; + virtual void setSource(std::string source) = 0; +}; + +using TransactionMetaDataList = std::vector; +using TransactionMetaDataListPtr = std::shared_ptr; +} // namespace protocol +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionReceipt.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionReceipt.h" new file mode 100644 index 00000000..9797773a --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionReceipt.h" @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2021 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. + * + * @brief interface for TransactionReceipt + * @file TransactionReceipt.h + */ +#pragma once + +#include "ProtocolTypeDef.h" +#include +#include +#include + +namespace bcos +{ +namespace protocol +{ +class LogEntry; +class TransactionReceipt +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + explicit TransactionReceipt(bcos::crypto::CryptoSuite::Ptr _cryptoSuite) + : m_cryptoSuite(_cryptoSuite) + {} + + virtual ~TransactionReceipt() {} + + virtual void decode(bytesConstRef _receiptData) = 0; + virtual void encode(bytes& _encodedData) const = 0; + virtual bcos::crypto::HashType hash() const = 0; + virtual int32_t version() const = 0; + virtual u256 gasUsed() const = 0; + virtual std::string_view contractAddress() const = 0; + virtual int32_t status() const = 0; + virtual bytesConstRef output() const = 0; + virtual gsl::span logEntries() const = 0; + virtual bcos::crypto::CryptoSuite::Ptr cryptoSuite() { return m_cryptoSuite; } + virtual BlockNumber blockNumber() const = 0; + + // additional information on transaction execution, no need to be involved in the hash + // calculation + virtual std::string const& message() const = 0; + virtual void setMessage(std::string const& _message) = 0; + virtual void setMessage(std::string&& _message) = 0; + +protected: + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; +}; +using Receipts = std::vector; +using ReceiptsPtr = std::shared_ptr; +using ReceiptsConstPtr = std::shared_ptr; +} // namespace protocol +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionReceiptFactory.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionReceiptFactory.h" new file mode 100644 index 00000000..b74240b9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionReceiptFactory.h" @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 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. + * + * @brief factory for TransactionReceipt + * @file TransactionReceiptFactory.h + * @author: yujiechen + * @date: 2021-03-23 + */ +#pragma once +#include "TransactionReceipt.h" +#include + +namespace bcos +{ +namespace protocol +{ +class LogEntry; +class TransactionReceiptFactory +{ +public: + using Ptr = std::shared_ptr; + TransactionReceiptFactory() = default; + virtual ~TransactionReceiptFactory() {} + virtual TransactionReceipt::Ptr createReceipt(bytesConstRef _receiptData) = 0; + virtual TransactionReceipt::Ptr createReceipt(bytes const& _receiptData) = 0; + virtual TransactionReceipt::Ptr createReceipt(u256 const& _gasUsed, + const std::string_view& _contractAddress, + std::shared_ptr> _logEntries, int32_t _status, bytes const& _output, + BlockNumber _blockNumber) = 0; + + virtual TransactionReceipt::Ptr createReceipt(u256 const& _gasUsed, + const std::string_view& _contractAddress, + std::shared_ptr> _logEntries, int32_t _status, bytes&& _output, + BlockNumber _blockNumber) = 0; + virtual bcos::crypto::CryptoSuite::Ptr cryptoSuite() = 0; +}; +} // namespace protocol +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionSubmitResult.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionSubmitResult.h" new file mode 100644 index 00000000..7f8b5140 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionSubmitResult.h" @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2021 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 TransactionSubmitResult.h + * @author: yujiechen + * @date: 2021-04-07 + */ +#pragma once +#include "ProtocolTypeDef.h" +#include "TransactionReceipt.h" +namespace bcos +{ +namespace protocol +{ +class TransactionSubmitResult +{ +public: + using Ptr = std::shared_ptr; + virtual ~TransactionSubmitResult() = default; + + virtual uint32_t status() const = 0; + virtual void setStatus(uint32_t status) = 0; + + virtual bcos::crypto::HashType txHash() const = 0; + virtual void setTxHash(bcos::crypto::HashType txHash) = 0; + + virtual bcos::crypto::HashType blockHash() const = 0; + virtual void setBlockHash(bcos::crypto::HashType blockHash) = 0; + + virtual int64_t transactionIndex() const = 0; + virtual void setTransactionIndex(int64_t index) = 0; + + virtual NonceType nonce() const = 0; + virtual void setNonce(NonceType nonce) = 0; + + virtual TransactionReceipt::Ptr transactionReceipt() const = 0; + virtual void setTransactionReceipt(TransactionReceipt::Ptr transactionReceipt) = 0; + + virtual std::string const& sender() const = 0; + virtual void setSender(std::string const& _sender) = 0; + + virtual std::string const& to() const = 0; + virtual void setTo(std::string const& _to) = 0; +}; + +using TransactionSubmitResults = std::vector; +using TransactionSubmitResultsPtr = std::shared_ptr; +} // namespace protocol +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionSubmitResultFactory.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionSubmitResultFactory.h" new file mode 100644 index 00000000..747d914a --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/protocol/TransactionSubmitResultFactory.h" @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 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 TransactionSubmitResultFactory.h + * @author: yujiechen + * @date: 2021-05-08 + */ +#pragma once +#include "BlockHeader.h" +#include "Transaction.h" +#include "TransactionReceipt.h" +#include "TransactionSubmitResult.h" + +namespace bcos +{ +namespace protocol +{ +class TransactionSubmitResultFactory +{ +public: + using Ptr = std::shared_ptr; + + virtual ~TransactionSubmitResultFactory() = default; + + virtual TransactionSubmitResult::Ptr createTxSubmitResult() = 0; +}; +} // namespace protocol +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/rpc/HandshakeRequest.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/rpc/HandshakeRequest.h" new file mode 100644 index 00000000..a25593a6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/rpc/HandshakeRequest.h" @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2021 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 HandshakeRequest.h + * @author: yujiechen + * @date 2022-2-29 + */ + +#pragma once +#include +#include +#include +#include +namespace bcos +{ +namespace rpc +{ +class HandshakeRequest +{ +public: + using Ptr = std::shared_ptr; + HandshakeRequest() : m_protocol(std::make_shared()) {} + + HandshakeRequest(bcos::protocol::ProtocolInfo::ConstPtr _protocol) + { + m_protocol = std::const_pointer_cast(_protocol); + } + + virtual std::shared_ptr encode() const + { + Json::Value request; + request["minVersion"] = m_protocol->minVersion(); + request["maxVersion"] = m_protocol->maxVersion(); + request["moduleID"] = m_protocol->protocolModuleID(); + Json::FastWriter fastWriter; + auto requestStr = fastWriter.write(request); + return std::make_shared(requestStr.begin(), requestStr.end()); + } + + virtual bool decode(bcos::bytes const& _data) + { + std::string dataStr(_data.begin(), _data.end()); + try + { + Json::Reader reader; + Json::Value request; + if (!reader.parse(dataStr, request)) + { + BCOS_LOG(WARNING) << LOG_DESC("HandshakeRequest: invalid json object") + << LOG_KV("data", dataStr); + return false; + } + // get the moduleID + auto moduleID = request["moduleID"].asUInt(); + if (moduleID > (uint32_t)(bcos::protocol::ProtocolModuleID::MAX_PROTOCOL_MODULE)) + { + BCOS_LOG(WARNING) << LOG_DESC("HandshakeRequest: invalid moduleID") + << LOG_KV("moduleID", moduleID) << LOG_KV("data", dataStr); + return false; + } + m_protocol->setProtocolModuleID((bcos::protocol::ProtocolModuleID)(moduleID)); + // set minVersion + m_protocol->setMinVersion(request["minVersion"].asUInt()); + // set maxVersion + m_protocol->setMaxVersion(request["maxVersion"].asUInt()); + BCOS_LOG(INFO) << LOG_DESC("HandshakeRequest") + << LOG_KV("module", m_protocol->protocolModuleID()) + << LOG_KV("minVersion", m_protocol->minVersion()) + << LOG_KV("maxVersion", m_protocol->maxVersion()); + return true; + } + catch (std::exception const& e) + { + BCOS_LOG(WARNING) << LOG_DESC("HandshakeRequest decode exception") + << LOG_KV("error", boost::diagnostic_information(e)) + << LOG_KV("data", dataStr); + } + return false; + } + bcos::protocol::ProtocolInfo const& protocol() const { return *m_protocol; } + +private: + bcos::protocol::ProtocolInfo::Ptr m_protocol; +}; +} // namespace rpc +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/rpc/RPCInterface.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/rpc/RPCInterface.h" new file mode 100644 index 00000000..ec8dc90d --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/rpc/RPCInterface.h" @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2021 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 RPCInterface.h + * @author: octopus + * @date 2021-07-01 + */ +#pragma once + +#include "bcos-framework/multigroup/GroupInfo.h" +#include "bcos-framework/protocol/ProtocolTypeDef.h" +#include + +namespace bcos +{ +namespace rpc +{ +enum AMOPNotifyMessageType : int16_t +{ + Unicast, + Broadcast, +}; +class RPCInterface +{ +public: + using Ptr = std::shared_ptr; + + virtual ~RPCInterface() {} + +public: + virtual void start() = 0; + virtual void stop() = 0; + +public: + /** + * @brief: notify blockNumber to rpc + * @param _blockNumber: blockNumber + * @param _callback: resp callback + * @return void + */ + virtual void asyncNotifyBlockNumber(std::string const& _groupID, std::string const& _nodeName, + bcos::protocol::BlockNumber _blockNumber, std::function _callback) = 0; + + /// multi-group manager related interfaces + /** + * @brief receive the latest group information notification from the GroupManagerInterface + * + * @param _groupInfo the latest group information + */ + virtual void asyncNotifyGroupInfo( + bcos::group::GroupInfo::Ptr _groupInfo, std::function) = 0; + + /// AMOP related interfaces + /** + * @brief receive the amop message from the gateway + * + * @param _requestData the AMOP data + * @param _callback the callback + */ + virtual void asyncNotifyAMOPMessage(int16_t _type, std::string const& _topic, + bytesConstRef _requestData, + std::function _callback) = 0; + + // the gateway notify the rpc to re-subscribe the topic when the gateway set-up + virtual void asyncNotifySubscribeTopic( + std::function _callback) = 0; +}; +} // namespace rpc +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/sealer/SealerInterface.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/sealer/SealerInterface.h" new file mode 100644 index 00000000..f2f8e46c --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/sealer/SealerInterface.h" @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for Sealer module + * @file SealerInterface.h + * @author: yujiechen + * @date 2021-05-14 + */ +#pragma once +#include +#include + +namespace bcos +{ +namespace sealer +{ +class SealerInterface +{ +public: + using Ptr = std::shared_ptr; + SealerInterface() = default; + virtual ~SealerInterface() {} + + virtual void start() = 0; + virtual void stop() = 0; + + // interface for the transaction module to notify the sealer seal new proposal + virtual void asyncNotifySealProposal(uint64_t _proposalIndex, uint64_t _proposalEndIndex, + uint64_t _maxTxsToSeal, std::function onRecvResponse) = 0; + // interface to notify the unsealed transactions size to the consensus module + virtual void asyncNoteUnSealedTxsSize( + uint64_t _unsealedTxsSize, std::function _onRecvResponse) = 0; + + // interface for the consensus module to notify the latest block number + virtual void asyncNoteLatestBlockNumber(int64_t _blockNumber) = 0; + // interface for the consensus module to notify reset the sealing transactions + virtual void asyncResetSealing(std::function _onRecvResponse) = 0; +}; +} // namespace sealer +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/security/DataEncryptInterface.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/security/DataEncryptInterface.h" new file mode 100644 index 00000000..9bfefec2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/security/DataEncryptInterface.h" @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2021 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. + */ +/** + * @brief : Encrypt file + * @author: jimmyshi, websterchen + * @date: 2018-12-06 + */ + +#pragma once +#include +#include + +namespace bcos +{ +namespace security +{ +class DataEncryptInterface +{ +public: + using Ptr = std::shared_ptr; + DataEncryptInterface() = default; + virtual ~DataEncryptInterface() = default; + +public: + virtual void init() = 0; + virtual void init(const std::string& dataKey, const bool smCryptoType) = 0; + + // use to decrypt node.key + virtual std::shared_ptr decryptContents(const std::shared_ptr& contents) = 0; + virtual std::shared_ptr decryptFile(const std::string& filename) = 0; + + // use to encrypt/decrypt in rocksdb + virtual std::string encrypt(const std::string& data) = 0; + virtual std::string decrypt(const std::string& data) = 0; +}; + +} // namespace security + +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/storage/Common.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/storage/Common.h" new file mode 100644 index 00000000..62c4efb1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/storage/Common.h" @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2021 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. + * + * @brief interface of Table + * @file Table.h + * @author: xingqiangbai + * @date: 2021-04-07 + */ +#pragma once + +#include "../protocol/ProtocolTypeDef.h" +#include "boost/algorithm/string.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STORAGE_LOG(LEVEL) BCOS_LOG(LEVEL) << "[STORAGE]" + +namespace bcos +{ +namespace storage +{ +enum StorageError +{ + UnknownError = -60000, + TableNotExists, + SystemTableNotExists, + TableExists, + UnknownEntryType, + ReadError, + WriteError, + EmptyStorage, + ReadOnly, + DatabaseError, + DatabaseRetryable, + TryLockFailed, + TimestampMismatch, +}; + +struct Condition +{ + Condition() = default; + ~Condition() = default; + void NE(const std::string& value) { m_conditions.emplace_back(Comparator::NE, value); } + // string compare, "2" > "12" + void GT(const std::string& value) { m_conditions.emplace_back(Comparator::GT, value); } + void GE(const std::string& value) { m_conditions.emplace_back(Comparator::GE, value); } + // string compare, "12" < "2" + void LT(const std::string& value) { m_conditions.emplace_back(Comparator::LT, value); } + void LE(const std::string& value) { m_conditions.emplace_back(Comparator::LE, value); } + void limit(size_t start, size_t count) { m_limit = std::pair(start, count); } + + std::pair getLimit() const { return m_limit; } + + bool isValid(const std::string_view& key) const + { // all conditions must be satisfied + for (auto& cond : m_conditions) + { // conditions should few, so not parallel check for now + switch (cond.cmp) + { + case Comparator::NE: + if (key == cond.value) + { + return false; + } + break; + case Comparator::GT: + if (key <= cond.value) + { + return false; + } + break; + case Comparator::GE: + if (key < cond.value) + { + return false; + } + break; + case Comparator::LT: + if (key >= cond.value) + { + return false; + } + break; + case Comparator::LE: + if (key > cond.value) + { + return false; + } + break; + default: + // undefined Comparator + break; + } + } + return true; + } + + enum class Comparator + { + EQ, + NE, + GT, + GE, + LT, + LE, + }; + struct cond + { + cond(Comparator _cmp, const std::string& _value) : cmp(_cmp), value(_value) {} + Comparator cmp; + std::string value; + // this method only for trace log + std::string toString() const + { + std::string cmpStr; + switch (cmp) + { + case Comparator::EQ: + cmpStr = "EQ"; + break; + case Comparator::NE: + cmpStr = "NE"; + break; + case Comparator::GT: + cmpStr = "GT"; + break; + case Comparator::GE: + cmpStr = "GE"; + break; + case Comparator::LT: + cmpStr = "NE"; + break; + case Comparator::LE: + cmpStr = "LE"; + break; + } + return cmpStr + " " + value; + } + }; + std::vector m_conditions; + std::pair m_limit; + // this method only for trace log + std::string toString() const + { + std::stringstream ss; + ss << "keyCond: "; + for (const auto& cond : m_conditions) + { + ss << cond.toString() << ";"; + } + ss << "limit start: " << m_limit.first << "limit count: " << m_limit.second; + return ss.str(); + } +}; + +class TableInfo +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + TableInfo(std::string name, std::vector fields) + : m_name(std::move(name)), m_fields(std::move(fields)) + { + m_order.reserve(m_fields.size()); + for (size_t i = 0; i < m_fields.size(); ++i) + { + m_order.push_back({m_fields[i], i}); + } + std::sort(m_order.begin(), m_order.end(), + [](auto&& lhs, auto&& rhs) { return std::get<0>(lhs) < std::get<0>(rhs); }); + } + + std::string_view name() const { return m_name; } + + const std::vector& fields() const { return m_fields; } + + size_t fieldIndex(const std::string_view& field) const + { + auto it = std::lower_bound(m_order.begin(), m_order.end(), field, + [](auto&& lhs, auto&& rhs) { return std::get<0>(lhs) < rhs; }); + if (it != m_order.end() && std::get<0>(*it) == field) + { + return std::get<1>(*it); + } + else + { + BOOST_THROW_EXCEPTION( + BCOS_ERROR(-1, std::string("Can't find field: ") + std::string(field))); + } + } + +private: + std::string m_name; + std::vector m_fields; + std::vector> m_order; + +private: + void* operator new(size_t s) { return malloc(s); }; + void operator delete(void* p) { free(p); }; +}; + +} // namespace storage +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/storage/Entry.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/storage/Entry.h" new file mode 100644 index 00000000..7f30766f --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/storage/Entry.h" @@ -0,0 +1,353 @@ +#pragma once + +#include "Common.h" +#include "bcos-crypto/interfaces/crypto/Hash.h" +#include "bcos-framework/protocol/Protocol.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::storage +{ +class Entry +{ +public: + enum Status : int8_t + { + NORMAL = 0, + DELETED = 1, + EMPTY = 2, + MODIFIED = 3, // dirty() can use status + }; + + constexpr static int32_t SMALL_SIZE = 32; + constexpr static int32_t MEDIUM_SIZE = 64; + constexpr static int32_t LARGE_SIZE = INT32_MAX; + + constexpr static int32_t ARCHIVE_FLAG = + boost::archive::no_header | boost::archive::no_codecvt | boost::archive::no_tracking; + + using SBOBuffer = std::array; + + using ValueType = std::variant, + std::vector, std::shared_ptr, + std::shared_ptr>, std::shared_ptr>>; + + Entry() = default; + + explicit Entry(TableInfo::ConstPtr) {} + + Entry(const Entry&) = default; + Entry(Entry&&) noexcept = default; + bcos::storage::Entry& operator=(const Entry&) = default; + bcos::storage::Entry& operator=(Entry&&) noexcept = default; + // auto operator<=>(const Entry&) const = default; + + ~Entry() noexcept {} + + template + void getObject(Out& out) const + { + auto view = get(); + boost::iostreams::stream inputStream( + view.data(), view.size()); + InputArchive archive(inputStream, flag); + + archive >> out; + } + + template + Out getObject() const + { + Out out; + getObject(out); + + return out; + } + + template + void setObject(const In& in) + { + std::string value; + boost::iostreams::stream> outputStream( + value); + OutputArchive archive(outputStream, flag); + + archive << in; + outputStream.flush(); + + setField(0, std::move(value)); + } + + std::string_view get() const { return outputValueView(m_value); } + + std::string_view getField(size_t index) const + { + if (index > 0) + { + BOOST_THROW_EXCEPTION( + BCOS_ERROR(-1, "Get field index: " + boost::lexical_cast(index) + + " failed, index out of range")); + } + + return get(); + } + + template + void setField(size_t index, T&& input) + { + if (index > 0) + { + BOOST_THROW_EXCEPTION( + BCOS_ERROR(-1, "Set field index: " + boost::lexical_cast(index) + + " failed, index out of range")); + } + + set(std::forward(input)); + } + + void set(const char* p) + { + auto view = std::string_view(p, strlen(p)); + m_size = view.size(); + if (view.size() <= SMALL_SIZE) + { + if (m_value.index() != 0) + { + m_value = SBOBuffer(); + } + + std::copy_n(view.data(), view.size(), std::get<0>(m_value).data()); + m_status = MODIFIED; + // m_dirty = true; + } + else + { + set(std::string(view)); + } + } + + template + void set(Input value) + { + auto view = inputValueView(value); + m_size = view.size(); + if (m_size <= SMALL_SIZE) + { + if (m_value.index() != 0) + { + m_value = SBOBuffer(); + } + + std::copy_n(view.data(), view.size(), std::get<0>(m_value).data()); + } + else if (m_size <= MEDIUM_SIZE) + { + m_value = std::move(value); + } + else + { + m_value = std::make_shared(std::move(value)); + } + m_status = MODIFIED; + // m_dirty = true; + } + + template + void setPointer(std::shared_ptr&& value) + { + m_size = value->size(); + m_value = value; + } + + Status status() const { return m_status; } + + void setStatus(Status status) + { + m_status = status; + if (m_status == DELETED) + { + m_size = 0; + m_value = std::string(); + } + // m_dirty = true; + } + + bool dirty() const + { + return (m_status == MODIFIED || m_status == DELETED); + // return m_dirty; + } + // void setDirty(bool dirty) + // { + // if(dirty) + // { + // m_status = MODIFIED; + // } + // else + // { + // m_status = NORMAL; + // } + // // m_dirty = dirty; + // } + + int32_t size() const { return m_size; } + + template + void importFields(std::initializer_list values) + { + if (values.size() != 1) + { + BOOST_THROW_EXCEPTION( + BCOS_ERROR(StorageError::UnknownEntryType, "Import fields not equal to 1")); + } + + setField(0, std::move(*values.begin())); + } + + auto&& exportFields() + { + m_size = 0; + return std::move(m_value); + } + + bool valid() const { return m_status == Status::NORMAL; } + crypto::HashType hash(std::string_view table, std::string_view key, + const bcos::crypto::Hash::Ptr& hashImpl, uint32_t blockVersion) const + { + bcos::crypto::HashType entryHash(0); + if (blockVersion >= (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION) + { + auto anyHasher = hashImpl->hasher(); + + std::visit( + [this, &table, &key, &entryHash](auto& hasher) { + hasher.update(table); + hasher.update(key); + + switch (m_status) + { + case MODIFIED: + { + auto data = get(); + hasher.update(data); + hasher.final(entryHash); + if (c_fileLogLevel <= TRACE) + { + STORAGE_LOG(TRACE) + << "Entry hash, dirty entry: " << table << " | " << toHex(key) + << " | " << toHex(table) << toHex(key) << toHex(data) + << LOG_KV("hash", entryHash.abridged()); + } + break; + } + case DELETED: + { + hasher.final(entryHash); + if (c_fileLogLevel <= TRACE) + { + STORAGE_LOG(TRACE) + << "Entry hash, deleted entry: " << table << " | " << toHex(key) + << LOG_KV("hash", entryHash.abridged()); + } + break; + } + default: + { + STORAGE_LOG(DEBUG) << "Entry hash, clean entry: " << table << " | " + << toHex(key) << " | " << (int)m_status; + break; + } + } + }, + anyHasher); + } + else + { // 3.0.0 + if (m_status == Entry::MODIFIED) + { + auto value = get(); + bcos::bytesConstRef ref((const bcos::byte*)value.data(), value.size()); + entryHash = hashImpl->hash(ref); + if (c_fileLogLevel <= TRACE) + { + STORAGE_LOG(TRACE) + << "Entry Calc hash, dirty entry: " << table << " | " << toHex(key) << " | " + << toHex(value) << LOG_KV("hash", entryHash.abridged()); + } + } + else if (m_status == Entry::DELETED) + { + entryHash = bcos::crypto::HashType(0x1); + if (c_fileLogLevel <= TRACE) + { + STORAGE_LOG(TRACE) << "Entry Calc hash, deleted entry: " << table << " | " + << toHex(key) << LOG_KV("hash", entryHash.abridged()); + } + } + else + { + STORAGE_LOG(DEBUG) << "Entry Calc hash, clean entry: " << table << " | " + << toHex(key) << " | " << (int)m_status; + } + } + return entryHash; + } + +private: + [[nodiscard]] auto outputValueView(const ValueType& value) const -> std::string_view + { + std::string_view view; + std::visit( + [this, &view](auto&& valueInside) { + auto viewRaw = inputValueView(valueInside); + view = std::string_view(viewRaw.data(), m_size); + }, + value); + return view; + } + + template + [[nodiscard]] auto inputValueView(const T& value) const -> std::string_view + { + std::string_view view((const char*)value.data(), value.size()); + return view; + } + + template + [[nodiscard]] auto inputValueView(const std::shared_ptr& value) const -> std::string_view + { + std::string_view view((const char*)value->data(), value->size()); + return view; + } + + ValueType m_value; // should serialization + int32_t m_size = 0; // no need to serialization + Status m_status = Status::EMPTY; // should serialization + // bool m_dirty = false; // no need to serialization +}; + +} // namespace bcos::storage + +namespace boost::serialization +{ +template +void serialize(Archive& ar, std::tuple& t, const unsigned int) +{ + std::apply([&](auto&... element) { ((ar & element), ...); }, t); +} +} // namespace boost::serialization \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/storage/KVStorageHelper.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/storage/KVStorageHelper.h" new file mode 100644 index 00000000..2b93b361 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/storage/KVStorageHelper.h" @@ -0,0 +1,90 @@ +#pragma once + +#include "StorageInterface.h" + +namespace bcos::storage +{ +class KVStorageHelper +{ +public: + KVStorageHelper(StorageInterface::Ptr storage) : m_storage(std::move(storage)) {} + ~KVStorageHelper() {} + + void asyncGet(std::string_view _columnFamily, std::string_view _key, + std::function _callback) + { + m_storage->asyncGetRow(_columnFamily, _key, + [callback = std::move(_callback)]( + Error::UniquePtr&& error, std::optional&& entry) { + if (error) + { + callback(std::move(error), std::string_view()); + return; + } + + if (entry) + { + callback(nullptr, entry->getField(0)); + } + else + { + callback(nullptr, ""); + } + }); + }; + + void asyncGetBatch(std::string_view _columnFamily, + const std::shared_ptr>& _keys, + std::function>)> callback) + { + m_storage->asyncGetRows(_columnFamily, *_keys, + [callback = std::move(callback)]( + Error::UniquePtr&& error, std::vector>&& entries) { + if (error) + { + callback(std::move(error), nullptr); + return; + } + + auto values = std::make_shared>(); + for (auto& it : entries) + { + if (it) + { + values->emplace_back(std::string(it->getField(0))); + } + else + { + values->emplace_back(""); + } + } + + callback(std::move(error), std::move(values)); + }); + } + + template + void asyncPut(std::string_view _columnFamily, std::string_view _key, T _value, + std::function _callback) + { + Entry value; + value.importFields({std::move(_value)}); + + m_storage->asyncSetRow(_columnFamily, _key, std::move(value), std::move(_callback)); + } + + void asyncRemove(std::string_view _columnFamily, std::string_view _key, + std::function _callback) + { + Entry value; + value.setStatus(Entry::DELETED); + + m_storage->asyncSetRow(_columnFamily, _key, std::move(value), std::move(_callback)); + } + StorageInterface::Ptr storage() { return m_storage; } + +private: + StorageInterface::Ptr m_storage; +}; + +} // namespace bcos::storage \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/storage/StorageInterface.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/storage/StorageInterface.h" new file mode 100644 index 00000000..a6dc07d1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/storage/StorageInterface.h" @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2021 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. + * + * @brief interface of StorageInterface + * @file StorageInterface.h + * @author: xingqiangbai + * @date: 2021-04-07 + * @brief interface of StorageInterface + * @file StorageInterface.h + * @author: ancelmo + * @date: 2021-09-07 + */ + +#pragma once +#include "../protocol/ProtocolTypeDef.h" +#include "Common.h" +#include "Entry.h" +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace storage +{ +class Table; +class StorageInterface +{ +public: + static constexpr const char SYS_TABLES[] = "s_tables"; + static constexpr const char SYS_TABLE_VALUE_FIELDS[] = "key_field,value_fields"; + + static TableInfo::ConstPtr getSysTableInfo(std::string_view tableName); + + using Ptr = std::shared_ptr; + + virtual ~StorageInterface() = default; + + virtual void asyncGetPrimaryKeys(std::string_view table, + const std::optional& _condition, + std::function)> _callback) = 0; + + virtual void asyncGetRow(std::string_view table, std::string_view _key, + std::function)> _callback) = 0; + + virtual void asyncGetRows(std::string_view table, + const std::variant, + const gsl::span>& _keys, + std::function>)> _callback) = 0; + + virtual void asyncSetRow(std::string_view table, std::string_view key, Entry entry, + std::function callback) = 0; + + virtual void asyncCreateTable(std::string _tableName, std::string _valueFields, + std::function)> callback); + + virtual void asyncOpenTable(std::string_view tableName, + std::function)> callback); + + virtual void asyncGetTableInfo(std::string_view tableName, + std::function callback); + virtual Error::Ptr setRows( + std::string_view, std::vector, std::vector) + { + throw std::invalid_argument("unimplement method"); + return nullptr; + }; +}; + +class TraverseStorageInterface : public virtual StorageInterface +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + virtual ~TraverseStorageInterface() = default; + + virtual void parallelTraverse(bool onlyDirty, + std::function + callback) const = 0; +}; + +class MergeableStorageInterface : public virtual StorageInterface +{ +public: + using Ptr = std::shared_ptr; + + virtual ~MergeableStorageInterface() = default; + + virtual void merge(bool onlyDirty, const TraverseStorageInterface& source) = 0; +}; + +class TransactionalStorageInterface : public virtual StorageInterface +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + ~TransactionalStorageInterface() override = default; + + virtual void asyncPrepare(const bcos::protocol::TwoPCParams& params, + const TraverseStorageInterface& storage, + std::function callback) = 0; + + virtual void asyncCommit(const bcos::protocol::TwoPCParams& params, + std::function callback) = 0; + + virtual void asyncRollback( + const bcos::protocol::TwoPCParams& params, std::function callback) = 0; +}; + +} // namespace storage +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/storage/Table.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/storage/Table.h" new file mode 100644 index 00000000..9e066cf6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/storage/Table.h" @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2021 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. + * + * @brief interface of Table + * @file Table.h + * @author: xingqiangbai + * @date: 2021-04-07 + */ +#pragma once + +#include "Common.h" +#include "StorageInterface.h" +#include +#include + +namespace bcos +{ +namespace storage +{ +class Table +{ +public: + Table(StorageInterface* _db, TableInfo::ConstPtr _tableInfo) + : m_storage(_db), m_tableInfo(std::move(_tableInfo)) + {} + + Table(const Table&) = default; + Table(Table&&) = default; + Table& operator=(const Table&) = default; + Table& operator=(Table&&) = default; + ~Table() {} + + std::optional getRow(std::string_view _key); + std::vector> getRows( + const std::variant, + const gsl::span>& _keys); + std::vector getPrimaryKeys(const std::optional& _condition); + + void setRow(std::string_view _key, Entry _entry); + + void asyncGetPrimaryKeys(std::optional const& _condition, + std::function)> _callback) noexcept; + + void asyncGetRow(std::string_view _key, + std::function)> _callback) noexcept; + + void asyncGetRows(const std::variant, + const gsl::span>& _keys, + std::function>)> + _callback) noexcept; + + void asyncSetRow( + std::string_view key, Entry entry, std::function callback) noexcept; + + TableInfo::ConstPtr tableInfo() const { return m_tableInfo; } + Entry newEntry() { return Entry(m_tableInfo); } + Entry newDeletedEntry() + { + auto deletedEntry = newEntry(); + deletedEntry.setStatus(Entry::DELETED); + return deletedEntry; + } + +protected: + StorageInterface* m_storage; + TableInfo::ConstPtr m_tableInfo; +}; + +} // namespace storage +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/sync/BlockSyncInterface.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/sync/BlockSyncInterface.h" new file mode 100644 index 00000000..b7073df6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/sync/BlockSyncInterface.h" @@ -0,0 +1,58 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interfaces for block sync + * @file BlockSyncInterface.h + * @author: yujiechen + * @date 2021-05-23 + */ + +#pragma once +#include "../ledger/LedgerConfig.h" +#include +#include +namespace bcos::sync +{ +class BlockSyncInterface +{ +public: + using Ptr = std::shared_ptr; + BlockSyncInterface() = default; + virtual ~BlockSyncInterface() = default; + + virtual void start() = 0; + virtual void stop() = 0; + + // called by the consensus module when commit a new block + virtual void asyncNotifyNewBlock( + bcos::ledger::LedgerConfig::Ptr _ledgerConfig, std::function _onRecv) = 0; + + // 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; + // called by the RPC to get the sync status + virtual void asyncGetSyncInfo(std::function _onGetSyncInfo) = 0; + + virtual void asyncNotifyCommittedIndex( + 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; +}; +} // namespace bcos::sync diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/sync/SyncConfig.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/sync/SyncConfig.h" new file mode 100644 index 00000000..9cb04dbc --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/sync/SyncConfig.h" @@ -0,0 +1,170 @@ +/** + * Copyright (C) 2021 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. + * + * @brief base class for sync config + * @file SyncConfig.h + * @author: yujiechen + * @date 2021-05-25 + */ +#pragma once +#include +#include +#include +namespace bcos +{ +namespace sync +{ +class SyncConfig +{ +public: + using Ptr = std::shared_ptr; + explicit SyncConfig(bcos::crypto::NodeIDPtr _nodeId) + : m_nodeId(_nodeId), + m_consensusNodeList(std::make_shared()), + m_observerNodeList(std::make_shared()), + m_nodeList(std::make_shared()), + m_connectedNodeList(std::make_shared()) + {} + virtual ~SyncConfig() {} + + bcos::crypto::NodeIDPtr nodeID() { return m_nodeId; } + // Note: copy here to ensure thread-safe + virtual bcos::consensus::ConsensusNodeList consensusNodeList() + { + ReadGuard l(x_consensusNodeList); + return *m_consensusNodeList; + } + // Note: only when the consensusNodeList or observerNodeList changed will call this interface + // for perf consideration + virtual void setConsensusNodeList(bcos::consensus::ConsensusNodeList const& _consensusNodeList) + { + { + WriteGuard l(x_consensusNodeList); + *m_consensusNodeList = _consensusNodeList; + } + updateNodeList(); + } + + // Note: only when the consensusNodeList or observerNodeList changed will call this interface + // for perf consideration + virtual void setConsensusNodeList(bcos::consensus::ConsensusNodeList&& _consensusNodeList) + { + { + WriteGuard l(x_consensusNodeList); + *m_consensusNodeList = std::move(_consensusNodeList); + } + updateNodeList(); + } + + // Note: only when the consensusNodeList or observerNodeList changed will call this interface + // for perf consideration + virtual void setObserverList(bcos::consensus::ConsensusNodeList const& _observerNodeList) + { + { + WriteGuard l(x_observerNodeList); + *m_observerNodeList = _observerNodeList; + } + updateNodeList(); + } + + virtual bcos::consensus::ConsensusNodeList observerNodeList() + { + ReadGuard l(x_observerNodeList); + return *m_observerNodeList; + } + + // Note: copy here to remove multithreading issues + virtual bcos::crypto::NodeIDSet connectedNodeList() + { + ReadGuard l(x_connectedNodeList); + return *m_connectedNodeList; + } + + virtual void setConnectedNodeList(bcos::crypto::NodeIDSet const& _connectedNodeList) + { + WriteGuard l(x_connectedNodeList); + *m_connectedNodeList = _connectedNodeList; + } + + virtual void setConnectedNodeList(bcos::crypto::NodeIDSet&& _connectedNodeList) + { + WriteGuard l(x_connectedNodeList); + *m_connectedNodeList = std::move(_connectedNodeList); + } + + virtual bool existsInGroup() + { + ReadGuard l(x_nodeList); + return m_nodeList->count(m_nodeId); + } + + + virtual bool existsInGroup(bcos::crypto::NodeIDPtr _nodeId) + { + ReadGuard l(x_nodeList); + return m_nodeList->count(_nodeId); + } + + virtual bool connected(bcos::crypto::NodeIDPtr _nodeId) + { + ReadGuard l(x_connectedNodeList); + return m_connectedNodeList->count(_nodeId); + } + + bcos::crypto::NodeIDSet groupNodeList() + { + ReadGuard l(x_nodeList); + return *m_nodeList; + } + + virtual void notifyConnectedNodes(bcos::crypto::NodeIDSet const& _connectedNodes, + std::function _onRecvResponse) + { + setConnectedNodeList(_connectedNodes); + if (!_onRecvResponse) + { + return; + } + _onRecvResponse(nullptr); + } + +private: + void updateNodeList() + { + auto nodeList = consensusNodeList() + observerNodeList(); + WriteGuard l(x_nodeList); + m_nodeList->clear(); + for (auto node : nodeList) + { + m_nodeList->insert(node->nodeID()); + } + } + +protected: + bcos::crypto::NodeIDPtr m_nodeId; + bcos::consensus::ConsensusNodeListPtr m_consensusNodeList; + mutable SharedMutex x_consensusNodeList; + + bcos::consensus::ConsensusNodeListPtr m_observerNodeList; + SharedMutex x_observerNodeList; + + bcos::crypto::NodeIDSetPtr m_nodeList; + mutable SharedMutex x_nodeList; + + bcos::crypto::NodeIDSetPtr m_connectedNodeList; + mutable SharedMutex x_connectedNodeList; +}; +} // namespace sync +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/TestPromptFixture.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/TestPromptFixture.h" new file mode 100644 index 00000000..6f5c2f9d --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/TestPromptFixture.h" @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2021 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: TestPromptFixture.h + * fixture class for test case prompt + */ + +#pragma once +#include "bcos-utilities/Common.h" +#include +#include + +namespace bcos +{ +namespace test +{ +class TestPrompt +{ +public: + static TestPrompt& get() + { + static TestPrompt instance; + return instance; + } + TestPrompt(TestPrompt const&) = delete; + void operator=(TestPrompt const&) = delete; + /** + * @brief : init every test-suite by printting the name of cases + * @param _maxTests + */ + void initTest(size_t _maxTests = 1) + { + m_currentTestName = "unknown"; + m_currentTestFileName = std::string(); + m_startTime = utcTime(); + m_currentTestCaseName = boost::unit_test::framework::current_test_case().p_name; + std::cout << "===== BCOS Test Case : " + m_currentTestCaseName << "=====" << std::endl; + m_maxTests = _maxTests; + m_currTest = 0; + }; + + /** + * @brief : release resources when test-suite finished + */ + void finishTest() + { + execTimeName res; + res.first = (double)(utcTime() - m_startTime); + res.second = caseName(); + std::cout << "#### Run " << res.second << " time elapsed: " << res.first << std::endl; + m_execTimeResults.push_back(res); + } + + void setCurrentTestFile(boost::filesystem::path const& _name) { m_currentTestFileName = _name; } + void setCurrentTestName(std::string const& _name) { m_currentTestName = _name; } + std::string const& testName() { return m_currentTestName; } + std::string const& caseName() { return m_currentTestCaseName; } + boost::filesystem::path const& testFile() { return m_currentTestFileName; } + +private: + TestPrompt() {} + size_t m_currTest; + size_t m_maxTests; + std::string m_currentTestName; + std::string m_currentTestCaseName; + boost::filesystem::path m_currentTestFileName; + typedef std::pair execTimeName; + std::vector m_execTimeResults; + int64_t m_startTime; +}; + +class TestPromptFixture +{ +public: + // init test-suite fixture + TestPromptFixture() { TestPrompt::get().initTest(); } + // release test-suite fixture + ~TestPromptFixture() { TestPrompt::get().finishTest(); } +}; +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeFrontService.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeFrontService.h" new file mode 100644 index 00000000..b5e85384 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeFrontService.h" @@ -0,0 +1,234 @@ +/** + * Copyright (C) 2021 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. + * + * @brief fake frontService + * @file FakeFrontService.h + * @author: yujiechen + * @date 2021-05-25 + */ +#pragma once + +#include "../../consensus/ConsensusInterface.h" +#include "../../front/FrontServiceInterface.h" +#include "../../sync/BlockSyncInterface.h" +#include "../../txpool/TxPoolInterface.h" +using namespace bcos; +using namespace bcos::front; +using namespace bcos::crypto; +using namespace bcos::protocol; +using namespace bcos::txpool; +using namespace bcos::consensus; +using namespace bcos::sync; + +namespace bcos +{ +namespace test +{ +class FakeGateWay +{ +public: + using Ptr = std::shared_ptr; + FakeGateWay() = default; + virtual ~FakeGateWay() {} + + void addTxPool(NodeIDPtr _nodeId, TxPoolInterface::Ptr _txpool) + { + m_nodeId2TxPool[_nodeId] = _txpool; + } + + void addSync(NodeIDPtr _nodeId, BlockSyncInterface::Ptr _sync) + { + m_nodeId2Sync[_nodeId] = _sync; + } + + void addConsensusInterface(NodeIDPtr _nodeId, ConsensusInterface::Ptr _consensusInterface) + { + m_nodeId2Consensus[_nodeId] = _consensusInterface; + } + + virtual void asyncSendMessageByNodeID(int _moduleId, NodeIDPtr _fromNode, NodeIDPtr _nodeId, + bytesConstRef _data, uint32_t, CallbackFunc _responseCallback) + { + std::string id; + { + Guard l(m_mutex); + m_uuid++; + id = std::to_string(m_uuid); + if (_responseCallback) + { + m_uuidToCallback[id] = _responseCallback; + } + if (_responseCallback) + { + std::cout << "asyncSendMessageByNodeID, from: " << _fromNode + << _fromNode->shortHex() << ", to: " << _nodeId->shortHex() + << ", id:" << id << std::endl; + } + } + + if (_moduleId == ModuleID::TxsSync && m_nodeId2TxPool.count(_nodeId)) + { + auto txpool = m_nodeId2TxPool[_nodeId]; + txpool->asyncNotifyTxsSyncMessage(nullptr, id, _fromNode, _data, nullptr); + } + if (_moduleId == ModuleID::ConsTxsSync && m_nodeId2TxPool.count(_nodeId)) + { + auto txpool = m_nodeId2TxPool[_nodeId]; + txpool->asyncNotifyTxsSyncMessage(nullptr, id, _fromNode, _data, nullptr); + } + + if (_moduleId == ModuleID::PBFT && m_nodeId2Consensus.count(_nodeId)) + { + auto consensus = m_nodeId2Consensus[_nodeId]; + consensus->asyncNotifyConsensusMessage(nullptr, id, _fromNode, _data, nullptr); + } + if (_moduleId == ModuleID::BlockSync && m_nodeId2Sync.count(_nodeId)) + { + auto sync = m_nodeId2Sync[_nodeId]; + sync->asyncNotifyBlockSyncMessage(nullptr, id, _fromNode, _data, nullptr); + } + } + + virtual void asyncSendResponse(const std::string& _id, int, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _responseData, ReceiveMsgFunc _receiveCallback) + { + CallbackFunc callback = nullptr; + { + Guard l(m_mutex); + if (m_uuidToCallback.count(_id)) + { + callback = m_uuidToCallback[_id]; + m_uuidToCallback.erase(_id); + } + } + if (callback) + { + callback(nullptr, _nodeID, _responseData, "", nullptr); + std::cout << "### find callback, id: " << _id << std::endl; + } + else + { + std::cout << "### not find callback for the id: " << _id << std::endl; + } + _receiveCallback(nullptr); + } + +private: + std::map m_uuidToCallback; + Mutex m_mutex; + + std::map m_nodeId2TxPool; + std::map m_nodeId2Consensus; + std::map m_nodeId2Sync; + std::atomic m_uuid = 0; +}; + +class FakeFrontService : public FrontServiceInterface +{ +public: + using Ptr = std::shared_ptr; + explicit FakeFrontService(NodeIDPtr _nodeId) : m_nodeId(_nodeId) {} + void setGateWay(FakeGateWay::Ptr _gateWay) { m_fakeGateWay = _gateWay; } + + ~FakeFrontService() override {} + + void start() override {} + void stop() override {} + + void asyncGetGroupNodeInfo(GetGroupNodeInfoFunc) override {} + // for gateway: useless here + void onReceiveGroupNodeInfo( + const std::string&, bcos::gateway::GroupNodeInfo::Ptr, ReceiveMsgFunc) override + {} + // for gateway: useless here + void onReceiveMessage(const std::string&, NodeIDPtr, bytesConstRef, ReceiveMsgFunc) override {} + void asyncSendMessageByNodeIDs( + int _moduleId, const std::vector& _nodeIdList, bytesConstRef _data) override + { + for (auto node : _nodeIdList) + { + if (node->data() == m_nodeId->data()) + { + continue; + } + asyncSendMessageByNodeID(_moduleId, node, _data, 0, nullptr); + } + } + + // useless for sync/pbft/txpool + void asyncSendBroadcastMessage(uint16_t, int _moduleId, bytesConstRef _data) override + { + for (auto node : m_nodeIDList) + { + if (node->data() == m_nodeId->data()) + { + continue; + } + asyncSendMessageByNodeID(_moduleId, node, _data, 0, nullptr); + } + } + + // useless for sync/pbft/txpool + void onReceiveBroadcastMessage( + const std::string&, NodeIDPtr, bytesConstRef, ReceiveMsgFunc) override + {} + + void asyncSendResponse(const std::string& _id, int _moduleId, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _responseData, ReceiveMsgFunc _responseCallback) override + { + return m_fakeGateWay->asyncSendResponse( + _id, _moduleId, _nodeID, _responseData, _responseCallback); + } + + void asyncSendMessageByNodeID(int _moduleId, NodeIDPtr _nodeId, bytesConstRef _data, + uint32_t _timeout, CallbackFunc _responseCallback) override + { + m_fakeGateWay->asyncSendMessageByNodeID( + _moduleId, m_nodeId, _nodeId, _data, _timeout, _responseCallback); + + if (m_nodeId2AsyncSendSize.count(_nodeId)) + { + m_nodeId2AsyncSendSize[_nodeId]++; + } + else + { + m_nodeId2AsyncSendSize[_nodeId] = 1; + } + m_totalSendMsgSize++; + } + + size_t getAsyncSendSizeByNodeID(NodeIDPtr _nodeId) + { + if (!m_nodeId2AsyncSendSize.count(_nodeId)) + { + return 0; + } + return m_nodeId2AsyncSendSize[_nodeId]; + } + + size_t totalSendMsgSize() { return m_totalSendMsgSize; } + FakeGateWay::Ptr gateWay() { return m_fakeGateWay; } + + void setNodeIDList(bcos::crypto::NodeIDSet const& _nodeIDList) { m_nodeIDList = _nodeIDList; } + +private: + NodeIDPtr m_nodeId; + std::map m_nodeId2AsyncSendSize; + size_t m_totalSendMsgSize = 0; + FakeGateWay::Ptr m_fakeGateWay; + bcos::crypto::NodeIDSet m_nodeIDList; +}; +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeKVStorage.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeKVStorage.h" new file mode 100644 index 00000000..2278ba3f --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeKVStorage.h" @@ -0,0 +1,111 @@ +/** + * Copyright (C) 2021 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. + * + * @brief fake KVStorage + * @file FakeKVStorage.h + * @author: yujiechen + * @date 2021-09-07 + */ +#pragma once +#include "../../protocol/CommonError.h" +#include "../../storage/StorageInterface.h" +#include "../../storage/KVStorageHelper.h" +#include +#include +#include + +using namespace bcos; +using namespace bcos::storage; +namespace bcos +{ +namespace test +{ +class FakeKVStorage : public bcos::storage::KVStorageHelper +{ +}; + +// class FakeKVStorage : public KVStorageHelper +// { +// public: +// using Ptr = std::shared_ptr; +// FakeKVStorage() = default; +// ~FakeKVStorage() override {} + +// void asyncGet(const std::string_view& _columnFamily, const std::string_view& _key, +// std::function _callback) override +// { +// auto key = getKey(_columnFamily, _key); +// if (!m_key2Data.count(key)) +// { +// _callback(std::make_shared( +// (int32_t)bcos::protocol::StorageErrorCode::NotFound, "key NotFound"), +// ""); +// return; +// } +// _callback(nullptr, m_key2Data[key]); +// } + +// void asyncGetBatch(const std::string_view& _columnFamily, +// const std::shared_ptr>& _keys, +// std::function>&)> +// callback) override +// { +// auto result = std::make_shared>(); +// for (auto const& _key : *_keys) +// { +// auto key = getKey(_columnFamily, _key); +// if (!m_key2Data.count(key)) +// { +// result->push_back(""); +// continue; +// } +// result->push_back(m_key2Data[key]); +// } +// callback(nullptr, result); +// } + +// void asyncPut(const std::string_view& _columnFamily, const std::string_view& _key, +// const std::string_view& _value, std::function _callback) +// override +// { +// auto key = getKey(_columnFamily, _key); +// m_key2Data[key] = _value; +// _callback(nullptr); +// } +// void asyncRemove(const std::string_view& _columnFamily, const std::string_view& _key, +// std::function _callback) override +// { +// auto key = getKey(_columnFamily, _key); +// if (!m_key2Data.count(key)) +// { +// _callback(std::make_shared( +// (int32_t)bcos::protocol::StorageErrorCode::NotFound, "key NotFound")); +// return; +// } +// m_key2Data.erase(key); +// _callback(nullptr); +// } + +// protected: +// std::string getKey(const std::string_view& _columnFamily, const std::string_view& _key) +// { +// std::string columnFamily(_columnFamily.data(), _columnFamily.size()); +// std::string key(_key.data(), _key.size()); +// return columnFamily + "_" + key; +// } +// std::map m_key2Data; +// }; +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeLedger.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeLedger.h" new file mode 100644 index 00000000..f86e1898 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeLedger.h" @@ -0,0 +1,369 @@ +/** + * Copyright (C) 2021 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. + * + * @brief fake ledger + * @file FakeLedger.h + * @author: yujiechen + * @date 2021-05-25 + */ +#pragma once +#include "../../ledger/LedgerConfig.h" +#include "../../ledger/LedgerInterface.h" +#include "../../protocol/Block.h" +#include + +using namespace bcos; +using namespace bcos::ledger; +using namespace bcos::protocol; +using namespace bcos::crypto; +using namespace bcos::consensus; +namespace bcos +{ +namespace test +{ +class FakeLedger : public LedgerInterface, public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + FakeLedger() = default; + FakeLedger(BlockFactory::Ptr _blockFactory, size_t _blockNumber, size_t _txsSize, size_t, + std::vector _sealerList) + : m_blockFactory(_blockFactory), + m_ledgerConfig(std::make_shared()), + m_sealerList(_sealerList) + { + init(_blockNumber, _txsSize, 0); + m_worker = std::make_shared("worker", 1); + } + ~FakeLedger() override + { + if (m_worker) + { + m_worker->stop(); + } + m_hash2Block.clear(); + std::map emptyHash2Block; + m_hash2Block.swap(emptyHash2Block); + + m_txsHashToData.clear(); + std::map emptyTxsData; + m_txsHashToData.swap(emptyTxsData); + } + + FakeLedger( + BlockFactory::Ptr _blockFactory, size_t _blockNumber, size_t _txsSize, size_t _receiptsSize) + : m_blockFactory(_blockFactory), m_ledgerConfig(std::make_shared()) + { + auto sigImpl = m_blockFactory->cryptoSuite()->signatureImpl(); + m_sealerList = fakeSealerList(m_keyPairVec, sigImpl, 4); + init(_blockNumber, _txsSize, _receiptsSize); + m_worker = std::make_shared("worker", 1); + } + + void init(size_t _blockNumber, size_t _txsSize, int64_t _timestamp = utcTime()) + { + auto genesisBlock = init(nullptr, true, 0, 0, 0); + m_ledger.push_back(genesisBlock); + m_hash2Block[genesisBlock->blockHeader()->hash()] = 0; + + auto parentHeader = genesisBlock->blockHeader(); + for (size_t i = 1; i < _blockNumber; i++) + { + auto block = init(parentHeader, true, i, _txsSize, _timestamp); + parentHeader = block->blockHeader(); + m_ledger.push_back(block); + m_hash2Block[block->blockHeader()->hash()] = i; + } + auto latestBlock = m_ledger[m_ledger.size() - 1]; + updateLedgerConfig(latestBlock->blockHeader()); + } + + Block::Ptr init(BlockHeader::Ptr _parentBlockHeader, bool _withHeader, BlockNumber _blockNumber, + size_t _txsSize, int64_t _timestamp = utcTime()) + { + auto block = fakeAndCheckBlock( + m_blockFactory->cryptoSuite(), m_blockFactory, false, _txsSize, 0, false); + if (!_withHeader) + { + return block; + } + ParentInfoList parentInfo; + if (_parentBlockHeader != nullptr) + { + ParentInfo info{_parentBlockHeader->number(), _parentBlockHeader->hash()}; + parentInfo.push_back(info); + } + auto rootHash = + m_blockFactory->cryptoSuite()->hashImpl()->hash(std::to_string(_blockNumber)); + u256 gasUsed = 1232342523; + + SignatureList signatureList; + // fake blockHeader + auto blockHeader = fakeAndTestBlockHeader(m_blockFactory->cryptoSuite(), 0, parentInfo, + rootHash, rootHash, rootHash, _blockNumber, gasUsed, _timestamp, 0, m_sealerList, + bytes(), signatureList, false); + auto sigImpl = m_blockFactory->cryptoSuite()->signatureImpl(); + signatureList = fakeSignatureList(sigImpl, m_keyPairVec, blockHeader->hash()); + blockHeader->setSignatureList(signatureList); + block->setBlockHeader(blockHeader); + return block; + } + + Block::Ptr populateFromHeader(BlockHeader::Ptr _blockHeader) + { + auto block = m_blockFactory->createBlock(); + block->setBlockHeader(_blockHeader); + return block; + } + + void updateLedgerConfig(BlockHeader::Ptr _blockHeader) + { + m_ledgerConfig->setBlockNumber(_blockHeader->number()); + m_ledgerConfig->setHash(_blockHeader->hash()); + } + + void asyncPrewriteBlock(bcos::storage::StorageInterface::Ptr storage, + bcos::protocol::TransactionsPtr, bcos::protocol::Block::ConstPtr block, + std::function callback) override + { + (void)storage; + (void)block; + callback(nullptr); + } + + void asyncPreStoreBlockTxs(bcos::protocol::TransactionsPtr, bcos::protocol::Block::ConstPtr, + std::function _callback) override + { + if (!_callback) + { + return; + } + _callback(nullptr); + } + + // the txpool module use this interface to store txs + void asyncStoreTransactions(std::shared_ptr> _txToStore, + crypto::HashListPtr _txHashList, std::function _onTxStored) override + { + WriteGuard l(x_txsHashToData); + size_t i = 0; + for (auto const& hash : *_txHashList) + { + auto txData = (*_txToStore)[i]; + m_txsHashToData[hash] = txData; + i++; + } + _onTxStored(nullptr); + } + + // maybe sync module or rpc module need this interface to return header/txs/receipts + void asyncGetBlockDataByNumber(BlockNumber _number, int32_t, + std::function _callback) override + { + ReadGuard l(x_ledger); + if (m_ledger.size() <= (size_t)(_number)) + { + _callback(std::make_unique(-1, "block not found"), nullptr); + return; + } + auto block = m_ledger[_number]; + _callback(nullptr, block); + } + + void asyncGetBlockNumberByHash( + crypto::HashType const&, std::function) override + {} + + void asyncGetBlockHashByNumber(BlockNumber _blockNumber, + std::function _onGetBlock) override + { + ReadGuard l(x_ledger); + auto const& hash = m_ledger[_blockNumber]->blockHeader()->hash(); + _onGetBlock(nullptr, hash); + } + + void asyncGetBlockNumber(std::function _onGetBlock) override + { + _onGetBlock(nullptr, m_ledgerConfig->blockNumber()); + } + + void asyncGetBatchTxsByHashList(crypto::HashListPtr _txHashList, bool, + std::function>)> + _onGetTx) override + { + ReadGuard l(x_txsHashToData); + auto txs = std::make_shared(); + for (auto const& hash : *_txHashList) + { + if (m_txsHashToData.count(hash)) + { + auto tx = m_blockFactory->transactionFactory()->createTransaction( + ref(*(m_txsHashToData[hash])), false); + txs->emplace_back(tx); + } + } + _onGetTx(nullptr, txs, nullptr); + } + + void asyncGetTransactionReceiptByHash(crypto::HashType const&, bool, + std::function _onGetTx) + override + { + _onGetTx(nullptr, nullptr, nullptr); + } + + void asyncGetTotalTransactionCount(std::function + _callback) override + { + _callback(nullptr, m_totalTxCount, 0, m_ledgerConfig->blockNumber()); + } + + void asyncGetSystemConfigByKey(std::string_view const& _key, + std::function _onGetConfig) override + { + std::string value = ""; + if (m_systemConfig.count(_key)) + { + value = m_systemConfig[std::string{_key}]; + } + _onGetConfig(nullptr, value, m_ledgerConfig->blockNumber()); + } + + void asyncGetNodeListByType(std::string_view const& _type, + std::function _onGetNodeList) override + { + if (_type == CONSENSUS_SEALER) + { + auto consensusNodes = std::make_shared(); + *consensusNodes = m_ledgerConfig->consensusNodeList(); + _onGetNodeList(nullptr, consensusNodes); + return; + } + if (_type == CONSENSUS_OBSERVER) + { + auto observerNodes = std::make_shared(); + *observerNodes = m_ledgerConfig->observerNodeList(); + _onGetNodeList(nullptr, observerNodes); + return; + } + _onGetNodeList(std::make_unique(-1, "invalid Type"), nullptr); + } + + void asyncGetNonceList(BlockNumber _startNumber, int64_t _offset, + std::function>)> + _onGetList) override + { + if (_startNumber > m_ledgerConfig->blockNumber()) + { + _onGetList(nullptr, nullptr); + } + auto endNumber = std::min(_startNumber + _offset - 1, m_ledgerConfig->blockNumber()); + auto nonceList = std::make_shared>(); + ReadGuard l(x_ledger); + for (auto index = _startNumber; index <= endNumber; index++) + { + auto nonces = m_ledger[index]->nonces(); + nonceList->insert(std::make_pair(index, nonces)); + } + _onGetList(nullptr, nonceList); + } + + void setStatus(bool _normal) { m_statusNormal = _normal; } + void setTotalTxCount(size_t _totalTxCount) { m_totalTxCount = _totalTxCount; } + void setSystemConfig(std::string_view _key, std::string const& _value) + { + m_systemConfig[std::string{_key}] = _value; + } + + void setConsensusNodeList(ConsensusNodeListPtr _consensusNodes) + { + m_ledgerConfig->setConsensusNodeList(*_consensusNodes); + } + void setObserverNodeList(ConsensusNodeListPtr _observerNodes) + { + m_ledgerConfig->setObserverNodeList(*_observerNodes); + } + + LedgerConfig::Ptr ledgerConfig() { return m_ledgerConfig; } + BlockNumber blockNumber() { return m_ledgerConfig->blockNumber(); } + std::vector const& ledgerData() + { + ReadGuard l(x_ledger); + return m_ledger; + } + + size_t storedTxsSize() + { + ReadGuard l(x_txsHashToData); + return m_txsHashToData.size(); + } + // Note thread-safe + std::map txsHashToData() + { + ReadGuard l(x_txsHashToData); + return m_txsHashToData; + } + + std::vector sealerList() { return m_sealerList; } + + // Consensus and block-sync module use this interface to commit block + virtual void asyncCommitBlock(const bcos::protocol::BlockHeader::ConstPtr& _blockHeader, + std::function _onCommitBlock) + { + auto nonConstHeader = std::const_pointer_cast(_blockHeader); + if (nonConstHeader->number() != m_ledgerConfig->blockNumber() + 1) + { + _onCommitBlock(std::make_shared(-1, "invalid block"), nullptr); + return; + } + + auto self = std::weak_ptr(shared_from_this()); + m_worker->enqueue([nonConstHeader, _onCommitBlock, self]() { + auto ledger = self.lock(); + if (!self.lock()) + { + return; + } + WriteGuard l(ledger->x_ledger); + auto block = ledger->populateFromHeader(nonConstHeader); + ledger->m_ledger.push_back(block); + ledger->updateLedgerConfig(nonConstHeader); + _onCommitBlock(nullptr, ledger->m_ledgerConfig); + }); + } + +private: + BlockFactory::Ptr m_blockFactory; + std::vector m_keyPairVec; + LedgerConfig::Ptr m_ledgerConfig; + + size_t m_totalTxCount; + bool m_statusNormal = true; + + std::vector m_ledger; + std::map m_hash2Block; + SharedMutex x_ledger; + + std::map m_txsHashToData; + SharedMutex x_txsHashToData; + + std::map> m_systemConfig; + std::vector m_sealerList; + std::shared_ptr m_worker = nullptr; +}; +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeScheduler.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeScheduler.h" new file mode 100644 index 00000000..15224f6d --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeScheduler.h" @@ -0,0 +1,93 @@ +/** + * Copyright (C) 2021 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. + * + * @brief faker for the Scheduler + * @file FakeScheduler.h + * @author: yujiechen + * @date 2021-09-07 + */ +#pragma once +#include "FakeLedger.h" +#include "../../dispatcher/SchedulerInterface.h" + +using namespace bcos; +using namespace bcos::scheduler; +namespace bcos +{ +namespace test +{ +class FakeScheduler : public SchedulerInterface +{ +public: + using Ptr = std::shared_ptr; + FakeScheduler(FakeLedger::Ptr _ledger, BlockFactory::Ptr _blockFactory) + : m_ledger(_ledger), m_blockFactory(_blockFactory) + {} + ~FakeScheduler() override {} + void executeBlock(bcos::protocol::Block::Ptr _block, bool, + std::function + _callback) noexcept override + { + auto blockHeader = _block->blockHeader(); + if (m_blockFactory) + { + blockHeader = + m_blockFactory->blockHeaderFactory()->populateBlockHeader(_block->blockHeader()); + } + _callback(nullptr, std::move(blockHeader), false); + return; + } + + // Consensus and block-sync module use this interface to commit block + void commitBlock(bcos::protocol::BlockHeader::Ptr _blockHeader, + std::function + _onCommitBlock) noexcept override + { + m_ledger->asyncCommitBlock(_blockHeader, _onCommitBlock); + } + + // by console, query committed committing executing + void status( + std::function) noexcept override + {} + + // by rpc + void call(protocol::Transaction::Ptr, + std::function) noexcept override + {} + + // by executor + void registerExecutor(std::string, bcos::executor::ParallelTransactionExecutorInterface::Ptr, + std::function) noexcept override + {} + + void unregisterExecutor(const std::string&, std::function) noexcept override + {} + + // clear all status + void reset(std::function) noexcept override {} + void getCode(std::string_view, std::function) override {} + void getABI(std::string_view, std::function) override {} + + // for performance, do the things before executing block in executor. + void preExecuteBlock( + bcos::protocol::Block::Ptr, bool, std::function) override{}; + +private: + FakeLedger::Ptr m_ledger; + BlockFactory::Ptr m_blockFactory; +}; +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeSealer.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeSealer.h" new file mode 100644 index 00000000..d8eb1035 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeSealer.h" @@ -0,0 +1,76 @@ +/** + * Copyright (C) 2021 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. + * + * @brief fake sealer + * @file FakeSealer.h + * @author: yujiechen + * @date 2021-05-26 + */ +#pragma once +#include "../../sealer/SealerInterface.h" +#include +#include + +using namespace bcos; +using namespace bcos::sealer; + +namespace bcos +{ +namespace test +{ +class FakeSealer : public SealerInterface +{ +public: + using Ptr = std::shared_ptr; + FakeSealer() = default; + ~FakeSealer() override {} + + void start() override {} + void stop() override {} + + void asyncNotifySealProposal(uint64_t _startIndex, uint64_t _endIndex, uint64_t _maxTxsToSeal, + std::function) override + { + m_proposalStartIndex = _startIndex; + m_proposalEndIndex = _endIndex; + m_maxTxsToSeal = _maxTxsToSeal; + } + + void asyncNoteUnSealedTxsSize( + uint64_t _unSealedTxsSize, std::function _onRecvResponse) override + { + m_unSealedTxsSize = _unSealedTxsSize; + _onRecvResponse(nullptr); + } + + uint64_t unSealedTxsSize() const { return m_unSealedTxsSize; } + + uint64_t proposalStartIndex() const { return m_proposalStartIndex; } + uint64_t proposalEndIndex() const { return m_proposalEndIndex; } + uint64_t maxTxsToSeal() const { return m_maxTxsToSeal; } + + void asyncNoteLatestBlockNumber(int64_t _blockNumber) override { m_blockNumber = _blockNumber; } + int64_t blockNumber() const { return m_blockNumber; } + void asyncResetSealing(std::function) override {} + +private: + std::atomic m_unSealedTxsSize = {0}; + uint64_t m_proposalStartIndex = 0; + uint64_t m_proposalEndIndex = 0; + uint64_t m_maxTxsToSeal = 0; + int64_t m_blockNumber; +}; +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeTxPool.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeTxPool.h" new file mode 100644 index 00000000..ace4c4aa --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/testutils/faker/FakeTxPool.h" @@ -0,0 +1,114 @@ +/** + * Copyright (C) 2021 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. + * + * @brief faker for the TxPool + * @file FakeTxPool.h + * @author: yujiechen + * @date 2021-05-28 + */ +#pragma once +#include "../../protocol/CommonError.h" +#include "../../txpool/TxPoolInterface.h" +#include + +using namespace bcos; +using namespace bcos::txpool; +using namespace bcos::protocol; +using namespace bcos::crypto; +using namespace bcos::consensus; + +namespace bcos +{ +namespace test +{ +class FakeTxPool : public TxPoolInterface +{ +public: + using Ptr = std::shared_ptr; + FakeTxPool() { m_worker = std::make_shared("txpool", 1); } + ~FakeTxPool() override {} + + void start() override {} + void stop() override {} + + // useless for PBFT, maybe needed by RPC + task::Task submitTransaction( + protocol::Transaction::Ptr transaction) override + { + co_return nullptr; + } + void asyncResetTxPool(std::function _callback) override + { + _callback(nullptr); + } + // useless for PBFT, needed by dispatcher to fetch block transactions + void asyncFillBlock(HashListPtr, std::function) override {} + + // useless for PBFT, maybe useful for the ledger + void asyncNotifyBlockResult( + BlockNumber, TransactionSubmitResultsPtr, std::function) override + {} + + // called by frontService to dispatch message + // useless for PBFT + void asyncNotifyTxsSyncMessage(Error::Ptr, std::string const&, NodeIDPtr, bytesConstRef, + std::function) override + {} + + // notify related interfaces: useless for the PBFT module + void notifyConsensusNodeList(ConsensusNodeList const&, std::function) override + {} + // notify related interfaces: useless for the PBFT module + void notifyObserverNodeList(ConsensusNodeList const&, std::function) override + {} + + void notifyConnectedNodes( + bcos::crypto::NodeIDSet const&, std::function) override + {} + void asyncSealTxs(uint64_t, TxsHashSetPtr, + std::function) + override + {} + + void asyncMarkTxs(HashListPtr, bool, bcos::protocol::BlockNumber, bcos::crypto::HashType const&, + std::function) override + {} + + void asyncVerifyBlock(PublicPtr, bytesConstRef const&, + std::function _onVerifyFinished) override + { + m_worker->enqueue([this, _onVerifyFinished]() { + if (m_verifyResult) + { + _onVerifyFinished(nullptr, m_verifyResult); + return; + } + _onVerifyFinished( + std::make_unique(CommonError::TransactionsMissing, "TransactionsMissing"), + m_verifyResult); + }); + } + + void setVerifyResult(bool _verifyResult) { m_verifyResult = _verifyResult; } + bool verifyResult() const { return m_verifyResult; } + + void asyncGetPendingTransactionSize(std::function) override {} + +private: + bool m_verifyResult = true; + std::shared_ptr m_worker = nullptr; +}; +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/txpool/TxPoolInterface.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/txpool/TxPoolInterface.h" new file mode 100644 index 00000000..c11462d6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/txpool/TxPoolInterface.h" @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2021 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 TxPoolInterface.h + * @author: yujiechen + * @date: 2021-04-07 + */ +#pragma once +#include "../consensus/ConsensusNodeInterface.h" +#include "../protocol/Block.h" +#include "../protocol/Transaction.h" +#include "../protocol/TransactionSubmitResult.h" +#include "TxPoolTypeDef.h" +#include +#include + +namespace bcos +{ +namespace txpool +{ +class TxPoolInterface +{ +public: + using Ptr = std::shared_ptr; + + TxPoolInterface() = default; + virtual ~TxPoolInterface() {} + + virtual void start() = 0; + virtual void stop() = 0; + + /** + * @brief submit a transaction + * + * @param transaction the transaction to be submitted + * @return protocol::TransactionSubmitResult::Ptr + */ + virtual task::Task submitTransaction( + protocol::Transaction::Ptr transaction) = 0; + + /** + * @brief fetch transactions from the txpool + * + * @param _txsLimit the max number of the transactions to be fetch + * @param _avoidTxs list of transactions that need to be filtered + * @param _sealCallback after the txpool responds to the sealed txs, the callback is triggered + */ + virtual void asyncSealTxs(uint64_t _txsLimit, TxsHashSetPtr _avoidTxs, + std::function + _sealCallback) = 0; + + virtual void asyncMarkTxs(bcos::crypto::HashListPtr _txsHash, bool _sealedFlag, + bcos::protocol::BlockNumber _batchId, bcos::crypto::HashType const& _batchHash, + std::function _onRecvResponse) = 0; + /** + * @brief verify transactions in Block for the consensus module + * + * @param _generatedNodeID the NodeID of the leader(When missing transactions, need to obtain + * the missing transactions from Leader) + * @param _blocks the block to be verified + * @param _onVerifyFinished callback to be called after the block verification is over + */ + virtual void asyncVerifyBlock(bcos::crypto::PublicPtr _generatedNodeID, + bytesConstRef const& _block, std::function _onVerifyFinished) = 0; + + /** + * @brief The dispatcher obtains the transaction list corresponding to the block from the + * transaction pool + * + * @param _block the block to be filled with transactions + * @param _onBlockFilled callback to be called after the block has been filled + */ + virtual void asyncFillBlock(bcos::crypto::HashListPtr _txsHash, + std::function _onBlockFilled) = 0; + + /** + * @brief After the blockchain is on-chain, the interface is called to notify the transaction + * receipt and other information + * + * @param _onChainBlock Including transaction receipt, on-chain transaction hash list, block + * header information + * @param _onChainCallback + */ + virtual void asyncNotifyBlockResult(bcos::protocol::BlockNumber _blockNumber, + bcos::protocol::TransactionSubmitResultsPtr _txsResult, + std::function _onNotifyFinished) = 0; + + // called by frontService to dispatch message + virtual void asyncNotifyTxsSyncMessage(bcos::Error::Ptr _error, std::string const& _id, + bcos::crypto::NodeIDPtr _nodeID, bytesConstRef _data, + std::function _onRecv) = 0; + virtual void notifyConsensusNodeList( + bcos::consensus::ConsensusNodeList const& _consensusNodeList, + std::function _onRecvResponse) = 0; + virtual void notifyObserverNodeList(bcos::consensus::ConsensusNodeList const& _observerNodeList, + std::function _onRecvResponse) = 0; + + // for RPC to get pending transactions + virtual void asyncGetPendingTransactionSize( + std::function _onGetTxsSize) = 0; + + // notify to reset the txpool when the consensus module startup + virtual void asyncResetTxPool(std::function _onRecvResponse) = 0; + + virtual void notifyConnectedNodes(bcos::crypto::NodeIDSet const& _connectedNodes, + std::function _onResponse) = 0; + + // determine to clean up txs periodically or not + virtual void registerTxsCleanUpSwitch(std::function) {} + virtual void clearAllTxs() {} +}; +} // namespace txpool +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/bcos-framework/txpool/TxPoolTypeDef.h" "b/BFPL\345\243\271/bcos-framework/bcos-framework/txpool/TxPoolTypeDef.h" new file mode 100644 index 00000000..6a5eab8f --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/bcos-framework/txpool/TxPoolTypeDef.h" @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 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 TxPoolTypeDef.h + * @author: yujiechen + * @date: 2021-05-07 + */ +#pragma once +#include "../Common.h" +#include + +#define TXPOOL_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("TXPOOL") + +namespace bcos +{ +namespace txpool +{ +using TxsHashSet = std::set; +using TxsHashSetPtr = std::shared_ptr; +} // namespace txpool +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-framework/test/CMakeLists.txt" new file mode 100644 index 00000000..234ab6b9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/test/CMakeLists.txt" @@ -0,0 +1,29 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of bcos-framework +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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(GLOB_RECURSE SOURCES "*.cpp" "*.h" "*.sol") + +# cmake settings +set(TEST_BINARY_NAME test-bcos-framework) + +add_executable(${TEST_BINARY_NAME} ${SOURCES}) +# target_include_directories(${TEST_BINARY_NAME} PRIVATE bcos-framework) + +find_package(Boost REQUIRED serialization unit_test_framework) + +target_link_libraries(${TEST_BINARY_NAME} ${UTILITIES_TARGET} bcos-framework Boost::serialization Boost::unit_test_framework) +add_test(NAME test-framework WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/test/unittests/interfaces/ConsensusNodeTest.cpp" "b/BFPL\345\243\271/bcos-framework/test/unittests/interfaces/ConsensusNodeTest.cpp" new file mode 100644 index 00000000..45f3b11c --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/test/unittests/interfaces/ConsensusNodeTest.cpp" @@ -0,0 +1,146 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Unit tests for the ConsensusNode + * @file ConsensusNodeTest.cpp + */ +#include "bcos-framework/consensus/ConsensusNode.h" +#include "bcos-framework/consensus/ConsensusNodeInterface.h" +#include "bcos-framework/protocol/Protocol.h" +#include +#include +#include +#include +using namespace bcos; +using namespace bcos::consensus; +using namespace bcos::crypto; +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(ConsensusNodeTest, TestPromptFixture) +BOOST_AUTO_TEST_CASE(testConsensusNode) +{ + // test operator + std::string node1 = "123"; + uint64_t weight = 1; + auto nodeId = std::make_shared(bytes(node1.begin(), node1.end())); + auto consensusNode1 = std::make_shared(nodeId, weight); + + std::string node2 = "1234"; + auto nodeId2 = std::make_shared(bytes(node2.begin(), node2.end())); + auto consensusNode2 = std::make_shared(nodeId2, weight); + + auto nodeId3 = std::make_shared(bytes(node1.begin(), node1.end())); + auto consensusNode3 = std::make_shared(nodeId3, weight); + + // test set + std::set consensusNodeList; + consensusNodeList.insert(consensusNode1); + BOOST_CHECK(consensusNodeList.count(consensusNode1)); + BOOST_CHECK(consensusNodeList.size() == 1); + + consensusNodeList.insert(consensusNode2); + BOOST_CHECK(consensusNodeList.count(consensusNode2)); + BOOST_CHECK(consensusNodeList.size() == 2); + + consensusNodeList.insert(consensusNode3); + BOOST_CHECK(consensusNodeList.count(consensusNode3)); + BOOST_CHECK(consensusNodeList.size() == 2); + + // check map + std::map nodeId2ConsensusNode; + nodeId2ConsensusNode.insert(std::make_pair(consensusNode1->nodeID(), consensusNode1)); + BOOST_CHECK(nodeId2ConsensusNode.count(consensusNode1->nodeID())); + BOOST_CHECK(!nodeId2ConsensusNode.count(consensusNode2->nodeID())); + BOOST_CHECK(nodeId2ConsensusNode.size() == 1); + + nodeId2ConsensusNode.insert(std::make_pair(consensusNode2->nodeID(), consensusNode2)); + BOOST_CHECK(nodeId2ConsensusNode.count(consensusNode2->nodeID())); + BOOST_CHECK(nodeId2ConsensusNode.count(consensusNode3->nodeID())); + BOOST_CHECK(nodeId2ConsensusNode.size() == 2); + nodeId2ConsensusNode.insert(std::make_pair(consensusNode3->nodeID(), consensusNode3)); + BOOST_CHECK(nodeId2ConsensusNode.size() == 2); + + // test NodeIDSet + NodeIDSet nodeIdSet; + nodeIdSet.insert(nodeId); + BOOST_CHECK(nodeIdSet.count(nodeId)); + BOOST_CHECK(nodeIdSet.size() == 1); + + nodeIdSet.insert(nodeId2); + BOOST_CHECK(nodeIdSet.count(nodeId2)); + BOOST_CHECK(nodeIdSet.size() == 2); + + nodeIdSet.insert(nodeId3); + BOOST_CHECK(nodeIdSet.count(nodeId3)); + BOOST_CHECK(nodeIdSet.size() == 2); +} + +BOOST_AUTO_TEST_CASE(test_stringToModuleID) +{ + BOOST_CHECK(bcos::protocol::ModuleID::Raft == protocol::stringToModuleID("raft").value()); + BOOST_CHECK(bcos::protocol::ModuleID::Raft == protocol::stringToModuleID("Raft").value()); + BOOST_CHECK(bcos::protocol::ModuleID::Raft == protocol::stringToModuleID("RAFT").value()); + + BOOST_CHECK(bcos::protocol::ModuleID::PBFT == protocol::stringToModuleID("pbft").value()); + BOOST_CHECK(bcos::protocol::ModuleID::PBFT == protocol::stringToModuleID("Pbft").value()); + BOOST_CHECK(bcos::protocol::ModuleID::PBFT == protocol::stringToModuleID("PBFT").value()); + + BOOST_CHECK(bcos::protocol::ModuleID::AMOP == protocol::stringToModuleID("amop").value()); + BOOST_CHECK(bcos::protocol::ModuleID::AMOP == protocol::stringToModuleID("Amop").value()); + BOOST_CHECK(bcos::protocol::ModuleID::AMOP == protocol::stringToModuleID("AMOP").value()); + + BOOST_CHECK( + bcos::protocol::ModuleID::BlockSync == protocol::stringToModuleID("block_sync").value()); + BOOST_CHECK( + bcos::protocol::ModuleID::BlockSync == protocol::stringToModuleID("Block_sync").value()); + BOOST_CHECK( + bcos::protocol::ModuleID::BlockSync == protocol::stringToModuleID("BLOCK_SYNC").value()); + + BOOST_CHECK( + bcos::protocol::ModuleID::TxsSync == protocol::stringToModuleID("txs_sync").value()); + BOOST_CHECK( + bcos::protocol::ModuleID::TxsSync == protocol::stringToModuleID("Txs_sync").value()); + BOOST_CHECK( + bcos::protocol::ModuleID::TxsSync == protocol::stringToModuleID("TXS_SYNC").value()); + + BOOST_CHECK(bcos::protocol::ModuleID::ConsTxsSync == + protocol::stringToModuleID("cons_txs_sync").value()); + BOOST_CHECK(bcos::protocol::ModuleID::ConsTxsSync == + protocol::stringToModuleID("cons_Txs_sync").value()); + BOOST_CHECK(bcos::protocol::ModuleID::ConsTxsSync == + protocol::stringToModuleID("CONS_TXS_SYNC").value()); + + + BOOST_CHECK(!protocol::stringToModuleID("aa").has_value()); +} + + +BOOST_AUTO_TEST_CASE(test_moduleIDToString) +{ + BOOST_CHECK("raft" == protocol::moduleIDToString(protocol::ModuleID::Raft)); + BOOST_CHECK("pbft" == protocol::moduleIDToString(protocol::ModuleID::PBFT)); + BOOST_CHECK("amop" == protocol::moduleIDToString(protocol::ModuleID::AMOP)); + BOOST_CHECK("block_sync" == protocol::moduleIDToString(protocol::ModuleID::BlockSync)); + BOOST_CHECK("txs_sync" == protocol::moduleIDToString(protocol::ModuleID::TxsSync)); + BOOST_CHECK("light_node" == protocol::moduleIDToString(protocol::ModuleID::LIGHTNODE_GET_BLOCK)); + BOOST_CHECK("cons_txs_sync" == protocol::moduleIDToString(protocol::ModuleID::ConsTxsSync)); +} + + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-framework/test/unittests/interfaces/ExecutorTest.cpp" "b/BFPL\345\243\271/bcos-framework/test/unittests/interfaces/ExecutorTest.cpp" new file mode 100644 index 00000000..eafd38b1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/test/unittests/interfaces/ExecutorTest.cpp" @@ -0,0 +1,60 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Unit tests for the Executor + * @file ExecutorTest.cpp + */ + +#include "bcos-framework/executor/ParallelTransactionExecutorInterface.h" +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::executor; + +namespace bcos +{ +namespace test +{ +struct ExecutorTestFixture +{ + ParallelTransactionExecutorInterface* executor; + + ExecutorTestFixture() : executor(nullptr) {} + + ~ExecutorTestFixture() {} +}; + +BOOST_FIXTURE_TEST_SUITE(ExecutorTest, ExecutorTestFixture) + +BOOST_AUTO_TEST_CASE(ExecutionParams) +{ + shared_ptr p = nullptr; +} + +BOOST_AUTO_TEST_CASE(ExecutionResult) +{ + shared_ptr p = nullptr; +} + +BOOST_AUTO_TEST_CASE(TableHash) +{ + shared_ptr p = nullptr; +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-framework/test/unittests/main/main.cpp" "b/BFPL\345\243\271/bcos-framework/test/unittests/main/main.cpp" new file mode 100644 index 00000000..5029377e --- /dev/null +++ "b/BFPL\345\243\271/bcos-framework/test/unittests/main/main.cpp" @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 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 main.cpp + * @author: yujiechen, jimmyshi + * @date 2021-02-24 + */ +#define BOOST_TEST_MODULE FISCO_BCOS_Tests +#define BOOST_TEST_MAIN + +#include +#include diff --git "a/BFPL\345\243\271/bcos-front/CMakeLists.txt" "b/BFPL\345\243\271/bcos-front/CMakeLists.txt" new file mode 100644 index 00000000..bc86c7d9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-front/CMakeLists.txt" @@ -0,0 +1,40 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for bcos-front +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 bcos-front +# 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. +#------------------------------------------------------------------------------ + +cmake_minimum_required(VERSION 3.10) +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum OS X deployment version") + +aux_source_directory(bcos-front SRCS) + +add_library(${FRONT_TARGET} ${SRCS}) +target_link_libraries(${FRONT_TARGET} PUBLIC bcos-framework ${UTILITIES_TARGET}) + +if (TESTS) + enable_testing() + set(CTEST_OUTPUT_ON_FAILURE TRUE) + add_subdirectory(test) +endif() + +# for doxygen +# include(BuildDocs) + +# for code coverage +if (COVERAGE) + include(Coverage) + config_coverage("front-coverage" "'/usr*' '${CMAKE_CURRENT_SOURCE_DIR}/bcos-cmake-scripts*' '${CMAKE_SOURCE_DIR}/test/mock**' '${CMAKE_SOURCE_DIR}/test/main**'") +endif () diff --git "a/BFPL\345\243\271/bcos-front/bcos-front/Common.h" "b/BFPL\345\243\271/bcos-front/bcos-front/Common.h" new file mode 100644 index 00000000..f88f59ea --- /dev/null +++ "b/BFPL\345\243\271/bcos-front/bcos-front/Common.h" @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021 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: octopus + * @date 2021-04-26 + */ +#pragma once + +#define FRONT_LOG(LEVEL) BCOS_LOG(LEVEL) << "[FrontService]" \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-front/bcos-front/FrontImpl.h" "b/BFPL\345\243\271/bcos-front/bcos-front/FrontImpl.h" new file mode 100644 index 00000000..ff13a9f3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-front/bcos-front/FrontImpl.h" @@ -0,0 +1,191 @@ +#pragma once + +#include "bcos-crypto/interfaces/crypto/KeyFactory.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::front +{ + +#define FRONT_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("Front") + +// clang-format off +struct NoNodeAvailable: public bcos::error::Exception {}; +// clang-format on + +class FrontImpl : public bcos::concepts::front::FrontBase +{ +public: + FrontImpl(bcos::front::FrontServiceInterface::Ptr front, + bcos::gateway::GatewayInterface::Ptr gateway, bcos::crypto::KeyFactory::Ptr keyFactory, + std::string groupID) + : m_front(std::move(front)), + m_gateway(std::move(gateway)), + m_keyFactory(std::move(keyFactory)), + m_groupID(std::move(groupID)) + {} + + task::Task> nodeIDs() + { + struct Awaitable + { + Awaitable(bcos::gateway::GatewayInterface::Ptr& gateway, std::string& groupID) + : m_gateway(gateway), m_groupID(groupID) + {} + + constexpr bool await_ready() const noexcept { return false; } + void await_suspend(CO_STD::coroutine_handle<> handle) + { + bcos::concepts::getRef(m_gateway).asyncGetPeers( + [this, m_handle = handle](Error::Ptr error, const gateway::GatewayInfo::Ptr&, + const gateway::GatewayInfosPtr& peerGatewayInfos) mutable { + if (error) + { + m_result = std::move(error); + } + else + { + if (!peerGatewayInfos->empty()) + { + for (const auto& peerGatewayInfo : *peerGatewayInfos) + { + auto nodeIDInfo = peerGatewayInfo->nodeIDInfo(); + auto it = nodeIDInfo.find(m_groupID); + + if (it != nodeIDInfo.end() && !it->second.empty()) + { + m_result = std::move(it->second); + break; + } + } + } + } + + m_handle.resume(); + }); + } + std::set await_resume() + { + if (std::holds_alternative(m_result)) + { + BOOST_THROW_EXCEPTION(*std::get(m_result)); + } + + if (std::holds_alternative(m_result)) + { + BOOST_THROW_EXCEPTION(NoNodeAvailable{}); + } + + return std::move(std::get>(m_result)); + } + + bcos::gateway::GatewayInterface::Ptr& m_gateway; + std::string& m_groupID; + + std::variant> m_result; + }; + + auto awaitable = Awaitable(m_gateway, m_groupID); + co_return co_await awaitable; + } + + task::Task impl_sendMessageByNodeID(int moduleID, auto const& nodeID, + concepts::serialize::Serializable auto const& request, + concepts::serialize::Serializable auto& response) + { + bcos::bytes requestBuffer; + bcos::concepts::serialize::encode(request, requestBuffer); + + crypto::NodeIDPtr nodeIDPtr; + using NodeIDType = std::decay_t; + if constexpr (std::is_same_v) + { + nodeIDPtr = nodeID; + } + else if constexpr (concepts::bytebuffer::ByteBuffer) + { + auto nodeIDBin = bcos::fromHex(nodeID); + nodeIDPtr = m_keyFactory->createKey(nodeIDBin); + } + else + { + static_assert(!sizeof(nodeID), "Unspported nodeID type!"); + } + + using ResponseType = std::remove_cvref_t; + struct Awaitable + { + constexpr bool await_ready() const { return false; } + + void await_suspend(CO_STD::coroutine_handle::promise_type> handle) + { + FRONT_LOG(DEBUG) << "P2P client send message: " << m_moduleID << " | " + << m_nodeID->hex() << " | " << m_requestBuffer.size(); + bcos::concepts::getRef(m_front).asyncSendMessageByNodeID(m_moduleID, m_nodeID, + bcos::ref(m_requestBuffer), DEFAULT_TIMEOUT, + [m_handle = handle, this](Error::Ptr error, const bcos::crypto::NodeIDPtr&, + bytesConstRef data, const std::string&, + const front::ResponseFunc&) mutable { + FRONT_LOG(DEBUG) << "P2P client receive message: " << m_moduleID << " | " + << m_nodeID->hex() << " | " << data.size() << " | " + << (error ? error->errorCode() : 0) << " | " + << (error ? error->errorMessage() : ""); + if (!error) + { + bcos::concepts::serialize::decode(data, m_response); + } + else + { + m_error = std::move(error); + } + + m_handle.resume(); + }); + } + + constexpr void await_resume() const + { + if (m_error) + { + BOOST_THROW_EXCEPTION(*m_error); + } + } + + // Request params + bcos::front::FrontServiceInterface::Ptr& m_front; + ModuleID m_moduleID = 0; + crypto::NodeIDPtr m_nodeID; + bcos::bytes m_requestBuffer; + + // Response params + Error::Ptr m_error; + ResponseType& m_response; + }; + + auto awaitable = Awaitable{.m_front = m_front, + .m_moduleID = moduleID, + .m_nodeID = std::move(nodeIDPtr), + .m_requestBuffer = std::move(requestBuffer), + .m_response = response}; + co_await awaitable; + } + + task::Task impl_broadcastMessage(NodeType type, ModuleID moduleID, + bcos::concepts::serialize::Serializable auto const& request) + {} + +private: + bcos::front::FrontServiceInterface::Ptr m_front; + bcos::gateway::GatewayInterface::Ptr m_gateway; + bcos::crypto::KeyFactory::Ptr m_keyFactory; + std::string m_groupID; + + constexpr static uint32_t DEFAULT_TIMEOUT = 3000; +}; +} // namespace bcos::front \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-front/bcos-front/FrontMessage.cpp" "b/BFPL\345\243\271/bcos-front/bcos-front/FrontMessage.cpp" new file mode 100644 index 00000000..eba6215d --- /dev/null +++ "b/BFPL\345\243\271/bcos-front/bcos-front/FrontMessage.cpp" @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2021 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 FrontMessage.cpp + * @author: octopus + * @date 2021-04-20 + */ + +#include "FrontMessage.h" +#include + +using namespace bcos; +using namespace front; + +bool FrontMessage::encode(bytes& _buffer) +{ + _buffer.clear(); + + /// moduleID :2 bytes + /// UUID length :1 bytes + /// UUID :UUID length bytes + /// ext :2 bytes + /// payload + + uint16_t moduleID = boost::asio::detail::socket_ops::host_to_network_short(m_moduleID); + uint16_t ext = boost::asio::detail::socket_ops::host_to_network_short(m_ext); + + size_t uuidLength = m_uuid->size(); + // uuid length should not be greater than 256 + if (uuidLength > MAX_MESSAGE_UUID_SIZE) + { + return false; + } + + _buffer.insert(_buffer.end(), (byte*)&moduleID, (byte*)&moduleID + 2); + _buffer.insert(_buffer.end(), (byte*)&uuidLength, (byte*)&uuidLength + 1); + if (uuidLength > 0) + { + _buffer.insert(_buffer.end(), m_uuid->begin(), m_uuid->end()); + } + _buffer.insert(_buffer.end(), (byte*)&ext, (byte*)&ext + 2); + + _buffer.insert(_buffer.end(), m_payload.begin(), m_payload.end()); + return true; +} + +ssize_t FrontMessage::decode(bytesConstRef _buffer) +{ + if (_buffer.size() < HEADER_MIN_LENGTH) + { + return MessageDecodeStatus::MESSAGE_ERROR; + } + + m_uuid->clear(); + m_payload.reset(); + + int32_t offset = 0; + m_moduleID = + boost::asio::detail::socket_ops::network_to_host_short(*((uint16_t*)&_buffer[offset])); + offset += 2; + + uint8_t uuidLength = *((uint8_t*)&_buffer[offset]); + offset += 1; + + if (uuidLength > 0) + { + m_uuid->assign(&_buffer[offset], &_buffer[offset] + uuidLength); + offset += uuidLength; + } + + m_ext = boost::asio::detail::socket_ops::network_to_host_short(*((uint16_t*)&_buffer[offset])); + offset += 2; + + m_payload = _buffer.getCroppedData(offset); + + return MessageDecodeStatus::MESSAGE_COMPLETE; +} diff --git "a/BFPL\345\243\271/bcos-front/bcos-front/FrontMessage.h" "b/BFPL\345\243\271/bcos-front/bcos-front/FrontMessage.h" new file mode 100644 index 00000000..2524c555 --- /dev/null +++ "b/BFPL\345\243\271/bcos-front/bcos-front/FrontMessage.h" @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2021 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 FrontMessage.h + * @author: octopus + * @date 2021-04-20 + */ + +#pragma once + +#include + +namespace bcos +{ +namespace front +{ +enum MessageDecodeStatus +{ + MESSAGE_ERROR = -1, + MESSAGE_COMPLETE = 0, + MESSAGE_INCOMPLETE = 1 +}; + +/// moduleID :2 bytes +/// UUID length :1 bytes +/// UUID :UUID length bytes +/// ext :2 bytes +/// payload +class FrontMessage +{ +public: + using Ptr = std::shared_ptr; + + /// moduleID(2) + UUID length(1) + ext(2) + const static size_t HEADER_MIN_LENGTH = 5; + /// The maximum front uuid length 10M + const static size_t MAX_MESSAGE_UUID_SIZE = 255; + + enum ExtFlag + { + Response = 0x0001, + }; + +public: + FrontMessage() + { + m_uuid = std::make_shared(); + m_payload = bytesConstRef(); + } + + virtual ~FrontMessage() {} + +public: + virtual uint16_t moduleID() { return m_moduleID; } + virtual void setModuleID(uint16_t _moduleID) { m_moduleID = _moduleID; } + + virtual uint16_t ext() { return m_ext; } + virtual void setExt(uint16_t _ext) { m_ext = _ext; } + + virtual std::shared_ptr uuid() { return m_uuid; } + virtual void setUuid(std::shared_ptr _uuid) { m_uuid = _uuid; } + + virtual bytesConstRef payload() { return m_payload; } + virtual void setPayload(bytesConstRef _payload) { m_payload = _payload; } + + virtual void setResponse() { m_ext |= ExtFlag::Response; } + virtual bool isResponse() { return m_ext & ExtFlag::Response; } + +public: + virtual bool encode(bytes& _buffer); + virtual ssize_t decode(bytesConstRef _buffer); + +protected: + uint16_t m_moduleID = 0; + std::shared_ptr m_uuid; + uint16_t m_ext = 0; + bytesConstRef m_payload; ///< message data +}; + +class FrontMessageFactory +{ +public: + using Ptr = std::shared_ptr; + + virtual ~FrontMessageFactory() {} + + virtual FrontMessage::Ptr buildMessage() + { + auto message = std::make_shared(); + return message; + } +}; + +} // namespace front +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-front/bcos-front/FrontService.cpp" "b/BFPL\345\243\271/bcos-front/bcos-front/FrontService.cpp" new file mode 100644 index 00000000..9e1d7375 --- /dev/null +++ "b/BFPL\345\243\271/bcos-front/bcos-front/FrontService.cpp" @@ -0,0 +1,683 @@ +/* + * Copyright (C) 2021 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 FrontService.cpp + * @author: octopus + * @date 2021-04-19 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace front; +using namespace protocol; + +FrontService::FrontService() +{ + m_localProtocol = g_BCOSConfig.protocolInfo(ProtocolModuleID::NodeService); + FRONT_LOG(INFO) << LOG_DESC("FrontService") << LOG_KV("this", this) + << LOG_KV("minVersion", m_localProtocol->minVersion()) + << LOG_KV("maxVersion", m_localProtocol->maxVersion()); +} + +FrontService::~FrontService() +{ + stop(); + FRONT_LOG(INFO) << LOG_DESC("~FrontService") << LOG_KV("this", this); +} + +// check the startup parameters, exception will be thrown if the required +// parameters are not set properly +void FrontService::checkParams() +{ + if (m_groupID.empty()) + { + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment(" FrontService groupID is uninitialized")); + } + + if (!m_nodeID) + { + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment(" FrontService nodeID is uninitialized")); + } + + if (!m_gatewayInterface) + { + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + " FrontService gatewayInterface is uninitialized")); + } + + if (!m_messageFactory) + { + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment(" FrontService messageFactory is uninitialized")); + } + + if (!m_ioService) + { + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment(" FrontService ioService is uninitialized")); + } + + return; +} + +void FrontService::start() +{ + if (m_run) + { + FRONT_LOG(INFO) << LOG_BADGE("start") << LOG_DESC("front service is running") + << LOG_KV("nodeID", m_nodeID->hex()) << LOG_KV("groupID", m_groupID); + return; + } + + checkParams(); + + m_run = true; + + // try to getNodeIDs from gateway + auto self = std::weak_ptr(shared_from_this()); + m_gatewayInterface->asyncGetGroupNodeInfo( + m_groupID, [self](Error::Ptr _error, bcos::gateway::GroupNodeInfo::Ptr _groupNodeInfo) { + if (_error) + { + FRONT_LOG(ERROR) << LOG_BADGE("start") << LOG_DESC("asyncGetGroupNodeInfo error") + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()); + return; + } + FRONT_LOG(INFO) << LOG_BADGE("start") << LOG_DESC("asyncGetGroupNodeInfo callback") + << LOG_KV("node size", + _groupNodeInfo ? _groupNodeInfo->nodeIDList().size() : 0); + auto frontService = self.lock(); + if (frontService) + { + frontService->onReceiveGroupNodeInfo( + frontService->groupID(), _groupNodeInfo, nullptr); + } + }); + + m_frontServiceThread = std::make_shared([=, this]() { + while (m_run) + { + try + { + boost::asio::io_service::work work(*m_ioService); + m_ioService->run(); + } + catch (std::exception& e) + { + FRONT_LOG(WARNING) + << LOG_DESC("IOService") << LOG_KV("error", boost::diagnostic_information(e)); + } + + if (m_run && m_ioService->stopped()) + { + m_ioService->restart(); + } + } + }); + + FRONT_LOG(INFO) << LOG_DESC("start") << LOG_KV("nodeID", m_nodeID->hex()) + << LOG_KV("groupID", m_groupID); + + FRONT_LOG(INFO) << LOG_DESC("register module") + << LOG_KV("count", m_moduleID2MessageDispatcher.size()); + for (const auto& module : m_moduleID2MessageDispatcher) + { + FRONT_LOG(INFO) << LOG_DESC("register module") << LOG_KV("moduleID", module.first); + } + + return; +} +void FrontService::stop() +{ + if (!m_run) + { + return; + } + + m_run = false; + + try + { + { + RecursiveGuard l(x_callback); + for (auto& callback : m_callback) + { + FRONT_LOG(INFO) << LOG_DESC("FrontService stopped, erase the callback") + << LOG_KV("uuid", callback.first); + // cancel the timer + if (callback.second->timeoutHandler) + { + callback.second->timeoutHandler->cancel(); + } + } + // clear the callback + m_callback.clear(); + } + + if (m_ioService) + { + m_ioService->stop(); + } + + if (m_threadPool) + { + m_threadPool->stop(); + } + + if (m_frontServiceThread && m_frontServiceThread->joinable()) + { + m_frontServiceThread->join(); + } + } + catch (const std::exception& e) + { + FRONT_LOG(ERROR) << LOG_DESC("FrontService stop") + << LOG_KV("error", boost::diagnostic_information(e)); + } + + FRONT_LOG(INFO) << LOG_DESC("FrontService stop") + << LOG_KV("nodeID", (m_nodeID ? m_nodeID->hex() : "")) + << LOG_KV("groupID", m_groupID); + + return; +} + +/** + * @brief: get nodeIDs from frontservice + * @param _onGetGroupNodeInfo: response callback + * @return void + */ +void FrontService::asyncGetGroupNodeInfo(GetGroupNodeInfoFunc _onGetGroupNodeInfo) +{ + bcos::gateway::GroupNodeInfo::Ptr groupNodeInfo; + { + Guard l(x_groupNodeInfo); + groupNodeInfo = m_groupNodeInfo; + } + + if (_onGetGroupNodeInfo) + { + if (m_threadPool) + { + m_threadPool->enqueue([_onGetGroupNodeInfo, groupNodeInfo]() { + _onGetGroupNodeInfo(nullptr, groupNodeInfo); + }); + } + else + { + _onGetGroupNodeInfo(nullptr, groupNodeInfo); + } + } + + FRONT_LOG(INFO) << LOG_DESC("asyncGetGroupNodeInfo") + << LOG_KV("nodeIDs.size()", + (groupNodeInfo ? groupNodeInfo->nodeIDList().size() : 0)); + + return; +} + +/** + * @brief: send message + * @param _moduleID: moduleID + * @param _nodeID: the receiver nodeID + * @param _data: send message data + * @param _timeout: timeout, in milliseconds. + * @param _callbackFunc: callback + * @return void + */ +void FrontService::asyncSendMessageByNodeID(int _moduleID, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, uint32_t _timeout, CallbackFunc _callbackFunc) +{ + try + { + static thread_local auto uuid_gen = + boost::uuids::basic_random_generator(); + std::string uuid = boost::uuids::to_string(uuid_gen()); + if (_callbackFunc) + { + auto callback = std::make_shared(); + callback->callbackFunc = _callbackFunc; + + if (_timeout > 0) + { + // create new timer to handle timeout + auto timeoutHandler = std::make_shared( + *m_ioService, boost::posix_time::milliseconds(_timeout)); + + callback->timeoutHandler = timeoutHandler; + auto frontServiceWeakPtr = std::weak_ptr(shared_from_this()); + // callback->startTime = utcSteadyTime(); + timeoutHandler->async_wait( + [frontServiceWeakPtr, _nodeID, uuid](const boost::system::error_code& e) { + auto frontService = frontServiceWeakPtr.lock(); + if (frontService) + { + frontService->onMessageTimeout(e, _nodeID, uuid); + } + }); + } + + addCallback(uuid, callback); + + FRONT_LOG(DEBUG) << LOG_DESC("asyncSendMessageByNodeID") << LOG_KV("groupID", m_groupID) + << LOG_KV("moduleID", _moduleID) << LOG_KV("uuid", uuid) + << LOG_KV("nodeID", _nodeID->hex()) + << LOG_KV("data.size()", _data.size()) << LOG_KV("timeout", _timeout); + } // if (_callback) + + auto self = weak_from_this(); + sendMessage(_moduleID, _nodeID, uuid, _data, false, + [self, _moduleID, _nodeID, uuid](Error::Ptr _error) { + auto front = self.lock(); + if (!front) + { + return; + } + if (_error && (_error->errorCode() != CommonError::SUCCESS)) + { + /* + FRONT_LOG(ERROR) << LOG_BADGE("sendMessage callback") << LOG_KV("uuid", uuid) + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()); + */ + front->handleCallback(_error, bytesConstRef(), uuid, _moduleID, _nodeID); + } + }); + } + catch (std::exception& e) + { + FRONT_LOG(ERROR) << LOG_BADGE("asyncSendMessageByNodeID") + << LOG_KV("error", boost::diagnostic_information(e)); + } +} + +/** + * @brief: send response + * @param _id: the request uuid + * @param _data: message + * @return void + */ +void FrontService::asyncSendResponse(const std::string& _id, int _moduleID, + bcos::crypto::NodeIDPtr _nodeID, bytesConstRef _data, ReceiveMsgFunc _receiveMsgCallback) +{ + sendMessage(_moduleID, _nodeID, _id, _data, true, _receiveMsgCallback); +} + +/** + * @brief: send message to multiple nodes + * @param _moduleID: moduleID + * @param _nodeIDs: the receiver nodeIDs + * @param _data: send message data + * @return void + */ +void FrontService::asyncSendMessageByNodeIDs( + int _moduleID, const crypto::NodeIDs& _nodeIDs, bytesConstRef _data) +{ + for (const auto& _nodeID : _nodeIDs) + { + asyncSendMessageByNodeID(_moduleID, _nodeID, _data, 0, CallbackFunc()); + } +} + +/** + * @brief: send broadcast message + * @param _moduleID: moduleID + * @param _data: send message data + * @return void + */ +void FrontService::asyncSendBroadcastMessage(uint16_t _type, int _moduleID, bytesConstRef _data) +{ + auto message = messageFactory()->buildMessage(); + message->setModuleID(_moduleID); + message->setPayload(_data); + + auto buffer = std::make_shared(); + message->encode(*buffer.get()); + + m_gatewayInterface->asyncSendBroadcastMessage( + _type, m_groupID, _moduleID, m_nodeID, bytesConstRef(buffer->data(), buffer->size())); +} + +/** + * @brief: receive nodeIDs from gateway + * @param _groupID: groupID + * @param _groupNodeInfo: nodeIDs pushed by gateway + * @param _receiveMsgCallback: response callback + * @return void + */ +void FrontService::onReceiveGroupNodeInfo(const std::string& _groupID, + bcos::gateway::GroupNodeInfo::Ptr _groupNodeInfo, ReceiveMsgFunc _receiveMsgCallback) +{ + { + protocolNegotiate(_groupNodeInfo); + Guard l(x_groupNodeInfo); + m_groupNodeInfo = _groupNodeInfo; + } + // To be considered: How to ensure orderly notifications in the pro/max mode + FRONT_LOG(INFO) << LOG_DESC("onReceiveGroupNodeInfo") << LOG_KV("groupID", _groupID) + << LOG_KV("nodeIDs.size()", + (_groupNodeInfo ? _groupNodeInfo->nodeIDList().size() : 0)); + + if (m_threadPool) + { + auto self = std::weak_ptr(shared_from_this()); + m_threadPool->enqueue([self, _groupID, _groupNodeInfo]() { + auto front = self.lock(); + if (!front) + { + return; + } + front->notifyGroupNodeInfo(_groupID, _groupNodeInfo); + }); + } + else + { + notifyGroupNodeInfo(_groupID, _groupNodeInfo); + } + + if (_receiveMsgCallback) + { + _receiveMsgCallback(nullptr); + } +} + +void FrontService::protocolNegotiate(bcos::gateway::GroupNodeInfo::Ptr _groupNodeInfo) +{ + auto const& protocolList = _groupNodeInfo->nodeProtocolList(); + auto const& nodeIDList = _groupNodeInfo->nodeIDList(); + size_t i = 0; + for (auto const& protocol : protocolList) + { + auto mutableProtocol = std::const_pointer_cast(protocol); + // negotiate failed: can't happen unless the code has a bug + if (mutableProtocol->minVersion() > m_localProtocol->maxVersion() || + mutableProtocol->maxVersion() < m_localProtocol->minVersion()) + { + FRONT_LOG(ERROR) << LOG_DESC("protocolNegotiate failed") + << LOG_KV("nodeID", nodeIDList.at(i)) + << LOG_KV("groupID", _groupNodeInfo->groupID()) + << LOG_KV("minVersion", mutableProtocol->minVersion()) + << LOG_KV("maxVersion", mutableProtocol->maxVersion()) + << LOG_KV("supportedMinVersion", m_localProtocol->minVersion()) + << LOG_KV("supportedMaxVersion", m_localProtocol->maxVersion()); + mutableProtocol->setVersion(ProtocolVersion::V0); + i++; + continue; + } + // set the negotiated version + auto version = std::min(m_localProtocol->maxVersion(), mutableProtocol->maxVersion()); + mutableProtocol->setVersion((ProtocolVersion)version); + FRONT_LOG(INFO) << LOG_DESC("protocolNegotiate success") + << LOG_KV("nodeID", nodeIDList.at(i)) + << LOG_KV("groupID", _groupNodeInfo->groupID()) + << LOG_KV("minVersion", mutableProtocol->minVersion()) + << LOG_KV("maxVersion", mutableProtocol->maxVersion()) + << LOG_KV("supportedMinVersion", m_localProtocol->minVersion()) + << LOG_KV("supportedMaxVersion", m_localProtocol->maxVersion()) + << LOG_KV("version", version); + i++; + } +} + +void FrontService::notifyGroupNodeInfo( + const std::string& _groupID, bcos::gateway::GroupNodeInfo::Ptr _groupNodeInfo) +{ + Guard l(x_notifierLock); + for (const auto& entry : m_module2GroupNodeInfoNotifier) + { + auto moduleID = entry.first; + entry.second(_groupNodeInfo, [_groupID, moduleID](Error::Ptr _error) { + if (_error) + { + FRONT_LOG(ERROR) << LOG_DESC("onReceiveGroupNodeInfo dispather failed") + << LOG_KV("groupID", _groupID) << LOG_KV("moduleID", moduleID); + } + }); + } +} + +void FrontService::handleCallback(bcos::Error::Ptr _error, bytesConstRef _payLoad, + std::string const& _uuid, int _moduleID, bcos::crypto::NodeIDPtr _nodeID) +{ + // callback message + auto callback = getAndRemoveCallback(_uuid); + if (!callback) + { + return; + } + auto frontServiceWeakPtr = std::weak_ptr(shared_from_this()); + auto respFunc = [frontServiceWeakPtr, _moduleID, _nodeID, _uuid](bytesConstRef _data) { + auto frontService = frontServiceWeakPtr.lock(); + if (frontService) + { + frontService->sendMessage( + _moduleID, _nodeID, _uuid, _data, true, [_uuid](Error::Ptr _error) { + if (_error && (_error->errorCode() != CommonError::SUCCESS)) + { + FRONT_LOG(ERROR) + << LOG_BADGE("onReceiveMessage sendMessage callback") + << LOG_KV("uuid", _uuid) << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()); + } + }); + } + }; + // cancel the timer first + if (callback->timeoutHandler) + { + callback->timeoutHandler->cancel(); + } + + if (m_threadPool) + { + // construct shared_ptr from message->payload() first for + // thead safe + std::shared_ptr buffer = std::make_shared(_payLoad.begin(), _payLoad.end()); + m_threadPool->enqueue([_uuid, _error, callback, buffer, _nodeID, respFunc] { + callback->callbackFunc( + _error, _nodeID, bytesConstRef(buffer->data(), buffer->size()), _uuid, respFunc); + }); + } + else + { + callback->callbackFunc(_error, _nodeID, _payLoad, _uuid, respFunc); + } +} +/** + * @brief: receive message from gateway + * @param _groupID: groupID + * @param _nodeID: the node send the message + * @param _data: received message data + * @param _receiveMsgCallback: response callback + * @return void + */ +void FrontService::onReceiveMessage(const std::string& _groupID, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, ReceiveMsgFunc _receiveMsgCallback) +{ + try + { + auto message = messageFactory()->buildMessage(); + auto ret = message->decode(_data); + if (MessageDecodeStatus::MESSAGE_COMPLETE != ret) + { + FRONT_LOG(ERROR) << LOG_DESC("onReceiveMessage") << LOG_DESC("illegal message") + << LOG_KV("length", _data.size()) << LOG_KV("nodeID", m_nodeID->hex()); + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment("illegal message")); + } + + int moduleID = message->moduleID(); + int ext = message->ext(); + std::string uuid = std::string(message->uuid()->begin(), message->uuid()->end()); + + FRONT_LOG(TRACE) << LOG_BADGE("onReceiveMessage") << LOG_KV("moduleID", moduleID) + << LOG_KV("uuid", uuid) << LOG_KV("ext", ext) + << LOG_KV("groupID", _groupID) << LOG_KV("nodeID", _nodeID->hex()) + << LOG_KV("length", _data.size()); + + if (message->isResponse()) + { + handleCallback(nullptr, message->payload(), uuid, moduleID, _nodeID); + } + else + { + auto it = m_moduleID2MessageDispatcher.find(moduleID); + if (it != m_moduleID2MessageDispatcher.end()) + { + if (m_threadPool) + { + auto callback = it->second; + // construct shared_ptr from message->payload() first for + // thead safe + std::shared_ptr buffer = std::make_shared( + message->payload().begin(), message->payload().end()); + m_threadPool->enqueue([uuid, callback, buffer, message, _nodeID] { + callback(_nodeID, uuid, bytesConstRef(buffer->data(), buffer->size())); + }); + } + else + { + it->second(_nodeID, uuid, message->payload()); + } + } + else + { + FRONT_LOG(WARNING) << LOG_DESC("unable find the register module message dispather") + << LOG_KV("moduleID", moduleID) << LOG_KV("uuid", uuid); + } + } + } + catch (const std::exception& e) + { + FRONT_LOG(ERROR) << "onReceiveMessage" << LOG_KV("error", boost::diagnostic_information(e)); + } + + if (_receiveMsgCallback) + { + if (m_threadPool) + { + m_threadPool->enqueue([_receiveMsgCallback]() { _receiveMsgCallback(nullptr); }); + } + else + { + _receiveMsgCallback(nullptr); + } + } +} + +/** + * @brief: receive broadcast message from gateway + * @param _groupID: groupID + * @param _nodeID: the node send the message + * @param _data: received message data + * @param _receiveMsgCallback: response callback + * @return void + */ +void FrontService::onReceiveBroadcastMessage(const std::string& _groupID, + bcos::crypto::NodeIDPtr _nodeID, bytesConstRef _data, ReceiveMsgFunc _receiveMsgCallback) +{ + onReceiveMessage(_groupID, _nodeID, _data, _receiveMsgCallback); +} + +/** + * @brief: send message + * @param _moduleID: moduleID + * @param _nodeID: the node the message sent to + * @param _uuid: uuid identify this message + * @param _data: send data payload + * @param isResponse: if send response message + * @param _receiveMsgCallback: response callback + * @return void + */ +void FrontService::sendMessage(int _moduleID, bcos::crypto::NodeIDPtr _nodeID, + const std::string& _uuid, bytesConstRef _data, bool isResponse, + ReceiveMsgFunc _receiveMsgCallback) +{ + auto message = messageFactory()->buildMessage(); + message->setModuleID(_moduleID); + message->setUuid(std::make_shared(_uuid.begin(), _uuid.end())); + message->setPayload(_data); + if (isResponse) + { + message->setResponse(); + } + + auto buffer = std::make_shared(); + message->encode(*buffer.get()); + + // call gateway interface to send the message + m_gatewayInterface->asyncSendMessageByNodeID(m_groupID, _moduleID, m_nodeID, _nodeID, + bytesConstRef(buffer->data(), buffer->size()), [_receiveMsgCallback](Error::Ptr _error) { + if (_receiveMsgCallback) + { + _receiveMsgCallback(_error); + } + }); +} + +/** + * @brief: handle message timeout + * @param _error: boost error code + * @param _uuid: message uuid + * @return void + */ +void FrontService::onMessageTimeout(const boost::system::error_code& _error, + bcos::crypto::NodeIDPtr _nodeID, const std::string& _uuid) +{ + if (_error) + { + return; + } + + try + { + Callback::Ptr callback = getAndRemoveCallback(_uuid); + if (callback) + { + auto errorPtr = std::make_shared(CommonError::TIMEOUT, "timeout"); + if (m_threadPool) + { + m_threadPool->enqueue([_uuid, _nodeID, callback, errorPtr]() { + callback->callbackFunc(errorPtr, _nodeID, bytesConstRef(), _uuid, + std::function()); + }); + } + else + { + callback->callbackFunc(errorPtr, _nodeID, bytesConstRef(), _uuid, + std::function()); + } + } + + FRONT_LOG(WARNING) << LOG_BADGE("onMessageTimeout") << LOG_KV("uuid", _uuid); + } + catch (std::exception& e) + { + FRONT_LOG(ERROR) << "onMessageTimeout" << LOG_KV("uuid", _uuid) + << LOG_KV("error", boost::diagnostic_information(e)); + } +} diff --git "a/BFPL\345\243\271/bcos-front/bcos-front/FrontService.h" "b/BFPL\345\243\271/bcos-front/bcos-front/FrontService.h" new file mode 100644 index 00000000..94ca3528 --- /dev/null +++ "b/BFPL\345\243\271/bcos-front/bcos-front/FrontService.h" @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2021 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 FrontService.h + * @author: octopus + * @date 2021-04-19 + */ + +#pragma once +#include "FrontMessage.h" +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace front +{ +class FrontService : public FrontServiceInterface, public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + + FrontService(); + FrontService(const FrontService&) = delete; + FrontService(FrontService&&) = delete; + virtual ~FrontService(); + + FrontService& operator=(const FrontService&) = delete; + FrontService& operator=(FrontService&&) = delete; + +public: + void start() override; + void stop() override; + + // check the startup parameters, if the required parameters are not set + // properly, exception will be thrown + void checkParams(); + +public: + /** + * @brief: get nodeIDs from frontservice + * @param _onGetGroupNodeInfoFunc: response callback + * @return void + */ + void asyncGetGroupNodeInfo(GetGroupNodeInfoFunc _onGetGroupNodeInfoFunc) override; + /** + * @brief: send message + * @param _moduleID: moduleID + * @param _nodeID: the receiver nodeID + * @param _data: send message data + * @param _timeout: timeout, in milliseconds. + * @param _callbackFunc: callback + * @return void + */ + void asyncSendMessageByNodeID(int _moduleID, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, uint32_t _timeout, CallbackFunc _callbackFunc) override; + + /** + * @brief: send response + * @param _id: the request id + * @param _moduleID: moduleID + * @param _nodeID: the receiver nodeID + * @param _data: message + * @return void + */ + void asyncSendResponse(const std::string& _id, int _moduleID, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, ReceiveMsgFunc _receiveMsgCallback) override; + + /** + * @brief: send message to multiple nodes + * @param _moduleID: moduleID + * @param _nodeIDs: the receiver nodeIDs + * @param _data: send message data + * @return void + */ + void asyncSendMessageByNodeIDs( + int _moduleID, const crypto::NodeIDs& _nodeIDs, bytesConstRef _data) override; + + /** + * @brief: send broadcast message + * @param _moduleID: moduleID + * @param _data: send message data + * @return void + */ + void asyncSendBroadcastMessage(uint16_t _type, int _moduleID, bytesConstRef _data) override; + + /** + * @brief: receive nodeIDs from gateway + * @param _groupID: groupID + * @param _nodeIDs: nodeIDs pushed by gateway + * @param _receiveMsgCallback: response callback + * @return void + */ + void onReceiveGroupNodeInfo(const std::string& _groupID, + bcos::gateway::GroupNodeInfo::Ptr _groupNodeInfo, + ReceiveMsgFunc _receiveMsgCallback) override; + + /** + * @brief: receive message from gateway + * @param _groupID: groupID + * @param _nodeID: the node send the message + * @param _data: received message data + * @param _receiveMsgCallback: response callback + * @return void + */ + void onReceiveMessage(const std::string& _groupID, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, ReceiveMsgFunc _receiveMsgCallback) override; + + /** + * @brief: receive broadcast message from gateway + * @param _groupID: groupID + * @param _nodeID: the node send the message + * @param _data: received message data + * @param _receiveMsgCallback: response callback + * @return void + */ + void onReceiveBroadcastMessage(const std::string& _groupID, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, ReceiveMsgFunc _receiveMsgCallback) override; + + /** + * @brief: send message + * @param _moduleID: moduleID + * @param _nodeID: the node the message sent to + * @param _uuid: uuid identify this message + * @param _data: send data payload + * @param isResponse: if send response message + * @param _receiveMsgCallback: response callback + * @return void + */ + void sendMessage(int _moduleID, bcos::crypto::NodeIDPtr _nodeID, const std::string& _uuid, + bytesConstRef _data, bool isResponse, ReceiveMsgFunc _receiveMsgCallback); + + /** + * @brief: handle message timeout + * @param _error: boost error code + * @param _uuid: message uuid + * @return void + */ + void onMessageTimeout(const boost::system::error_code& _error, bcos::crypto::NodeIDPtr _nodeID, + const std::string& _uuid); + +public: + FrontMessageFactory::Ptr messageFactory() const { return m_messageFactory; } + + void setMessageFactory(FrontMessageFactory::Ptr _messageFactory) + { + m_messageFactory = std::move(_messageFactory); + } + + bcos::crypto::NodeIDPtr nodeID() const { return m_nodeID; } + void setNodeID(bcos::crypto::NodeIDPtr _nodeID) { m_nodeID = _nodeID; } + std::string groupID() const { return m_groupID; } + void setGroupID(const std::string& _groupID) { m_groupID = _groupID; } + + std::shared_ptr gatewayInterface() { return m_gatewayInterface; } + + void setGatewayInterface(std::shared_ptr _gatewayInterface) + { + m_gatewayInterface = _gatewayInterface; + } + + std::shared_ptr ioService() const { return m_ioService; } + void setIoService(std::shared_ptr _ioService) + { + m_ioService = _ioService; + } + + bcos::ThreadPool::Ptr threadPool() const { return m_threadPool; } + void setThreadPool(bcos::ThreadPool::Ptr _threadPool) { m_threadPool = _threadPool; } + + // register message _dispatcher for module + void registerModuleMessageDispatcher(int _moduleID, + std::function + _dispatcher) + { + m_moduleID2MessageDispatcher[_moduleID] = _dispatcher; + } + + // only for ut + std::unordered_map> + moduleID2MessageDispatcher() const + { + return m_moduleID2MessageDispatcher; + } + + // only for ut + std::unordered_map> + module2GroupNodeInfoNotifier() const + { + return m_module2GroupNodeInfoNotifier; + } + // register nodeIDs _dispatcher for module + void registerGroupNodeInfoNotification(int _moduleID, + std::function + _dispatcher) + { + m_module2GroupNodeInfoNotifier[_moduleID] = _dispatcher; + } + +public: + struct Callback : public std::enable_shared_from_this + { + using Ptr = std::shared_ptr; + uint64_t startTime = utcSteadyTime(); + CallbackFunc callbackFunc; + std::shared_ptr timeoutHandler; + }; + // lock m_callback + mutable bcos::RecursiveMutex x_callback; + // uuid to callback + std::unordered_map m_callback; + + // only for ut + std::unordered_map callback() const { return m_callback; } + + Callback::Ptr getAndRemoveCallback(const std::string& _uuid) + { + Callback::Ptr callback = nullptr; + + { + RecursiveGuard l(x_callback); + auto it = m_callback.find(_uuid); + if (it != m_callback.end()) + { + callback = it->second; + m_callback.erase(it); + } + } + + return callback; + } + + void addCallback(const std::string& _uuid, Callback::Ptr _callback) + { + RecursiveGuard l(x_callback); + m_callback[_uuid] = _callback; + } + +protected: + virtual void handleCallback(bcos::Error::Ptr _error, bytesConstRef _payLoad, + std::string const& _uuid, int _moduleID, bcos::crypto::NodeIDPtr _nodeID); + void notifyGroupNodeInfo( + const std::string& _groupID, bcos::gateway::GroupNodeInfo::Ptr _groupNodeInfo); + + virtual void protocolNegotiate(bcos::gateway::GroupNodeInfo::Ptr _groupNodeInfo); + +private: + // thread pool + bcos::ThreadPool::Ptr m_threadPool; + // timer + std::shared_ptr m_ioService; + /// gateway interface + std::shared_ptr m_gatewayInterface; + + FrontMessageFactory::Ptr m_messageFactory; + + std::unordered_map> + m_moduleID2MessageDispatcher; + + std::unordered_map> + m_module2GroupNodeInfoNotifier; + + // service is running or not + bool m_run = false; + // + std::shared_ptr m_frontServiceThread; + // NodeID + bcos::crypto::NodeIDPtr m_nodeID; + // GroupID + std::string m_groupID; + // lock notifyNodeIDs + mutable bcos::Mutex x_notifierLock; + + // groupNodeInfo pushed by the gateway + bcos::gateway::GroupNodeInfo::Ptr m_groupNodeInfo = nullptr; + // lock m_nodeID + mutable bcos::Mutex x_groupNodeInfo; + + // the local protocolInfo + // Note: frontService is responsible for version negotiation of blockchain nodes + bcos::protocol::ProtocolInfo::ConstPtr m_localProtocol; +}; +} // namespace front +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-front/bcos-front/FrontServiceFactory.cpp" "b/BFPL\345\243\271/bcos-front/bcos-front/FrontServiceFactory.cpp" new file mode 100644 index 00000000..b24578e7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-front/bcos-front/FrontServiceFactory.cpp" @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2021 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 FrontServiceFactory.cpp + * @author: octopus + * @date 2021-05-20 + */ + +#include +#include +#include +#include + +using namespace bcos; +using namespace front; + +FrontService::Ptr FrontServiceFactory::buildFrontService( + const std::string& _groupID, const bcos::crypto::NodeIDPtr _nodeID) +{ + if (!m_gatewayInterface) + { + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "FrontServiceFactory::init gateway is uninitialized")); + } + + /* + if (!m_threadPool) { + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment( + "FrontServiceFactory::init threadPool is uninitialized")); + } + */ + + FRONT_LOG(INFO) << LOG_DESC("FrontServiceFactory::buildFrontService") + << LOG_KV("groupID", _groupID) << LOG_KV("nodeID", _nodeID->hex()); + + auto factory = std::make_shared(); + auto ioService = std::make_shared(); + auto frontService = std::make_shared(); + + frontService->setMessageFactory(factory); + frontService->setGroupID(_groupID); + frontService->setNodeID(_nodeID); + frontService->setIoService(ioService); + frontService->setGatewayInterface(m_gatewayInterface); + frontService->setThreadPool(m_threadPool); + + return frontService; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-front/bcos-front/FrontServiceFactory.h" "b/BFPL\345\243\271/bcos-front/bcos-front/FrontServiceFactory.h" new file mode 100644 index 00000000..71d6b1e7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-front/bcos-front/FrontServiceFactory.h" @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2021 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 FrontServiceFactory.h + * @author: octopus + * @date 2021-05-20 + */ + +#pragma once + +#include +#include +#include + +namespace bcos +{ +namespace front +{ +class FrontServiceFactory +{ +public: + using Ptr = std::shared_ptr; + +public: + FrontService::Ptr buildFrontService( + const std::string& _groupID, const bcos::crypto::NodeIDPtr _nodeID); + +public: + void setGatewayInterface(bcos::gateway::GatewayInterface::Ptr _gatewayInterface) + { + m_gatewayInterface = _gatewayInterface; + } + + std::shared_ptr threadPool() { return m_threadPool; } + void setThreadPool(std::shared_ptr _threadPool) + { + m_threadPool = _threadPool; + } + +private: + // gatewayInterface + bcos::gateway::GatewayInterface::Ptr m_gatewayInterface; + // threadpool + std::shared_ptr m_threadPool; +}; + +} // namespace front +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-front/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-front/test/CMakeLists.txt" new file mode 100644 index 00000000..e7b9f8c5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-front/test/CMakeLists.txt" @@ -0,0 +1,27 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of bcos-front +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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(GLOB_RECURSE SOURCES "*.cpp" "*.h" "*.sol") + +# cmake settings +set(TEST_BINARY_NAME test-bcos-front) + +add_executable(${TEST_BINARY_NAME} ${SOURCES}) +target_include_directories(${TEST_BINARY_NAME} PRIVATE .) +find_package(Boost REQUIRED unit_test_framework) +target_link_libraries(${TEST_BINARY_NAME} PUBLIC ${FRONT_TARGET} ${TARS_PROTOCOL_TARGET} Boost::unit_test_framework) +add_test(NAME test-front WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-front/test/unittests/FakeGateway.cpp" "b/BFPL\345\243\271/bcos-front/test/unittests/FakeGateway.cpp" new file mode 100644 index 00000000..68155e4e --- /dev/null +++ "b/BFPL\345\243\271/bcos-front/test/unittests/FakeGateway.cpp" @@ -0,0 +1,81 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Gateway fake implementation + * @file FakeGateway.cpp + * @author: octopus + * @date 2021-04-27 + */ + +#include "FakeGateway.h" +#include + +using namespace bcos; +using namespace bcos::front; +using namespace bcos::front::test; +using namespace bcos::gateway; +/** + * @brief: send message to a single node + * @param _groupID: groupID + * @param _moduleID: moduleID + * @param _nodeID: the receiver nodeID + * @param _payload: message content + * @param _options: option parameters + * @param _callback: callback + * @return void + */ +void FakeGateway::asyncSendMessageByNodeID(const std::string& _groupID, int, + bcos::crypto::NodeIDPtr _srcNodeID, bcos::crypto::NodeIDPtr _dstNodeID, bytesConstRef _payload, + bcos::gateway::ErrorRespFunc _errorRespFunc) +{ + m_frontService->onReceiveMessage(_groupID, _dstNodeID, _payload, _errorRespFunc); + + FRONT_LOG(DEBUG) << "[FakeGateway] asyncSendMessageByNodeID" << LOG_KV("groupID", _groupID) + << LOG_KV("nodeID", _srcNodeID->hex()) << LOG_KV("nodeID", _dstNodeID->hex()); +} + +/** + * @brief: send message to multiple nodes + * @param _groupID: groupID + * @param _moduleID: moduleID + * @param _nodeIDs: the receiver nodeIDs + * @param _payload: message content + * @return void + */ +void FakeGateway::asyncSendMessageByNodeIDs(const std::string& _groupID, int, + bcos::crypto::NodeIDPtr _srcNodeID, const bcos::crypto::NodeIDs& _dstNodeIDs, + bytesConstRef _payload) +{ + if (!_dstNodeIDs.empty()) + { + m_frontService->onReceiveMessage( + _groupID, _srcNodeID, _payload, bcos::gateway::ErrorRespFunc()); + } + + FRONT_LOG(DEBUG) << "[FakeGateway] asyncSendMessageByNodeIDs" << LOG_KV("groupID", _groupID); +} + +/** + * @brief: send message to all nodes + * @param _groupID: groupID + * @param _payload: message content + * @return void + */ +void FakeGateway::asyncSendBroadcastMessage(uint16_t, const std::string& _groupID, int, + bcos::crypto::NodeIDPtr _srcNodeID, bytesConstRef _payload) +{ + m_frontService->onReceiveBroadcastMessage(_groupID, _srcNodeID, _payload, ErrorRespFunc()); + FRONT_LOG(DEBUG) << "asyncSendBroadcastMessage" << LOG_KV("groupID", _groupID); +} diff --git "a/BFPL\345\243\271/bcos-front/test/unittests/FakeGateway.h" "b/BFPL\345\243\271/bcos-front/test/unittests/FakeGateway.h" new file mode 100644 index 00000000..5d0be645 --- /dev/null +++ "b/BFPL\345\243\271/bcos-front/test/unittests/FakeGateway.h" @@ -0,0 +1,128 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Gateway fake implementation + * @file FakeGateway.h + * @author: octopus + * @date 2021-04-27 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace front +{ +namespace test +{ +class FakeGateway : public gateway::GatewayInterface, + public std::enable_shared_from_this +{ +public: + virtual ~FakeGateway() {} + +public: + std::shared_ptr m_frontService; + void setFrontService(std::shared_ptr _frontService) + { + m_frontService = _frontService; + } + +public: + /** + * @brief: start/stop service + */ + void start() override {} + void stop() override {} + void asyncGetPeers(std::function) override + {} + /** + * @brief: get nodeIDs from gateway + * @param _groupID: + * @param _onGetGroupNodeInfo: get nodeIDs callback + * @return void + */ + void asyncGetGroupNodeInfo( + const std::string& _groupID, GetGroupNodeInfoFunc _onGetGroupNodeInfo) override + { + boost::ignore_unused(_groupID, _onGetGroupNodeInfo); + } + + /** + * @brief: send message to a single node + * @param _groupID: groupID + * @param _moduleID: moduleID + * @param _srcNodeID: the sender nodeID + * @param _dstNodeID: the receiver nodeID + * @param _payload: message content + * @return void + */ + void asyncSendMessageByNodeID(const std::string& _groupID, int _moduleID, + bcos::crypto::NodeIDPtr _srcNodeID, bcos::crypto::NodeIDPtr _dstNodeID, + bytesConstRef _payload, bcos::gateway::ErrorRespFunc _errorRespFunc) override; + + /** + * @brief: send message to multiple nodes + * @param _groupID: groupID + * @param _moduleID: moduleID + * @param _srcNodeID: the sender nodeID + * @param _nodeIDs: the receiver nodeIDs + * @param _payload: message content + * @return void + */ + void asyncSendMessageByNodeIDs(const std::string& _groupID, int _moduleID, + bcos::crypto::NodeIDPtr _srcNodeID, const bcos::crypto::NodeIDs& _dstNodeIDs, + bytesConstRef _payload) override; + + /** + * @brief: send message to all nodes + * @param _nodeType: nodeType + * @param _groupID: groupID + * @param _moduleID: moduleID + * @param _srcNodeID: the sender nodeID + * @param _payload: message content + * @return void + */ + void asyncSendBroadcastMessage(uint16_t _nodeType, const std::string& _groupID, int _moduleID, + bcos::crypto::NodeIDPtr _srcNodeID, bytesConstRef _payload) override; + + void asyncNotifyGroupInfo( + bcos::group::GroupInfo::Ptr, std::function) override + {} + + void asyncSendMessageByTopic(const std::string&, bcos::bytesConstRef, + std::function) override + {} + void asyncSendBroadcastMessageByTopic(const std::string&, bcos::bytesConstRef) override {} + + void asyncSubscribeTopic( + std::string const&, std::string const&, std::function) override + {} + void asyncRemoveTopic(std::string const&, std::vector const&, + std::function) override + {} +}; + +} // namespace test +} // namespace front +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-front/test/unittests/FrontMessageTest.cpp" "b/BFPL\345\243\271/bcos-front/test/unittests/FrontMessageTest.cpp" new file mode 100644 index 00000000..ec18e126 --- /dev/null +++ "b/BFPL\345\243\271/bcos-front/test/unittests/FrontMessageTest.cpp" @@ -0,0 +1,230 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for front related messages + * @file FrontMessageTest.h + * @author: octopus + * @date 2021-04-26 + */ + +#include +#include +#include + +using namespace bcos; +using namespace bcos::test; +using namespace bcos::front; + +BOOST_FIXTURE_TEST_SUITE(FrontMessageTest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(testFrontMessage_0) +{ + auto factory = std::make_shared(); + auto message = factory->buildMessage(); + BOOST_CHECK_EQUAL(message->moduleID(), 0); + BOOST_CHECK_EQUAL(message->ext(), 0); + BOOST_CHECK_EQUAL(message->uuid()->size(), 0); + BOOST_CHECK_EQUAL(message->payload().size(), 0); + + std::shared_ptr buffer = std::make_shared(); + auto r = message->encode(*buffer.get()); + BOOST_CHECK(r); + BOOST_CHECK(buffer->size() == FrontMessage::HEADER_MIN_LENGTH); +} + +BOOST_AUTO_TEST_CASE(testFrontMessage_1) +{ + auto factory = std::make_shared(); + auto message = factory->buildMessage(); + int moduleID = 0; + int ext = 0; + std::string uuid = ""; + std::string payload = ""; + + message->setModuleID(moduleID); + message->setExt(ext); + message->setUuid(std::make_shared(uuid.begin(), uuid.end())); + auto payloadPtr = std::make_shared(payload.begin(), payload.end()); + message->setPayload(bytesConstRef(payloadPtr->data(), payloadPtr->size())); + + // encode + std::shared_ptr buffer = std::make_shared(); + auto r = message->encode(*buffer.get()); + BOOST_CHECK(r); + BOOST_CHECK(!buffer->empty()); + + // decode + auto decodeMessage = factory->buildMessage(); + + auto bcr = bytesConstRef(buffer->data(), buffer->size()); + + auto r1 = decodeMessage->decode(bcr); + BOOST_CHECK_EQUAL(r1, MessageDecodeStatus::MESSAGE_COMPLETE); + + BOOST_CHECK_EQUAL(moduleID, decodeMessage->moduleID()); + BOOST_CHECK_EQUAL(ext, decodeMessage->ext()); + BOOST_CHECK_EQUAL( + uuid, std::string(decodeMessage->uuid()->begin(), decodeMessage->uuid()->end())); + BOOST_CHECK_EQUAL( + payload, std::string(decodeMessage->payload().begin(), decodeMessage->payload().end())); +} + +BOOST_AUTO_TEST_CASE(testFrontMessage_2) +{ + auto factory = std::make_shared(); + auto message = factory->buildMessage(); + int moduleID = 1; + int ext = 2; + std::string uuid = "1234567890"; + std::string payload = "payload"; + + message->setModuleID(moduleID); + message->setExt(ext); + message->setUuid(std::make_shared(uuid.begin(), uuid.end())); + auto payloadPtr = std::make_shared(payload.begin(), payload.end()); + message->setPayload(bytesConstRef(payloadPtr->data(), payloadPtr->size())); + + // encode + std::shared_ptr buffer = std::make_shared(); + auto r = message->encode(*buffer.get()); + BOOST_CHECK(r); + BOOST_CHECK(!buffer->empty()); + + // decode + auto decodeMessage = factory->buildMessage(); + + auto bcr = bytesConstRef(buffer->data(), buffer->size()); + + auto r1 = decodeMessage->decode(bcr); + BOOST_CHECK_EQUAL(r1, MessageDecodeStatus::MESSAGE_COMPLETE); + + BOOST_CHECK_EQUAL(moduleID, decodeMessage->moduleID()); + BOOST_CHECK_EQUAL(ext, decodeMessage->ext()); + BOOST_CHECK_EQUAL( + uuid, std::string(decodeMessage->uuid()->begin(), decodeMessage->uuid()->end())); + BOOST_CHECK_EQUAL( + payload, std::string(decodeMessage->payload().begin(), decodeMessage->payload().end())); +} + +BOOST_AUTO_TEST_CASE(testFrontMessage_3) +{ + auto factory = std::make_shared(); + auto message = factory->buildMessage(); + int moduleID = 1; + int ext = 2; + std::string uuid = "1234567890"; + std::string payload = "payload"; + + message->setModuleID(moduleID); + message->setExt(ext); + message->setUuid(std::make_shared(uuid.begin(), uuid.end())); + auto payloadPtr = std::make_shared(payload.begin(), payload.end()); + message->setPayload(bytesConstRef(payloadPtr->data(), payloadPtr->size())); + + // encode + std::shared_ptr buffer = std::make_shared(); + auto r = message->encode(*buffer.get()); + BOOST_CHECK(r); + BOOST_CHECK(!buffer->empty()); + + // decode + auto decodeMessage = factory->buildMessage(); + + auto bcr = bytesConstRef(buffer->data(), buffer->size()); + + auto r1 = decodeMessage->decode(bcr); + BOOST_CHECK_EQUAL(r1, MessageDecodeStatus::MESSAGE_COMPLETE); + + BOOST_CHECK_EQUAL(moduleID, decodeMessage->moduleID()); + BOOST_CHECK_EQUAL(ext, decodeMessage->ext()); + BOOST_CHECK_EQUAL( + uuid, std::string(decodeMessage->uuid()->begin(), decodeMessage->uuid()->end())); + BOOST_CHECK_EQUAL( + payload, std::string(decodeMessage->payload().begin(), decodeMessage->payload().end())); +} + +BOOST_AUTO_TEST_CASE(testFrontMessage_4) +{ + auto factory = std::make_shared(); + auto message = factory->buildMessage(); + int moduleID = 1; + int ext = 2; + std::string uuid = + "567890123456789012345678901234567890123456789012345678901234567890123456" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "567890123456789012345678901234567890123456789012345678901234567890123456" + "78901234567890123456789012345678901234567890123456789012345678901234567" + "8"; + std::string payload = "payload"; + + message->setModuleID(moduleID); + message->setExt(ext); + message->setUuid(std::make_shared(uuid.begin(), uuid.end())); + auto payloadPtr = std::make_shared(payload.begin(), payload.end()); + message->setPayload(bytesConstRef(payloadPtr->data(), payloadPtr->size())); + + // encode + std::shared_ptr buffer = std::make_shared(); + auto r = message->encode(*buffer.get()); + BOOST_CHECK(!r); + + buffer->clear(); + auto decodeMessage = factory->buildMessage(); + auto r1 = decodeMessage->decode(bytesConstRef(buffer->data(), buffer->size())); + BOOST_CHECK_EQUAL(r1, MessageDecodeStatus::MESSAGE_ERROR); +} + +BOOST_AUTO_TEST_CASE(testFrontMessage_5) +{ + auto factory = std::make_shared(); + auto message = factory->buildMessage(); + int moduleID = 111; + std::string uuid; + std::string payload = std::string(1000, 'x'); + + message->setModuleID(moduleID); + message->setUuid(std::make_shared(uuid.begin(), uuid.end())); + auto payloadPtr = std::make_shared(payload.begin(), payload.end()); + message->setPayload(bytesConstRef(payloadPtr->data(), payloadPtr->size())); + + // encode + std::shared_ptr buffer = std::make_shared(); + auto r = message->encode(*buffer.get()); + BOOST_CHECK(r); + + auto decodeMessage = factory->buildMessage(); + auto r1 = decodeMessage->decode(bytesConstRef(buffer->data(), buffer->size())); + BOOST_CHECK_EQUAL(r1, MessageDecodeStatus::MESSAGE_COMPLETE); + BOOST_CHECK_EQUAL( + payload, std::string(decodeMessage->payload().begin(), decodeMessage->payload().end())); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git "a/BFPL\345\243\271/bcos-front/test/unittests/FrontServiceTest.cpp" "b/BFPL\345\243\271/bcos-front/test/unittests/FrontServiceTest.cpp" new file mode 100644 index 00000000..72e5d573 --- /dev/null +++ "b/BFPL\345\243\271/bcos-front/test/unittests/FrontServiceTest.cpp" @@ -0,0 +1,323 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for front service + * @file FrontServiceTest.h + * @author: octopus + * @date 2021-04-26 + */ + +#define BOOST_TEST_MAIN + +#include "FakeGateway.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::test; +using namespace bcos::front; +using namespace bcos::front::test; + +const static std::string g_groupID = "front.service.group"; +const static std::string g_srcNodeID = "front.src.nodeid"; +const static std::string g_dstNodeID_0 = "front.dst.nodeid.0"; +const static std::string g_dstNodeID_1 = "front.dst.nodeid.1"; + +bcos::crypto::NodeIDPtr createKey(const std::string& _strNodeID) +{ + auto keyFactory = std::make_shared(); + auto nodeID = + keyFactory->createKey(bytesConstRef((bcos::byte*)_strNodeID.data(), _strNodeID.size())); + return nodeID; +} + +std::shared_ptr buildFrontService() +{ + auto gateway = std::make_shared(); + auto srcNodeID = createKey(g_srcNodeID); + + auto threadPool = std::make_shared("frontServiceTest", 16); + auto frontServiceFactory = std::make_shared(); + frontServiceFactory->setThreadPool(threadPool); + frontServiceFactory->setGatewayInterface(gateway); + auto frontService = frontServiceFactory->buildFrontService(g_groupID, srcNodeID); + frontService->start(); + + gateway->setFrontService(frontService); + + return frontService; +} + +BOOST_FIXTURE_TEST_SUITE(FrontServiceTest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(testFrontService_buildFrontService) +{ + auto frontService = buildFrontService(); + BOOST_CHECK_EQUAL(frontService->groupID(), g_groupID); + // BOOST_CHECK_EQUAL(frontService->nodeID()->hex(), g_srcNodeID); + BOOST_CHECK(frontService->gatewayInterface()); + BOOST_CHECK(frontService->messageFactory()); + BOOST_CHECK(frontService->ioService()); + BOOST_CHECK(frontService->callback().empty()); + BOOST_CHECK(frontService->moduleID2MessageDispatcher().empty()); +} + +BOOST_AUTO_TEST_CASE(testFrontService_asyncSendMessageByNodeID_withoutCallback) +{ + auto frontService = buildFrontService(); + auto gateway = std::static_pointer_cast(frontService->gatewayInterface()); + + auto dstNodeID = createKey(g_dstNodeID_0); + std::string data(1000, 'x'); + + std::promise p; + auto f = p.get_future(); + auto moduleCallback = [&p, dstNodeID, data](bcos::crypto::NodeIDPtr _nodeID, + const std::string& _id, bytesConstRef _data) { + BOOST_CHECK(!_id.empty()); + BOOST_CHECK_EQUAL(dstNodeID->hex(), _nodeID->hex()); + BOOST_CHECK_EQUAL(std::string(_data.begin(), _data.end()), data); + p.set_value(true); + }; + + int moduleID = 111; + frontService->registerModuleMessageDispatcher(moduleID, moduleCallback); + BOOST_CHECK(frontService->moduleID2MessageDispatcher().find(moduleID) != + frontService->moduleID2MessageDispatcher().end()); + + frontService->asyncSendMessageByNodeID(moduleID, dstNodeID, + bytesConstRef((unsigned char*)data.data(), data.size()), 0, CallbackFunc()); + BOOST_CHECK(frontService->callback().empty()); + f.get(); +} + +BOOST_AUTO_TEST_CASE(testFrontService_onRecieveNodeIDsAnd) +{ + auto frontService = buildFrontService(); + int moduleID = 1000; + std::promise p; + auto f = p.get_future(); + std::vector expectedNodeIDList; + expectedNodeIDList.emplace_back(g_dstNodeID_0); + expectedNodeIDList.emplace_back(g_dstNodeID_0); + auto orgExpectedNodeIDList = expectedNodeIDList; + + std::vector nodeIDs0; + frontService->registerGroupNodeInfoNotification( + moduleID, [&p, &nodeIDs0](bcos::gateway::GroupNodeInfo::Ptr _groupNodeInfo, + ReceiveMsgFunc _receiveMsgCallback) { + nodeIDs0 = _groupNodeInfo->nodeIDList(); + p.set_value(true); + if (_receiveMsgCallback) + { + _receiveMsgCallback(nullptr); + } + }); + + BOOST_CHECK(frontService->module2GroupNodeInfoNotifier().find(moduleID) != + frontService->module2GroupNodeInfoNotifier().end()); + BOOST_CHECK(frontService->module2GroupNodeInfoNotifier().find(moduleID + 1) == + frontService->module2GroupNodeInfoNotifier().end()); + + auto groupNodeInfo = std::make_shared(); + groupNodeInfo->setNodeIDList(std::move(expectedNodeIDList)); + frontService->onReceiveGroupNodeInfo( + "1", groupNodeInfo, [](Error::Ptr _error) { BOOST_CHECK(_error == nullptr); }); + + f.get(); + BOOST_CHECK(nodeIDs0.size() == orgExpectedNodeIDList.size()); +} + +BOOST_AUTO_TEST_CASE(testFrontService_asyncSendMessageByNodeID_callback) +{ + auto frontService = buildFrontService(); + auto gateway = std::static_pointer_cast(frontService->gatewayInterface()); + + auto dstNodeID = createKey(g_dstNodeID_0); + std::string data(100000, '#'); + int moduleID = 12345; + + { + std::promise p; + auto f = p.get_future(); + auto callback = [dstNodeID, data, &p](Error::Ptr _error, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, const std::string& _uuid, + std::function _respFunc) { + (void)_uuid; + (void)_respFunc; + BOOST_CHECK(_error == nullptr); + BOOST_CHECK_EQUAL(dstNodeID->hex(), _nodeID->hex()); + BOOST_CHECK_EQUAL(std::string(_data.begin(), _data.end()), data); + p.set_value(true); + }; + frontService->asyncSendMessageByNodeID(moduleID, dstNodeID, + bytesConstRef((unsigned char*)data.data(), data.size()), 0, callback); + BOOST_CHECK(!frontService->callback().empty()); + auto uuid = frontService->callback().begin()->first; + frontService->asyncSendResponse(uuid, moduleID, dstNodeID, + bytesConstRef((unsigned char*)data.data(), data.size()), + [](Error::Ptr _error) { (void)_error; }); + f.get(); + BOOST_CHECK(frontService->callback().empty()); + } +} + +BOOST_AUTO_TEST_CASE(testFrontService_asyncSendMessageByNodeIDcmak_timeout) +{ + auto frontService = buildFrontService(); + auto gateway = std::static_pointer_cast(frontService->gatewayInterface()); + auto message = frontService->messageFactory()->buildMessage(); + + int moduleID = 222; + auto dstNodeID = createKey(g_dstNodeID_0); + std::string data(100000, '#'); + + BOOST_CHECK(frontService->callback().empty()); + + { + std::promise barrier; + Error::Ptr _error; + auto callback = [&](Error::Ptr _error, bcos::crypto::NodeIDPtr _nodeID, bytesConstRef _data, + const std::string& _uuid, + std::function _respFunc) { + (void)_nodeID; + (void)_data; + (void)_respFunc; + (void)_uuid; + BOOST_CHECK_EQUAL(_error->errorCode(), bcos::protocol::CommonError::TIMEOUT); + barrier.set_value(); + }; + + frontService->asyncSendMessageByNodeID(moduleID, dstNodeID, + bytesConstRef((unsigned char*)data.data(), data.size()), 2000, callback); + + BOOST_CHECK(frontService->callback().size() == 1); + std::future barrier_future = barrier.get_future(); + barrier_future.wait(); + BOOST_CHECK(frontService->callback().empty()); + } +} + +BOOST_AUTO_TEST_CASE(testFrontService_asyncSendBroadcastMessage) +{ + auto frontService = buildFrontService(); + auto gateway = std::static_pointer_cast(frontService->gatewayInterface()); + + auto dstNodeID = createKey(g_srcNodeID); + std::string data(1000, 'x'); + + std::promise p; + auto f = p.get_future(); + auto moduleCallback = [&p, dstNodeID, data](bcos::crypto::NodeIDPtr _nodeID, + const std::string& _id, bytesConstRef _data) { + (void)_id; + BOOST_CHECK_EQUAL(dstNodeID->hex(), _nodeID->hex()); + BOOST_CHECK_EQUAL(std::string(_data.begin(), _data.end()), data); + p.set_value(true); + }; + + int moduleID = 111; + frontService->registerModuleMessageDispatcher(moduleID, moduleCallback); + BOOST_CHECK(frontService->moduleID2MessageDispatcher().find(moduleID) != + frontService->moduleID2MessageDispatcher().end()); + + frontService->asyncSendBroadcastMessage(bcos::protocol::NodeType::CONSENSUS_NODE, moduleID, + bytesConstRef((unsigned char*)data.data(), data.size())); + BOOST_CHECK(frontService->callback().empty()); + f.get(); +} + +BOOST_AUTO_TEST_CASE(testFrontService_asyncSendMessageByNodeIDs) +{ + auto frontService = buildFrontService(); + auto gateway = std::static_pointer_cast(frontService->gatewayInterface()); + + auto dstNodeID = createKey(g_dstNodeID_0); + std::string data(1000, 'x'); + + std::promise p; + auto f = p.get_future(); + auto moduleCallback = [&p, dstNodeID, data](bcos::crypto::NodeIDPtr _nodeID, + const std::string& _id, bytesConstRef _data) { + (void)_id; + BOOST_CHECK_EQUAL(dstNodeID->hex(), _nodeID->hex()); + BOOST_CHECK_EQUAL(std::string(_data.begin(), _data.end()), data); + p.set_value(true); + }; + + int moduleID = 111; + frontService->registerModuleMessageDispatcher(moduleID, moduleCallback); + BOOST_CHECK(frontService->moduleID2MessageDispatcher().find(moduleID) != + frontService->moduleID2MessageDispatcher().end()); + + frontService->asyncSendMessageByNodeIDs(moduleID, bcos::crypto::NodeIDs{dstNodeID}, + bytesConstRef((unsigned char*)data.data(), data.size())); + + BOOST_CHECK(frontService->callback().empty()); + f.get(); +} + +BOOST_AUTO_TEST_CASE(testFrontService_loopTimeout) +{ + auto frontService = buildFrontService(); + auto gateway = std::static_pointer_cast(frontService->gatewayInterface()); + auto message = frontService->messageFactory()->buildMessage(); + + int moduleID = 12345; + auto dstNodeID = createKey(g_dstNodeID_0); + std::string data(1000, '#'); + + BOOST_CHECK(frontService->callback().empty()); + + std::vector> barriers; + barriers.resize(1000); + + for (auto& barrier : barriers) + { + Error::Ptr _error; + auto callback = [&](Error::Ptr _error, bcos::crypto::NodeIDPtr _nodeID, bytesConstRef _data, + const std::string& _uuid, + std::function _respFunc) { + (void)_nodeID; + (void)_data; + (void)_uuid; + (void)_respFunc; + BOOST_CHECK_EQUAL(_error->errorCode(), bcos::protocol::CommonError::TIMEOUT); + barrier.set_value(); + }; + + frontService->asyncSendMessageByNodeID(moduleID, dstNodeID, + bytesConstRef((unsigned char*)data.data(), data.size()), 2000, callback); + } + + BOOST_CHECK(frontService->callback().size() == barriers.size()); + + for (auto& barrier : barriers) + { + std::future barrier_future = barrier.get_future(); + barrier_future.wait(); + } + + BOOST_CHECK(frontService->callback().empty()); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git "a/BFPL\345\243\271/bcos-gateway/CMakeLists.txt" "b/BFPL\345\243\271/bcos-gateway/CMakeLists.txt" new file mode 100644 index 00000000..70601d53 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/CMakeLists.txt" @@ -0,0 +1,54 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for bcos-gateway +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 bcos-gateway +# 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. +#------------------------------------------------------------------------------ + +cmake_minimum_required(VERSION 3.10) +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum OS X deployment version") + +include(Version) +project(bcos-gateway VERSION ${VERSION}) + +find_package(jsoncpp CONFIG REQUIRED) +find_package(Boost REQUIRED COMPONENTS filesystem) +find_package(redis++ CONFIG REQUIRED) + +file(GLOB_RECURSE SRCS bcos-gateway/*.cpp) + +find_package(tarscpp REQUIRED) + +add_library(${GATEWAY_TARGET} ${SRCS}) +target_link_libraries(${GATEWAY_TARGET} PUBLIC ${PROTOCOL_TARGET} jsoncpp_static redis++::redis++_static Boost::filesystem bcos-boostssl ${TARS_PROTOCOL_TARGET} tarscpp::tarsservant tarscpp::tarsutil) + +if (APPLE) +# target_compile_options(${GATEWAY_TARGET} PRIVATE -faligned-allocation) +endif() + +# ut +if (TESTS) + enable_testing() + set(CTEST_OUTPUT_ON_FAILURE TRUE) + add_subdirectory(test) +endif() + +# for doxygen +# include(BuildDocs) + +# for code coverage +if (COVERAGE) + include(Coverage) + config_coverage("gateway-coverage" "'/usr*' '${CMAKE_CURRENT_SOURCE_DIR}/bcos-cmake-scripts*' '${CMAKE_SOURCE_DIR}/test/mock**' '${CMAKE_SOURCE_DIR}/test/main**'") +endif () diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/Common.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/Common.h" new file mode 100644 index 00000000..44bac5c3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/Common.h" @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2021 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: octopus + * @date 2021-05-04 + */ +#pragma once +#include "libnetwork/Common.h" + +#define GATEWAY_LOG(LEVEL) BCOS_LOG(LEVEL) << "[Gateway][Gateway]" +#define GATEWAY_CONFIG_LOG(LEVEL) BCOS_LOG(LEVEL) << "[Gateway][Config]" +#define GATEWAY_FACTORY_LOG(LEVEL) BCOS_LOG(LEVEL) << "[Gateway][Factory]" +#define NODE_MANAGER_LOG(LEVEL) BCOS_LOG(LEVEL) << "[Gateway][GatewayNodeManager]" +#define ROUTER_LOG(LEVEL) BCOS_LOG(LEVEL) << "[Gateway][Router]" +#define RATELIMIT_LOG(LEVEL) BCOS_LOG(LEVEL) << "[Gateway][RateLimiter]" +#define RATELIMIT_MGR_LOG(LEVEL) BCOS_LOG(LEVEL) << "[Gateway][RateLimiterManager]" + +namespace bcos +{ +namespace gateway +{ +enum GroupType : uint16_t +{ + // group with at-least one consensus node + GROUP_WITH_CONSENSUS_NODE = 0x0, + // group without consensus node + GROUP_WITHOUT_CONSENSUS_NODE = 0x1, + // group without consensus node and observer node + OUTSIDE_GROUP = 0x2, +}; + +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/Gateway.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/Gateway.cpp" new file mode 100644 index 00000000..686ca36a --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/Gateway.cpp" @@ -0,0 +1,548 @@ +/* + * Copyright (C) 2021 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 Gateway.cpp + * @author: octopus + * @date 2021-04-19 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::protocol; +using namespace bcos::gateway; +using namespace bcos::group; +using namespace bcos::amop; +using namespace bcos::crypto; + +void Gateway::start() +{ + if (m_gatewayRateLimiter) + { + m_gatewayRateLimiter->start(); + } + if (m_p2pInterface) + { + m_p2pInterface->start(); + } + if (m_amop) + { + m_amop->start(); + } + if (m_gatewayNodeManager) + { + m_gatewayNodeManager->start(); + } + + GATEWAY_LOG(INFO) << LOG_DESC("start end."); + + return; +} + +void Gateway::stop() +{ + // erase the registered handler + if (m_p2pInterface) + { + m_p2pInterface->eraseHandlerByMsgType(GatewayMessageType::PeerToPeerMessage); + m_p2pInterface->eraseHandlerByMsgType(GatewayMessageType::BroadcastMessage); + m_p2pInterface->stop(); + } + if (m_amop) + { + m_amop->stop(); + } + if (m_gatewayNodeManager) + { + m_gatewayNodeManager->stop(); + } + if (m_gatewayRateLimiter) + { + m_gatewayRateLimiter->stop(); + } + GATEWAY_LOG(INFO) << LOG_DESC("stop end."); + return; +} + +void Gateway::asyncGetPeers( + std::function _onGetPeers) +{ + if (!_onGetPeers) + { + return; + } + auto sessionInfos = m_p2pInterface->sessionInfos(); + auto peersNodeIDList = m_gatewayNodeManager->peersRouterTable()->getAllPeers(); + GatewayInfosPtr peerGatewayInfos = std::make_shared(); + // append the peers sessionInfos + for (auto const& info : sessionInfos) + { + auto gatewayInfo = std::make_shared(info); + auto nodeIDList = m_gatewayNodeManager->peersNodeIDList(info.p2pID); + gatewayInfo->setNodeIDInfo(std::move(nodeIDList)); + peerGatewayInfos->emplace_back(gatewayInfo); + peersNodeIDList.erase(info.p2pID); + } + // append peers that are not directly connected to nodeSelf + for (auto const& peer : peersNodeIDList) + { + P2PInfo p2pInfo; + p2pInfo.p2pID = peer; + auto gatewayInfo = std::make_shared(p2pInfo); + auto nodeIDList = m_gatewayNodeManager->peersNodeIDList(peer); + gatewayInfo->setNodeIDInfo(std::move(nodeIDList)); + peerGatewayInfos->emplace_back(gatewayInfo); + } + auto localP2pInfo = m_p2pInterface->localP2pInfo(); + auto localGatewayInfo = std::make_shared(localP2pInfo); + auto localNodeInfo = m_gatewayNodeManager->localRouterTable()->nodeListInfo(); + localGatewayInfo->setNodeIDInfo(std::move(localNodeInfo)); + _onGetPeers(nullptr, localGatewayInfo, peerGatewayInfos); +} + +/** + * @brief: get nodeIDs from gateway + * @param _groupID: + * @param _onGetGroupNodeInfo: get nodeIDs callback + * @return void + */ +void Gateway::asyncGetGroupNodeInfo( + const std::string& _groupID, GetGroupNodeInfoFunc _onGetGroupNodeInfo) +{ + auto groupNodeInfo = m_gatewayNodeManager->getGroupNodeInfoList(_groupID); + _onGetGroupNodeInfo(nullptr, groupNodeInfo); +} + + +/** + * @brief: send message + * @param _groupID: groupID + * @param _moduleID: moduleID + * @param _srcNodeID: the sender nodeID + * @param _dstNodeID: the receiver nodeID + * @param _payload: message payload + * @param _errorRespFunc: error func + * @return void + */ +void Gateway::asyncSendMessageByNodeID(const std::string& _groupID, int _moduleID, + NodeIDPtr _srcNodeID, NodeIDPtr _dstNodeID, bytesConstRef _payload, + ErrorRespFunc _errorRespFunc) +{ + auto p2pIDs = + m_gatewayNodeManager->peersRouterTable()->queryP2pIDs(_groupID, _dstNodeID->hex()); + if (p2pIDs.empty()) + { + if (m_gatewayNodeManager->localRouterTable()->sendMessage( + _groupID, _srcNodeID, _dstNodeID, _payload, _errorRespFunc)) + { + return; + } + GATEWAY_LOG(ERROR) << LOG_DESC("could not find a gateway to send this message") + << LOG_KV("groupID", _groupID) << LOG_KV("srcNodeID", _srcNodeID->hex()) + << LOG_KV("dstNodeID", _dstNodeID->hex()); + + auto errorPtr = std::make_shared(CommonError::NotFoundFrontServiceSendMsg, + "could not find a gateway to " + "send this message, groupID:" + + _groupID + " ,dstNodeID:" + _dstNodeID->hex()); + if (_errorRespFunc) + { + _errorRespFunc(errorPtr); + } + return; + } + + class Retry : public std::enable_shared_from_this + { + public: + // random choose one p2pID to send message + P2pID chooseP2pID() + { + auto p2pId = P2pID(); + if (!m_p2pIDs.empty()) + { + p2pId = *m_p2pIDs.begin(); + m_p2pIDs.erase(m_p2pIDs.begin()); + } + + return p2pId; + } + + // send the message with retry + void trySendMessage() + { + if (m_p2pIDs.empty()) + { + GATEWAY_LOG(ERROR) + << LOG_DESC("[Gateway::Retry]") << LOG_DESC("unable to send the message") + << LOG_KV("srcNodeID", m_srcNodeID->hex()) + << LOG_KV("dstNodeID", m_dstNodeID->hex()) + << LOG_KV("seq", std::to_string(m_p2pMessage->seq())); + + if (m_respFunc) + { + auto errorPtr = std::make_shared( + CommonError::GatewaySendMsgFailed, "unable to send the message"); + m_respFunc(errorPtr); + } + return; + } + auto p2pID = chooseP2pID(); + auto self = shared_from_this(); + auto startT = utcTime(); + auto callback = [self, startT, p2pID](NetworkException e, + std::shared_ptr session, + std::shared_ptr message) { + std::ignore = session; + if (e.errorCode() != P2PExceptionType::Success) + { + // bandwidth overflow , do'not try again + if (e.errorCode() == P2PExceptionType::BandwidthOverFlow) + { + if (self->m_respFunc) + { + auto errorPtr = std::make_shared( + CommonError::NetworkBandwidthOverFlow, e.what()); + self->m_respFunc(errorPtr); + } + + return; + } + + GATEWAY_LOG(ERROR) + << LOG_BADGE("Retry") << LOG_DESC("network callback") + << LOG_KV("dstP2P", p2pID) << LOG_KV("errorCode", e.errorCode()) + << LOG_KV("errorMessage", e.what()) + << LOG_KV("timeCost", (utcTime() - startT)); + // try again + self->trySendMessage(); + return; + } + + try + { + auto payload = message->payload(); + int respCode = + boost::lexical_cast(std::string(payload->begin(), payload->end())); + // the peer gateway not response not ok ,it means the gateway not dispatch the + // message successfully,find another gateway and try again + if (respCode != CommonError::SUCCESS) + { + GATEWAY_LOG(WARNING) + << LOG_BADGE("Retry") << LOG_KV("p2pid", p2pID) + << LOG_KV("errorCode", respCode) << LOG_KV("errorMessage", e.what()); + // try again + self->trySendMessage(); + return; + } + GATEWAY_LOG(TRACE) + << LOG_BADGE("Retry: asyncSendMessageByNodeID success") + << LOG_KV("dstP2P", p2pID) << LOG_KV("srcNodeID", self->m_srcNodeID->hex()) + << LOG_KV("dstNodeID", self->m_dstNodeID->hex()); + // send message successfully + if (self->m_respFunc) + { + self->m_respFunc(nullptr); + } + return; + } + catch (const std::exception& e) + { + GATEWAY_LOG(ERROR) + << LOG_BADGE("trySendMessage and receive response exception") + << LOG_KV("payload", + std::string(message->payload()->begin(), message->payload()->end())) + << LOG_KV("packetType", message->packetType()) + << LOG_KV("src", message->options() ? + toHex(*(message->options()->srcNodeID())) : + "unknown") + << LOG_KV("size", message->length()) << LOG_KV("error", e.what()); + + self->trySendMessage(); + } + }; + m_p2pInterface->asyncSendMessageByNodeID(p2pID, m_p2pMessage, callback, Options(10000)); + } + + public: + std::vector m_p2pIDs; + NodeIDPtr m_srcNodeID; + NodeIDPtr m_dstNodeID; + std::shared_ptr m_p2pMessage; + std::shared_ptr m_p2pInterface; + ErrorRespFunc m_respFunc; + }; + + auto retry = std::make_shared(); + auto message = + std::static_pointer_cast(m_p2pInterface->messageFactory()->buildMessage()); + + auto msgExtAttr = std::make_shared(); + msgExtAttr->setGroupID(_groupID); + msgExtAttr->setModuleID(_moduleID); + + message->setPacketType(GatewayMessageType::PeerToPeerMessage); + message->setSeq(m_p2pInterface->messageFactory()->newSeq()); + message->options()->setGroupID(_groupID); + message->options()->setSrcNodeID(_srcNodeID->encode()); + message->options()->dstNodeIDs().push_back(_dstNodeID->encode()); + message->setPayload(std::make_shared(_payload.begin(), _payload.end())); + message->setExtAttributes(msgExtAttr); + + retry->m_p2pMessage = message; + retry->m_p2pIDs.insert(retry->m_p2pIDs.begin(), p2pIDs.begin(), p2pIDs.end()); + retry->m_respFunc = _errorRespFunc; + retry->m_srcNodeID = _srcNodeID; + retry->m_dstNodeID = _dstNodeID; + retry->m_p2pInterface = m_p2pInterface; + + retry->trySendMessage(); +} + +/** + * @brief: send message to multiple nodes + * @param _groupID: groupID + * @param _moduleID: moduleID + * @param _srcNodeID: the sender nodeID + * @param _nodeIDs: the receiver nodeIDs + * @param _payload: message content + * @return void + */ +void Gateway::asyncSendMessageByNodeIDs(const std::string& _groupID, int _moduleID, + NodeIDPtr _srcNodeID, const NodeIDs& _dstNodeIDs, bytesConstRef _payload) +{ + for (auto dstNodeID : _dstNodeIDs) + { + asyncSendMessageByNodeID(_groupID, _moduleID, _srcNodeID, dstNodeID, _payload, + [_groupID, _srcNodeID, dstNodeID](Error::Ptr _error) { + if (!_error) + { + return; + } + GATEWAY_LOG(TRACE) + << LOG_DESC("asyncSendMessageByNodeIDs callback") << LOG_KV("groupID", _groupID) + << LOG_KV("srcNodeID", _srcNodeID->hex()) + << LOG_KV("dstNodeID", dstNodeID->hex()) << LOG_KV("code", _error->errorCode()); + }); + } +} + +/** + * @brief: send message to all nodes + * @param _groupID: groupID + * @param _moduleID: moduleID + * @param _srcNodeID: the sender nodeID + * @param _payload: message content + * @return void + */ +void Gateway::asyncSendBroadcastMessage(uint16_t _type, const std::string& _groupID, int _moduleID, + NodeIDPtr _srcNodeID, bytesConstRef _payload) +{ + // broadcast message to the local nodes + auto ret = m_gatewayNodeManager->localRouterTable()->asyncBroadcastMsg( + _type, _groupID, _moduleID, _srcNodeID, _payload); + auto message = + std::static_pointer_cast(m_p2pInterface->messageFactory()->buildMessage()); + message->setPacketType(GatewayMessageType::BroadcastMessage); + message->setExt(_type); + message->setSeq(m_p2pInterface->messageFactory()->newSeq()); + message->options()->setGroupID(_groupID); + message->options()->setSrcNodeID(_srcNodeID->encode()); + message->setPayload(std::make_shared(_payload.begin(), _payload.end())); + + auto msgExtAttr = std::make_shared(); + msgExtAttr->setGroupID(_groupID); + msgExtAttr->setModuleID(_moduleID); + message->setExtAttributes(msgExtAttr); + + // broadcast message to the peers + m_gatewayNodeManager->peersRouterTable()->asyncBroadcastMsg( + _type, _groupID, _moduleID, message); +} + +/** + * @brief: receive p2p message from p2p network module + * @param _groupID: groupID + * @param _srcNodeID: the sender nodeID + * @param _dstNodeID: the receiver nodeID + * @param _payload: message content + * @param _callback: callback + * @return void + */ +void Gateway::onReceiveP2PMessage(const std::string& _groupID, NodeIDPtr _srcNodeID, + NodeIDPtr _dstNodeID, bytesConstRef _payload, ErrorRespFunc _errorRespFunc) +{ + auto frontService = + m_gatewayNodeManager->localRouterTable()->getFrontService(_groupID, _dstNodeID); + if (!frontService) + { + GATEWAY_LOG(ERROR) << LOG_DESC( + "onReceiveP2PMessage unable to find front " + "service to dispatch this message") + << LOG_KV("groupID", _groupID) << LOG_KV("srcNodeID", _srcNodeID->hex()) + << LOG_KV("dstNodeID", _dstNodeID->hex()); + + auto errorPtr = std::make_shared(CommonError::NotFoundFrontServiceDispatchMsg, + "unable to find front service dispatch message to " + "groupID:" + + _groupID + " ,nodeID:" + _dstNodeID->hex()); + + if (_errorRespFunc) + { + _errorRespFunc(errorPtr); + } + return; + } + + frontService->frontService()->onReceiveMessage(_groupID, _srcNodeID, _payload, + [_groupID, _srcNodeID, _dstNodeID, _errorRespFunc](Error::Ptr _error) { + if (_errorRespFunc) + { + _errorRespFunc(_error); + } + GATEWAY_LOG(TRACE) << LOG_DESC("onReceiveP2PMessage callback") + << LOG_KV("groupID", _groupID) + << LOG_KV("srcNodeID", _srcNodeID->hex()) + << LOG_KV("dstNodeID", _dstNodeID->hex()) + << LOG_KV("code", (_error ? _error->errorCode() : 0)) + << LOG_KV("msg", (_error ? _error->errorMessage() : "")); + }); +} + +bool Gateway::checkGroupInfo(bcos::group::GroupInfo::Ptr _groupInfo) +{ + // check the serviceName + auto nodeList = _groupInfo->nodeInfos(); + for (auto const& node : nodeList) + { + auto const& expectedGatewayService = + node.second->serviceName(bcos::protocol::ServiceType::GATEWAY); + if (expectedGatewayService != m_gatewayServiceName) + { + GATEWAY_LOG(INFO) << LOG_DESC( + "unfollowed groupInfo for inconsistent gateway service name") + << LOG_KV("expected", expectedGatewayService) + << LOG_KV("selfName", m_gatewayServiceName); + return false; + } + } + return true; +} + +void Gateway::asyncNotifyGroupInfo( + bcos::group::GroupInfo::Ptr _groupInfo, std::function _callback) +{ + if (!checkGroupInfo(_groupInfo)) + { + if (_callback) + { + _callback(nullptr); + } + return; + } + m_gatewayNodeManager->updateFrontServiceInfo(_groupInfo); + if (_callback) + { + _callback(nullptr); + } +} + +void Gateway::onReceiveP2PMessage( + NetworkException const& _e, P2PSession::Ptr _session, std::shared_ptr _msg) +{ + if (_e.errorCode()) + { + GATEWAY_LOG(WARNING) << LOG_DESC("onReceiveP2PMessage error") + << LOG_KV("code", _e.errorCode()) << LOG_KV("msg", _e.what()); + return; + } + + auto options = _msg->options(); + auto msgPayload = _msg->payload(); + auto payload = bytesConstRef(msgPayload->data(), msgPayload->size()); + // groupID + auto groupID = options->groupID(); + // moduleID + auto moduleID = options->moduleID(); + + m_gatewayRateLimiter->checkInComing(groupID, moduleID, _msg->length()); + + auto srcNodeID = options->srcNodeID(); + const auto& dstNodeIDs = options->dstNodeIDs(); + auto srcNodeIDPtr = m_gatewayNodeManager->keyFactory()->createKey(*srcNodeID.get()); + auto dstNodeIDPtr = m_gatewayNodeManager->keyFactory()->createKey(*dstNodeIDs[0].get()); + auto gateway = std::weak_ptr(shared_from_this()); + onReceiveP2PMessage(groupID, srcNodeIDPtr, dstNodeIDPtr, payload, + [groupID, srcNodeIDPtr, dstNodeIDPtr, _session, _msg, gateway](Error::Ptr _error) { + auto gatewayPtr = gateway.lock(); + if (!gatewayPtr) + { + return; + } + + auto errorCode = + std::to_string(_error ? _error->errorCode() : (int)protocol::CommonError::SUCCESS); + if (_error) + { + GATEWAY_LOG(DEBUG) + << "onReceiveP2PMessage callback" << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()) << LOG_KV("group", groupID) + << LOG_KV("src", srcNodeIDPtr->shortHex()) + << LOG_KV("dst", dstNodeIDPtr->shortHex()); + } + gatewayPtr->m_p2pInterface->sendRespMessageBySession( + bytesConstRef((byte*)errorCode.data(), errorCode.size()), _msg, _session); + }); +} + +void Gateway::onReceiveBroadcastMessage( + NetworkException const& _e, P2PSession::Ptr _session, std::shared_ptr _msg) +{ + if (_e.errorCode() != 0) + { + GATEWAY_LOG(WARNING) << LOG_DESC("onReceiveBroadcastMessage error") + << LOG_KV("code", _e.errorCode()) << LOG_KV("msg", _e.what()); + return; + } + + auto options = _msg->options(); + auto msgPayload = _msg->payload(); + + // groupID + auto groupID = options->groupID(); + // moduleID + uint16_t moduleID = options->moduleID(); + + m_gatewayRateLimiter->checkInComing(groupID, moduleID, _msg->length()); + + auto srcNodeIDPtr = + m_gatewayNodeManager->keyFactory()->createKey(*(_msg->options()->srcNodeID())); + + auto type = _msg->ext(); + GATEWAY_LOG(TRACE) << LOG_DESC("onReceiveBroadcastMessage") << LOG_KV("groupID", groupID) + << LOG_KV("src", _msg->srcP2PNodeID()) << LOG_KV("dst", _msg->dstP2PNodeID()) + << LOG_KV("type", type); + m_gatewayNodeManager->localRouterTable()->asyncBroadcastMsg(type, groupID, moduleID, + srcNodeIDPtr, bytesConstRef(_msg->payload()->data(), _msg->payload()->size())); +} diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/Gateway.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/Gateway.h" new file mode 100644 index 00000000..2c6969b8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/Gateway.h" @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2021 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 Gateway.h + * @author: octopus + * @date 2021-04-19 + */ + +#pragma once + +#include "bcos-gateway/libratelimit/GatewayRateLimiter.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace gateway +{ +class Gateway : public GatewayInterface, public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + Gateway(std::string const& _chainID, P2PInterface::Ptr _p2pInterface, + GatewayNodeManager::Ptr _gatewayNodeManager, bcos::amop::AMOPImpl::Ptr _amop, + ratelimiter::GatewayRateLimiter::Ptr _gatewayRateLimiter, + std::string _gatewayServiceName = "localGateway") + : m_gatewayServiceName(_gatewayServiceName), + m_chainID(_chainID), + m_p2pInterface(_p2pInterface), + m_gatewayNodeManager(_gatewayNodeManager), + m_amop(_amop), + m_gatewayRateLimiter(_gatewayRateLimiter) + { + m_p2pInterface->registerHandlerByMsgType(GatewayMessageType::PeerToPeerMessage, + boost::bind(&Gateway::onReceiveP2PMessage, this, boost::placeholders::_1, + boost::placeholders::_2, boost::placeholders::_3)); + + m_p2pInterface->registerHandlerByMsgType(GatewayMessageType::BroadcastMessage, + boost::bind(&Gateway::onReceiveBroadcastMessage, this, boost::placeholders::_1, + boost::placeholders::_2, boost::placeholders::_3)); + } + ~Gateway() override { stop(); } + + void start() override; + void stop() override; + + /** + * @brief: get connected peers + * @return void + */ + void asyncGetPeers( + std::function _onGetPeers) override; + /** + * @brief: get nodeIDs from gateway + * @param _groupID: + * @param _onGetGroupNodeInfo: get nodeIDs callback + * @return void + */ + void asyncGetGroupNodeInfo( + const std::string& _groupID, GetGroupNodeInfoFunc _onGetGroupNodeInfo) override; + /** + * @brief: send message + * @param _groupID: groupID + * @param _moduleID: moduleID + * @param _srcNodeID: the sender nodeID + * @param _dstNodeID: the receiver nodeID + * @param _payload: message payload + * @param _errorRespFunc: error func + * @return void + */ + void asyncSendMessageByNodeID(const std::string& _groupID, int _moduleID, + bcos::crypto::NodeIDPtr _srcNodeID, bcos::crypto::NodeIDPtr _dstNodeID, + bytesConstRef _payload, ErrorRespFunc _errorRespFunc) override; + + /** + * @brief: send message to multiple nodes + * @param _groupID: groupID + * @param _moduleID: moduleID + * @param _srcNodeID: the sender nodeID + * @param _nodeIDs: the receiver nodeIDs + * @param _payload: message payload + * @return void + */ + void asyncSendMessageByNodeIDs(const std::string& _groupID, int _moduleID, + bcos::crypto::NodeIDPtr _srcNodeID, const bcos::crypto::NodeIDs& _nodeIDs, + bytesConstRef _payload) override; + + /** + * @brief: send broadcast message + * @param _groupID: groupID + * @param _moduleID: moduleID + * @param _srcNodeID: the sender nodeID + * @param _payload: message payload + * @return void + */ + void asyncSendBroadcastMessage(uint16_t _type, const std::string& _groupID, int _moduleID, + bcos::crypto::NodeIDPtr _srcNodeID, bytesConstRef _payload) override; + + /** + * @brief: receive p2p message + * @param _groupID: groupID + * @param _srcNodeID: the sender nodeID + * @param _dstNodeID: the receiver nodeID + * @param _payload: message content + * @param _errorRespFunc: error func + * @return void + */ + virtual void onReceiveP2PMessage(const std::string& _groupID, + bcos::crypto::NodeIDPtr _srcNodeID, bcos::crypto::NodeIDPtr _dstNodeID, + bytesConstRef _payload, ErrorRespFunc _errorRespFunc = ErrorRespFunc()); + + + P2PInterface::Ptr p2pInterface() const { return m_p2pInterface; } + GatewayNodeManager::Ptr gatewayNodeManager() { return m_gatewayNodeManager; } + /** + * @brief receive the latest group information notification from the GroupManagerInterface + * + * @param _groupInfo the latest group information + */ + void asyncNotifyGroupInfo( + bcos::group::GroupInfo::Ptr, std::function) override; + + /// for AMOP + void asyncSendMessageByTopic(const std::string& _topic, bcos::bytesConstRef _data, + std::function _respFunc) override + { + m_amop->asyncSendMessageByTopic(_topic, _data, _respFunc); + } + void asyncSendBroadcastMessageByTopic( + const std::string& _topic, bcos::bytesConstRef _data) override + { + m_amop->asyncSendBroadcastMessageByTopic(_topic, _data); + } + + void asyncSubscribeTopic(std::string const& _clientID, std::string const& _topicInfo, + std::function _callback) override + { + m_amop->asyncSubscribeTopic(_clientID, _topicInfo, _callback); + } + + void asyncRemoveTopic(std::string const& _clientID, std::vector const& _topicList, + std::function _callback) override + { + m_amop->asyncRemoveTopic(_clientID, _topicList, _callback); + } + + bcos::amop::AMOPImpl::Ptr amop() { return m_amop; } + + bool registerNode(const std::string& _groupID, bcos::crypto::NodeIDPtr _nodeID, + bcos::protocol::NodeType _nodeType, bcos::front::FrontServiceInterface::Ptr _frontService, + bcos::protocol::ProtocolInfo::ConstPtr _protocolInfo) override + { + return m_gatewayNodeManager->registerNode( + _groupID, _nodeID, _nodeType, _frontService, _protocolInfo); + } + + virtual bool unregisterNode(const std::string& _groupID, std::string const& _nodeID) + { + return m_gatewayNodeManager->unregisterNode(_groupID, _nodeID); + } + +protected: + // for UT + Gateway() {} + virtual void onReceiveP2PMessage( + NetworkException const& _e, P2PSession::Ptr _session, std::shared_ptr _msg); + + /** + * @brief: receive group broadcast message + * @param _groupID: groupID + * @param _srcNodeID: the sender nodeID + * @param _payload: message content + * @return void + */ + virtual void onReceiveBroadcastMessage( + NetworkException const& _e, P2PSession::Ptr _session, std::shared_ptr _msg); + + + bool checkGroupInfo(bcos::group::GroupInfo::Ptr _groupInfo); + +private: + std::string m_gatewayServiceName; + std::string m_chainID; + // p2p service interface + P2PInterface::Ptr m_p2pInterface; + // GatewayNodeManager + GatewayNodeManager::Ptr m_gatewayNodeManager; + bcos::amop::AMOPImpl::Ptr m_amop; + + // For rate limit + ratelimiter::GatewayRateLimiter::Ptr m_gatewayRateLimiter; +}; +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/GatewayConfig.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/GatewayConfig.cpp" new file mode 100644 index 00000000..830de624 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/GatewayConfig.cpp" @@ -0,0 +1,667 @@ +/** @file GatewayConfig.cpp + * @author octopus + * @date 2021-05-19 + */ + +#include "bcos-gateway/Common.h" +#include "bcos-utilities/BoostLog.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace security; +using namespace gateway; + +bool GatewayConfig::isValidPort(int port) +{ + if (port <= 1024 || port > 65535) + return false; + return true; +} + +// check if the ip valid +bool GatewayConfig::isValidIP(const std::string& _ip) +{ + boost::system::error_code ec; + boost::asio::ip::address ip_address = boost::asio::ip::make_address(_ip, ec); + std::ignore = ip_address; + return ec.value() == 0; +} + +// MB to bit +int64_t GatewayConfig::doubleMBToBit(double _d) +{ + _d *= (1024 * 1024 / 8); + + return (int64_t)_d; +} + +void GatewayConfig::hostAndPort2Endpoint(const std::string& _host, NodeIPEndpoint& _endpoint) +{ + std::string ip; + uint16_t port; + + std::vector s; + boost::split(s, _host, boost::is_any_of("]"), boost::token_compress_on); + if (s.size() == 2) + { // ipv6 + ip = s[0].data() + 1; + port = boost::lexical_cast(s[1].data() + 1); + } + else if (s.size() == 1) + { // ipv4 + std::vector v; + boost::split(v, _host, boost::is_any_of(":"), boost::token_compress_on); + if (v.size() < 2) + { + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "GatewayConfig: invalid host , host=" + _host)); + } + ip = v[0]; + port = boost::lexical_cast(v[1]); + } + else + { + GATEWAY_CONFIG_LOG(ERROR) << LOG_DESC("not valid host value") << LOG_KV("host", _host); + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "GatewayConfig: the host is invalid, host=" + _host)); + } + + if (!isValidPort(port)) + { + GATEWAY_CONFIG_LOG(ERROR) << LOG_DESC("the port is not valid") << LOG_KV("port", port); + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment( + "GatewayConfig: the port is invalid, port=" + std::to_string(port))); + } + + boost::system::error_code ec; + boost::asio::ip::address ip_address = boost::asio::ip::make_address(ip, ec); + if (ec.value() != 0) + { + GATEWAY_CONFIG_LOG(ERROR) << LOG_DESC("the host is invalid, make_address error") + << LOG_KV("host", _host); + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment( + "GatewayConfig: the host is invalid make_address error, host=" + _host)); + } + + _endpoint = NodeIPEndpoint{ip_address, port}; +} + +void GatewayConfig::parseConnectedJson( + const std::string& _json, std::set& _nodeIPEndpointSet) +{ + /* + {"nodes":["127.0.0.1:30355","127.0.0.1:30356"}]} + */ + Json::Value root; + Json::Reader jsonReader; + + try + { + if (!jsonReader.parse(_json, root)) + { + GATEWAY_CONFIG_LOG(ERROR) + << "unable to parse connected nodes json" << LOG_KV("json:", _json); + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment("GatewayConfig: unable to parse p2p " + "connected nodes json")); + } + + std::set nodeIPEndpointSet; + Json::Value jNodes = root["nodes"]; + if (jNodes.isArray()) + { + unsigned int jNodesSize = jNodes.size(); + for (unsigned int i = 0; i < jNodesSize; i++) + { + std::string host = jNodes[i].asString(); + + NodeIPEndpoint endpoint; + hostAndPort2Endpoint(host, endpoint); + _nodeIPEndpointSet.insert(endpoint); + + GATEWAY_CONFIG_LOG(INFO) + << LOG_DESC("add one connected node") << LOG_KV("host", host); + } + } + } + catch (const std::exception& e) + { + GATEWAY_CONFIG_LOG(ERROR) << LOG_KV( + "parseConnectedJson error: ", boost::diagnostic_information(e)); + BOOST_THROW_EXCEPTION(e); + } +} + +/** + * @brief: loads configuration items from the config.ini + * @param _configPath: config.ini path + * @return void + */ +void GatewayConfig::initConfig(std::string const& _configPath, bool _uuidRequired) +{ + try + { + boost::property_tree::ptree pt; + boost::property_tree::ini_parser::read_ini(_configPath, pt); + initP2PConfig(pt, _uuidRequired); + initRateLimitConfig(pt); + if (m_smSSL) + { + initSMCertConfig(pt); + } + else + { + initCertConfig(pt); + } + } + catch (const std::exception& e) + { + boost::filesystem::path full_path(boost::filesystem::current_path()); + + GATEWAY_CONFIG_LOG(ERROR) << LOG_KV("configPath", _configPath) + << LOG_KV("currentPath", full_path.string()) + << LOG_KV("initConfig error: ", boost::diagnostic_information(e)); + + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment("initConfig: currentPath:" + full_path.string() + + " ,error:" + boost::diagnostic_information(e))); + } + + GATEWAY_CONFIG_LOG(INFO) << LOG_DESC("initConfig ok!") << LOG_KV("configPath", _configPath) + << LOG_KV("listenIP", m_listenIP) << LOG_KV("listenPort", m_listenPort) + << LOG_KV("smSSL", m_smSSL) + << LOG_KV("peers", m_connectedNodes.size()); +} + +/// loads p2p configuration items from the configuration file +void GatewayConfig::initP2PConfig(const boost::property_tree::ptree& _pt, bool _uuidRequired) +{ + /* + [p2p] + ; uuid + uuid = + ; ssl or sm ssl + sm_ssl=true + listen_ip=0.0.0.0 + listen_port=30300 + nodes_path=./ + nodes_file=nodes.json + */ + m_uuid = _pt.get("p2p.uuid", ""); + if (_uuidRequired && m_uuid.size() == 0) + { + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "initP2PConfig: invalid uuid! Must be non-empty!")); + } + bool smSSL = _pt.get("p2p.sm_ssl", false); + std::string listenIP = _pt.get("p2p.listen_ip", "0.0.0.0"); + int listenPort = _pt.get("p2p.listen_port", 30300); + if (!isValidPort(listenPort)) + { + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment( + "initP2PConfig: invalid listen port, port=" + std::to_string(listenPort))); + } + + // not set the nodePath, load from the config + if (m_nodePath.size() == 0) + { + m_nodePath = _pt.get("p2p.nodes_path", "./"); + } + + m_nodeFileName = _pt.get("p2p.nodes_file", "nodes.json"); + + m_smSSL = smSSL; + m_listenIP = listenIP; + m_listenPort = (uint16_t)listenPort; + + GATEWAY_CONFIG_LOG(INFO) << LOG_DESC("initP2PConfig ok!") << LOG_KV("listenIP", listenIP) + << LOG_KV("listenPort", listenPort) << LOG_KV("smSSL", smSSL) + << LOG_KV("nodePath", m_nodePath) + << LOG_KV("nodeFileName", m_nodeFileName); +} + +// load p2p connected peers +void GatewayConfig::loadP2pConnectedNodes() +{ + std::string nodeFilePath = m_nodePath + "/" + m_nodeFileName; + // load p2p connected nodes + std::set nodes; + auto jsonContent = readContentsToString(boost::filesystem::path(nodeFilePath)); + if (!jsonContent || jsonContent->empty()) + { + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment( + "initP2PConfig: unable to read nodes json file, path=" + nodeFilePath)); + } + + parseConnectedJson(*jsonContent.get(), nodes); + m_connectedNodes = nodes; + + GATEWAY_CONFIG_LOG(INFO) << LOG_DESC("loadP2pConnectedNodes ok!") + << LOG_KV("nodePath", m_nodePath) + << LOG_KV("nodeFileName", m_nodeFileName) + << LOG_KV("nodes", nodes.size()); +} + +/// loads ca configuration items from the configuration file +void GatewayConfig::initCertConfig(const boost::property_tree::ptree& _pt) +{ + /* + [cert] + ; directory the certificates located in + ca_path=./ + ; the ca certificate file + ca_cert=ca.crt + ; the node private key file + node_key=ssl.key + ; the node certificate file + node_cert=ssl.crt + */ + if (m_certPath.size() == 0) + { + m_certPath = _pt.get("cert.ca_path", "./"); + } + std::string caCertFile = m_certPath + "/" + _pt.get("cert.ca_cert", "ca.crt"); + std::string nodeCertFile = m_certPath + "/" + _pt.get("cert.node_cert", "ssl.crt"); + std::string nodeKeyFile = m_certPath + "/" + _pt.get("cert.node_key", "ssl.key"); + + GATEWAY_CONFIG_LOG(INFO) << LOG_DESC("initCertConfig") << LOG_KV("ca_path", m_certPath) + << LOG_KV("ca_cert", caCertFile) << LOG_KV("node_cert", nodeCertFile) + << LOG_KV("node_key", nodeKeyFile); + + checkFileExist(caCertFile); + checkFileExist(nodeCertFile); + checkFileExist(nodeKeyFile); + + CertConfig certConfig; + certConfig.caCert = caCertFile; + certConfig.nodeCert = nodeCertFile; + certConfig.nodeKey = nodeKeyFile; + + m_certConfig = certConfig; + + GATEWAY_CONFIG_LOG(INFO) << LOG_DESC("initCertConfig") << LOG_KV("ca", certConfig.caCert) + << LOG_KV("node_cert", certConfig.nodeCert) + << LOG_KV("node_key", certConfig.nodeKey); +} + +// loads sm ca configuration items from the configuration file +void GatewayConfig::initSMCertConfig(const boost::property_tree::ptree& _pt) +{ + /* + [cert] + ; directory the certificates located in + ca_path=./ + ; the ca certificate file + sm_ca_cert=sm_ca.crt + ; the node private key file + sm_node_key=sm_ssl.key + ; the node certificate file + sm_node_cert=sm_ssl.crt + ; the node private key file + sm_ennode_key=sm_enssl.key + ; the node certificate file + sm_ennode_cert=sm_enssl.crt + */ + // not set the certPath, load from the configuration + if (m_certPath.size() == 0) + { + m_certPath = _pt.get("cert.ca_path", "./"); + } + std::string smCaCertFile = + m_certPath + "/" + _pt.get("cert.sm_ca_cert", "sm_ca.crt"); + std::string smNodeCertFile = + m_certPath + "/" + _pt.get("cert.sm_node_cert", "sm_ssl.crt"); + std::string smNodeKeyFile = + m_certPath + "/" + _pt.get("cert.sm_node_key", "sm_ssl.key"); + std::string smEnNodeCertFile = + m_certPath + "/" + _pt.get("cert.sm_ennode_cert", "sm_enssl.crt"); + std::string smEnNodeKeyFile = + m_certPath + "/" + _pt.get("cert.sm_ennode_key", "sm_enssl.key"); + + checkFileExist(smCaCertFile); + checkFileExist(smNodeCertFile); + checkFileExist(smNodeKeyFile); + checkFileExist(smEnNodeCertFile); + checkFileExist(smEnNodeKeyFile); + + SMCertConfig smCertConfig; + smCertConfig.caCert = smCaCertFile; + smCertConfig.nodeCert = smNodeCertFile; + smCertConfig.nodeKey = smNodeKeyFile; + smCertConfig.enNodeCert = smEnNodeCertFile; + smCertConfig.enNodeKey = smEnNodeKeyFile; + + m_smCertConfig = smCertConfig; + + GATEWAY_CONFIG_LOG(INFO) << LOG_DESC("initSMCertConfig") << LOG_KV("ca_path", m_certPath) + << LOG_KV("sm_ca_cert", smCertConfig.caCert) + << LOG_KV("sm_node_cert", smCertConfig.nodeCert) + << LOG_KV("sm_node_key", smCertConfig.nodeKey) + << LOG_KV("sm_ennode_cert", smCertConfig.enNodeCert) + << LOG_KV("sm_ennode_key", smCertConfig.enNodeKey); +} + +// loads rate limit configuration items from the configuration file +void GatewayConfig::initRateLimitConfig(const boost::property_tree::ptree& _pt) +{ + /* + [flow_control] + ; the switch for distributed rate limit + ; enable_distributed_ratelimit=true + ; enable_distributed_ratelimit_cache=true + ; distributed_ratelimit_cache_percent=15 + ; + ; rate limiter stat reporter interval, unit: ms + ; stat_reporter_interval=60000 + + ; the module that does not limit bandwidth + ; list of all modules: raft,pbft,amop,block_sync,txs_sync,light_node,cons_txs_sync + ; + ; modules_without_bw_limit=raft,pbft + + ; restrict the outgoing bandwidth of the node + ; both integer and decimal is support, unit: Mb + ; + ; total_outgoing_bw_limit=10 + + ; restrict the outgoing bandwidth of the the connection + ; both integer and decimal is support, unit: Mb + ; + ; conn_outgoing_bw_limit=2 + ; + ; specify IP to limit bandwidth, format: conn_outgoing_bw_limit_x.x.x.x=n + ; conn_outgoing_bw_limit_192.108.0.1=3 + ; conn_outgoing_bw_limit_192.108.0.2=3 + ; conn_outgoing_bw_limit_192.108.0.3=3 + ; + ; default bandwidth limit for the group + ; group_outgoing_bw_limit=2 + ; + ; specify group to limit bandwidth, group_outgoing_bw_limit_groupName=n + ; group_outgoing_bw_limit_group0=2 + ; group_outgoing_bw_limit_group1=2 + ; group_outgoing_bw_limit_group2=2 + */ + + // enable_distributed_ratelimit=false + bool enableDistributedRatelimit = + _pt.get("flow_control.enable_distributed_ratelimit", false); + // enable_distributed_ratelimit=false + bool enableDistributedRateLimitCache = + _pt.get("flow_control.enable_distributed_ratelimit_cache", true); + // enable_distributed_ratelimit=false + int32_t distributedRateLimitCachePercent = + _pt.get("flow_control.distributed_ratelimit_cache_percent", 20); + // stat_reporter_interval=60000 + int32_t statInterval = _pt.get("flow_control.stat_reporter_interval", 60000); + + // modules_without_bw_limit=raft,pbft + std::string strModulesWithoutLimit = + _pt.get("flow_control.modules_without_bw_limit", "raft,pbft,cons_txs_sync"); + + std::set moduleIDs; + std::vector modules; + + if (!strModulesWithoutLimit.empty()) + { + boost::split( + modules, strModulesWithoutLimit, boost::is_any_of(","), boost::token_compress_on); + + for (auto module : modules) + { + boost::trim(module); + boost::algorithm::to_lower(module); + auto optModuleID = protocol::stringToModuleID(module); + if (!optModuleID.has_value()) + { + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "unrecognized module: " + module + + " ,list of available modules: " + "raft,pbft,amop,block_sync,txs_sync,light_node")); + } + moduleIDs.insert(optModuleID.value()); + } + } + + int64_t totalOutgoingBwLimit = -1; + // total_outgoing_bw_limit + std::string value = _pt.get("flow_control.total_outgoing_bw_limit", ""); + if (value.empty()) + { + GATEWAY_CONFIG_LOG(INFO) << LOG_DESC("the total_outgoing_bw_limit is not initialized"); + } + else + { + double bw = boost::lexical_cast(value); + totalOutgoingBwLimit = doubleMBToBit(bw); + + GATEWAY_CONFIG_LOG(INFO) << LOG_DESC("the total_outgoing_bw_limit has been initialized") + << LOG_KV("value", value) << LOG_KV("bw", bw) + << LOG_KV("totalOutgoingBwLimit", totalOutgoingBwLimit); + } + + + bool enableGroupRateLimit = false; + bool enableConRateLimit = false; + + int64_t connOutgoingBwLimit = -1; + // conn_outgoing_bw_limit + value = _pt.get("flow_control.conn_outgoing_bw_limit", ""); + if (value.empty()) + { + GATEWAY_CONFIG_LOG(INFO) << LOG_DESC("the conn_outgoing_bw_limit is not initialized"); + } + else + { + enableConRateLimit = true; + auto bw = boost::lexical_cast(value); + connOutgoingBwLimit = doubleMBToBit(bw); + + GATEWAY_CONFIG_LOG(INFO) << LOG_DESC("the conn_outgoing_bw_limit has been initialized") + << LOG_KV("value", value) << LOG_KV("bw", bw) + << LOG_KV("connOutgoingBwLimit", connOutgoingBwLimit); + } + + int64_t groupOutgoingBwLimit = -1; + // group_outgoing_bw_limit + value = _pt.get("flow_control.group_outgoing_bw_limit", ""); + if (value.empty()) + { + GATEWAY_CONFIG_LOG(INFO) << LOG_DESC("the group_outgoing_bw_limit is not initialized"); + } + else + { + enableGroupRateLimit = true; + auto bw = boost::lexical_cast(value); + groupOutgoingBwLimit = doubleMBToBit(bw); + + GATEWAY_CONFIG_LOG(INFO) << LOG_DESC("the group_outgoing_bw_limit has been initialized") + << LOG_KV("value", value) << LOG_KV("bw", bw) + << LOG_KV("groupOutgoingBwLimit", groupOutgoingBwLimit); + } + + // ip => bw && group => bw + if (_pt.get_child_optional("flow_control")) + { + for (auto const& it : _pt.get_child("flow_control")) + { + auto key = it.first; + auto value = it.second.data(); + + boost::trim(key); + boost::trim(value); + + if (boost::starts_with(key, "conn_outgoing_bw_limit_")) + { + enableConRateLimit = true; + // ip_outgoing_bw_x.x.x.x = + std::string ip = key.substr(strlen("conn_outgoing_bw_limit_")); + if (!isValidIP(ip)) + { + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment( + "flow_control.ip_outgoing_bw_x.x.x.x config, invalid ip format, ip: " + + ip)); + } + auto bw = boost::lexical_cast(value); + m_rateLimiterConfig.ip2BwLimit[ip] = doubleMBToBit(bw); + + GATEWAY_CONFIG_LOG(INFO) + << LOG_BADGE("initRateLimiterConfig") << LOG_DESC("add ip bandwidth limit") + << LOG_KV("ip", ip) << LOG_KV("bandwidth", bw); + } // group_outgoing_bw_group0 + else if (boost::starts_with(key, "group_outgoing_bw_limit_")) + { + enableGroupRateLimit = true; + // group_xxxx = + std::string group = key.substr(strlen("group_outgoing_bw_limit_")); + auto bw = boost::lexical_cast(value); + m_rateLimiterConfig.group2BwLimit[group] = doubleMBToBit(bw); + + GATEWAY_CONFIG_LOG(INFO) + << LOG_BADGE("initRateLimiterConfig") << LOG_DESC("add group bandwidth limit") + << LOG_KV("group", group) << LOG_KV("bandwidth", bw); + } + } + } + + m_rateLimiterConfig.statInterval = statInterval; + m_rateLimiterConfig.modulesWithoutLimit = moduleIDs; + m_rateLimiterConfig.totalOutgoingBwLimit = totalOutgoingBwLimit; + m_rateLimiterConfig.connOutgoingBwLimit = connOutgoingBwLimit; + m_rateLimiterConfig.groupOutgoingBwLimit = groupOutgoingBwLimit; + m_rateLimiterConfig.enableDistributedRatelimit = enableDistributedRatelimit; + m_rateLimiterConfig.enableDistributedRateLimitCache = enableDistributedRateLimitCache; + m_rateLimiterConfig.distributedRateLimitCachePercent = distributedRateLimitCachePercent; + m_rateLimiterConfig.enableGroupRateLimit = enableGroupRateLimit; + m_rateLimiterConfig.enableConRateLimit = enableConRateLimit; + + if (totalOutgoingBwLimit > 0 && connOutgoingBwLimit > 0 && + connOutgoingBwLimit > totalOutgoingBwLimit) + { + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "flow_control.conn_outgoing_bw_limit should not greater " + "than flow_control.total_outgoing_bw_limit")); + } + + if (totalOutgoingBwLimit > 0 && groupOutgoingBwLimit > 0 && + groupOutgoingBwLimit > totalOutgoingBwLimit) + { + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "flow_control.group_outgoing_bw_limit should not greater " + "than flow_control.total_outgoing_bw_limit")); + } + + GATEWAY_CONFIG_LOG(INFO) << LOG_BADGE("initRateLimiterConfig") + << LOG_KV("enableRateLimit", m_rateLimiterConfig.enableRateLimit()) + << LOG_KV("statInterval", statInterval) + << LOG_KV("enableDistributedRatelimit", enableDistributedRatelimit) + << LOG_KV("enableDistributedRateLimitCache", + enableDistributedRateLimitCache) + << LOG_KV("distributedRateLimitCachePercent", + distributedRateLimitCachePercent) + << LOG_KV("enableGroupRateLimit", enableGroupRateLimit) + << LOG_KV("enableConRateLimit", enableConRateLimit) + << LOG_KV("totalOutgoingBwLimit", totalOutgoingBwLimit) + << LOG_KV("connOutgoingBwLimit", connOutgoingBwLimit) + << LOG_KV("groupOutgoingBwLimit", groupOutgoingBwLimit) + << LOG_KV("moduleIDs", boost::join(modules, ",")) + << LOG_KV("ips size", m_rateLimiterConfig.ip2BwLimit.size()) + << LOG_KV("groups size", m_rateLimiterConfig.group2BwLimit.size()); + + if (m_rateLimiterConfig.enableDistributedRatelimit) + { + GATEWAY_CONFIG_LOG(INFO) + << LOG_BADGE("initRateLimiterConfig") + << LOG_DESC( + "distributed ratelimit switch is turn on, then load the redis configurations"); + + initRedisConfig(_pt); + } +} + +// loads redis config +void GatewayConfig::initRedisConfig(const boost::property_tree::ptree& _pt) +{ + /* + [redis] + server_ip= + server_port= + request_timeout= + connection_pool_size= + password= + db= + */ + + // server_ip + std::string redisServerIP = _pt.get("redis.server_ip", ""); + if (redisServerIP.empty()) + { + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "initRedisConfig: invalid redis.server_ip! Must be non-empty!")); + } + + if (!isValidIP(redisServerIP)) + { + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "initRedisConfig: invalid redis.server_ip! Invalid ip format!")); + } + + // server_port + uint16_t redisServerPort = _pt.get("redis.server_port", 0); + if (!isValidPort(redisServerPort)) + { + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment("initRedisConfig: invalid redis.server_port! " + "redis port must be in range (1024,65535]!")); + } + + // request_timeout + int32_t redisTimeout = _pt.get("redis.request_timeout", -1); + + // connection_pool_size + int32_t redisPoolSize = _pt.get("redis.connection_pool_size", 16); + + // password + std::string redisPassword = _pt.get("redis.password", ""); + + // db + int redisDB = _pt.get("redis.db", 0); + + m_redisConfig.host = redisServerIP; + m_redisConfig.port = redisServerPort; + m_redisConfig.timeout = redisTimeout; + m_redisConfig.connectionPoolSize = redisPoolSize; + m_redisConfig.password = redisPassword; + m_redisConfig.db = redisDB; + + GATEWAY_CONFIG_LOG(INFO) << LOG_BADGE("initRedisConfig") + << LOG_KV("redisServerIP", redisServerIP) + << LOG_KV("redisServerPort", redisServerPort) + << LOG_KV("redisDB", redisDB) << LOG_KV("redisTimeout", redisTimeout) + << LOG_KV("redisPoolSize", redisPoolSize) + << LOG_KV("redisPassword", redisPassword); +} + +void GatewayConfig::checkFileExist(const std::string& _path) +{ + auto fileContent = readContentsToString(boost::filesystem::path(_path)); + if (!fileContent || fileContent->empty()) + { + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment("checkFileExist: unable to load file content " + " maybe file not exist, path: " + + _path)); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/GatewayConfig.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/GatewayConfig.h" new file mode 100644 index 00000000..2c28ab2f --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/GatewayConfig.h" @@ -0,0 +1,189 @@ +/** @file GatewayConfig.h + * @author octopus + * @date 2021-05-19 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace gateway +{ +class GatewayConfig +{ +public: + using Ptr = std::shared_ptr; + + GatewayConfig() = default; + ~GatewayConfig() = default; + +public: + // cert for ssl connection + struct CertConfig + { + std::string caCert; + std::string nodeKey; + std::string nodeCert; + }; + + // cert for sm ssl connection + struct SMCertConfig + { + std::string caCert; + std::string nodeCert; + std::string nodeKey; + std::string enNodeCert; + std::string enNodeKey; + }; + + // config for redis + struct RedisConfig + { + // redis server ip + std::string host; + // redis server port + uint16_t port; + // redis request timeout + int32_t timeout = -1; + // redis connection pool size, default 16 + int32_t connectionPoolSize = 16; + // redis password, default empty + std::string password; + // redis db, default 0th + int db = 0; + }; + + // config for rate limit + struct RateLimiterConfig + { + bool enableGroupRateLimit = false; + bool enableConRateLimit = false; + + // if turn on distributed ratelimit + bool enableDistributedRatelimit = false; + // + bool enableDistributedRateLimitCache = true; + // + int32_t distributedRateLimitCachePercent = 20; + // stat reporter interval, unit: ms + int32_t statInterval = 60000; + + // total outgoing bandwidth limit + int64_t totalOutgoingBwLimit = -1; + + // per connection outgoing bandwidth limit + int64_t connOutgoingBwLimit = -1; + // specify IP bandwidth limiting + std::unordered_map ip2BwLimit; + + // per connection outgoing bandwidth limit + int64_t groupOutgoingBwLimit = -1; + // specify group bandwidth limiting + std::unordered_map group2BwLimit; + + // the message of modules that do not limit bandwidth + std::set modulesWithoutLimit; + + // whether any configuration takes effect + bool enableRateLimit() const + { + if (totalOutgoingBwLimit > 0 || connOutgoingBwLimit > 0 || groupOutgoingBwLimit > 0) + { + return true; + } + + if (!group2BwLimit.empty() || !ip2BwLimit.empty()) + { + return true; + } + + return false; + } + }; + + /** + * @brief: loads configuration items from the config.ini + * @param _configPath: config.ini path + * @return void + */ + void initConfig(std::string const& _configPath, bool _uuidRequired = false); + + void setCertPath(std::string const& _certPath) { m_certPath = _certPath; } + void setNodePath(std::string const& _nodePath) { m_nodePath = _nodePath; } + void setNodeFileName(const std::string& _nodeFileName) { m_nodeFileName = _nodeFileName; } + + std::string const& certPath() const { return m_certPath; } + std::string const& nodePath() const { return m_nodePath; } + std::string const& nodeFileName() const { return m_nodeFileName; } + + // check if the port valid + bool isValidPort(int port); + // check if the ip valid + bool isValidIP(const std::string& _ip); + // MB to bit + int64_t doubleMBToBit(double _d); + void hostAndPort2Endpoint(const std::string& _host, NodeIPEndpoint& _endpoint); + void parseConnectedJson(const std::string& _json, std::set& _nodeIPEndpointSet); + // loads p2p configuration items from the configuration file + void initP2PConfig(const boost::property_tree::ptree& _pt, bool _uuidRequired); + // loads ca configuration items from the configuration file + void initCertConfig(const boost::property_tree::ptree& _pt); + // loads sm ca configuration items from the configuration file + void initSMCertConfig(const boost::property_tree::ptree& _pt); + // loads ratelimit config + void initRateLimitConfig(const boost::property_tree::ptree& _pt); + // loads redis config + void initRedisConfig(const boost::property_tree::ptree& _pt); + // check if file exist, exception will be throw if the file not exist + void checkFileExist(const std::string& _path); + // load p2p connected peers + void loadP2pConnectedNodes(); + + std::string listenIP() const { return m_listenIP; } + uint16_t listenPort() const { return m_listenPort; } + uint32_t threadPoolSize() const { return m_threadPoolSize; } + bool smSSL() const { return m_smSSL; } + + CertConfig certConfig() const { return m_certConfig; } + SMCertConfig smCertConfig() const { return m_smCertConfig; } + RateLimiterConfig rateLimiterConfig() const { return m_rateLimiterConfig; } + RedisConfig redisConfig() const { return m_redisConfig; } + + const std::set& connectedNodes() const { return m_connectedNodes; } + + std::string const& uuid() const { return m_uuid; } + void setUUID(std::string const& _uuid) { m_uuid = _uuid; } + +private: + std::string m_uuid; + // if SM SSL connection or not + bool m_smSSL; + // p2p network listen IP + std::string m_listenIP; + // p2p network listen Port + uint16_t m_listenPort; + // threadPool size + uint32_t m_threadPoolSize{16}; + // p2p connected nodes host list + std::set m_connectedNodes; + // cert config for ssl connection + CertConfig m_certConfig; + SMCertConfig m_smCertConfig; + + RateLimiterConfig m_rateLimiterConfig; + RedisConfig m_redisConfig; + + std::string m_certPath; + std::string m_nodePath; + std::string m_nodeFileName; +}; + +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/GatewayFactory.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/GatewayFactory.cpp" new file mode 100644 index 00000000..86f88be6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/GatewayFactory.cpp" @@ -0,0 +1,779 @@ +/** @file GatewayFactory.cpp + * @author octopus + * @date 2021-05-17 + */ + +#include "bcos-gateway/libratelimit/DistributedRateLimiter.h" +#include "bcos-gateway/libratelimit/GatewayRateLimiter.h" +#include "bcos-utilities/BoostLog.h" +#include "bcos-utilities/Common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos::rpc; +using namespace bcos; +using namespace security; +using namespace gateway; +using namespace bcos::amop; +using namespace bcos::protocol; +using namespace bcos::boostssl; + +// register the function fetch pub hex from the cert +void GatewayFactory::initCert2PubHexHandler() +{ + auto handler = [](const std::string& _cert, std::string& _pubHex) -> bool { + std::string errorMessage; + do + { + auto certContent = readContentsToString(boost::filesystem::path(_cert)); + if (!certContent || certContent->empty()) + { + errorMessage = "unable to load cert content, cert: " + _cert; + break; + } + + GATEWAY_FACTORY_LOG(INFO) << LOG_DESC("initCert2PubHexHandler") << LOG_KV("cert", _cert) + << LOG_KV("certContent: ", certContent); + + std::shared_ptr bioMem(BIO_new(BIO_s_mem()), [](BIO* p) { + if (p != NULL) + { + BIO_free(p); + } + }); + + if (!bioMem) + { + errorMessage = "BIO_new error"; + break; + } + + BIO_write(bioMem.get(), certContent->data(), certContent->size()); + std::shared_ptr x509Ptr( + PEM_read_bio_X509(bioMem.get(), NULL, NULL, NULL), [](X509* p) { + if (p != NULL) + { + X509_free(p); + } + }); + + if (!x509Ptr) + { + errorMessage = "PEM_read_bio_X509 error"; + break; + } + + ASN1_BIT_STRING* pubKey = X509_get0_pubkey_bitstr(x509Ptr.get()); + if (!pubKey) + { + errorMessage = "X509_get0_pubkey_bitstr error"; + break; + } + + auto hex = bcos::toHexString(pubKey->data, pubKey->data + pubKey->length, ""); + _pubHex = *hex.get(); + + GATEWAY_FACTORY_LOG(INFO) << LOG_DESC("initCert2PubHexHandler ") + << LOG_KV("cert", _cert) << LOG_KV("pubHex: ", _pubHex); + return true; + } while (0); + + GATEWAY_FACTORY_LOG(ERROR) << LOG_DESC("initCert2PubHexHandler") << LOG_KV("cert", _cert) + << LOG_KV("errorMessage", errorMessage); + return false; + }; + + m_certPubHexHandler = handler; +} + +// register the function fetch public key from the ssl context +void GatewayFactory::initSSLContextPubHexHandler() +{ + auto handler = [](X509* x509, std::string& _pubHex) -> bool { + ASN1_BIT_STRING* pubKey = + X509_get0_pubkey_bitstr(x509); // csc->current_cert is an X509 struct + if (pubKey == NULL) + { + GATEWAY_FACTORY_LOG(ERROR) + << LOG_DESC("initSSLContextPubHexHandler X509_get0_pubkey_bitstr error"); + return false; + } + + auto hex = bcos::toHexString(pubKey->data, pubKey->data + pubKey->length, ""); + _pubHex = *hex.get(); + + GATEWAY_FACTORY_LOG(INFO) << LOG_DESC("[NEW]SSLContext pubHex: " + _pubHex); + return true; + }; + + m_sslContextPubHandler = handler; +} + +std::shared_ptr GatewayFactory::buildSSLContext( + bool _server, const GatewayConfig::CertConfig& _certConfig) +{ + std::ignore = _server; + std::shared_ptr sslContext = + std::make_shared(boost::asio::ssl::context::tlsv12); + /* + std::shared_ptr ecdh(EC_KEY_new_by_curve_name(NID_secp384r1), + [](EC_KEY *p) { EC_KEY_free(p); }); + SSL_CTX_set_tmp_ecdh(sslContext->native_handle(), ecdh.get()); + + sslContext->set_verify_mode(boost::asio::ssl::context_base::verify_none); + */ + std::shared_ptr keyContent; + if (!_certConfig.nodeKey.empty()) + { + try + { + if (nullptr == m_dataEncrypt) // storage_security.enable = false + keyContent = readContents(boost::filesystem::path(_certConfig.nodeKey)); + else + keyContent = m_dataEncrypt->decryptFile(_certConfig.nodeKey); + } + catch (std::exception& e) + { + GATEWAY_FACTORY_LOG(ERROR) + << LOG_BADGE("SecureInitializer") << LOG_DESC("open privateKey failed") + << LOG_KV("file", _certConfig.nodeKey); + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment( + "buildSSLContext: unable read content of key: " + _certConfig.nodeKey)); + } + } + if (!keyContent || keyContent->empty()) + { + GATEWAY_FACTORY_LOG(ERROR) + << LOG_DESC("buildSSLContext: unable read content of key: " + _certConfig.nodeKey); + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment( + "buildSSLContext: unable read content of key: " + _certConfig.nodeKey)); + } + + boost::asio::const_buffer keyBuffer(keyContent->data(), keyContent->size()); + sslContext->use_private_key(keyBuffer, boost::asio::ssl::context::file_format::pem); + + // node.crt + sslContext->use_certificate_chain_file(_certConfig.nodeCert); + /*if (!SSL_CTX_get0_certificate(sslContext->native_handle())) { + GATEWAY_FACTORY_LOG(ERROR) + << LOG_DESC("buildSSLContext: SSL_CTX_get0_certificate failed"); + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment( + "buildSSLContext: SSL_CTX_get0_certificate failed, node_cert=" + + _certConfig.nodeCert)); + }*/ + + auto caCertContent = + readContentsToString(boost::filesystem::path(_certConfig.caCert)); // ca.crt + if (!caCertContent || caCertContent->empty()) + { + GATEWAY_FACTORY_LOG(ERROR) + << LOG_DESC("buildSSLContext: unable read content of ca: " + _certConfig.caCert); + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment( + "buildSSLContext: unable read content of ca: " + _certConfig.caCert)); + } + sslContext->add_certificate_authority( + boost::asio::const_buffer(caCertContent->data(), caCertContent->size())); + + std::string caPath; + if (!caPath.empty()) + { + sslContext->add_verify_path(caPath); + } + + sslContext->set_verify_mode(boost::asio::ssl::context_base::verify_peer | + boost::asio::ssl::verify_fail_if_no_peer_cert); + + return sslContext; +} + +std::shared_ptr GatewayFactory::buildSSLContext( + bool _server, const GatewayConfig::SMCertConfig& _smCertConfig) +{ + SSL_CTX* ctx = NULL; + if (_server) + { + const SSL_METHOD* meth = SSLv23_server_method(); + ctx = SSL_CTX_new(meth); + } + else + { + const SSL_METHOD* meth = CNTLS_client_method(); + ctx = SSL_CTX_new(meth); + } + + std::shared_ptr sslContext = + std::make_shared(ctx); + + sslContext->set_verify_mode(boost::asio::ssl::context_base::verify_none); + + /* Load the server certificate into the SSL_CTX structure */ + if (SSL_CTX_use_certificate_file( + sslContext->native_handle(), _smCertConfig.nodeCert.c_str(), SSL_FILETYPE_PEM) <= 0) + { + ERR_print_errors_fp(stderr); + BOOST_THROW_EXCEPTION(std::runtime_error("SSL_CTX_use_certificate_file error")); + } + + std::shared_ptr keyContent; + if (!_smCertConfig.nodeKey.empty()) + { + try + { + if (nullptr == m_dataEncrypt) // storage_security.enable = false + keyContent = readContents(boost::filesystem::path(_smCertConfig.nodeKey)); + else + keyContent = m_dataEncrypt->decryptFile(_smCertConfig.nodeKey); + } + catch (std::exception& e) + { + GATEWAY_FACTORY_LOG(ERROR) + << LOG_BADGE("SecureInitializer") << LOG_DESC("open privateKey failed") + << LOG_KV("file", _smCertConfig.nodeKey); + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment( + "buildSSLContext: unable read content of key: " + _smCertConfig.nodeKey)); + } + } + // nodekey + boost::asio::const_buffer keyBuffer(keyContent->data(), keyContent->size()); + sslContext->use_private_key(keyBuffer, boost::asio::ssl::context::file_format::pem); + + /* Check if the server certificate and private-key matches */ + if (!SSL_CTX_check_private_key(sslContext->native_handle())) + { + ERR_print_errors_fp(stderr); + BOOST_THROW_EXCEPTION(std::runtime_error("SSL_CTX_check_private_key error")); + } + + std::shared_ptr enNodeKeyContent; + if (!_smCertConfig.enNodeKey.empty()) + { + try + { + if (nullptr == m_dataEncrypt) // storage_security.enable = false + enNodeKeyContent = readContents(boost::filesystem::path(_smCertConfig.enNodeKey)); + else + enNodeKeyContent = m_dataEncrypt->decryptFile(_smCertConfig.enNodeKey); + } + catch (std::exception& e) + { + GATEWAY_FACTORY_LOG(ERROR) + << LOG_BADGE("SecureInitializer") << LOG_DESC("open privateKey failed") + << LOG_KV("file", _smCertConfig.enNodeKey); + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment( + "buildSSLContext: unable read content of key: " + _smCertConfig.enNodeKey)); + } + } + + + /* Load the server encrypt certificate into the SSL_CTX structure */ + if (SSL_CTX_use_enc_certificate_file( + sslContext->native_handle(), _smCertConfig.enNodeCert.c_str(), SSL_FILETYPE_PEM) <= 0) + { + ERR_print_errors_fp(stderr); + BOOST_THROW_EXCEPTION(std::runtime_error("SSL_CTX_use_enc_certificate_file error")); + } + std::string enNodeKeyStr((const char*)enNodeKeyContent->data(), enNodeKeyContent->size()); + + if (SSL_CTX_use_enc_PrivateKey(sslContext->native_handle(), toEvpPkey(enNodeKeyStr.c_str())) <= + 0) + { + GATEWAY_FACTORY_LOG(ERROR) << LOG_DESC("SSL_CTX_use_enc_PrivateKey error"); + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment("GatewayFactory::buildSSLContext " + "SSL_CTX_use_enc_PrivateKey error")); + } + + auto caContent = + readContentsToString(boost::filesystem::path(_smCertConfig.caCert)); // node.key content + + sslContext->add_certificate_authority( + boost::asio::const_buffer(caContent->data(), caContent->size())); + + std::string caPath; + if (!caPath.empty()) + { + sslContext->add_verify_path(caPath); + } + + sslContext->set_verify_mode(boost::asio::ssl::context_base::verify_peer | + boost::asio::ssl::verify_fail_if_no_peer_cert); + + return sslContext; +} + +/** + * @brief: construct Gateway + * @param _configPath: config.ini path + * @return void + */ +std::shared_ptr GatewayFactory::buildGateway(const std::string& _configPath, + bool _airVersion, bcos::election::LeaderEntryPointInterface::Ptr _entryPoint, + std::string const& _gatewayServiceName) +{ + auto config = std::make_shared(); + // load config + if (_airVersion) + { + // the air mode not require the uuid(use p2pID as uuid by default) + config->initConfig(_configPath, false); + } + else + { + // the pro mode require the uuid + config->initConfig(_configPath, true); + } + config->loadP2pConnectedNodes(); + return buildGateway(config, _airVersion, _entryPoint, _gatewayServiceName); +} + +std::shared_ptr GatewayFactory::buildGatewayRateLimiter( + const GatewayConfig::RateLimiterConfig& _rateLimiterConfig, + const GatewayConfig::RedisConfig& _redisConfig) +{ + auto rateLimiterStat = std::make_shared(); + rateLimiterStat->setStatInterval(_rateLimiterConfig.statInterval); + + // redis instance + std::shared_ptr redis = nullptr; + if (_rateLimiterConfig.enableDistributedRatelimit) + { // init redis first + redis = initRedis(_redisConfig); + } + + auto rateLimiterManager = buildRateLimiterManager(_rateLimiterConfig, redis); + + auto gatewayRateLimiter = + std::make_shared(rateLimiterManager, rateLimiterStat); + + return gatewayRateLimiter; +} + +std::shared_ptr GatewayFactory::buildRateLimiterManager( + const GatewayConfig::RateLimiterConfig& _rateLimiterConfig, + std::shared_ptr _redis) +{ + // rate limiter factory + auto rateLimiterFactory = std::make_shared(_redis); + // rate limiter manager + auto rateLimiterManager = std::make_shared(_rateLimiterConfig); + + // total outgoing bandwidth Limit for p2p network + ratelimiter::RateLimiterInterface::Ptr totalOutgoingRateLimiter = nullptr; + if (_rateLimiterConfig.totalOutgoingBwLimit > 0) + { + totalOutgoingRateLimiter = rateLimiterFactory->buildTokenBucketRateLimiter( + _rateLimiterConfig.totalOutgoingBwLimit); + + rateLimiterManager->registerRateLimiter( + ratelimiter::RateLimiterManager::TOTAL_OUTGOING_KEY, totalOutgoingRateLimiter); + } + + // ip connection => rate limit + if (!_rateLimiterConfig.ip2BwLimit.empty()) + { + for (const auto& [ip, bandWidth] : _rateLimiterConfig.ip2BwLimit) + { + auto rateLimiterInterface = rateLimiterFactory->buildTokenBucketRateLimiter(bandWidth); + rateLimiterManager->registerRateLimiter(ip, rateLimiterInterface); + } + } + + // group => rate limit + if (!_rateLimiterConfig.group2BwLimit.empty()) + { + for (const auto& [group, bandWidth] : _rateLimiterConfig.group2BwLimit) + { + ratelimiter::RateLimiterInterface::Ptr rateLimiterInterface = nullptr; + if (_rateLimiterConfig.enableDistributedRatelimit) + { + rateLimiterInterface = rateLimiterFactory->buildRedisDistributedRateLimiter( + rateLimiterFactory->toTokenKey(group), bandWidth, 1, + _rateLimiterConfig.enableDistributedRateLimitCache, + _rateLimiterConfig.distributedRateLimitCachePercent); + } + else + { + rateLimiterInterface = rateLimiterFactory->buildTokenBucketRateLimiter(bandWidth); + } + + rateLimiterManager->registerRateLimiter(group, rateLimiterInterface); + } + } + + // modules without bandwidth limit + rateLimiterManager->setModulesWithoutLimit(_rateLimiterConfig.modulesWithoutLimit); + rateLimiterManager->setRateLimiterFactory(rateLimiterFactory); + + return rateLimiterManager; +} + +/** + * @brief: construct Gateway + * @param _config: config parameter object + * @return void + */ +// Note: _gatewayServiceName is used to check the validation of groupInfo when localRouter update +// groupInfo +std::shared_ptr GatewayFactory::buildGateway(GatewayConfig::Ptr _config, bool _airVersion, + bcos::election::LeaderEntryPointInterface::Ptr _entryPoint, + std::string const& _gatewayServiceName) +{ + try + { + std::string nodeCert = + (_config->smSSL() ? _config->smCertConfig().nodeCert : _config->certConfig().nodeCert); + std::string pubHex; + if (!m_certPubHexHandler(nodeCert, pubHex)) + { + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "GatewayFactory::init unable parse myself pub id")); + } + + std::shared_ptr srvCtx = + (_config->smSSL() ? buildSSLContext(true, _config->smCertConfig()) : + buildSSLContext(true, _config->certConfig())); + + std::shared_ptr clientCtx = + (_config->smSSL() ? buildSSLContext(false, _config->smCertConfig()) : + buildSSLContext(false, _config->certConfig())); + + // init ASIOInterface + auto asioInterface = std::make_shared(); + asioInterface->setIOServicePool(std::make_shared()); + asioInterface->setSrvContext(srvCtx); + asioInterface->setClientContext(clientCtx); + asioInterface->setType(ASIOInterface::ASIO_TYPE::SSL); + + // Message Factory + auto messageFactory = std::make_shared(); + // Session Factory + auto sessionFactory = std::make_shared(pubHex); + // KeyFactory + auto keyFactory = std::make_shared(); + // init Host + auto host = std::make_shared(asioInterface, sessionFactory, messageFactory); + + host->setHostPort(_config->listenIP(), _config->listenPort()); + host->setThreadPool(std::make_shared("P2P", _config->threadPoolSize())); + host->setSSLContextPubHandler(m_sslContextPubHandler); + + // init Service + auto routerTableFactory = std::make_shared(); + auto service = std::make_shared(pubHex, routerTableFactory); + service->setHost(host); + service->setStaticNodes(_config->connectedNodes()); + + GATEWAY_FACTORY_LOG(INFO) << LOG_DESC("GatewayFactory::init") + << LOG_KV("myself pub id", pubHex) + << LOG_KV("rpcService", m_rpcServiceName) + << LOG_KV("gatewayServiceName", _gatewayServiceName); + service->setMessageFactory(messageFactory); + service->setKeyFactory(keyFactory); + + auto gatewayRateLimiter = + buildGatewayRateLimiter(_config->rateLimiterConfig(), _config->redisConfig()); + auto gatewayRateLimiterWeakPtr = + std::weak_ptr(gatewayRateLimiter); + + // init GatewayNodeManager + GatewayNodeManager::Ptr gatewayNodeManager; + AMOPImpl::Ptr amop; + if (_airVersion) + { + gatewayNodeManager = + std::make_shared(_config->uuid(), pubHex, keyFactory, service); + amop = buildLocalAMOP(service, pubHex); + } + else + { + // Note: no need to use nodeAliveDetector when enable failover + if (_entryPoint) + { + gatewayNodeManager = std::make_shared( + _config->uuid(), pubHex, keyFactory, service); + } + else + { + gatewayNodeManager = std::make_shared( + _config->uuid(), pubHex, keyFactory, service); + } + amop = buildAMOP(service, pubHex); + } + // init Gateway + auto gateway = std::make_shared( + m_chainID, service, gatewayNodeManager, amop, gatewayRateLimiter, _gatewayServiceName); + auto GatewayNodeManagerWeakPtr = std::weak_ptr(gatewayNodeManager); + // register disconnect handler + service->registerDisconnectHandler( + [GatewayNodeManagerWeakPtr](NetworkException e, P2PSession::Ptr p2pSession) { + (void)e; + auto gatewayNodeManager = GatewayNodeManagerWeakPtr.lock(); + if (gatewayNodeManager && p2pSession) + { + gatewayNodeManager->onRemoveNodeIDs(p2pSession->p2pID()); + } + }); + service->registerUnreachableHandler( + [GatewayNodeManagerWeakPtr](std::string const& _unreachableNode) { + auto nodeMgr = GatewayNodeManagerWeakPtr.lock(); + if (!nodeMgr) + { + return; + } + nodeMgr->onRemoveNodeIDs(_unreachableNode); + }); + + service->setBeforeMessageHandler([gatewayRateLimiterWeakPtr](SessionFace::Ptr _session, + Message::Ptr _msg, SessionCallbackFunc _callback) { + auto gatewayRateLimiter = gatewayRateLimiterWeakPtr.lock(); + if (!gatewayRateLimiter) + { + return true; + } + + GatewayMessageExtAttributes::Ptr msgExtAttributes = nullptr; + if (_msg->extAttributes()) + { + msgExtAttributes = + std::dynamic_pointer_cast(_msg->extAttributes()); + } + + std::string groupID = msgExtAttributes ? msgExtAttributes->groupID() : std::string(); + uint16_t moduleID = msgExtAttributes ? msgExtAttributes->moduleID() : 0; + std::string endpoint = _session->nodeIPEndpoint().address(); + uint64_t msgLength = _msg->length(); + + // bandwidth limit check + auto r = gatewayRateLimiter->checkOutGoing(endpoint, groupID, moduleID, msgLength); + if (!r.first && _callback) + { + _callback(NetworkException(BandwidthOverFlow, r.second), Message::Ptr()); + } + + return r.first; + }); + + service->setOnMessageHandler( + [gatewayRateLimiterWeakPtr](SessionFace::Ptr _session, Message::Ptr _message) { + auto gatewayRateLimiter = gatewayRateLimiterWeakPtr.lock(); + if (!gatewayRateLimiter) + { + return true; + } + + auto endpoint = _session->nodeIPEndpoint().address(); + auto msgLength = _message->length(); + gatewayRateLimiter->checkInComing(endpoint, msgLength); + + return true; + }); + + GATEWAY_FACTORY_LOG(INFO) << LOG_DESC("GatewayFactory::init ok"); + if (!_entryPoint) + { + return gateway; + } + initFailOver(gateway, _entryPoint); + + return gateway; + } + catch (const std::exception& e) + { + GATEWAY_FACTORY_LOG(ERROR) << LOG_DESC("GatewayFactory::init") + << LOG_KV("error", boost::diagnostic_information(e)); + BOOST_THROW_EXCEPTION(e); + } +} + +void GatewayFactory::initFailOver( + std::shared_ptr _gateWay, bcos::election::LeaderEntryPointInterface::Ptr _entryPoint) +{ + auto groupInfoCodec = std::make_shared(); + _entryPoint->addMemberChangeNotificationHandler( + [_gateWay, groupInfoCodec]( + std::string const& _leaderKey, bcos::protocol::MemberInterface::Ptr _leader) { + auto const& groupInfoStr = _leader->memberConfig(); + auto groupInfo = groupInfoCodec->deserialize(groupInfoStr); + GATEWAY_FACTORY_LOG(INFO) + << LOG_DESC("The leader entryPoint changed") << LOG_KV("key", _leaderKey) + << LOG_KV("memberID", _leader->memberID()) << LOG_KV("modifyIndex", _leader->seq()) + << LOG_KV("groupID", groupInfo->groupID()); + _gateWay->asyncNotifyGroupInfo(groupInfo, [](Error::Ptr&& _error) { + if (_error) + { + GATEWAY_FACTORY_LOG(INFO) << LOG_DESC("memberChangedNotification error") + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + return; + } + GATEWAY_FACTORY_LOG(INFO) << LOG_DESC("memberChangedNotification success"); + }); + }); + + _entryPoint->addMemberDeleteNotificationHandler( + [_gateWay, groupInfoCodec]( + std::string const& _leaderKey, bcos::protocol::MemberInterface::Ptr _leader) { + auto const& groupInfoStr = _leader->memberConfig(); + auto groupInfo = groupInfoCodec->deserialize(groupInfoStr); + GATEWAY_FACTORY_LOG(INFO) + << LOG_DESC("The leader entryPoint has been deleted") << LOG_KV("key", _leaderKey) + << LOG_KV("memberID", _leader->memberID()) << LOG_KV("modifyIndex", _leader->seq()) + << LOG_KV("groupID", groupInfo->groupID()); + auto nodeInfos = groupInfo->nodeInfos(); + for (auto const& node : nodeInfos) + { + _gateWay->unregisterNode(groupInfo->groupID(), node.second->nodeID()); + GATEWAY_FACTORY_LOG(INFO) + << LOG_DESC("unregisterNode") << LOG_KV("group", groupInfo->groupID()) + << LOG_KV("node", node.second->nodeID()); + } + }); + GATEWAY_FACTORY_LOG(INFO) << LOG_DESC("initFailOver for gateway success"); +} + +/** + * @brief + * + * @param _redisConfig + * @return std::shared_ptr + */ +std::shared_ptr GatewayFactory::initRedis( + const GatewayConfig::RedisConfig& _redisConfig) +{ + GATEWAY_FACTORY_LOG(INFO) << LOG_BADGE("initRedis") << LOG_DESC("start connect to redis") + << LOG_KV("host", _redisConfig.host) + << LOG_KV("port", _redisConfig.port) << LOG_KV("db", _redisConfig.db) + << LOG_KV("poolSize", _redisConfig.connectionPoolSize) + << LOG_KV("timeout", _redisConfig.timeout) + << LOG_KV("password", _redisConfig.password); + + sw::redis::ConnectionOptions connection_options; + connection_options.host = _redisConfig.host; // Required. + connection_options.port = _redisConfig.port; // Optional. + connection_options.db = _redisConfig.db; // Optional. Use the 0th database by default. + if (!_redisConfig.password.empty()) + { + connection_options.password = _redisConfig.password; // Optional. No password by default. + } + + // Optional. Timeout before we successfully send request to or receive response from redis. + // By default, the timeout is 0ms, i.e. never timeout and block until we send or receive + // successfully. NOTE: if any command is timed out, we throw a TimeoutError exception. + connection_options.socket_timeout = std::chrono::milliseconds(_redisConfig.timeout); + // connection_options.connect_timeout = std::chrono::milliseconds(3000); + connection_options.keep_alive = true; + + sw::redis::ConnectionPoolOptions pool_options; + // Pool size, i.e. max number of connections. + pool_options.size = _redisConfig.connectionPoolSize; + + std::shared_ptr redis = nullptr; + try + { + // Connect to Redis server with a connection pool. + redis = std::make_shared(connection_options, pool_options); + + // test whether redis functions properly + // 1. set key + // 2. get key + // 3. del key + + std::string key = "Gateway -> " + std::to_string(utcTime()); + std::string value = "Hello, FISCO-BCOS 3.0."; + + bool setR = redis->set(key, value); + if (setR) + { + GATEWAY_FACTORY_LOG(INFO) << LOG_BADGE("initRedis") << LOG_DESC("set ok"); + + auto getR = redis->get(key); + if (getR) + { + GATEWAY_FACTORY_LOG(INFO) << LOG_BADGE("initRedis") << LOG_DESC("get ok") + << LOG_KV("key", key) << LOG_KV("value", getR.value()); + } + else + { + GATEWAY_FACTORY_LOG(WARNING) + << LOG_BADGE("initRedis") << LOG_DESC("get failed, why???"); + } + + redis->del(key); + } + else + { + GATEWAY_FACTORY_LOG(WARNING) + << LOG_BADGE("initRedis") << LOG_DESC("set failed, why???"); + } + } + catch (std::exception& e) + { + // Note: redis++ exception handling + // https://github.com/sewenew/redis-plus-plus#exception + std::exception_ptr ePtr = std::make_exception_ptr(e); + + GATEWAY_FACTORY_LOG(ERROR) + << LOG_BADGE("initRedis") << LOG_DESC("initialize redis exception") + << LOG_KV("error", e.what()); + + std::throw_with_nested(e); + } + + GATEWAY_FACTORY_LOG(INFO) << LOG_BADGE("initRedis") << LOG_DESC("initialize redis completely"); + + return redis; +} + +bcos::amop::AMOPImpl::Ptr GatewayFactory::buildAMOP( + P2PInterface::Ptr _network, P2pID const& _p2pNodeID) +{ + auto topicManager = std::make_shared(m_rpcServiceName, _network); + auto amopMessageFactory = std::make_shared(); + auto requestFactory = std::make_shared(); + return std::make_shared( + topicManager, amopMessageFactory, requestFactory, _network, _p2pNodeID); +} + +bcos::amop::AMOPImpl::Ptr GatewayFactory::buildLocalAMOP( + P2PInterface::Ptr _network, P2pID const& _p2pNodeID) +{ + // Note: must set rpc to the topicManager before start the amop + auto topicManager = std::make_shared(m_rpcServiceName, _network); + auto amopMessageFactory = std::make_shared(); + auto requestFactory = std::make_shared(); + return std::make_shared( + topicManager, amopMessageFactory, requestFactory, _network, _p2pNodeID); +} diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/GatewayFactory.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/GatewayFactory.h" new file mode 100644 index 00000000..6887babf --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/GatewayFactory.h" @@ -0,0 +1,131 @@ +/** @file GatewayFactory.h + * @author octopus + * @date 2021-05-17 + */ + +#pragma once + +#include "bcos-gateway/libratelimit/GatewayRateLimiter.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace gateway +{ +class GatewayFactory +{ +public: + using Ptr = std::shared_ptr; + GatewayFactory(std::string const& _chainID, std::string const& _rpcServiceName, + bcos::security::DataEncryptInterface::Ptr _dataEncrypt = nullptr) + : m_chainID(_chainID), m_rpcServiceName(_rpcServiceName), m_dataEncrypt(_dataEncrypt) + { + initCert2PubHexHandler(); + initSSLContextPubHexHandler(); + } + + virtual ~GatewayFactory() = default; + + // init the function calc public hex from the cert + void initCert2PubHexHandler(); + // init the function calc public key from the ssl context + void initSSLContextPubHexHandler(); + + std::function sslContextPubHandler() + { + return m_sslContextPubHandler; + } + + std::function certPubHexHandler() + { + return m_certPubHexHandler; + } + + // build ssl context + std::shared_ptr buildSSLContext( + bool _server, const GatewayConfig::CertConfig& _certConfig); + // build sm ssl context + std::shared_ptr buildSSLContext( + bool _server, const GatewayConfig::SMCertConfig& _smCertConfig); + + // + std::shared_ptr buildRateLimiterManager( + const GatewayConfig::RateLimiterConfig& _rateLimiterConfig, + std::shared_ptr _redis); + + /** + * @brief construct Gateway + * + * @param _configPath + * @param _airVersion + * @param _entryPoint + * @param _gatewayServiceName + * @return Gateway::Ptr + */ + Gateway::Ptr buildGateway(const std::string& _configPath, bool _airVersion, + bcos::election::LeaderEntryPointInterface::Ptr _entryPoint, + std::string const& _gatewayServiceName); + + /** + * @brief construct Gateway + * + * @param _config + * @param _airVersion + * @param _entryPoint + * @param _gatewayServiceName + * @return Gateway::Ptr + */ + Gateway::Ptr buildGateway(GatewayConfig::Ptr _config, bool _airVersion, + bcos::election::LeaderEntryPointInterface::Ptr _entryPoint, + std::string const& _gatewayServiceName); + + /** + * @brief + * + * @param _rateLimiterConfig + * @param _redisConfig + * @return std::shared_ptr + */ + std::shared_ptr buildGatewayRateLimiter( + const GatewayConfig::RateLimiterConfig& _rateLimiterConfig, + const GatewayConfig::RedisConfig& _redisConfig); + + /** + * @brief + * + * @param _redisConfig + * @return std::shared_ptr + */ + std::shared_ptr initRedis(const GatewayConfig::RedisConfig& _redisConfig); + +protected: + virtual bcos::amop::AMOPImpl::Ptr buildAMOP( + bcos::gateway::P2PInterface::Ptr _network, bcos::gateway::P2pID const& _p2pNodeID); + virtual bcos::amop::AMOPImpl::Ptr buildLocalAMOP( + bcos::gateway::P2PInterface::Ptr _network, bcos::gateway::P2pID const& _p2pNodeID); + +private: + std::function m_sslContextPubHandler; + + std::function m_certPubHexHandler; + + void initFailOver(std::shared_ptr _gateWay, + bcos::election::LeaderEntryPointInterface::Ptr _entryPoint); + +private: + std::string m_chainID; + std::string m_rpcServiceName; + + bcos::security::DataEncryptInterface::Ptr m_dataEncrypt{nullptr}; +}; +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/FrontServiceInfo.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/FrontServiceInfo.h" new file mode 100644 index 00000000..90a71d3d --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/FrontServiceInfo.h" @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2021 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 FrontServiceInfo.h + * @author: octopus + * @date 2021-05-13 + */ +#pragma once +#include "bcos-tars-protocol/Common.h" +#include +#include +#include +#include +#include +#include +namespace bcos +{ +namespace gateway +{ +class FrontServiceInfo +{ +public: + using Ptr = std::shared_ptr; + FrontServiceInfo(std::string _nodeID, bcos::front::FrontServiceInterface::Ptr _frontService, + bcos::protocol::NodeType _type, bcostars::FrontServicePrx _frontServicePrx) + : m_nodeID(_nodeID), + m_nodeType(_type), + m_frontService(_frontService), + m_frontServicePrx(_frontServicePrx) + {} + bcos::front::FrontServiceInterface::Ptr frontService() { return m_frontService; } + bcostars::FrontServicePrx frontServicePrx() { return m_frontServicePrx; } + + bool unreachable() + { + if (!m_frontServicePrx) + { + return false; + } + + return !bcostars::checkConnection( + "FrontService", "unreachable", m_frontServicePrx, nullptr, false); + } + + std::string const& nodeID() const { return m_nodeID; } + + bcos::protocol::NodeType nodeType() const { return m_nodeType; } + + // the protocolInfo of the nodeService + void setProtocolInfo(bcos::protocol::ProtocolInfo::ConstPtr _protocolInfo) + { + m_protocolInfo = _protocolInfo; + } + bcos::protocol::ProtocolInfo::ConstPtr protocolInfo() const { return m_protocolInfo; } + +private: + std::string m_nodeID; + bcos::protocol::NodeType m_nodeType; + bcos::front::FrontServiceInterface::Ptr m_frontService; + bcostars::FrontServicePrx m_frontServicePrx; + + bcos::protocol::ProtocolInfo::ConstPtr m_protocolInfo; +}; +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayMessageExtAttributes.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayMessageExtAttributes.h" new file mode 100644 index 00000000..6217ab5a --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayMessageExtAttributes.h" @@ -0,0 +1,34 @@ +/** @file GatewayConfig.h + * @author octopus + * @date 2021-05-19 + */ + +#pragma once + +#include + +namespace bcos +{ +namespace gateway +{ + +class GatewayMessageExtAttributes : public MessageExtAttributes +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + +public: + uint16_t moduleID() { return m_moduleID; } + void setModuleID(uint16_t _moduleID) { m_moduleID = _moduleID; } + + std::string groupID() { return m_groupID; } + void setGroupID(const std::string& _groupID) { m_groupID = _groupID; } + +private: + uint16_t m_moduleID = 0; + std::string m_groupID; +}; + +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayNodeManager.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayNodeManager.cpp" new file mode 100644 index 00000000..e945d7a8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayNodeManager.cpp" @@ -0,0 +1,329 @@ + +/* + * Copyright (C) 2021 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 GatewayNodeManager.cpp + * @author: octopus + * @date 2021-05-13 + */ +#include "GatewayNodeManager.h" +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace gateway; +using namespace bcos::protocol; +using namespace bcos::group; +using namespace bcos::crypto; + +GatewayNodeManager::GatewayNodeManager(std::string const& _uuid, P2pID const& _nodeID, + std::shared_ptr _keyFactory, P2PInterface::Ptr _p2pInterface) + : GatewayNodeManager(_uuid, _keyFactory, _p2pInterface) +{ + m_uuid = _uuid; + if (_uuid.size() == 0) + { + m_uuid = _nodeID; + } + m_p2pNodeID = _nodeID; + m_p2pInterface = _p2pInterface; + // SyncNodeSeq + m_p2pInterface->registerHandlerByMsgType(GatewayMessageType::SyncNodeSeq, + boost::bind(&GatewayNodeManager::onReceiveStatusSeq, this, boost::placeholders::_1, + boost::placeholders::_2, boost::placeholders::_3)); + // RequestNodeStatus + m_p2pInterface->registerHandlerByMsgType(GatewayMessageType::RequestNodeStatus, + boost::bind(&GatewayNodeManager::onRequestNodeStatus, this, boost::placeholders::_1, + boost::placeholders::_2, boost::placeholders::_3)); + // ResponseNodeStatus + m_p2pInterface->registerHandlerByMsgType(GatewayMessageType::ResponseNodeStatus, + boost::bind(&GatewayNodeManager::onReceiveNodeStatus, this, boost::placeholders::_1, + boost::placeholders::_2, boost::placeholders::_3)); + m_timer = std::make_shared(SEQ_SYNC_PERIOD, "seqSync"); + // broadcast seq periodically + m_timer->registerTimeoutHandler([this]() { broadcastStatusSeq(); }); +} + +void GatewayNodeManager::stop() +{ + if (m_p2pInterface) + { + m_p2pInterface->eraseHandlerByMsgType(GatewayMessageType::SyncNodeSeq); + m_p2pInterface->eraseHandlerByMsgType(GatewayMessageType::RequestNodeStatus); + m_p2pInterface->eraseHandlerByMsgType(GatewayMessageType::ResponseNodeStatus); + } + if (m_timer) + { + m_timer->stop(); + } +} + +bool GatewayNodeManager::registerNode(const std::string& _groupID, bcos::crypto::NodeIDPtr _nodeID, + bcos::protocol::NodeType _nodeType, bcos::front::FrontServiceInterface::Ptr _frontService, + bcos::protocol::ProtocolInfo::ConstPtr _protocolInfo) +{ + auto ret = + m_localRouterTable->insertNode(_groupID, _nodeID, _nodeType, _frontService, _protocolInfo); + if (ret) + { + increaseSeq(); + } + return ret; +} + +bool GatewayNodeManager::unregisterNode(const std::string& _groupID, std::string const& _nodeID) +{ + auto ret = m_localRouterTable->removeNode(_groupID, _nodeID); + if (ret) + { + increaseSeq(); + } + return ret; +} + +void GatewayNodeManager::onReceiveStatusSeq( + NetworkException const& _e, P2PSession::Ptr _session, std::shared_ptr _msg) +{ + if (_e.errorCode()) + { + NODE_MANAGER_LOG(WARNING) << LOG_DESC("onReceiveStatusSeq error") + << LOG_KV("code", _e.errorCode()) << LOG_KV("msg", _e.what()); + return; + } + auto statusSeq = boost::asio::detail::socket_ops::network_to_host_long( + *((uint32_t*)_msg->payload()->data())); + auto const& from = (_msg->srcP2PNodeID().size() > 0) ? _msg->srcP2PNodeID() : _session->p2pID(); + auto statusSeqChanged = statusChanged(from, statusSeq); + if (!statusSeqChanged) + { + return; + } + NODE_MANAGER_LOG(TRACE) << LOG_DESC("onReceiveStatusSeq request nodeStatus") + << LOG_KV("from", from) << LOG_KV("statusSeq", statusSeq); + m_p2pInterface->asyncSendMessageByP2PNodeID( + GatewayMessageType::RequestNodeStatus, from, bytesConstRef()); +} + +bool GatewayNodeManager::statusChanged(std::string const& _p2pNodeID, uint32_t _seq) +{ + bool ret = true; + ReadGuard l(x_p2pID2Seq); + auto it = m_p2pID2Seq.find(_p2pNodeID); + if (it != m_p2pID2Seq.end()) + { + ret = (_seq > it->second); + } + return ret; +} + +void GatewayNodeManager::onReceiveNodeStatus( + NetworkException const& _e, P2PSession::Ptr _session, std::shared_ptr _msg) +{ + if (_e.errorCode()) + { + NODE_MANAGER_LOG(WARNING) << LOG_DESC("onReceiveNodeStatus error") + << LOG_KV("code", _e.errorCode()) << LOG_KV("msg", _e.what()); + return; + } + auto gatewayNodeStatus = m_gatewayNodeStatusFactory->createGatewayNodeStatus(); + gatewayNodeStatus->decode(bytesConstRef(_msg->payload()->data(), _msg->payload()->size())); + auto const& from = (_msg->srcP2PNodeID().size() > 0) ? _msg->srcP2PNodeID() : _session->p2pID(); + + NODE_MANAGER_LOG(INFO) << LOG_DESC("onReceiveNodeStatus") << LOG_KV("from", from) + << LOG_KV("seq", gatewayNodeStatus->seq()) + << LOG_KV("uuid", gatewayNodeStatus->uuid()); + updatePeerStatus(from, gatewayNodeStatus); +} + +void GatewayNodeManager::updatePeerStatus(std::string const& _p2pID, GatewayNodeStatus::Ptr _status) +{ + auto seq = _status->seq(); + { + UpgradableGuard l(x_p2pID2Seq); + if (m_p2pID2Seq.count(_p2pID) && (m_p2pID2Seq.at(_p2pID) >= seq)) + { + return; + } + UpgradeGuard ul(l); + m_p2pID2Seq[_p2pID] = seq; + } + // remove peers info + // insert the latest peers info + m_peersRouterTable->updatePeerStatus(_p2pID, _status); + // notify nodeIDs to front service + syncLatestNodeIDList(); +} + +bool GatewayNodeManager::updateFrontServiceInfo(bcos::group::GroupInfo::Ptr _groupInfo) +{ + auto updated = m_localRouterTable->updateGroupNodeInfos(_groupInfo); + if (updated) + { + increaseSeq(); + syncLatestNodeIDList(); + } + return updated; +} + +void GatewayNodeManager::onRequestNodeStatus( + NetworkException const& _e, P2PSession::Ptr _session, std::shared_ptr _msg) +{ + if (_e.errorCode()) + { + NODE_MANAGER_LOG(WARNING) << LOG_DESC("onRequestNodeStatus network error") + << LOG_KV("code", _e.errorCode()) << LOG_KV("msg", _e.what()); + return; + } + auto const& from = (_msg->srcP2PNodeID().size() > 0) ? _msg->srcP2PNodeID() : _session->p2pID(); + auto nodeStatusData = generateNodeStatus(); + if (!nodeStatusData) + { + NODE_MANAGER_LOG(WARNING) << LOG_DESC("onRequestNodeStatus: generate nodeInfo error") + << LOG_KV("from", from); + return; + } + NODE_MANAGER_LOG(TRACE) << LOG_DESC("onRequestNodeStatus") << LOG_KV("from", from); + m_p2pInterface->asyncSendMessageByP2PNodeID(GatewayMessageType::ResponseNodeStatus, from, + bytesConstRef((byte*)nodeStatusData->data(), nodeStatusData->size())); +} + +bytesPointer GatewayNodeManager::generateNodeStatus() +{ + auto nodeStatus = m_gatewayNodeStatusFactory->createGatewayNodeStatus(); + nodeStatus->setUUID(m_uuid); + nodeStatus->setSeq(statusSeq()); + auto nodeList = m_localRouterTable->nodeList(); + std::vector groupNodeInfos; + for (auto const& it : nodeList) + { + auto groupNodeInfo = m_gatewayNodeStatusFactory->createGroupNodeInfo(); + groupNodeInfo->setGroupID(it.first); + // get nodeID and type + std::vector nodeIDList; + std::vector protocolList; + + auto groupType = GroupType::OUTSIDE_GROUP; + bool hasObserverNode = false; + for (auto const& pNodeInfo : it.second) + { + nodeIDList.emplace_back(pNodeInfo.first); + protocolList.emplace_back(pNodeInfo.second->protocolInfo()); + if ((NodeType)(pNodeInfo.second->nodeType()) == NodeType::CONSENSUS_NODE) + { + groupType = GroupType::GROUP_WITH_CONSENSUS_NODE; + } + // the group has observerNode + if ((NodeType)(pNodeInfo.second->nodeType()) == NodeType::OBSERVER_NODE) + { + hasObserverNode = true; + } + } + // the group has only observerNode + if (groupType == GroupType::OUTSIDE_GROUP && hasObserverNode) + { + groupType = GroupType::GROUP_WITHOUT_CONSENSUS_NODE; + } + groupNodeInfo->setType(groupType); + groupNodeInfo->setNodeIDList(std::move(nodeIDList)); + groupNodeInfo->setNodeProtocolList(std::move(protocolList)); + groupNodeInfos.emplace_back(groupNodeInfo); + NODE_MANAGER_LOG(INFO) << LOG_DESC("generateNodeStatus") << LOG_KV("groupType", groupType) + << LOG_KV("groupID", it.first) + << LOG_KV("nodeListSize", groupNodeInfo->nodeIDList().size()) + << LOG_KV("seq", statusSeq()); + } + NODE_MANAGER_LOG(INFO) << LOG_DESC("generateNodeStatus") + << LOG_KV("nodeListSize", nodeList.size()); + nodeStatus->setGroupNodeInfos(std::move(groupNodeInfos)); + return nodeStatus->encode(); +} + +void GatewayNodeManager::onRemoveNodeIDs(const P2pID& _p2pID) +{ + NODE_MANAGER_LOG(INFO) << LOG_DESC("onRemoveNodeIDs") << LOG_KV("p2pid", _p2pID); + { + // remove statusSeq info + WriteGuard l(x_p2pID2Seq); + m_p2pID2Seq.erase(_p2pID); + } + m_peersRouterTable->removeP2PID(_p2pID); + // notify nodeIDs to front service + syncLatestNodeIDList(); +} + + +void GatewayNodeManager::broadcastStatusSeq() +{ + m_timer->restart(); + auto message = + std::static_pointer_cast(m_p2pInterface->messageFactory()->buildMessage()); + message->setPacketType(GatewayMessageType::SyncNodeSeq); + auto seq = statusSeq(); + auto statusSeq = boost::asio::detail::socket_ops::host_to_network_long(seq); + auto payload = std::make_shared((byte*)&statusSeq, (byte*)&statusSeq + 4); + message->setPayload(payload); + NODE_MANAGER_LOG(TRACE) << LOG_DESC("broadcastStatusSeq") << LOG_KV("seq", seq); + m_p2pInterface->asyncBroadcastMessage(message, Options()); +} + +void GatewayNodeManager::syncLatestNodeIDList() +{ + auto nodeList = m_localRouterTable->nodeList(); + for (auto const& it : nodeList) + { + auto groupID = it.first; + auto const& localNodeEntryPoints = it.second; + auto groupNodeInfos = getGroupNodeInfoList(groupID); + NODE_MANAGER_LOG(INFO) << LOG_DESC("syncLatestNodeIDList") << LOG_KV("groupID", groupID) + << LOG_KV("nodeCount", groupNodeInfos->nodeIDList().size()); + for (const auto& entry : localNodeEntryPoints) + { + entry.second->frontService()->onReceiveGroupNodeInfo( + groupID, groupNodeInfos, [](Error::Ptr _error) { + if (!_error) + { + return; + } + NODE_MANAGER_LOG(WARNING) + << LOG_DESC("syncLatestNodeIDList onReceiveGroupNodeInfo callback") + << LOG_KV("codeCode", _error->errorCode()) + << LOG_KV("codeMessage", _error->errorMessage()); + }); + } + } +} + +GroupNodeInfo::Ptr GatewayNodeManager::getGroupNodeInfoList(const std::string& _groupID) +{ + auto groupNodeInfo = m_gatewayNodeStatusFactory->createGroupNodeInfo(); + groupNodeInfo->setGroupID(_groupID); + + m_localRouterTable->getGroupNodeInfoList(groupNodeInfo, _groupID); + m_peersRouterTable->getGroupNodeInfoList(groupNodeInfo, _groupID); + return groupNodeInfo; +} + +std::map, std::less<>> GatewayNodeManager::peersNodeIDList( + std::string const& _p2pNodeID) +{ + if (_p2pNodeID == m_p2pNodeID) + { + return m_localRouterTable->nodeListInfo(); + } + return m_peersRouterTable->peersNodeIDList(_p2pNodeID); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayNodeManager.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayNodeManager.h" new file mode 100644 index 00000000..272b7bae --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayNodeManager.h" @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2021 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 GatewayNodeManager.h + * @author: octopus + * @date 2021-05-13 + */ + +#pragma once +#include "LocalRouterTable.h" +#include "PeersRouterTable.h" +#include +#include +#include +#include +#include +#include +#include +namespace bcos +{ +namespace gateway +{ +class GatewayNodeManager +{ +public: + using Ptr = std::shared_ptr; + GatewayNodeManager(std::string const& _uuid, P2pID const& _nodeID, + std::shared_ptr _keyFactory, P2PInterface::Ptr _p2pInterface); + virtual ~GatewayNodeManager() {} + + virtual void start() { m_timer->start(); } + virtual void stop(); + + void onRemoveNodeIDs(const P2pID& _p2pID); + + GroupNodeInfo::Ptr getGroupNodeInfoList(const std::string& _groupID); + + virtual bool registerNode(const std::string& _groupID, bcos::crypto::NodeIDPtr _nodeID, + bcos::protocol::NodeType _nodeType, bcos::front::FrontServiceInterface::Ptr _frontService, + bcos::protocol::ProtocolInfo::ConstPtr _protocolInfo); + virtual bool unregisterNode(const std::string& _groupID, std::string const& _nodeID); + // for multi-group support + virtual bool updateFrontServiceInfo(bcos::group::GroupInfo::Ptr _groupInfo); + + LocalRouterTable::Ptr localRouterTable() { return m_localRouterTable; } + PeersRouterTable::Ptr peersRouterTable() { return m_peersRouterTable; } + std::shared_ptr keyFactory() { return m_keyFactory; } + + std::map, std::less<>> peersNodeIDList( + std::string const& _p2pNodeID); + +protected: + // for ut + GatewayNodeManager(std::string const& _uuid, + std::shared_ptr _keyFactory, P2PInterface::Ptr _p2pInterface) + : m_uuid(_uuid), + m_keyFactory(_keyFactory), + m_localRouterTable(std::make_shared(_keyFactory)), + m_peersRouterTable(std::make_shared(_uuid, _keyFactory, _p2pInterface)), + m_gatewayNodeStatusFactory(std::make_shared()) + {} + + uint32_t increaseSeq() + { + uint32_t statusSeq = ++m_statusSeq; + return statusSeq; + } + bool statusChanged(std::string const& _p2pNodeID, uint32_t _seq); + uint32_t statusSeq() { return m_statusSeq; } + // Note: must broadcast the status seq periodically ensure that the seq can be synced to + // restarted or re-connected nodes + virtual void broadcastStatusSeq(); + + virtual void onReceiveStatusSeq( + NetworkException const& _e, P2PSession::Ptr _session, std::shared_ptr _msg); + virtual void onRequestNodeStatus( + NetworkException const& _e, P2PSession::Ptr _session, std::shared_ptr _msg); + virtual void onReceiveNodeStatus( + NetworkException const& _e, P2PSession::Ptr _session, std::shared_ptr _msg); + virtual bytesPointer generateNodeStatus(); + virtual void syncLatestNodeIDList(); + + virtual void updatePeerStatus(std::string const& _p2pID, GatewayNodeStatus::Ptr _status); + +protected: + P2pID m_p2pNodeID; + std::string m_uuid; + std::shared_ptr m_keyFactory; + P2PInterface::Ptr m_p2pInterface; + // statusSeq + std::atomic m_statusSeq{1}; + // P2pID => statusSeq + std::map m_p2pID2Seq; + mutable SharedMutex x_p2pID2Seq; + + LocalRouterTable::Ptr m_localRouterTable; + PeersRouterTable::Ptr m_peersRouterTable; + + unsigned const SEQ_SYNC_PERIOD = 1000; + std::shared_ptr m_timer; + + GatewayNodeStatusFactory::Ptr m_gatewayNodeStatusFactory; +}; +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayStatus.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayStatus.cpp" new file mode 100644 index 00000000..f836c122 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayStatus.cpp" @@ -0,0 +1,144 @@ +/* + * 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 GatewayStatus.h + * @author: yujiechen + * @date 2022-1-07 + */ +#include "GatewayStatus.h" + +using namespace bcos; +using namespace bcos::gateway; +using namespace bcos::protocol; + +void GatewayStatus::update(std::string const& _p2pNodeID, GatewayNodeStatus::ConstPtr _nodeStatus) +{ + if (_nodeStatus->uuid() != m_uuid) + { + return; + } + UpgradableGuard l(x_groupP2PNodeList); + auto const& groupNodeInfos = _nodeStatus->groupNodeInfos(); + for (auto const& node : groupNodeInfos) + { + auto const& groupID = node->groupID(); + auto type = (GroupType)(node->type()); + if (m_groupP2PNodeList.count(groupID) && m_groupP2PNodeList[groupID].count(type) && + m_groupP2PNodeList[groupID][type].count(_p2pNodeID)) + { + continue; + } + UpgradeGuard ul(l); + // remove the _p2pNodeID from the cache + removeP2PIDWithoutLock(groupID, _p2pNodeID); + // insert the new p2pNodeID + if (!m_groupP2PNodeList.count(groupID) || !m_groupP2PNodeList[groupID].count(type)) + { + m_groupP2PNodeList[groupID][type] = std::set(); + } + (m_groupP2PNodeList[groupID][type]).insert(_p2pNodeID); + ROUTER_LOG(INFO) << LOG_DESC("GatewayStatus: update") << LOG_KV("group", groupID) + << LOG_KV("type", type) << LOG_KV("p2pID", _p2pNodeID); + } +} + +bool GatewayStatus::randomChooseP2PNode( + std::string& _p2pNodeID, uint16_t _type, std::string const& _groupID) const +{ + auto ret = false; + // If need to send a message to a consensus node, select the consensus node first + if (_type & NodeType::CONSENSUS_NODE) + { + ret = randomChooseNode(_p2pNodeID, GroupType::GROUP_WITH_CONSENSUS_NODE, _groupID); + } + // select the observer node + if (_type & NodeType::OBSERVER_NODE) + { + ret = randomChooseNode(_p2pNodeID, GroupType::GROUP_WITHOUT_CONSENSUS_NODE, _groupID); + } + // select the OUTSIDE_GROUP(AMOP message needed) + if (_type & NodeType::NODE_OUTSIDE_GROUP) + { + ret = randomChooseNode(_p2pNodeID, GroupType::OUTSIDE_GROUP, _groupID); + } + return ret; +} + +bool GatewayStatus::randomChooseNode( + std::string& _choosedNode, GroupType _type, std::string const& _groupID) const +{ + std::set p2pNodeList; + { + ReadGuard l(x_groupP2PNodeList); + if (!m_groupP2PNodeList.count(_groupID) || !m_groupP2PNodeList.at(_groupID).count(_type)) + { + return false; + } + p2pNodeList = m_groupP2PNodeList.at(_groupID).at(_type); + } + if (p2pNodeList.size() == 0) + { + return false; + } + srand(utcTime()); + auto selectedP2PNode = rand() % p2pNodeList.size(); + auto it = p2pNodeList.begin(); + if (selectedP2PNode > 0) + { + std::advance(it, selectedP2PNode); + } + _choosedNode = *it; + return true; +} + +void GatewayStatus::removeP2PIDWithoutLock( + std::string const& _groupID, std::string const& _p2pNodeID) +{ + if (!m_groupP2PNodeList.count(_groupID)) + { + return; + } + auto& p2pNodeList = m_groupP2PNodeList[_groupID]; + for (auto it = p2pNodeList.begin(); it != p2pNodeList.end();) + { + if (it->second.count(_p2pNodeID)) + { + it->second.erase(_p2pNodeID); + } + if (it->second.size() == 0) + { + it = p2pNodeList.erase(it); + continue; + } + it++; + } +} + +void GatewayStatus::removeP2PNode(std::string const& _p2pNodeID) +{ + WriteGuard l(x_groupP2PNodeList); + for (auto pGroupInfo = m_groupP2PNodeList.begin(); pGroupInfo != m_groupP2PNodeList.end();) + { + auto& p2pNodeList = m_groupP2PNodeList[pGroupInfo->first]; + removeP2PIDWithoutLock(pGroupInfo->first, _p2pNodeID); + if (p2pNodeList.size() == 0) + { + pGroupInfo = m_groupP2PNodeList.erase(pGroupInfo); + continue; + } + pGroupInfo++; + } + ROUTER_LOG(INFO) << LOG_DESC("GatewayStatus: removeP2PNode") << LOG_KV("p2pID", _p2pNodeID); +} diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayStatus.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayStatus.h" new file mode 100644 index 00000000..e1f7f3de --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/GatewayStatus.h" @@ -0,0 +1,72 @@ +/* + * 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 GatewayStatus.h + * @author: yujiechen + * @date 2022-1-07 + */ +#pragma once +#include "bcos-gateway/Common.h" +#include +namespace bcos +{ +namespace gateway +{ +class GatewayStatus +{ +public: + using Ptr = std::shared_ptr; + GatewayStatus(std::string const& _uuid) : m_uuid(_uuid) {} + virtual ~GatewayStatus() {} + + std::string const& uuid() const { return m_uuid; } + + // update the gateway info when receive new gatewayNodeStatus + void update(std::string const& _p2pNodeID, GatewayNodeStatus::ConstPtr _nodeStatus); + + // random choose the p2pNode to send message + bool randomChooseP2PNode( + std::string& _p2pNodeID, uint16_t _type, std::string const& _groupID) const; + + // remove the p2p node from the gatewayInfo after the node disconnected + void removeP2PNode(std::string const& _p2pNodeID); + +protected: + bool randomChooseNode( + std::string& _choosedNode, GroupType _type, std::string const& _groupID) const; + + void removeP2PIDWithoutLock(std::string const& _groupID, std::string const& _p2pNodeID); + +private: + std::string m_uuid; + // groupID => groupType => P2PNodeIDList + std::map>> m_groupP2PNodeList; + mutable SharedMutex x_groupP2PNodeList; +}; + +class GatewayStatusFactory +{ +public: + using Ptr = std::shared_ptr; + GatewayStatusFactory() = default; + virtual ~GatewayStatusFactory() {} + + virtual GatewayStatus::Ptr createGatewayInfo(std::string const& _uuid) + { + return std::make_shared(_uuid); + } +}; +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/LocalRouterTable.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/LocalRouterTable.cpp" new file mode 100644 index 00000000..ba92e6bb --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/LocalRouterTable.cpp" @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2021 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 LocalRouterTable.cpp + * @author: octopus + * @date 2021-12-29 + */ +#include "LocalRouterTable.h" +#include "fisco-bcos-tars-service/Common/TarsUtils.h" +#include +#include +using namespace bcos; +using namespace bcos::protocol; +using namespace bcos::gateway; +using namespace bcos::front; +using namespace bcos::crypto; + +FrontServiceInfo::Ptr LocalRouterTable::getFrontService( + const std::string& _groupID, NodeIDPtr _nodeID) const +{ + ReadGuard l(x_nodeList); + if (!m_nodeList.count(_groupID) || !m_nodeList.at(_groupID).count(_nodeID->hex())) + { + return nullptr; + } + return m_nodeList.at(_groupID).at(_nodeID->hex()); +} + +std::vector LocalRouterTable::getGroupFrontServiceList( + const std::string& _groupID) const +{ + std::vector nodeServiceList; + ReadGuard l(x_nodeList); + if (!m_nodeList.count(_groupID)) + { + return nodeServiceList; + } + for (auto const& it : m_nodeList.at(_groupID)) + { + nodeServiceList.emplace_back(it.second); + } + return nodeServiceList; +} + +void LocalRouterTable::getGroupNodeInfoList( + GroupNodeInfo::Ptr _groupNodeInfo, const std::string& _groupID) const +{ + ReadGuard l(x_nodeList); + if (!m_nodeList.count(_groupID)) + { + return; + } + for (auto const& item : m_nodeList.at(_groupID)) + { + _groupNodeInfo->appendNodeID(item.first); + _groupNodeInfo->appendProtocol(item.second->protocolInfo()); + } +} + +std::map, std::less<>> LocalRouterTable::nodeListInfo() const +{ + std::map, std::less<>> nodeList; + ReadGuard l(x_nodeList); + for (auto const& it : m_nodeList) + { + auto groupID = it.first; + if (!nodeList.count(groupID)) + { + nodeList[groupID] = std::set(); + } + auto const& infos = it.second; + for (auto const& nodeIt : infos) + { + nodeList[groupID].insert(nodeIt.first); + } + } + return nodeList; +} + +/** + * @brief: insert node + * @param _groupID: groupID + * @param _nodeID: nodeID + * @param _frontService: FrontService + */ +bool LocalRouterTable::insertNode(const std::string& _groupID, NodeIDPtr _nodeID, + bcos::protocol::NodeType _type, FrontServiceInterface::Ptr _frontService, + bcos::protocol::ProtocolInfo::ConstPtr _protocolInfo) +{ + auto nodeIDStr = _nodeID->hex(); + UpgradableGuard l(x_nodeList); + if (m_nodeList.count(_groupID) && m_nodeList[_groupID].count(nodeIDStr)) + { + auto nodeType = (m_nodeList.at(_groupID).at(nodeIDStr))->nodeType(); + if (_type == nodeType) + { + ROUTER_LOG(INFO) << LOG_DESC("insertNode: the node has already existed") + << LOG_KV("groupID", _groupID) << LOG_KV("nodeID", nodeIDStr) + << LOG_KV("nodeType", _type); + return false; + } + } + auto frontServiceInfo = + std::make_shared(nodeIDStr, _frontService, _type, nullptr); + frontServiceInfo->setProtocolInfo(_protocolInfo); + UpgradeGuard ul(l); + m_nodeList[_groupID][nodeIDStr] = frontServiceInfo; + ROUTER_LOG(INFO) << LOG_DESC("insertNode") << LOG_KV("groupID", _groupID) + << LOG_KV("minVersion", _protocolInfo->minVersion()) + << LOG_KV("maxVersion", _protocolInfo->maxVersion()) + << LOG_KV("nodeID", nodeIDStr) << LOG_KV("nodeType", _type); + return true; +} + +/** + * @brief: removeNode + * @param _groupID: groupID + * @param _nodeID: nodeID + */ +bool LocalRouterTable::removeNode(const std::string& _groupID, std::string const& _nodeID) +{ + UpgradableGuard l(x_nodeList); + if (!m_nodeList.count(_groupID) || !m_nodeList[_groupID].count(_nodeID)) + { + ROUTER_LOG(INFO) << LOG_DESC("removeNode: the node is not registered") + << LOG_KV("groupID", _groupID) << LOG_KV("nodeID", _nodeID); + return false; + } + // erase the node from m_nodeList + UpgradeGuard ul(l); + (m_nodeList[_groupID]).erase(_nodeID); + if (m_nodeList.at(_groupID).empty()) + { + m_nodeList.erase(_groupID); + } + ROUTER_LOG(INFO) << LOG_DESC("removeNode") << LOG_KV("groupID", _groupID) + << LOG_KV("nodeID", _nodeID); + return true; +} + +bool LocalRouterTable::updateGroupNodeInfos(bcos::group::GroupInfo::Ptr _groupInfo) +{ + UpgradableGuard l(x_nodeList); + auto const& groupID = _groupInfo->groupID(); + auto const& nodeInfos = _groupInfo->nodeInfos(); + bool frontServiceUpdated = false; + for (auto const& it : nodeInfos) + { + auto const& nodeInfo = it.second; + auto const& nodeID = nodeInfo->nodeID(); + // the node is registered + if (m_nodeList.count(groupID) && m_nodeList[groupID].count(nodeID)) + { + auto currentNodeInfo = m_nodeList.at(groupID).at(nodeID); + auto nodeType = currentNodeInfo->nodeType(); + auto protocol = nodeInfo->nodeProtocol(); + auto currentProtocol = currentNodeInfo->protocolInfo(); + if (nodeType == nodeInfo->nodeType() && + (protocol->minVersion() == currentProtocol->minVersion()) && + (protocol->maxVersion() == currentProtocol->maxVersion())) + { + continue; + } + } + // insert the new node + auto serviceName = nodeInfo->serviceName(bcos::protocol::FRONT); + if (serviceName.size() == 0) + { + continue; + } + + // TODO:: tars + auto frontPrx = bcostars::createServantProxy(serviceName); + auto frontClient = std::make_shared(frontPrx, m_keyFactory); + + UpgradeGuard ul(l); + auto frontServiceInfo = std::make_shared( + nodeInfo->nodeID(), frontClient, nodeInfo->nodeType(), frontPrx); + frontServiceInfo->setProtocolInfo(nodeInfo->nodeProtocol()); + m_nodeList[groupID][nodeID] = frontServiceInfo; + ROUTER_LOG(INFO) << LOG_DESC("updateGroupNodeInfos: insert frontService for the node") + << LOG_KV("nodeID", nodeInfo->nodeID()) + << LOG_KV("minVersion", nodeInfo->nodeProtocol()->minVersion()) + << LOG_KV("maxVersion", nodeInfo->nodeProtocol()->maxVersion()) + << LOG_KV("serviceName", serviceName) << printNodeInfo(nodeInfo); + frontServiceUpdated = true; + } + return frontServiceUpdated; +} + +bool LocalRouterTable::eraseUnreachableNodes() +{ + bool updated = false; + UpgradableGuard l(x_nodeList); + for (auto it = m_nodeList.begin(); it != m_nodeList.end();) + { + auto& nodesInfo = it->second; + for (auto pFrontService = nodesInfo.begin(); pFrontService != nodesInfo.end();) + { + auto frontService = pFrontService->second; + if (frontService->unreachable()) + { + ROUTER_LOG(INFO) << LOG_DESC("remove FrontService for unreachable") + << LOG_KV("node", pFrontService->first); + UpgradeGuard ul(l); + pFrontService = nodesInfo.erase(pFrontService); + updated = true; + continue; + } + pFrontService++; + } + if (nodesInfo.size() == 0) + { + UpgradeGuard ul(l); + it = m_nodeList.erase(it); + updated = true; + continue; + } + it++; + } + return updated; +} + +bool LocalRouterTable::asyncBroadcastMsg(uint16_t _nodeType, const std::string& _groupID, + uint16_t _moduleID, NodeIDPtr _srcNodeID, bytesConstRef _payload) +{ + auto frontServiceList = getGroupFrontServiceList(_groupID); + if (frontServiceList.size() == 0) + { + return false; + } + for (auto const& it : frontServiceList) + { + if (it->nodeID() == _srcNodeID->hex()) + { + continue; + } + // not expected to send message to the type of node + if ((_nodeType & it->nodeType()) == 0) + { + continue; + } + auto frontService = it->frontService(); + auto dstNodeID = it->nodeID(); + ROUTER_LOG(TRACE) << LOG_BADGE( + "LocalRouterTable: dispatcher broadcast-type message to node") + << LOG_KV("type", _nodeType) << LOG_KV("groupID", _groupID) + << LOG_KV("moduleID", _moduleID) << LOG_KV("payloadSize", _payload.size()) + << LOG_KV("dst", dstNodeID); + frontService->onReceiveMessage(_groupID, _srcNodeID, _payload, + [_groupID, _moduleID, _srcNodeID, dstNodeID](Error::Ptr _error) { + if (_error) + { + GATEWAY_LOG(ERROR) + << LOG_DESC("ROUTER_LOG error") << LOG_KV("groupID", _groupID) + << LOG_KV("moduleID", _moduleID) << LOG_KV("src", _srcNodeID->hex()) + << LOG_KV("dst", dstNodeID) << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + } + }); + } + return true; +} + + +// send message to the local nodes +bool LocalRouterTable::sendMessage(const std::string& _groupID, NodeIDPtr _srcNodeID, + NodeIDPtr _dstNodeID, bytesConstRef _payload, ErrorRespFunc _errorRespFunc) +{ + auto frontServiceInfo = getFrontService(_groupID, _dstNodeID); + if (!frontServiceInfo) + { + return false; + } + frontServiceInfo->frontService()->onReceiveMessage( + _groupID, _srcNodeID, _payload, [_errorRespFunc](Error::Ptr _error) { + if (_errorRespFunc) + { + _errorRespFunc(_error); + } + }); + return true; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/LocalRouterTable.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/LocalRouterTable.h" new file mode 100644 index 00000000..e2ec4e03 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/LocalRouterTable.h" @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2021 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 LocalRouterTable.h + * @author: octopus + * @date 2021-12-29 + */ +#pragma once +#include "FrontServiceInfo.h" +#include "bcos-gateway/libp2p/P2PSession.h" +#include +#include +#include +#include +#include +namespace bcos +{ +namespace gateway +{ +class LocalRouterTable +{ +public: + using Ptr = std::shared_ptr; + using GroupNodeListType = std::map>; + LocalRouterTable(bcos::crypto::KeyFactory::Ptr _keyFactory) : m_keyFactory(_keyFactory) {} + virtual ~LocalRouterTable() {} + + FrontServiceInfo::Ptr getFrontService( + const std::string& _groupID, bcos::crypto::NodeIDPtr _nodeID) const; + + std::vector getGroupFrontServiceList(const std::string& _groupID) const; + void getGroupNodeInfoList(GroupNodeInfo::Ptr _groupNodeInfo, const std::string& _groupID) const; + bool insertNode(const std::string& _groupID, bcos::crypto::NodeIDPtr _nodeID, + bcos::protocol::NodeType _type, bcos::front::FrontServiceInterface::Ptr _frontService, + bcos::protocol::ProtocolInfo::ConstPtr _protocolInfo); + bool removeNode(const std::string& _groupID, std::string const& _nodeID); + + std::map, std::less<>> nodeListInfo() const; + + bool updateGroupNodeInfos(bcos::group::GroupInfo::Ptr _groupInfo); + bool eraseUnreachableNodes(); + + // Note: copy to ensure thread-safe + // groupID => nodeID => FrontServiceInfo + GroupNodeListType nodeList() const + { + ReadGuard l(x_nodeList); + return m_nodeList; + } + + bool asyncBroadcastMsg(uint16_t _nodeType, const std::string& _groupID, uint16_t _moduleID, + bcos::crypto::NodeIDPtr _srcNodeID, bytesConstRef _payload); + + bool sendMessage(const std::string& _groupID, bcos::crypto::NodeIDPtr _srcNodeID, + bcos::crypto::NodeIDPtr _dstNodeID, bytesConstRef _payload, ErrorRespFunc _errorRespFunc); + +private: + bcos::crypto::KeyFactory::Ptr m_keyFactory; + // groupID => nodeID => FrontServiceInfo + GroupNodeListType m_nodeList; + mutable SharedMutex x_nodeList; +}; +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/PeersRouterTable.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/PeersRouterTable.cpp" new file mode 100644 index 00000000..a0852a16 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/PeersRouterTable.cpp" @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2021 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 PeersRouterTable.cpp + * @author: octopus + * @date 2021-12-29 + */ +#include "PeersRouterTable.h" +#include "bcos-utilities/BoostLog.h" + +using namespace bcos; +using namespace bcos::protocol; +using namespace bcos::gateway; +using namespace bcos::crypto; + +void PeersRouterTable::getGroupNodeInfoList( + GroupNodeInfo::Ptr _groupInfo, const std::string& _groupID) const +{ + ReadGuard l(x_groupNodeList); + if (!m_groupNodeList.count(_groupID)) + { + return; + } + for (auto const& it : m_groupNodeList.at(_groupID)) + { + auto nodeID = it.first; + _groupInfo->appendNodeID(nodeID); + if (m_nodeProtocolInfo.count(nodeID)) + { + _groupInfo->appendProtocol(m_nodeProtocolInfo.at(nodeID)); + } + } +} + +std::set PeersRouterTable::queryP2pIDs( + const std::string& _groupID, const std::string& _nodeID) const +{ + ReadGuard l(x_groupNodeList); + if (!m_groupNodeList.count(_groupID) || !m_groupNodeList.at(_groupID).count(_nodeID)) + { + return std::set(); + } + return m_groupNodeList.at(_groupID).at(_nodeID); +} + +std::set PeersRouterTable::queryP2pIDsByGroupID(const std::string& _groupID) const +{ + std::set p2pNodeIDList; + ReadGuard l(x_groupNodeList); + if (!m_groupNodeList.count(_groupID)) + { + return p2pNodeIDList; + } + for (const auto& it : m_groupNodeList.at(_groupID)) + { + p2pNodeIDList.insert(it.second.begin(), it.second.end()); + } + return p2pNodeIDList; +} + +void PeersRouterTable::updatePeerStatus( + P2pID const& _p2pID, GatewayNodeStatus::Ptr _gatewayNodeStatus) +{ + auto const& nodeList = _gatewayNodeStatus->groupNodeInfos(); + ROUTER_LOG(INFO) << LOG_DESC("updatePeerStatus") + << LOG_KV("gatewayUUID", _gatewayNodeStatus->uuid()) + << LOG_KV("nodeList", nodeList.size()); + // remove the old nodeList from the groupNodeList + removeP2PIDFromGroupNodeList(_p2pID); + // insert the new nodeList into the groupNodeList + batchInsertNodeList(_p2pID, nodeList); + // update the peers status + updatePeerNodeList(_p2pID, _gatewayNodeStatus); + // update the gatewayInfo + updateGatewayInfo(_p2pID, _gatewayNodeStatus); +} + +void PeersRouterTable::batchInsertNodeList( + P2pID const& _p2pNodeID, std::vector const& _nodeList) +{ + WriteGuard l(x_groupNodeList); + for (auto const& it : _nodeList) + { + auto groupID = it->groupID(); + auto const& nodeIDList = it->nodeIDList(); + int64_t i = 0; + for (auto const& nodeID : nodeIDList) + { + if (!m_groupNodeList.count(groupID) || !m_groupNodeList.at(groupID).count(nodeID)) + { + m_groupNodeList[groupID][nodeID] = std::set(); + } + m_groupNodeList[groupID][nodeID].insert(_p2pNodeID); + if (it->protocol(i)) + { + m_nodeProtocolInfo[nodeID] = it->protocol(i); + } + i++; + } + ROUTER_LOG(INFO) << LOG_DESC("batchInsertNodeList") << LOG_KV("group", it->groupID()) + << LOG_KV("nodeIDs", it->nodeIDList().size()) + << LOG_KV("protocols", it->nodeProtocolList().size()); + } +} + +void PeersRouterTable::removeP2PID(const P2pID& _p2pID) +{ + ROUTER_LOG(INFO) << LOG_DESC("PeersRouterTable: removeP2PID") << LOG_KV("p2pID", _p2pID); + // remove p2pID from groupNodeList + removeP2PIDFromGroupNodeList(_p2pID); + // remove p2pID from peerStatus + removePeerStatus(_p2pID); + // remove p2pID from the gatewayInfo + removeNodeFromGatewayInfo(_p2pID); +} + +void PeersRouterTable::removeP2PIDFromGroupNodeList(const P2pID& _p2pID) +{ + WriteGuard l(x_groupNodeList); + // remove all nodeIDs info belong to p2pID + for (auto it = m_groupNodeList.begin(); it != m_groupNodeList.end();) + { + for (auto innerIt = it->second.begin(); innerIt != it->second.end();) + { + for (auto innerIt2 = innerIt->second.begin(); innerIt2 != innerIt->second.end();) + { + if (*innerIt2 == _p2pID) + { + innerIt2 = innerIt->second.erase(innerIt2); + } + else + { + ++innerIt2; + } + } + + if (innerIt->second.empty()) + { + innerIt = it->second.erase(innerIt); + } + else + { + ++innerIt; + } + } + + if (it->second.empty()) + { + it = m_groupNodeList.erase(it); + } + else + { + ++it; + } + } +} + +void PeersRouterTable::updatePeerNodeList(P2pID const& _p2pNodeID, GatewayNodeStatus::Ptr _status) +{ + WriteGuard l(x_peersStatus); + m_peersStatus[_p2pNodeID] = _status; +} + +void PeersRouterTable::removePeerStatus(P2pID const& _p2pNodeID) +{ + UpgradableGuard l(x_peersStatus); + if (m_peersStatus.count(_p2pNodeID)) + { + UpgradeGuard ul(l); + m_peersStatus.erase(_p2pNodeID); + } +} + +PeersRouterTable::Group2NodeIDListType PeersRouterTable::peersNodeIDList( + P2pID const& _p2pNodeID) const +{ + ReadGuard l(x_peersStatus); + PeersRouterTable::Group2NodeIDListType nodeIDList; + if (!m_peersStatus.count(_p2pNodeID)) + { + return nodeIDList; + } + auto const& groupNodeInfos = m_peersStatus.at(_p2pNodeID)->groupNodeInfos(); + for (auto const& it : groupNodeInfos) + { + auto const& groupNodeIDList = it->nodeIDList(); + nodeIDList[it->groupID()] = + std::set(groupNodeIDList.begin(), groupNodeIDList.end()); + } + return nodeIDList; +} + +std::set PeersRouterTable::getAllPeers() const +{ + std::set peers; + ReadGuard l(x_peersStatus); + for (auto const& peerInfo : m_peersStatus) + { + peers.insert(peerInfo.first); + } + return peers; +} + +GatewayStatus::Ptr PeersRouterTable::gatewayInfo(std::string const& _uuid) +{ + ReadGuard l(x_gatewayInfos); + if (m_gatewayInfos.count(_uuid)) + { + return m_gatewayInfos.at(_uuid); + } + return nullptr; +} + +void PeersRouterTable::updateGatewayInfo(P2pID const& _p2pNodeID, GatewayNodeStatus::Ptr _status) +{ + GatewayStatus::Ptr gatewayStatus; + { + UpgradableGuard l(x_gatewayInfos); + if (!m_gatewayInfos.count(_status->uuid())) + { + UpgradeGuard ul(l); + m_gatewayInfos[_status->uuid()] = + m_gatewayStatusFactory->createGatewayInfo(_status->uuid()); + } + gatewayStatus = m_gatewayInfos.at(_status->uuid()); + } + gatewayStatus->update(_p2pNodeID, _status); +} + +void PeersRouterTable::removeNodeFromGatewayInfo(P2pID const& _p2pID) +{ + ReadGuard l(x_gatewayInfos); + for (auto const& it : m_gatewayInfos) + { + it.second->removeP2PNode(_p2pID); + } +} + +// broadcast message to given group +void PeersRouterTable::asyncBroadcastMsg( + uint16_t _type, std::string const& _groupID, uint16_t _moduleID, P2PMessage::Ptr _msg) +{ + std::vector selectedPeers; + { + ReadGuard l(x_gatewayInfos); + for (auto const& it : m_gatewayInfos) + { + // not broadcast message to the gateway-self + if (it.first == m_uuid) + { + continue; + } + std::string p2pNodeID; + if (it.second->randomChooseP2PNode(p2pNodeID, _type, _groupID)) + { + selectedPeers.emplace_back(p2pNodeID); + } + } + } + ROUTER_LOG(TRACE) << LOG_BADGE("PeersRouterTable") + << LOG_DESC("asyncBroadcastMsg: randomChooseP2PNode") << LOG_KV("type", _type) + << LOG_KV("moduleID", _moduleID) + << LOG_KV("payloadSize", _msg->payload()->size()) + << LOG_KV("peersSize", selectedPeers.size()); + for (auto const& peer : selectedPeers) + { + ROUTER_LOG(TRACE) << LOG_BADGE("PeersRouterTable") << LOG_DESC("asyncBroadcastMsg") + << LOG_KV("type", _type) << LOG_KV("moduleID", _moduleID) + << LOG_KV("dst", peer); + m_p2pInterface->asyncSendMessageByNodeID(peer, _msg, CallbackFuncWithSession()); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/PeersRouterTable.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/PeersRouterTable.h" new file mode 100644 index 00000000..4ffd6051 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/PeersRouterTable.h" @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2021 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 PeersRouterTable.h + * @author: octopus + * @date 2021-12-29 + */ +#pragma once +#include "FrontServiceInfo.h" +#include "GatewayStatus.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace gateway +{ +class PeersRouterTable +{ +public: + using Ptr = std::shared_ptr; + PeersRouterTable(std::string _uuid, bcos::crypto::KeyFactory::Ptr _keyFactory, + P2PInterface::Ptr _p2pInterface) + : m_uuid(_uuid), + m_keyFactory(_keyFactory), + m_p2pInterface(_p2pInterface), + m_gatewayStatusFactory(std::make_shared()) + {} + virtual ~PeersRouterTable() {} + + void getGroupNodeInfoList(GroupNodeInfo::Ptr _groupInfo, const std::string& _groupID) const; + std::set queryP2pIDs(const std::string& _groupID, const std::string& _nodeID) const; + std::set queryP2pIDsByGroupID(const std::string& _groupID) const; + void removeP2PID(const P2pID& _p2pID); + + void updatePeerStatus(P2pID const& _p2pID, GatewayNodeStatus::Ptr _gatewayNodeStatus); + + using Group2NodeIDListType = std::map, std::less<>>; + Group2NodeIDListType peersNodeIDList(P2pID const& _p2pNodeID) const; + + void asyncBroadcastMsg( + uint16_t _type, std::string const& _group, uint16_t _moduleID, P2PMessage::Ptr _msg); + + std::set getAllPeers() const; + +protected: + void batchInsertNodeList( + P2pID const& _p2pNodeID, std::vector const& _nodeList); + void updatePeerNodeList(P2pID const& _p2pNodeID, GatewayNodeStatus::Ptr _status); + + void removeP2PIDFromGroupNodeList(P2pID const& _p2pID); + void removePeerStatus(P2pID const& _p2pNodeID); + + void updateGatewayInfo(P2pID const& _p2pNodeID, GatewayNodeStatus::Ptr _status); + void removeNodeFromGatewayInfo(P2pID const& _p2pID); + GatewayStatus::Ptr gatewayInfo(std::string const& _uuid); + +private: + std::string m_uuid; + bcos::crypto::KeyFactory::Ptr m_keyFactory; + P2PInterface::Ptr m_p2pInterface; + // used for peer-to-peer router + // groupID => NodeID => set + std::map, std::less<>>, std::less<>> + m_groupNodeList; + std::map m_nodeProtocolInfo; + mutable SharedMutex x_groupNodeList; + + // the nodeIDList infos of the peers + // p2pNodeID => GatewayNodeStatus + std::map m_peersStatus; + mutable SharedMutex x_peersStatus; + + GatewayStatusFactory::Ptr m_gatewayStatusFactory; + // uuid => gatewayInfo + std::map m_gatewayInfos; + mutable SharedMutex x_gatewayInfos; +}; +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/ProGatewayNodeManager.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/ProGatewayNodeManager.cpp" new file mode 100644 index 00000000..ff782f7d --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/ProGatewayNodeManager.cpp" @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2021 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 ProGatewayNodeManager.cpp + * @author: yujiechen + * @date 2021-10-28 + */ +#include "ProGatewayNodeManager.h" + +using namespace bcos; +using namespace bcos::gateway; +using namespace bcos::protocol; + +void ProGatewayNodeManager::DetectNodeAlive() +{ + m_nodeAliveDetector->restart(); + auto updated = m_localRouterTable->eraseUnreachableNodes(); + if (!updated) + { + return; + } + increaseSeq(); + syncLatestNodeIDList(); +} + +bool ProGatewayNodeManager::updateFrontServiceInfo(bcos::group::GroupInfo::Ptr _groupInfo) +{ + auto ret = GatewayNodeManager::updateFrontServiceInfo(_groupInfo); + if (ret) + { + m_nodeAliveDetector->restart(); + } + return ret; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/ProGatewayNodeManager.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/ProGatewayNodeManager.h" new file mode 100644 index 00000000..de68dbe6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/gateway/ProGatewayNodeManager.h" @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 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 ProGatewayNodeManager.h + * @author: yujiechen + * @date 2021-10-28 + */ +#pragma once +#include "GatewayNodeManager.h" +#include +namespace bcos +{ +namespace gateway +{ +class ProGatewayNodeManager : public GatewayNodeManager +{ +public: + using Ptr = std::shared_ptr; + ProGatewayNodeManager(std::string const& _uuid, P2pID const& _nodeID, + std::shared_ptr _keyFactory, P2PInterface::Ptr _p2pInterface) + : GatewayNodeManager(_uuid, _nodeID, _keyFactory, _p2pInterface) + { + m_nodeAliveDetector = + std::make_shared(c_tarsAdminRefreshTimeInterval, "nodeUpdater"); + m_nodeAliveDetector->registerTimeoutHandler([this]() { DetectNodeAlive(); }); + } + + void start() override + { + GatewayNodeManager::start(); + m_nodeAliveDetector->start(); + } + void stop() override + { + GatewayNodeManager::stop(); + m_nodeAliveDetector->stop(); + } + bool updateFrontServiceInfo(bcos::group::GroupInfo::Ptr _groupInfo) override; + +private: + virtual void DetectNodeAlive(); + +private: + std::shared_ptr m_nodeAliveDetector; + // Note: since tars need at-least 1min to update the endpoint info, we schedule DetectNodeAlive + // every 1min + uint64_t c_tarsAdminRefreshTimeInterval = 60 * 1000; +}; +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AMOPImpl.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AMOPImpl.cpp" new file mode 100644 index 00000000..0bf5ba6d --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AMOPImpl.cpp" @@ -0,0 +1,574 @@ +/* + * Copyright (C) 2021 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 AMOPImpl.cpp + * @author: octopus + * @date 2021-10-26 + */ +#include "AMOPImpl.h" +#include +#include +#include +#include +using namespace bcos; +using namespace bcos::gateway; +using namespace bcos::amop; +using namespace bcos::protocol; + +AMOPImpl::AMOPImpl(TopicManager::Ptr _topicManager, + bcos::amop::AMOPMessageFactory::Ptr _messageFactory, AMOPRequestFactory::Ptr _requestFactory, + P2PInterface::Ptr _network, P2pID const& _p2pNodeID) + : m_topicManager(_topicManager), + m_messageFactory(_messageFactory), + m_requestFactory(_requestFactory), + m_network(_network), + m_p2pNodeID(_p2pNodeID) +{ + m_threadPool = std::make_shared("amopDispatcher", 1); + m_timer = std::make_shared(TOPIC_SYNC_PERIOD, "topicSync"); + m_timer->registerTimeoutHandler([this]() { broadcastTopicSeq(); }); + + m_network->registerHandlerByMsgType(GatewayMessageType::AMOPMessageType, + boost::bind(&AMOPImpl::onAMOPMessage, this, boost::placeholders::_1, + boost::placeholders::_2, boost::placeholders::_3)); +} + +void AMOPImpl::start() +{ + m_timer->start(); + m_topicManager->start(); +} + +void AMOPImpl::stop() +{ + m_timer->stop(); + m_topicManager->stop(); +} + +void AMOPImpl::broadcastTopicSeq() +{ + auto topicSeq = std::to_string(m_topicManager->topicSeq()); + auto buffer = buildAndEncodeMessage( + AMOPMessage::Type::TopicSeq, bytesConstRef((byte*)topicSeq.data(), topicSeq.size())); + m_network->asyncBroadcastMessageToP2PNodes( + GatewayMessageType::AMOPMessageType, protocol::ModuleID::AMOP, ref(*buffer), Options(0)); + AMOP_LOG(TRACE) << LOG_BADGE("broadcastTopicSeq") << LOG_KV("topicSeq", topicSeq); + m_timer->restart(); +} + +// receive the topic seq of other nodes, and try to request the latest topic when seq falling behind +void AMOPImpl::onReceiveTopicSeqMessage(P2pID const& _nodeID, AMOPMessage::Ptr _msg) +{ + try + { + uint32_t topicSeq = + boost::lexical_cast(std::string(_msg->data().begin(), _msg->data().end())); + if (!m_topicManager->checkTopicSeq(_nodeID, topicSeq)) + { + return; + } + AMOP_LOG(INFO) << LOG_BADGE( + "onReceiveTopicSeqMessage: try to request latest AMOP information") + << LOG_KV("nodeID", _nodeID) << LOG_KV("topicSeq", topicSeq); + + auto buffer = buildAndEncodeMessage(AMOPMessage::Type::RequestTopic, bytesConstRef()); + Options option(0); + m_network->asyncSendMessageByP2PNodeID(GatewayMessageType::AMOPMessageType, _nodeID, + bytesConstRef(buffer->data(), buffer->size()), option, + [_nodeID](Error::Ptr&& _error, int16_t, bytesPointer) { + if (_error && (_error->errorCode() != CommonError::SUCCESS)) + { + AMOP_LOG(WARNING) + << LOG_BADGE("onReceiveTopicSeqMessage") + << LOG_DESC("receive error callback") << LOG_KV("dstNode", _nodeID) + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()); + return; + } + }); + } + catch (const std::exception& e) + { + AMOP_LOG(ERROR) << LOG_DESC("onReceiveTopicSeqMessage") << LOG_KV("nodeID", _nodeID) + << LOG_KV("error", boost::diagnostic_information(e)); + } +} + +/** + * @brief: create message and encode the message to bytes + * @param _type: message type + * @param _data: message data + * @return std::shared_ptr + */ +std::shared_ptr AMOPImpl::buildAndEncodeMessage(uint32_t _type, bcos::bytesConstRef _data) +{ + auto message = m_messageFactory->buildMessage(); + message->setType(_type); + message->setData(_data); + auto buffer = std::make_shared(); + message->encode(*buffer.get()); + return buffer; +} + +// receive topic response and update the local topicManager +void AMOPImpl::onReceiveResponseTopicMessage(P2pID const& _nodeID, AMOPMessage::Ptr _msg) +{ + try + { + uint32_t topicSeq; + TopicItems topicItems; + std::string topicJson = std::string(_msg->data().begin(), _msg->data().end()); + if (m_topicManager->parseTopicItemsJson(topicSeq, topicItems, topicJson)) + { + m_topicManager->updateSeqAndTopicsByNodeID(_nodeID, topicSeq, topicItems); + } + } + catch (const std::exception& e) + { + AMOP_LOG(ERROR) << LOG_BADGE("onReceiveResponseTopicMessage") << LOG_KV("nodeID", _nodeID) + << LOG_KV("error", boost::diagnostic_information(e)); + } +} + +// response topic message to the given node +void AMOPImpl::onReceiveRequestTopicMessage(P2pID const& _nodeID, AMOPMessage::Ptr _msg) +{ + (void)_msg; + try + { + // the current node subscribed topic info + std::string topicJson = m_topicManager->queryTopicsSubByClient(); + + AMOP_LOG(INFO) << LOG_BADGE("onReceiveRequestTopicMessage") << LOG_KV("nodeID", _nodeID) + << LOG_KV("topicJson", topicJson); + + auto buffer = buildAndEncodeMessage(AMOPMessage::Type::ResponseTopic, + bytesConstRef((byte*)topicJson.data(), topicJson.size())); + Options option(0); + m_network->asyncSendMessageByP2PNodeID(GatewayMessageType::AMOPMessageType, _nodeID, + bytesConstRef(buffer->data(), buffer->size()), option, + [_nodeID](Error::Ptr&& _error, int16_t, bytesPointer) { + if (_error && (_error->errorCode() != CommonError::SUCCESS)) + { + AMOP_LOG(WARNING) + << LOG_BADGE("onReceiveRequestTopicMessage") + << LOG_DESC("callback response error") << LOG_KV("dstNode", _nodeID) + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()); + } + }); + } + catch (const std::exception& e) + { + AMOP_LOG(ERROR) << LOG_BADGE("onReceiveRequestTopicMessage") << LOG_KV("nodeID", _nodeID) + << LOG_KV("error", boost::diagnostic_information(e)); + } +} + +// receive AMOP request message from the given node +void AMOPImpl::onReceiveAMOPMessage(P2pID const& _nodeID, AMOPMessage::Ptr _msg, + std::function const& _responseCallback) +{ + // AMOPRequest + auto request = m_requestFactory->buildRequest(_msg->data()); + // message seq + std::string topic = request->topic(); + onReceiveAMOPMessage(_nodeID, topic, _msg->data(), _responseCallback); +} + +void AMOPImpl::onReceiveAMOPMessage(P2pID const& _nodeID, std::string const& _topic, + bytesConstRef _data, std::function const& _responseCallback) +{ + std::vector clients; + m_topicManager->queryClientsByTopic(_topic, clients); + bcos::rpc::RPCInterface::Ptr clientService = nullptr; + std::string choosedClient; + if (!clients.empty()) + { + choosedClient = randomChoose(clients); + clientService = m_topicManager->createAndGetServiceByClient(choosedClient); + } + if (!clientService) + { + auto amopMsg = m_messageFactory->buildMessage(); + auto buffer = std::make_shared(); + amopMsg->setStatus(CommonError::NotFoundClientByTopicDispatchMsg); + amopMsg->setType(AMOPMessage::Type::AMOPResponse); + std::string errorMessage = "NotFoundClientByTopicDispatchMsg"; + amopMsg->setData(bytesConstRef((bcos::byte*)errorMessage.c_str(), errorMessage.size())); + amopMsg->encode(*buffer); + m_threadPool->enqueue([buffer, _responseCallback]() { + _responseCallback(buffer, GatewayMessageType::AMOPMessageType); + }); + AMOP_LOG(WARNING) << LOG_BADGE("onRecvAMOPMessage") + << LOG_DESC("no client subscribe the topic") << LOG_KV("topic", _topic) + << LOG_KV("nodeID", _nodeID); + return; + } + + AMOP_LOG(INFO) << LOG_DESC("onRecvAMOPMessage") << LOG_KV("topic", _topic) + << LOG_KV("from", _nodeID) << LOG_KV("choosedClient", choosedClient); + clientService->asyncNotifyAMOPMessage(bcos::rpc::AMOPNotifyMessageType::Unicast, _topic, _data, + [this, _responseCallback](Error::Ptr&& _error, bytesPointer _responseData) { + if (!_error || _error->errorCode() == CommonError::SUCCESS) + { + _responseCallback(_responseData, GatewayMessageType::WSMessageType); + return; + } + auto amopMsg = m_messageFactory->buildMessage(); + amopMsg->setStatus(_error->errorCode()); + amopMsg->setType(AMOPMessage::Type::AMOPResponse); + auto const& errorMessage = _error->errorMessage(); + amopMsg->setData(bytesConstRef((bcos::byte*)errorMessage.c_str(), errorMessage.size())); + auto buffer = std::make_shared(); + amopMsg->encode(*buffer); + _responseCallback(buffer, GatewayMessageType::AMOPMessageType); + AMOP_LOG(WARNING) << LOG_DESC("asyncNotifyAMOPMessage error") + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + }); +} + +// receive the AMOP broadcast message from given node +void AMOPImpl::onReceiveAMOPBroadcastMessage(P2pID const& _nodeID, AMOPMessage::Ptr _msg) +{ + // AMOPRequest + auto request = m_requestFactory->buildRequest(_msg->data()); + // message seq + std::string topic = request->topic(); + std::vector clients; + m_topicManager->queryClientsByTopic(topic, clients); + if (clients.empty()) + { + AMOP_LOG(WARNING) << LOG_BADGE("onRecvAMOPBroadcastMessage") + << LOG_DESC("no client subscribe the topic") << LOG_KV("topic", topic) + << LOG_KV("from", _nodeID); + return; + } + for (const auto& client : clients) + { + auto clientService = m_topicManager->createAndGetServiceByClient(client); + if (!clientService) + { + continue; + } + AMOP_LOG(DEBUG) << LOG_BADGE("onRecvAMOPBroadcastMessage") + << LOG_DESC("push message to client") << LOG_KV("topic", topic) + << LOG_KV("client", client); + clientService->asyncNotifyAMOPMessage(bcos::rpc::AMOPNotifyMessageType::Broadcast, topic, + _msg->data(), [client](Error::Ptr&& _error, bytesPointer) { + if (_error) + { + AMOP_LOG(WARNING) + << LOG_BADGE("onRecvAMOPBroadcastMessage") + << LOG_DESC("asyncNotifyAMOPMessage error") << LOG_KV("client", client) + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + } + }); + } + AMOP_LOG(DEBUG) << LOG_DESC("onReceiveAMOPBroadcastMessage") << LOG_KV("nodeID", _nodeID); +} + +bool AMOPImpl::trySendTopicMessageToLocalClient(const std::string& _topic, + bcos::bytesConstRef _data, + std::function _respFunc) +{ + std::vector clients; + m_topicManager->queryClientsByTopic(_topic, clients); + if (clients.size() == 0) + { + AMOP_LOG(INFO) << LOG_DESC("trySendTopicMessageToLocalClient failed for empty client") + << LOG_KV("topic", _topic); + return false; + } + AMOP_LOG(INFO) << LOG_DESC("trySendTopicMessageToLocalClient") << LOG_KV("topic", _topic) + << LOG_KV("clientsSubscribeTopic", clients.size()); + auto self = shared_from_this(); + onReceiveAMOPMessage(m_p2pNodeID, _topic, _data, + [self, _topic, _respFunc](bytesPointer _response, int16_t _type) { + self->onRecvAMOPResponse(_type, _response, _respFunc); + AMOP_LOG(INFO) << LOG_DESC("trySendTopicMessageToLocalClient: receive response") + << LOG_KV("topic", _topic); + }); + + return true; +} + +// asyncSendMessage to the given topic +void AMOPImpl::asyncSendMessageByTopic(const std::string& _topic, bcos::bytesConstRef _data, + std::function _respFunc) +{ + std::vector nodeIDs; + m_topicManager->queryNodeIDsByTopic(_topic, nodeIDs); + if (nodeIDs.empty()) + { + if (trySendTopicMessageToLocalClient(_topic, _data, _respFunc)) + { + return; + } + auto errorPtr = std::make_shared(CommonError::NotFoundPeerByTopicSendMsg, + "there has no node subscribe this topic, topic: " + _topic); + if (_respFunc) + { + _respFunc(std::move(errorPtr), 0, nullptr); + } + + AMOP_LOG(WARNING) << LOG_BADGE("asyncSendMessage") + << LOG_DESC("there has no node subscribe the topic") + << LOG_KV("topic", _topic); + return; + } + AMOP_LOG(INFO) << LOG_DESC("asyncSendMessageByTopic") << LOG_KV("topic", _topic) + << LOG_KV("nodeIDsSize", nodeIDs.size()); + auto buffer = buildAndEncodeMessage(AMOPMessage::Type::AMOPRequest, _data); + + class RetrySender : public std::enable_shared_from_this + { + public: + std::vector m_nodeIDs; + std::shared_ptr m_buffer; + std::function m_callback; + P2PInterface::Ptr m_network; + std::shared_ptr m_messageFactory; + + public: + void sendMessage() + { + if (m_nodeIDs.empty()) + { + auto errorPtr = std::make_shared( + CommonError::AMOPSendMsgFailed, "unable to send message to peer by topic"); + if (m_callback) + { + m_callback(std::move(errorPtr), 0, nullptr); + } + + return; + } + auto choosedNodeID = randomChoose(m_nodeIDs); + AMOP_LOG(INFO) << LOG_DESC("asyncSendMessageByTopic") + << LOG_KV("choosedNodeID", choosedNodeID); + // erase in case of select the same node when retry + m_nodeIDs.erase(m_nodeIDs.begin()); + // try to send message to node + Options option(0); + auto self = shared_from_this(); + m_network->asyncSendMessageByP2PNodeID(GatewayMessageType::AMOPMessageType, + choosedNodeID, bytesConstRef(m_buffer->data(), m_buffer->size()), option, + [self, choosedNodeID, callback = m_callback]( + Error::Ptr&& _error, int16_t _type, bytesPointer _responseData) { + if (_error && (_error->errorCode() != CommonError::SUCCESS)) + { + AMOP_LOG(DEBUG) + << LOG_BADGE("RetrySender::sendMessage") + << LOG_DESC("asyncSendMessageByNodeID callback response error") + << LOG_KV("nodeID", choosedNodeID) + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + self->sendMessage(); + return; + } + bcos::Error::Ptr error = nullptr; + if (_type == bcos::gateway::GatewayMessageType::AMOPMessageType) + { + // zero copy overhead + auto amopMsg = self->m_messageFactory->buildMessage(ref(*_responseData)); + auto errorMessage = + std::string(amopMsg->data().begin(), amopMsg->data().end()); + auto errorCode = amopMsg->status(); + // tars error + if (amopMsg->status() == (uint16_t)(-8) || + amopMsg->status() == (uint16_t)(-7)) + { + errorMessage = + "Access to the remote RPC service timed out, please make sure it " + "is online"; + errorCode = -1; + } + error = std::make_shared(errorCode, errorMessage); + + AMOP_LOG(INFO) + << LOG_DESC("asyncSendMessageByTopic error: receive responseData") + << LOG_KV("status", amopMsg->status()) << LOG_KV("msg", errorMessage); + } + if (callback) + { + AMOP_LOG(INFO) + << LOG_DESC("asyncSendMessageByTopic: receive responseData") + << LOG_KV("size", _responseData->size()) << LOG_KV("type", _type); + callback(std::move(error), _type, _responseData); + } + }); + } + }; + + auto sender = std::make_shared(); + sender->m_nodeIDs = nodeIDs; + sender->m_buffer = buffer; + sender->m_network = m_network; + sender->m_callback = _respFunc; + sender->m_messageFactory = m_messageFactory; + // send message + sender->sendMessage(); +} + +void AMOPImpl::onRecvAMOPResponse(int16_t _type, bytesPointer _responseData, + std::function _callback) +{ + bcos::Error::Ptr error = nullptr; + if (_type == bcos::gateway::GatewayMessageType::AMOPMessageType) + { + // zero copy overhead + auto amopMsg = m_messageFactory->buildMessage(ref(*_responseData)); + auto errorMessage = std::string(amopMsg->data().begin(), amopMsg->data().end()); + auto errorCode = amopMsg->status(); + // tars error + if (amopMsg->status() == (uint16_t)(-8) || amopMsg->status() == (uint16_t)(-7)) + { + errorMessage = + "Access to the remote RPC service timed out, please make sure it " + "is online"; + errorCode = -1; + } + error = std::make_shared(errorCode, errorMessage); + + AMOP_LOG(INFO) << LOG_DESC("asyncSendMessageByTopic error: receive responseData") + << LOG_KV("status", amopMsg->status()) << LOG_KV("msg", errorMessage); + } + if (_callback) + { + AMOP_LOG(INFO) << LOG_DESC("asyncSendMessageByTopic: receive responseData") + << LOG_KV("size", _responseData->size()) << LOG_KV("type", _type); + _callback(std::move(error), _type, _responseData); + } +} + +void AMOPImpl::asyncSendBroadcastMessageByTopic( + const std::string& _topic, bcos::bytesConstRef _data) +{ + std::vector nodeIDs; + m_topicManager->queryNodeIDsByTopic(_topic, nodeIDs); + if (nodeIDs.empty()) + { + AMOP_LOG(WARNING) << LOG_BADGE("asyncSendBroadbastMessage") + << LOG_DESC("there no node subscribe this topic") + << LOG_KV("topic", _topic); + return; + } + auto buffer = buildAndEncodeMessage(AMOPMessage::Type::AMOPBroadcast, _data); + m_network->asyncSendMessageByP2PNodeIDs(GatewayMessageType::AMOPMessageType, nodeIDs, + bytesConstRef(buffer->data(), buffer->size()), Options(0)); + AMOP_LOG(DEBUG) << LOG_BADGE("asyncSendBroadbastMessage") << LOG_DESC("send broadcast message") + << LOG_KV("topic", _topic) << LOG_KV("data size", _data.size()); +} + +void AMOPImpl::onAMOPMessage( + NetworkException const& _e, P2PSession::Ptr _session, std::shared_ptr _message) +{ + auto self = std::weak_ptr(shared_from_this()); + m_threadPool->enqueue([self, _e, _session, _message]() { + auto amop = self.lock(); + if (!amop) + { + return; + } + try + { + amop->dispatcherAMOPMessage(_e, _session, _message); + } + catch (std::exception const& e) + { + AMOP_LOG(WARNING) << LOG_DESC("dispatcher AMOPMessage exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +void AMOPImpl::dispatcherAMOPMessage( + NetworkException const& _e, P2PSession::Ptr _session, std::shared_ptr _message) +{ + if (_e.errorCode() != 0 || !_message) + { + AMOP_LOG(WARNING) << LOG_DESC("onAMOPMessage error for NetworkException") + << LOG_KV("error", _e.what()) << LOG_KV("code", _e.errorCode()); + return; + } + if (_message->packetType() != GatewayMessageType::AMOPMessageType) + { + return; + } + // zero copy overhead + auto amopMessage = m_messageFactory->buildMessage(ref(*_message->payload())); + auto amopMsgType = amopMessage->type(); + auto fromNodeID = + _message->srcP2PNodeID().empty() ? _session->p2pID() : _message->srcP2PNodeID(); + switch (amopMsgType) + { + case AMOPMessage::Type::TopicSeq: + onReceiveTopicSeqMessage(fromNodeID, amopMessage); + break; + case AMOPMessage::Type::RequestTopic: + onReceiveRequestTopicMessage(fromNodeID, amopMessage); + break; + case AMOPMessage::Type::ResponseTopic: + onReceiveResponseTopicMessage(fromNodeID, amopMessage); + break; + case AMOPMessage::Type::AMOPRequest: + onReceiveAMOPMessage(fromNodeID, amopMessage, + [this, _session, _message](bytesPointer _responseData, int16_t _type) { + auto responseP2PMsg = std::dynamic_pointer_cast( + m_network->messageFactory()->buildMessage()); + AMOP_LOG(INFO) << LOG_DESC("onReceiveAMOPMessage: sendResponse") + << LOG_KV("type", _type) << LOG_KV("data", _responseData->size()); + responseP2PMsg->setDstP2PNodeID(_message->srcP2PNodeID()); + responseP2PMsg->setSrcP2PNodeID(_message->dstP2PNodeID()); + responseP2PMsg->setSeq(_message->seq()); + responseP2PMsg->setRespPacket(); + responseP2PMsg->setPayload(_responseData); + responseP2PMsg->setPacketType(_type); + m_network->asyncSendMessageByNodeID( + responseP2PMsg->dstP2PNodeID(), responseP2PMsg, nullptr); + }); + break; + case AMOPMessage::Type::AMOPBroadcast: + onReceiveAMOPBroadcastMessage(fromNodeID, amopMessage); + break; + default: + AMOP_LOG(WARNING) << LOG_DESC("unknown AMOP message type") << LOG_KV("type", amopMsgType); + } +} + +void AMOPImpl::asyncSubscribeTopic(std::string const& _clientID, std::string const& _topicInfo, + std::function _callback) +{ + m_topicManager->subTopic(_clientID, _topicInfo); + if (!_callback) + { + return; + } + _callback(nullptr); +} + +void AMOPImpl::asyncRemoveTopic(std::string const& _clientID, + std::vector const& _topicList, std::function _callback) +{ + m_topicManager->removeTopics(_clientID, _topicList); + if (!_callback) + { + return; + } + _callback(nullptr); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AMOPImpl.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AMOPImpl.h" new file mode 100644 index 00000000..9f35d234 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AMOPImpl.h" @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2021 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 AMOPImpl.h + * @author: octopus + * @date 2021-10-26 + */ +#pragma once +#include "Common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +namespace bcos +{ +namespace amop +{ +class AMOPImpl : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + AMOPImpl(TopicManager::Ptr _topicManager, AMOPMessageFactory::Ptr _messageFactory, + bcos::protocol::AMOPRequestFactory::Ptr _requestFactory, + bcos::gateway::P2PInterface::Ptr _network, bcos::gateway::P2pID const& _p2pNodeID); + virtual ~AMOPImpl() {} + + virtual void start(); + virtual void stop(); + virtual void asyncSubscribeTopic(std::string const& _clientID, std::string const& _topicInfo, + std::function _callback); + virtual void asyncRemoveTopic(std::string const& _clientID, + std::vector const& _topicList, std::function _callback); + + /** + * @brief: async send message to random node subscribe _topic + * @param _topic: topic + * @param _data: message data + * @param _respFunc: callback + * @return void + */ + virtual void asyncSendMessageByTopic(const std::string& _topic, bcos::bytesConstRef _data, + std::function _respFunc); + + /** + * @brief: async send message to all nodes subscribe _topic + * @param _topic: topic + * @param _data: message data + * @return void + */ + virtual void asyncSendBroadcastMessageByTopic( + const std::string& _topic, bcos::bytesConstRef _data); + + virtual void onAMOPMessage(bcos::gateway::NetworkException const& _e, + bcos::gateway::P2PSession::Ptr _session, + std::shared_ptr _message); + + virtual TopicManager::Ptr topicManager() { return m_topicManager; } + +protected: + virtual void dispatcherAMOPMessage(bcos::gateway::NetworkException const& _e, + bcos::gateway::P2PSession::Ptr _session, + std::shared_ptr _message); + /** + * @brief: periodically send topicSeq to all other nodes + * @return void + */ + virtual void broadcastTopicSeq(); + + /** + * @brief: receive topicSeq from other nodes + * @param _nodeID: the sender nodeID + * @param _id: the message id + * @param _msg: message + * @return void + */ + virtual void onReceiveTopicSeqMessage( + bcos::gateway::P2pID const& _nodeID, AMOPMessage::Ptr _msg); + + /** + * @brief: receive request topic message from other nodes + * @param _nodeID: the sender nodeID + * @param _id: the message id + * @param _msg: message + * @return void + */ + void onReceiveRequestTopicMessage(bcos::gateway::P2pID const& _nodeID, AMOPMessage::Ptr _msg); + + /** + * @brief: receive topic response message from other nodes + * @param _nodeID: the sender nodeID + * @param _id: the message id + * @param _msg: message + * @return void + */ + virtual void onReceiveResponseTopicMessage( + bcos::gateway::P2pID const& _nodeID, AMOPMessage::Ptr _msg); + + /** + * @brief: receive amop message + * @param _nodeID: the sender nodeID + * @param _id: the message id + * @param _msg: message + * @return void + */ + virtual void onReceiveAMOPMessage(bcos::gateway::P2pID const& _nodeID, AMOPMessage::Ptr _msg, + std::function const& _responseCallback); + + /** + * @brief: receive broadcast message + * @param _nodeID: the sender nodeID + * @param _id: the message id + * @param _msg: message + * @return void + */ + virtual void onReceiveAMOPBroadcastMessage( + bcos::gateway::P2pID const& _nodeID, AMOPMessage::Ptr _msg); + +private: + std::shared_ptr buildAndEncodeMessage(uint32_t _type, bcos::bytesConstRef _data); + virtual void onReceiveAMOPMessage(bcos::gateway::P2pID const& _nodeID, + std::string const& _topic, bytesConstRef _data, + std::function const& _responseCallback); + void onRecvAMOPResponse(int16_t _type, bytesPointer _responseData, + std::function _callback); + bool trySendTopicMessageToLocalClient(const std::string& _topic, bcos::bytesConstRef _data, + std::function _respFunc); + +private: + std::shared_ptr m_topicManager; + std::shared_ptr m_messageFactory; + std::shared_ptr m_requestFactory; + std::shared_ptr m_timer; + bcos::gateway::P2PInterface::Ptr m_network; + bcos::gateway::P2pID m_p2pNodeID; + ThreadPool::Ptr m_threadPool; + + unsigned const TOPIC_SYNC_PERIOD = 2000; +}; +} // namespace amop +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AMOPMessage.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AMOPMessage.cpp" new file mode 100644 index 00000000..385aac36 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AMOPMessage.cpp" @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2021 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 AMOPMessage.cpp + * @author: octopus + * @date 2021-06-21 + */ + +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::amop; + +const size_t AMOPMessage::HEADER_LENGTH; + +bool AMOPMessage::encode(bcos::bytes& _buffer) +{ + // encode version + uint16_t version = boost::asio::detail::socket_ops::host_to_network_short(m_version); + _buffer.insert(_buffer.end(), (byte*)&version, (byte*)&version + 2); + + uint16_t type = boost::asio::detail::socket_ops::host_to_network_short(m_type); + _buffer.insert(_buffer.end(), (byte*)&type, (byte*)&type + 2); + uint16_t status = boost::asio::detail::socket_ops::host_to_network_short(m_status); + _buffer.insert(_buffer.end(), (byte*)&status, (byte*)&status + 2); + _buffer.insert(_buffer.end(), m_data.begin(), m_data.end()); + return true; +} + +ssize_t AMOPMessage::decode(bcos::bytesConstRef _buffer) +{ + if (_buffer.size() < HEADER_LENGTH) + { + AMOP_MSG_LOG(ERROR) + << LOG_BADGE("decode") + << LOG_DESC("the topic length smaller than the minimum length(2), data size:" + + std::to_string(_buffer.size())) + << LOG_KV("data", *toHexString(_buffer)); + return -1; + } + std::size_t offset = 0; + // decode version + m_version = boost::asio::detail::socket_ops::network_to_host_short( + *((uint16_t*)(_buffer.data() + offset))); + offset += 2; + + m_type = boost::asio::detail::socket_ops::network_to_host_short( + *((uint16_t*)(_buffer.data() + offset))); + offset += 2; + m_status = boost::asio::detail::socket_ops::network_to_host_short( + *((uint16_t*)(_buffer.data() + offset))); + offset += 2; + m_data = _buffer.getCroppedData(offset); + + return _buffer.size(); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AMOPMessage.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AMOPMessage.h" new file mode 100644 index 00000000..76db89da --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AMOPMessage.h" @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2021 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 AMOPMessage.h + * @author: octopus + * @date 2021-06-17 + */ +#pragma once +#include + +namespace bcos +{ +namespace amop +{ +class AMOPMessage +{ +public: + enum Type : uint16_t + { + TopicSeq = 0x1, + RequestTopic = 0x2, + ResponseTopic = 0x3, + AMOPRequest = 0x4, + AMOPResponse = 0x5, + AMOPBroadcast = 0x5 + }; + /// type(2) + status(2) + version(2) + const static size_t HEADER_LENGTH = 6; + /// the max length of topic(65535) + const static size_t MAX_TOPIC_LENGTH = 0xffff; + + + using Ptr = std::shared_ptr; + AMOPMessage() {} + AMOPMessage(bytesConstRef _data) { decode(_data); } + virtual ~AMOPMessage() {} + + virtual uint16_t type() const { return m_type; } + virtual void setType(uint16_t _type) { m_type = _type; } + + virtual bytesConstRef data() const { return m_data; } + // Note: must maintain life time for _data + virtual void setData(bcos::bytesConstRef _data) { m_data = _data; } + virtual void setStatus(uint16_t _status) { m_status = _status; } + virtual uint16_t status() const { return m_status; } + + virtual uint16_t version() const { return m_version; } + virtual void setVersion(uint16_t version) { m_version = version; } + +public: + bool encode(bytes& _buffer); + ssize_t decode(bytesConstRef _buffer); + +private: + uint16_t m_version = 0; + uint16_t m_type{0}; + uint16_t m_status{0}; + bcos::bytesConstRef m_data = bytesConstRef(); +}; +class AMOPMessageFactory +{ +public: + using Ptr = std::shared_ptr; + AMOPMessageFactory() = default; + AMOPMessage::Ptr buildMessage() { return std::make_shared(); } + // Note: must maintain lifetime of _data + AMOPMessage::Ptr buildMessage(bytesConstRef _data) + { + return std::make_shared(_data); + } +}; +} // namespace amop +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AirTopicManager.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AirTopicManager.h" new file mode 100644 index 00000000..d824885a --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/AirTopicManager.h" @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2021 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 AirTopicManager.h + * @author: octopus + * @date 2021-06-18 + */ +#pragma once +#include +#include +namespace bcos +{ +namespace amop +{ +class LocalTopicManager : public TopicManager +{ +public: + using Ptr = std::shared_ptr; + LocalTopicManager(std::string const& _rpcServiceName, bcos::gateway::P2PInterface::Ptr _network) + : TopicManager(_rpcServiceName, _network) + {} + ~LocalTopicManager() override {} + + void setLocalClient(bcos::rpc::RPCInterface::Ptr _rpc) { m_rpc = _rpc; } + bcos::rpc::RPCInterface::Ptr createAndGetServiceByClient(std::string const&) override + { + return m_rpc; + } + void start() override {} + void stop() override {} + +private: + bcos::rpc::RPCInterface::Ptr m_rpc; +}; +} // namespace amop +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/Common.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/Common.h" new file mode 100644 index 00000000..a7745879 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/Common.h" @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2021 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: octopus + * @date 2021-06-21 + */ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +#define TOPIC_LOG(LEVEL) BCOS_LOG(LEVEL) << "[AMOP][TOPIC]" +#define AMOP_MSG_LOG(LEVEL) BCOS_LOG(LEVEL) << "[AMOP][MSG]" +#define AMOP_LOG(LEVEL) BCOS_LOG(LEVEL) << "[AMOP][AMOP]" +namespace bcos +{ +namespace amop +{ +class TopicItem +{ +public: + using Ptr = std::shared_ptr; + +public: + TopicItem() {} + TopicItem(const std::string& _topicName) : m_topicName(_topicName) {} + +private: + std::string m_topicName; + +public: + std::string topicName() const { return m_topicName; } + void setTopicName(const std::string& _topicName) { m_topicName = _topicName; } +}; + +inline bool operator<(const TopicItem& _topicItem0, const TopicItem& _topicItem1) +{ + return _topicItem0.topicName() < _topicItem1.topicName(); +} +using TopicItems = std::set; + +inline std::string randomChoose(std::vector _datas) +{ + auto seed = std::chrono::system_clock::now().time_since_epoch().count(); + std::default_random_engine e(seed); + std::shuffle(_datas.begin(), _datas.end(), e); + return *(_datas.begin()); +} + +inline std::string shortHex(std::string const& _nodeID) +{ + return _nodeID.substr(0, 8); +} +} // namespace amop +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/TopicManager.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/TopicManager.cpp" new file mode 100644 index 00000000..473a82c5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/TopicManager.cpp" @@ -0,0 +1,488 @@ +/* + * Copyright (C) 2021 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 TopicManager.cpp + * @author: octopus + * @date 2021-06-18 + */ + +#include "bcos-tars-protocol/Common.h" +#include "bcos-utilities/BoostLog.h" +#include "fisco-bcos-tars-service/Common/TarsUtils.h" +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::amop; +using namespace bcos::gateway; + +/** + * @brief: parse client sub topics json + * @param _topicItems: return value, topics + * @param _json: json + * @return void + */ +bool TopicManager::parseSubTopicsJson(const std::string& _json, TopicItems& _topicItems) +{ + Json::Value root; + Json::Reader jsonReader; + + try + { + if (!jsonReader.parse(_json, root)) + { + TOPIC_LOG(ERROR) << LOG_BADGE("parseSubTopicsJson") << LOG_DESC("unable to parse json") + << LOG_KV("json:", _json); + return false; + } + + TopicItems topicItems; + + auto topicItemsSize = root["topics"].size(); + + for (unsigned int i = 0; i < topicItemsSize; i++) + { + std::string topic = root["topics"][i].asString(); + topicItems.insert(TopicItem(topic)); + } + + _topicItems = topicItems; + + TOPIC_LOG(INFO) << LOG_BADGE("parseSubTopicsJson") + << LOG_KV("topicItems size", topicItems.size()) << LOG_KV("json", _json); + return true; + } + catch (const std::exception& e) + { + TOPIC_LOG(ERROR) << LOG_BADGE("parseSubTopicsJson") + << LOG_KV("error", boost::diagnostic_information(e)) + << LOG_KV("json:", _json); + return false; + } +} + +/** + * @brief: client subscribe topic + * @param _clientID: client identify, to be defined + * @param _topicJson: topics subscribe by client + * @return void + */ +void TopicManager::subTopic(const std::string& _client, const std::string& _topicJson) +{ + TopicItems topicItems; + // parser topic json + if (parseSubTopicsJson(_topicJson, topicItems)) + { + subTopic(_client, topicItems); + }; +} + +/** + * @brief: client subscribe topic + * @param _clientID: client identify, to be defined + * @param _topicItems: topics subscribe by client + * @return void + */ +void TopicManager::subTopic(const std::string& _client, const TopicItems& _topicItems) +{ + { + std::unique_lock lock(x_clientTopics); + m_client2TopicItems[_client] = _topicItems; // Override the previous value + incTopicSeq(); + } + createAndGetServiceByClient(_client); + TOPIC_LOG(INFO) << LOG_BADGE("subTopic") << LOG_KV("client", _client) + << LOG_KV("topicSeq", topicSeq()) + << LOG_KV("topicItems size", _topicItems.size()); +} + +/** + * @brief: query topics sub by client + * @param _clientID: client identify, to be defined + * @param _topicItems: topics subscribe by client + * @return void + */ +bool TopicManager::queryTopicItemsByClient(const std::string& _client, TopicItems& _topicItems) +{ + bool result = false; + { + std::shared_lock lock(x_clientTopics); + auto it = m_client2TopicItems.find(_client); + if (it != m_client2TopicItems.end()) + { + _topicItems = it->second; + result = true; + } + } + + TOPIC_LOG(INFO) << LOG_BADGE("queryTopicItemsByClient") << LOG_KV("client", _client) + << LOG_KV("result", result) << LOG_KV("topicItems size", _topicItems.size()); + return result; +} + +/** + * @brief: clear all topics subscribe by client + * @param _clientID: client identify, to be defined + * @return void + */ +void TopicManager::removeTopics( + const std::string& _client, std::vector const& _topicList) +{ + if (_topicList.size() == 0) + { + return; + } + { + std::unique_lock lock(x_clientTopics); + if (!m_client2TopicItems.count(_client)) + { + return; + } + for (auto const& topic : _topicList) + { + if (m_client2TopicItems[_client].count(topic)) + { + m_client2TopicItems[_client].erase(topic); + } + TOPIC_LOG(INFO) << LOG_BADGE("removeTopics") << LOG_KV("client", _client) + << LOG_KV("topicSeq", topicSeq()) << LOG_KV("topic", topic); + } + incTopicSeq(); + } +} + +void TopicManager::removeTopicsByClient(const std::string& _client) +{ + std::size_t result = 0; + { + std::unique_lock lock(x_clientTopics); + + result = m_client2TopicItems.erase(_client); + } + + incTopicSeq(); + + TOPIC_LOG(INFO) << LOG_BADGE("removeTopicsByClient") << LOG_KV("client", _client) + << LOG_KV("success", result); +} + +/** + * @brief: query topics subscribe by all connected clients + * @return result in json format + */ +std::string TopicManager::queryTopicsSubByClient() +{ + try + { + uint32_t seq; + TopicItems topicItems; + { + std::shared_lock lock(x_clientTopics); + seq = topicSeq(); + for (const auto& m : m_client2TopicItems) + { + topicItems.insert(m.second.begin(), m.second.end()); + } + } + + Json::Value jTopics = Json::Value(Json::arrayValue); + for (const auto& topicItem : topicItems) + { + jTopics.append(topicItem.topicName()); + } + + Json::Value jResp; + jResp["topicSeq"] = seq; + jResp["topicItems"] = jTopics; + + Json::FastWriter writer; + std::string topicJson = writer.write(jResp); + + TOPIC_LOG(DEBUG) << LOG_BADGE("queryTopicsSubByClient") << LOG_KV("topicSeq", seq) + << LOG_KV("topicJson", topicJson); + return topicJson; + } + catch (const std::exception& e) + { + TOPIC_LOG(ERROR) << LOG_BADGE("queryTopicsSubByClient") + << LOG_KV("error", boost::diagnostic_information(e)); + return ""; + } +} + +/** + * @brief: parse json to fetch topicSeq and topicItems + * @param _topicSeq: topicSeq + * @param _topicItems: topics + * @param _json: json + * @return void + */ +bool TopicManager::parseTopicItemsJson( + uint32_t& _topicSeq, TopicItems& _topicItems, const std::string& _json) +{ + Json::Value root; + Json::Reader jsonReader; + + try + { + if (!jsonReader.parse(_json, root)) + { + TOPIC_LOG(ERROR) << LOG_BADGE("parseTopicItemsJson") << LOG_DESC("unable to parse json") + << LOG_KV("json:", _json); + return false; + } + + uint32_t topicSeq; + TopicItems topicItems; + + topicSeq = root["topicSeq"].asUInt(); + auto topicItemsSize = root["topicItems"].size(); + + for (unsigned int i = 0; i < topicItemsSize; i++) + { + std::string topic = root["topicItems"][i].asString(); + topicItems.insert(TopicItem(topic)); + } + + _topicSeq = topicSeq; + _topicItems = topicItems; + + TOPIC_LOG(INFO) << LOG_BADGE("parseTopicItemsJson") << LOG_KV("topicSeq", topicSeq) + << LOG_KV("topicItems size", topicItems.size()) << LOG_KV("json", _json); + return true; + } + catch (const std::exception& e) + { + TOPIC_LOG(ERROR) << LOG_BADGE("parseTopicItemsJson") << LOG_DESC("parse json exception") + << LOG_KV("error", boost::diagnostic_information(e)) + << LOG_KV("json:", _json); + return false; + } +} + +/** + * @brief: check if the topicSeq of nodeID changed + * @param _nodeID: the peer nodeID + * @param _topicSeq: the topicSeq of the nodeID + * @return bool: if the nodeID has been changed + */ +bool TopicManager::checkTopicSeq(P2pID const& _nodeID, uint32_t _topicSeq) +{ + std::shared_lock lock(x_topics); + auto it = m_nodeID2TopicSeq.find(_nodeID); + if (it != m_nodeID2TopicSeq.end() && it->second == _topicSeq) + { + return false; + } + return true; +} + +/** + * @brief: update online nodeIDs, clean up the offline nodeIDs state + * @param _nodeIDs: the online nodeIDs + * @return void + */ +void TopicManager::notifyNodeIDs(const std::vector& _nodeIDs) +{ + int removeCount = 0; + { + std::unique_lock lock(x_topics); + for (auto it = m_nodeID2TopicSeq.begin(); it != m_nodeID2TopicSeq.end();) + { + if (std::find_if(_nodeIDs.begin(), _nodeIDs.end(), [&it](std::string _nodeID) -> bool { + return it->first == _nodeID; + }) == _nodeIDs.end()) + { // nodeID is offline, remove the nodeID's state + m_nodeID2TopicItems.erase(it->first); + it = m_nodeID2TopicSeq.erase(it); + removeCount++; + } + else + { + ++it; + } + } + } + + TOPIC_LOG(INFO) << LOG_BADGE("notifyNodeIDs") << LOG_KV("removeCount", removeCount); +} + +/** + * @brief: update the topicSeq and topicItems of the nodeID's + * @param _nodeID: nodeID + * @param _topicSeq: topicSeq + * @param _topicItems: topicItems + * @return void + */ +void TopicManager::updateSeqAndTopicsByNodeID( + P2pID const& _nodeID, uint32_t _topicSeq, const TopicItems& _topicItems) +{ + { + std::unique_lock lock(x_topics); + m_nodeID2TopicSeq[_nodeID] = _topicSeq; + m_nodeID2TopicItems[_nodeID] = _topicItems; + } + + TOPIC_LOG(INFO) << LOG_BADGE("updateSeqAndTopicsByNodeID") << LOG_KV("nodeID", _nodeID) + << LOG_KV("topicSeq", _topicSeq) + << LOG_KV("topicItems size", _topicItems.size()); +} + +/** + * @brief: find the nodeIDs by topic + * @param _topic: topic + * @param _nodeIDs: nodeIDs + * @return void + */ +void TopicManager::queryNodeIDsByTopic( + const std::string& _topic, std::vector& _nodeIDs) +{ + std::shared_lock lock(x_topics); + for (auto it = m_nodeID2TopicItems.begin(); it != m_nodeID2TopicItems.end(); ++it) + { + auto findIt = std::find_if(it->second.begin(), it->second.end(), + [_topic](const TopicItem& _topicItem) { return _topic == _topicItem.topicName(); }); + // only return the connected nodes + if (findIt != it->second.end() && m_network->isReachable(it->first)) + { + _nodeIDs.push_back(it->first); + } + } + return; +} + +/** + * @brief: find clients by topic + * @param _topic: topic + * @param _nodeIDs: nodeIDs + * @return void + */ +void TopicManager::queryClientsByTopic( + const std::string& _topic, std::vector& _clients) +{ + { + std::shared_lock lock(x_clientTopics); + for (const auto& items : m_client2TopicItems) + { + auto it = std::find_if(items.second.begin(), items.second.end(), + [_topic](const TopicItem& _topicItem) { return _topic == _topicItem.topicName(); }); + if (it != items.second.end()) + { + _clients.push_back(items.first); + } + } + } + + TOPIC_LOG(INFO) << LOG_BADGE("queryClientsByTopic") << LOG_KV("topic", _topic) + << LOG_KV("clients size", _clients.size()); +} + +// +bcos::rpc::RPCInterface::Ptr TopicManager::createAndGetServiceByClient(std::string const& _clientID) +{ + try + { + UpgradableGuard l(x_clientInfo); + if (m_clientInfo.count(_clientID)) + { + return m_clientInfo[_clientID]; + } + + auto serviceName = m_rpcServiceName; + + auto topicManagerWeakPtr = std::weak_ptr(shared_from_this()); + auto servicePrx = bcostars::createServantProxy( + tars::Application::getCommunicator().get(), _clientID, + bcostars::TarsServantProxyOnConnectHandler(), + [serviceName, topicManagerWeakPtr](const tars::TC_Endpoint& ep) { + auto topicManager = topicManagerWeakPtr.lock(); + if (!topicManager) + { + return; + } + + auto endPointUrl = bcostars::endPointToString(serviceName, ep); + topicManager->removeTopicsByClient(endPointUrl); + }); + + auto rpcClient = std::make_shared(servicePrx, m_rpcServiceName); + + { + UpgradeGuard ul(l); + m_clientInfo[_clientID] = rpcClient; + } + + TOPIC_LOG(INFO) << LOG_DESC("createAndGetServiceByClient") << LOG_KV("clientID", _clientID); + return rpcClient; + } + catch (std::exception const& e) + { + TOPIC_LOG(WARNING) << LOG_DESC("createAndGetServiceByClient exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + return nullptr; +} + +void TopicManager::notifyRpcToSubscribeTopics() +{ + try + { + auto servicePrx = bcostars::createServantProxy(m_rpcServiceName); + + auto rpcClient = std::make_shared(servicePrx, m_rpcServiceName); + + auto activeEndPoints = bcostars::tarsProxyAvailableEndPoints(rpcClient->prx()); + + TOPIC_LOG(INFO) << LOG_DESC("notifyRpcToSubscribeTopics") + << LOG_KV("rpcServiceName", m_rpcServiceName) + << LOG_KV("activeEndPoints size", activeEndPoints.size()); + + for (auto const& endPoint : activeEndPoints) + { + auto endPointStr = bcostars::endPointToString(m_rpcServiceName, endPoint); + + auto servicePrx = + bcostars::createServantProxy(m_rpcServiceName, endPoint); + + auto serviceClient = + std::make_shared(servicePrx, m_rpcServiceName); + serviceClient->asyncNotifySubscribeTopic( + [this, endPointStr](Error::Ptr _error, std::string _topicInfo) { + if (_error) + { + TOPIC_LOG(INFO) << LOG_DESC("asyncNotifySubscribeTopic error") + << LOG_KV("endPoint", endPointStr) + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + return; + } + TOPIC_LOG(INFO) + << LOG_DESC("asyncNotifySubscribeTopic success") + << LOG_KV("endPoint", endPointStr) << LOG_KV("topicInfo", _topicInfo); + + subTopic(endPointStr, _topicInfo); + }); + } + } + catch (std::exception const& e) + { + TOPIC_LOG(WARNING) << LOG_DESC("notifyRpcToSubscribeTopics exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/TopicManager.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/TopicManager.h" new file mode 100644 index 00000000..cbef0d84 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libamop/TopicManager.h" @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2021 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 TopicManager.h + * @author: octopus + * @date 2021-06-18 + */ +#pragma once + +#include "fisco-bcos-tars-service/Common/TarsUtils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace amop +{ +class TopicManager : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + TopicManager(std::string const& _rpcServiceName, bcos::gateway::P2PInterface::Ptr _network) + { + m_rpcServiceName = _rpcServiceName; + m_network = _network; + } + virtual ~TopicManager() {} + + virtual void start() { notifyRpcToSubscribeTopics(); } + virtual void stop() {} + + uint32_t topicSeq() const { return m_topicSeq; } + uint32_t incTopicSeq() + { + uint32_t topicSeq = ++m_topicSeq; + return topicSeq; + } + + /** + * @brief: parse client sub topics json + * @param _topicItems: return value, topics + * @param _json: json + * @return void + */ + bool parseSubTopicsJson(const std::string& _json, TopicItems& _topicItems); + /** + * @brief: client subscribe topic + * @param _clientID: client identify, to be defined + * @param _topicJson: topics subscribe by client + * @return void + */ + void subTopic(const std::string& _client, const std::string& _topicJson); + /** + * @brief: client subscribe topic + * @param _clientID: client identify, to be defined + * @param _topicItems: topics subscribe by client + * @return void + */ + void subTopic(const std::string& _client, const TopicItems& _topicItems); + /** + * @brief: query topics sub by client + * @param _clientID: client identify, to be defined + * @param _topicItems: topics subscribe by client + * @return bool + */ + bool queryTopicItemsByClient(const std::string& _client, TopicItems& _topicItems); + /** + * @brief: remove all topics subscribed by client + * @param _clientID: client identify, to be defined + * @return void + */ + void removeTopics(const std::string& _client, std::vector const& _topicList); + + void removeTopicsByClient(const std::string& _client); + /** + * @brief: query topics subscribed by all connected clients + * @return json string result, include topicSeq and topicItems fields + */ + std::string queryTopicsSubByClient(); + /** + * @brief: parse json to fetch topicSeq and topicItems + * @param _topicSeq: return value, topicSeq + * @param _topicItems: return value, topics + * @param _json: json + * @return void + */ + bool parseTopicItemsJson( + uint32_t& _topicSeq, TopicItems& _topicItems, const std::string& _json); + /** + * @brief: check if the topicSeq of nodeID changed + * @param _nodeID: the peer nodeID + * @param _topicSeq: the topicSeq of the nodeID + * @return bool: if the nodeID has been changed + */ + bool checkTopicSeq(bcos::gateway::P2pID const& _nodeID, uint32_t _topicSeq); + /** + * @brief: update online nodeIDs, clean up the offline nodeIDs state + * @param _nodeIDs: the online nodeIDs + * @return void + */ + void notifyNodeIDs(const std::vector& _nodeIDs); + /** + * @brief: update the topicSeq and topicItems of the nodeID's + * @param _nodeID: nodeID + * @param _topicSeq: topicSeq + * @param _topicItems: topicItems + * @return void + */ + void updateSeqAndTopicsByNodeID( + bcos::gateway::P2pID const& _nodeID, uint32_t _topicSeq, const TopicItems& _topicItems); + /** + * @brief: find the nodeIDs by topic + * @param _topic: topic + * @param _nodeIDs: nodeIDs + * @return void + */ + void queryNodeIDsByTopic(const std::string& _topic, std::vector& _nodeIDs); + /** + * @brief: find clients by topic + * @param _topic: topic + * @param _nodeIDs: nodeIDs + * @return void + */ + void queryClientsByTopic(const std::string& _topic, std::vector& _clients); + + virtual bcos::rpc::RPCInterface::Ptr createAndGetServiceByClient(std::string const& _clientID); + +protected: + virtual void notifyRpcToSubscribeTopics(); + + // m_client2TopicItems lock + mutable std::shared_mutex x_clientTopics; + // client => TopicItems + // Note: the clientID is the rpc node endpoint + std::unordered_map m_client2TopicItems; + + // topicSeq + std::atomic m_topicSeq{1}; + + // nodeID => topicSeq + std::unordered_map m_nodeID2TopicSeq; + // m_nodeID2TopicSeq lock + mutable std::shared_mutex x_topics; + + // nodeID => topicItems + std::unordered_map m_nodeID2TopicItems; + + std::map m_clientInfo; + mutable SharedMutex x_clientInfo; + + std::string m_rpcServiceName; + bcos::gateway::P2PInterface::Ptr m_network; +}; +} // namespace amop +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/ASIOInterface.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/ASIOInterface.cpp" new file mode 100644 index 00000000..372983c0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/ASIOInterface.cpp" @@ -0,0 +1,36 @@ +/** + * @brief: inteface for boost::asio(for unittest) + * + * @file AsioInterface.cpp + * @author: bxq2011hust + * @date 2019-07-244 + */ +#include + +namespace ba = boost::asio; +namespace bi = ba::ip; +using namespace bcos; +using namespace bcos::gateway; +using namespace std; + +void ASIOInterface::asyncResolveConnect(std::shared_ptr socket, Handler_Type handler) +{ + auto protocol = socket->nodeIPEndpoint().isIPv6() ? bi::tcp::tcp::v6() : bi::tcp::tcp::v4(); + m_resolver->async_resolve(protocol, socket->nodeIPEndpoint().address(), + to_string(socket->nodeIPEndpoint().port()), + [=](const boost::system::error_code& ec, bi::tcp::resolver::results_type results) { + if (!ec) + { + // results is a iterator, but only use first endpoint. + socket->ref().async_connect(results->endpoint(), handler); + ASIO_LOG(INFO) << LOG_DESC("asyncResolveConnect") + << LOG_KV("endpoint", results->endpoint()); + } + else + { + ASIO_LOG(WARNING) << LOG_DESC("asyncResolve failed") + << LOG_KV("host", socket->nodeIPEndpoint().address()) + << LOG_KV("port", socket->nodeIPEndpoint().port()); + } + }); +} diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/ASIOInterface.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/ASIOInterface.h" new file mode 100644 index 00000000..0804e97b --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/ASIOInterface.h" @@ -0,0 +1,187 @@ +/** + * @brief: inteface for boost::asio(for unittest) + * + * @file AsioInterface.h + * @author: yujiechen + * @date 2018-09-13 + */ +#pragma once +#include +#include +#include +#include +#include + +namespace ba = boost::asio; +namespace bi = ba::ip; + +namespace bcos +{ +namespace gateway +{ +class ASIOInterface +{ +public: + enum ASIO_TYPE + { + TCP_ONLY = 0, + SSL = 1 + }; + + /// CompletionHandler + using Base_Handler = boost::function; + /// accept handler + using Handler_Type = boost::function; + /// write handler + using ReadWriteHandler = boost::function; + using VerifyCallback = boost::function; + + virtual ~ASIOInterface() {} + virtual void setType(int type) { m_type = type; } + + virtual std::shared_ptr ioService() { return m_ioServicePool->getIOService(); } + virtual void setIOServicePool(IOServicePool::Ptr _ioServicePool) + { + m_ioServicePool = _ioServicePool; + m_timerIOService = m_ioServicePool->getIOService(); + } + + virtual std::shared_ptr srvContext() { return m_srvContext; } + virtual std::shared_ptr clientContext() { return m_clientContext; } + + virtual void setSrvContext(std::shared_ptr _srvContext) + { + m_srvContext = _srvContext; + } + virtual void setClientContext(std::shared_ptr _clientContext) + { + m_clientContext = _clientContext; + } + + virtual std::shared_ptr newTimer(uint32_t timeout) + { + return std::make_shared( + *(m_timerIOService), boost::posix_time::milliseconds(timeout)); + } + + virtual std::shared_ptr newSocket( + bool _server, NodeIPEndpoint nodeIPEndpoint = NodeIPEndpoint()) + { + std::shared_ptr m_socket = + std::make_shared(m_ioServicePool->getIOService(), + _server ? *m_srvContext : *m_clientContext, nodeIPEndpoint); + return m_socket; + } + + virtual std::shared_ptr acceptor() { return m_acceptor; } + + virtual void init(std::string listenHost, uint16_t listenPort) + { + m_strand = + std::make_shared(*(m_ioServicePool->getIOService())); + m_resolver = std::make_shared(*(m_ioServicePool->getIOService())); + m_acceptor = std::make_shared(*(m_ioServicePool->getIOService()), + bi::tcp::endpoint(bi::make_address(listenHost), listenPort)); + boost::asio::socket_base::reuse_address optionReuseAddress(true); + m_acceptor->set_option(optionReuseAddress); + } + + virtual void start() { m_ioServicePool->start(); } + virtual void stop() { m_ioServicePool->stop(); } + + virtual void asyncAccept(std::shared_ptr socket, Handler_Type handler, + boost::system::error_code = boost::system::error_code()) + { + m_acceptor->async_accept(socket->ref(), handler); + } + + virtual void asyncResolveConnect(std::shared_ptr socket, Handler_Type handler); + + virtual void asyncWrite(std::shared_ptr socket, + boost::asio::mutable_buffers_1 buffers, ReadWriteHandler handler) + { + auto type = m_type; + auto ioService = socket->ioService(); + ioService->post([type, socket, buffers, handler]() { + if (socket->isConnected()) + { + switch (type) + { + case TCP_ONLY: + { + ba::async_write(socket->ref(), buffers, handler); + break; + } + case SSL: + { + ba::async_write(socket->sslref(), buffers, handler); + break; + } + } + } + }); + } + + virtual void asyncRead(std::shared_ptr socket, + boost::asio::mutable_buffers_1 buffers, ReadWriteHandler handler) + { + switch (m_type) + { + case TCP_ONLY: + { + ba::async_read(socket->ref(), buffers, handler); + break; + } + case SSL: + { + ba::async_read(socket->sslref(), buffers, handler); + break; + } + } + } + + virtual void asyncReadSome(std::shared_ptr socket, + boost::asio::mutable_buffers_1 buffers, ReadWriteHandler handler) + { + switch (m_type) + { + case TCP_ONLY: + { + socket->ref().async_read_some(buffers, handler); + break; + } + case SSL: + { + socket->sslref().async_read_some(buffers, handler); + break; + } + } + } + + virtual void asyncHandshake(std::shared_ptr socket, + ba::ssl::stream_base::handshake_type type, Handler_Type handler) + { + socket->sslref().async_handshake(type, handler); + } + + virtual void setVerifyCallback( + std::shared_ptr socket, VerifyCallback callback, bool = true) + { + socket->sslref().set_verify_callback(callback); + } + + virtual void strandPost(Base_Handler handler) { m_strand->post(handler); } + +protected: + IOServicePool::Ptr m_ioServicePool; + std::shared_ptr m_timerIOService; + std::shared_ptr m_strand; + std::shared_ptr m_acceptor; + std::shared_ptr m_resolver; + + std::shared_ptr m_srvContext; + std::shared_ptr m_clientContext; + int m_type = 0; +}; +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Common.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Common.h" new file mode 100644 index 00000000..0ae428b1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Common.h" @@ -0,0 +1,140 @@ + +/** @file Common.h + * Miscellanea required for the Host/Session/NodeTable classes. + * + * @author yujiechen + * @date: 2018-09-19 + */ + +#pragma once + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +namespace ba = boost::asio; +namespace bi = boost::asio::ip; +#define HOST_LOG(LEVEL) BCOS_LOG(LEVEL) << "[NETWORK][Host]" +#define SESSION_LOG(LEVEL) BCOS_LOG(LEVEL) << "[SESSION][Session]" +#define ASIO_LOG(LEVEL) BCOS_LOG(LEVEL) << "[ASIO][ASIO]" + +namespace bcos +{ +namespace gateway +{ +enum MessageDecodeStatus +{ + MESSAGE_ERROR = -1, + MESSAGE_INCOMPLETE = 0, +}; +enum DisconnectReason +{ + DisconnectRequested = 0, + TCPError, + BadProtocol, + UselessPeer, + TooManyPeers, + DuplicatePeer, + IncompatibleProtocol, + NullIdentity, + ClientQuit, + UnexpectedIdentity, + LocalIdentity, + PingTimeout, + UserReason = 0x10, + IdleWaitTimeout = 0x11, + NegotiateFailed = 0x12, + NoDisconnect = 0xffff +}; + +///< P2PExceptionType and g_P2PExceptionMsg used in P2PException +enum P2PExceptionType +{ + Success = 0, + ProtocolError, + NetworkTimeout, + Disconnect, + P2PExceptionTypeCnt, + ConnectError, + DuplicateSession, + NotInWhitelist, + BandwidthOverFlow, + ALL, +}; + +// +using P2pID = std::string; +using P2pIDs = std::set; +struct Options +{ + Options() {} + Options(uint32_t _timeout) : timeout(_timeout) {} + uint32_t timeout = 0; ///< The timeout value of async function, in milliseconds. +}; + +class NetworkException : public std::exception +{ +public: + NetworkException(){}; + NetworkException(int _errorCode, const std::string& _msg) + : m_errorCode(_errorCode), m_msg(_msg){}; + + virtual int errorCode() const { return m_errorCode; }; + const char* what() const noexcept override { return m_msg.c_str(); }; + bool operator!() const { return m_errorCode == 0; } + + virtual Error::Ptr toError() { return std::make_shared(errorCode(), m_msg); } + +private: + int m_errorCode = 0; + std::string m_msg; +}; + +/// @returns the string form of the given disconnection reason. +inline std::string reasonOf(DisconnectReason _r) +{ + switch (_r) + { + case DisconnectRequested: + return "Disconnect was requested."; + case TCPError: + return "Low-level TCP communication error."; + case BadProtocol: + return "Data format error."; + case UselessPeer: + return "Peer had no use for this node."; + case TooManyPeers: + return "Peer had too many connections."; + case DuplicatePeer: + return "Peer was already connected."; + case IncompatibleProtocol: + return "Peer protocol versions are incompatible."; + case NullIdentity: + return "Null identity given."; + case ClientQuit: + return "Peer is exiting."; + case UnexpectedIdentity: + return "Unexpected identity given."; + case LocalIdentity: + return "Connected to ourselves."; + case UserReason: + return "Subprotocol reason."; + case NoDisconnect: + return "(No disconnect has happened.)"; + case IdleWaitTimeout: + return "(Idle connection for no network io happens during 5s time " + "intervals.)"; + default: + return "Unknown reason."; + } +} +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Host.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Host.cpp" new file mode 100644 index 00000000..11d33c9d --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Host.cpp" @@ -0,0 +1,515 @@ + +/** @file Host.cpp + * @author Alex Leverington + * @author Gav Wood + * @date 2014 + * @author toxotguo + * @date 2018 + * + * @ author: yujiechen + * @ date: 2018-09-19 + * @ modifications: + * 1. modify io_service value from 1 to 2 + * (construction of io_service is io_service(std::size_t concurrency_hint);) + * (currenncy_hint means that "A suggestion to the implementation on how many + * threads it should allow to run simultaneously.") (since ethereum use 2, we + * modify io_service from 1 to 2) 2. + */ +#include // for ASIOIn... +#include // for HOST_LOG +#include +#include // for Sessio... +#include // for Socket... +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace std; +using namespace bcos; +using namespace bcos::gateway; + +/** + * @brief: accept connection requests, maily include procedures: + * 1. async_accept: accept connection requests + * 2. ssl handshake: obtain node id from the certificate during ssl + * handshake + * 3. if ssl handshake success, call 'handshakeServer' to init client + * socket and get caps, version of the connecting client, and startPeerSession + * (mainly init the caps and session, and update peer related + * information) + * @attention: this function is called repeatedly + */ +void Host::startAccept(boost::system::error_code boost_error) +{ + /// accept the connection + if (m_run) + { + HOST_LOG(INFO) << LOG_DESC("P2P StartAccept") << LOG_KV("Host", m_listenHost) << ":" + << m_listenPort; + auto socket = m_asioInterface->newSocket(true, NodeIPEndpoint()); + // get and set the accepted endpoint to socket(client endpoint) + /// define callback after accept connections + m_asioInterface->asyncAccept( + socket, + [=, this](boost::system::error_code ec) { + /// get the endpoint information of remote client after accept the + /// connections + auto endpoint = socket->remoteEndpoint(); + HOST_LOG(TRACE) << LOG_DESC("P2P Recv Connect, From=") << endpoint; + /// network acception failed + if (ec || !m_run) + { + HOST_LOG(ERROR) << "Error: " << ec; + socket->close(); + startAccept(); + + return; + } + + /// if the connected peer over the limitation, drop socket + socket->setNodeIPEndpoint(endpoint); + HOST_LOG(INFO) << LOG_DESC("P2P Recv Connect, From=") << endpoint; + /// register ssl callback to get the NodeID of peers + std::shared_ptr endpointPublicKey = std::make_shared(); + m_asioInterface->setVerifyCallback(socket, newVerifyCallback(endpointPublicKey)); + m_asioInterface->asyncHandshake(socket, ba::ssl::stream_base::server, + boost::bind(&Host::handshakeServer, shared_from_this(), ba::placeholders::error, + endpointPublicKey, socket)); + + startAccept(); + }, + boost_error); + } +} + +/** + * @brief : functions called after openssl handshake, + * maily to get node id and verify whether the certificate has been + * expired + * @param nodeIDOut : also return value, pointer points to the node id string + * @return std::function: + * return true: verify success + * return false: verify failed + * modifications 2019.03.20: append subject name and issuer name after nodeIDOut + * for demand of fisco-bcos-browser + */ +std::function Host::newVerifyCallback( + std::shared_ptr nodeIDOut) +{ + auto host = std::weak_ptr(shared_from_this()); + return [host, nodeIDOut](bool preverified, boost::asio::ssl::verify_context& ctx) { + auto hostPtr = host.lock(); + if (!hostPtr) + { + return false; + } + + try + { + /// return early when the certificate is invalid + if (!preverified) + { + return false; + } + /// get the object points to certificate + X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); + if (!cert) + { + HOST_LOG(ERROR) << LOG_DESC("Get cert failed"); + return preverified; + } + + if (!hostPtr->sslContextPubHandler()(cert, *nodeIDOut.get())) + { + return preverified; + } + + int crit = 0; + BASIC_CONSTRAINTS* basic = + (BASIC_CONSTRAINTS*)X509_get_ext_d2i(cert, NID_basic_constraints, &crit, NULL); + if (!basic) + { + HOST_LOG(INFO) << LOG_DESC("Get ca basic failed"); + return preverified; + } + + /// ignore ca + if (basic->ca) + { + // ca or agency certificate + HOST_LOG(TRACE) << LOG_DESC("Ignore CA certificate"); + BASIC_CONSTRAINTS_free(basic); + return preverified; + } + + BASIC_CONSTRAINTS_free(basic); + // if (!hostPtr->sslContextPubHandler()(cert, *nodeIDOut.get())) { + // return preverified; + // } + + /// append cert-name and issuer name after node ID + /// get subject name + const char* certName = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); + /// get issuer name + const char* issuerName = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0); + /// format: {nodeID}#{issuer-name}#{cert-name} + nodeIDOut->append("#"); + nodeIDOut->append(issuerName); + nodeIDOut->append("#"); + nodeIDOut->append(certName); + OPENSSL_free((void*)certName); + OPENSSL_free((void*)issuerName); + return preverified; + } + catch (std::exception& e) + { + HOST_LOG(ERROR) << LOG_DESC("Cert verify failed") << boost::diagnostic_information(e); + return preverified; + } + }; +} + +P2PInfo Host::p2pInfo() +{ + try + { + if (m_p2pInfo.p2pID.empty()) + { + /// get certificate + auto sslContext = m_asioInterface->srvContext()->native_handle(); + X509* cert = SSL_CTX_get0_certificate(sslContext); + + /// get issuer name + const char* issuer = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0); + std::string issuerName(issuer); + + /// get subject name + const char* subject = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); + std::string subjectName(subject); + + /// get p2pID + std::string nodeIDOut; + if (m_sslContextPubHandler(cert, nodeIDOut)) + { + m_p2pInfo.p2pID = boost::to_upper_copy(nodeIDOut); + HOST_LOG(INFO) << LOG_DESC("Get node information from cert") + << LOG_KV("p2pid", m_p2pInfo.p2pID); + } + + /// fill in the node informations + m_p2pInfo.agencyName = obtainCommonNameFromSubject(issuerName); + m_p2pInfo.nodeName = obtainCommonNameFromSubject(subjectName); + m_p2pInfo.nodeIPEndpoint = NodeIPEndpoint(m_listenHost, m_listenPort); + /// free resources + OPENSSL_free((void*)issuer); + OPENSSL_free((void*)subject); + } + } + catch (std::exception& e) + { + HOST_LOG(ERROR) << LOG_DESC("Get node information from cert failed.") + << boost::diagnostic_information(e); + return m_p2pInfo; + } + return m_p2pInfo; +} + +/** + * @brief: obtain the common name from the subject of certificate + * + * @param subject : the subject of the certificat + * the subject format is: /CN=xx/O=xxx/OU=xxx/ commonly + * @return std::string: the common name of the certificate + */ +std::string Host::obtainCommonNameFromSubject(std::string const& subject) +{ + std::vector fields; + boost::split(fields, subject, boost::is_any_of("/"), boost::token_compress_on); + for (auto field : fields) + { + std::size_t pos = field.find("CN"); + if (pos != std::string::npos) + { + std::vector cn_fields; + boost::split(cn_fields, field, boost::is_any_of("="), boost::token_compress_on); + /// use the whole fields as the common name + if (cn_fields.size() < 2) + { + return field; + } + /// return real common name + return cn_fields[1]; + } + } + return subject; +} + +/// obtain p2pInfo from given vector +void Host::obtainNodeInfo(P2PInfo& info, std::string const& node_info) +{ + std::vector node_info_vec; + boost::split(node_info_vec, node_info, boost::is_any_of("#"), boost::token_compress_on); + if (!node_info_vec.empty()) + { + info.p2pID = node_info_vec[0]; + } + if (node_info_vec.size() > 1) + { + info.agencyName = obtainCommonNameFromSubject(node_info_vec[1]); + } + if (node_info_vec.size() > 2) + { + info.nodeName = obtainCommonNameFromSubject(node_info_vec[2]); + } + + HOST_LOG(INFO) << "obtainP2pInfo " << LOG_KV("node_info", node_info) + << LOG_KV("p2pid", info.p2pID); +} + +/** + * @brief: server calls handshakeServer to after handshake + * mainly calls RLPxHandshake to obtain informations(client version, + * caps, etc), start peer session and start accepting procedure repeatedly + * @param error: error information triggered in the procedure of ssl handshake + * @param endpointPublicKey: public key obtained from certificate during + * handshake + * @param socket: socket related to the endpoint of the connected client + */ +void Host::handshakeServer(const boost::system::error_code& error, + std::shared_ptr endpointPublicKey, std::shared_ptr socket) +{ + if (error) + { + HOST_LOG(WARNING) << LOG_DESC("handshakeServer Handshake failed") + << LOG_KV("errorValue", error.value()) + << LOG_KV("message", error.message()) + << LOG_KV("endpoint", socket->nodeIPEndpoint()); + socket->close(); + return; + } + if (endpointPublicKey->empty()) + { + HOST_LOG(WARNING) << LOG_DESC("handshakeServer get p2pID failed") + << LOG_KV("remote endpoint", socket->remoteEndpoint()); + socket->close(); + return; + } + if (m_run) + { + /// node info splitted with # + /// format: {nodeId}{#}{agencyName}{#}{nodeName} + std::string node_info(*endpointPublicKey); + P2PInfo info; + obtainNodeInfo(info, node_info); + HOST_LOG(INFO) << LOG_DESC("handshakeServer succ") + << LOG_KV("remote endpoint", socket->remoteEndpoint()) + << LOG_KV("nodeid", info.p2pID); + startPeerSession(info, socket, m_connectionHandler); + } +} + +/** + * @brief: start peer sessions after handshake succeed(called by + * RLPxHandshake), mainly include four functions: + * 1. disconnect connecting host with invalid capability + * 2. modify m_peers && disconnect already-connected session + * 3. modify m_sessions and m_staticNodes + * 4. start new session (session->start()) + * @param _pub: node id of the connecting client + * @param _rlp: informations obtained from the client-peer during handshake + * now include protocolVersion, clientVersion, caps and + * listenPort + * @param _s : connected socket(used to init session object) + */ +// TODO: asyncConnect pass handle to startPeerSession, make use of it +void Host::startPeerSession(P2PInfo const& p2pInfo, std::shared_ptr const& socket, + std::function)>) +{ + auto weakHost = std::weak_ptr(shared_from_this()); + std::shared_ptr ps = + m_sessionFactory->create_session(weakHost, socket, m_messageFactory); + + auto connectionHandler = m_connectionHandler; + m_threadPool->enqueue([ps, connectionHandler, p2pInfo]() { + if (connectionHandler) + { + connectionHandler(NetworkException(0, ""), p2pInfo, ps); + } + else + { + HOST_LOG(WARNING) << LOG_DESC("No connectionHandler, new connection may lost"); + } + }); + HOST_LOG(INFO) << LOG_DESC("startPeerSession, Remote=") << socket->remoteEndpoint() + << LOG_KV("local endpoint", socket->localEndpoint()) + << LOG_KV("p2pid", p2pInfo.p2pID); +} + +/** + * @brief: remove expired timer + * modify alived peers to m_peers + * reconnect all nodes recorded in m_staticNodes periodically + */ +void Host::start() +{ + /// if the p2p network has been stoped, then stop related service + if (!haveNetwork()) + { + m_run = true; + m_asioInterface->init(m_listenHost, m_listenPort); + if (m_asioInterface->acceptor()) + { + startAccept(); + } + m_asioInterface->start(); + } +} + +/** + * @brief : connect to the server + * @param _nodeIPEndpoint : the endpoint of the connected server + */ +void Host::asyncConnect(NodeIPEndpoint const& _nodeIPEndpoint, + std::function)> callback) +{ + if (!m_run) + { + return; + } + HOST_LOG(INFO) << LOG_DESC("Connecting to node") << LOG_KV("endpoint", _nodeIPEndpoint); + { + Guard l(x_pendingConns); + if (m_pendingConns.count(_nodeIPEndpoint)) + { + BCOS_LOG(TRACE) << LOG_DESC("asyncConnected node is in the pending list") + << LOG_KV("endpoint", _nodeIPEndpoint); + return; + } + } + + std::shared_ptr socket = m_asioInterface->newSocket(false, _nodeIPEndpoint); + /// if async connect timeout, close the socket directly + auto connect_timer = std::make_shared( + *(socket->ioService()), boost::posix_time::milliseconds(m_connectTimeThre)); + connect_timer->async_wait([=, this](const boost::system::error_code& error) { + /// return when cancel has been called + if (error == boost::asio::error::operation_aborted) + { + HOST_LOG(DEBUG) << LOG_DESC("AsyncConnect handshake handler revoke this operation"); + return; + } + /// connection timer error + if (error && error != boost::asio::error::operation_aborted) + { + HOST_LOG(ERROR) << LOG_DESC("AsyncConnect timer failed") + << LOG_KV("errorValue", error.value()) + << LOG_KV("message", error.message()); + } + if (socket->isConnected()) + { + HOST_LOG(WARNING) << LOG_DESC("AsyncConnect timeout erase") + << LOG_KV("endpoint", _nodeIPEndpoint); + erasePendingConns(_nodeIPEndpoint); + socket->close(); + } + }); + /// callback async connect + m_asioInterface->asyncResolveConnect(socket, [=, this](boost::system::error_code const& ec) { + if (ec) + { + HOST_LOG(ERROR) << LOG_DESC("TCP Connection refused by node") + << LOG_KV("endpoint", _nodeIPEndpoint) + << LOG_KV("message", ec.message()); + socket->close(); + + m_threadPool->enqueue([callback, _nodeIPEndpoint]() { + callback(NetworkException(ConnectError, "Connect failed"), P2PInfo(), + std::shared_ptr()); + }); + return; + } + else + { + insertPendingConns(_nodeIPEndpoint); + /// get the public key of the server during handshake + std::shared_ptr endpointPublicKey = std::make_shared(); + m_asioInterface->setVerifyCallback(socket, newVerifyCallback(endpointPublicKey)); + /// call handshakeClient after handshake succeed + m_asioInterface->asyncHandshake(socket, ba::ssl::stream_base::client, + boost::bind(&Host::handshakeClient, shared_from_this(), ba::placeholders::error, + socket, endpointPublicKey, callback, _nodeIPEndpoint, connect_timer)); + } + }); +} + +/** + * @brief : start RLPxHandshake procedure after ssl handshake succeed + * @param error: error returned by ssl handshake + * @param socket : ssl socket + * @param endpointPublicKey: public key of the server obtained from the + * certificate + * @param _nodeIPEndpoint : endpoint of the server to connect + */ +void Host::handshakeClient(const boost::system::error_code& error, + std::shared_ptr socket, std::shared_ptr endpointPublicKey, + std::function)> callback, + NodeIPEndpoint _nodeIPEndpoint, std::shared_ptr timerPtr) +{ + timerPtr->cancel(); + erasePendingConns(_nodeIPEndpoint); + if (error) + { + HOST_LOG(WARNING) << LOG_DESC("handshakeClient failed") + << LOG_KV("endpoint", _nodeIPEndpoint) + << LOG_KV("errorValue", error.value()) + << LOG_KV("message", error.message()); + + if (socket->isConnected()) + { + socket->close(); + } + return; + } + if (endpointPublicKey->empty()) + { + HOST_LOG(WARNING) << LOG_DESC("handshakeClient get p2pID failed") + << LOG_KV("local endpoint", socket->localEndpoint()); + socket->close(); + return; + } + + if (m_run) + { + std::string node_info(*endpointPublicKey); + P2PInfo info; + obtainNodeInfo(info, node_info); + HOST_LOG(INFO) << LOG_DESC("handshakeClient succ") + << LOG_KV("local endpoint", socket->localEndpoint()); + startPeerSession(info, socket, callback); + } +} + +/// stop the network and worker thread +void Host::stop() +{ + // ignore if already stopped/stopping + if (!m_run) + return; + // signal run() to prepare for shutdown and reset m_timer + m_run = false; + if (m_asioInterface) + { + m_asioInterface->stop(); + } + if (m_threadPool) + { + m_threadPool->stop(); + } +} diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Host.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Host.h" new file mode 100644 index 00000000..1466ea3b --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Host.h" @@ -0,0 +1,180 @@ +/** @file Host.h + * @author monan <651932351@qq.com> + * @date 2018 + */ +#pragma once + +#include // for NodeIP... +#include // for Message +#include // for Guard, Mutex +#include +#include +#include // for deadline_timer +#include // for error_code +#include // for set +#include // for string +#include // for thread +#include // for swap, move +#include // for vector + + +namespace boost +{ +namespace asio +{ +namespace ssl +{ +class verify_context; +} +} // namespace asio +} // namespace boost +namespace bcos +{ +class ThreadPool; + +namespace gateway +{ +class SessionFactory; +class SessionFace; +class SocketFace; +class ASIOInterface; + +using x509PubHandler = std::function; + +class Host : public std::enable_shared_from_this +{ +public: + Host(std::shared_ptr _asioInterface, + std::shared_ptr _sessionFactory, MessageFactory::Ptr _messageFactory) + : m_asioInterface(_asioInterface), + m_sessionFactory(_sessionFactory), + m_messageFactory(_messageFactory){}; + virtual ~Host() { stop(); }; + + using Ptr = std::shared_ptr; + + virtual uint16_t listenPort() const { return m_listenPort; } + + virtual void start(); + virtual void stop(); + + virtual void asyncConnect(NodeIPEndpoint const& _nodeIPEndpoint, + std::function)> + callback); + + virtual bool haveNetwork() const { return m_run; } + + virtual std::string listenHost() const { return m_listenHost; } + virtual void setHostPort(std::string host, uint16_t port) + { + m_listenHost = host; + m_listenPort = port; + } + + virtual std::function)> + connectionHandler() const + { + return m_connectionHandler; + } + virtual void setConnectionHandler( + std::function)> + connectionHandler) + { + m_connectionHandler = connectionHandler; + } + + virtual std::function sslContextPubHandler() + { + return m_sslContextPubHandler; + } + + virtual void setSSLContextPubHandler( + std::function _sslContextPubHandler) + { + m_sslContextPubHandler = _sslContextPubHandler; + } + + virtual std::shared_ptr threadPool() const { return m_threadPool; } + virtual void setThreadPool(std::shared_ptr threadPool) + { + m_threadPool = threadPool; + } + + virtual std::shared_ptr asioInterface() const { return m_asioInterface; } + virtual std::shared_ptr sessionFactory() const { return m_sessionFactory; } + virtual MessageFactory::Ptr messageFactory() const { return m_messageFactory; } + virtual P2PInfo p2pInfo(); + +private: + /// obtain the common name from the subject: + /// the subject format is: /CN=xx/O=xxx/OU=xxx/ commonly + std::string obtainCommonNameFromSubject(std::string const& subject); + + /// called by 'startedWorking' to accept connections + void startAccept(boost::system::error_code ec = boost::system::error_code()); + /// functions called after openssl handshake, + /// maily to get node id and verify whether the certificate has been expired + /// @return: node id of the connected peer + std::function newVerifyCallback( + std::shared_ptr nodeIDOut); + + /// obtain nodeInfo from given vector + void obtainNodeInfo(P2PInfo& info, std::string const& node_info); + + /// server calls handshakeServer to after handshake, mainly calls + /// RLPxHandshake to obtain informations(client version, caps, etc),start peer + /// session and start accepting procedure repeatedly + void handshakeServer(const boost::system::error_code& error, + std::shared_ptr endpointPublicKey, std::shared_ptr socket); + + void startPeerSession(P2PInfo const& p2pInfo, std::shared_ptr const& socket, + std::function)> + handler); + + void handshakeClient(const boost::system::error_code& error, std::shared_ptr socket, + std::shared_ptr endpointPublicKey, + std::function)> + callback, + NodeIPEndpoint _nodeIPEndpoint, std::shared_ptr timerPtr); + + void erasePendingConns(NodeIPEndpoint const& _nodeIPEndpoint) + { + bcos::Guard l(x_pendingConns); + if (m_pendingConns.count(_nodeIPEndpoint)) + m_pendingConns.erase(_nodeIPEndpoint); + } + + void insertPendingConns(NodeIPEndpoint const& _nodeIPEndpoint) + { + bcos::Guard l(x_pendingConns); + if (!m_pendingConns.count(_nodeIPEndpoint)) + m_pendingConns.insert(_nodeIPEndpoint); + } + + std::shared_ptr m_threadPool; + + /// representing to the network state + std::shared_ptr m_asioInterface; + std::shared_ptr m_sessionFactory; + int m_connectTimeThre = 50000; + std::set m_pendingConns; + bcos::Mutex x_pendingConns; + + MessageFactory::Ptr m_messageFactory; + + std::string m_listenHost = ""; + uint16_t m_listenPort = 0; + + std::function)> + m_connectionHandler; + + // get the hex public key of the peer from the the SSL connection + std::function m_sslContextPubHandler; + + bool m_run = false; + + P2PInfo m_p2pInfo; +}; +} // namespace gateway + +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Message.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Message.h" new file mode 100644 index 00000000..2b7f6059 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Message.h" @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2021 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 Message.h + * @author: octopus + * @date 2021-05-06 + */ + +#pragma once + +#include +#include +#include +#include + +namespace bcos +{ +namespace gateway +{ + +class MessageExtAttributes +{ +public: + using Ptr = std::shared_ptr; + +public: + virtual ~MessageExtAttributes() = default; +}; + +class Message +{ +public: + using Ptr = std::shared_ptr; + +public: + virtual ~Message() {} + + virtual uint32_t length() const = 0; + virtual uint32_t seq() const = 0; + virtual uint16_t version() const = 0; + virtual uint16_t packetType() const = 0; + virtual uint16_t ext() const = 0; + virtual bool isRespPacket() const = 0; + virtual bool encode(bcos::bytes& _buffer) = 0; + virtual ssize_t decode(bytesConstRef _buffer) = 0; + + virtual std::string const& srcP2PNodeID() const = 0; + virtual std::string const& dstP2PNodeID() const = 0; + + virtual MessageExtAttributes::Ptr extAttributes() = 0; +}; + +// + +class MessageFactory +{ +public: + using Ptr = std::shared_ptr; + +public: + virtual ~MessageFactory() {} + virtual Message::Ptr buildMessage() = 0; + + virtual uint32_t newSeq() + { + uint32_t seq = ++m_seq; + return seq; + } + std::atomic m_seq = {1}; +}; + +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Session.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Session.cpp" new file mode 100644 index 00000000..e3b9a8ae --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Session.cpp" @@ -0,0 +1,626 @@ + +/** @file Session.cpp + * @author Gav Wood + * @author Alex Leverington + * @date 2014 + * @author toxotguo + * @date 2018 + */ + +#include // for ASIOIn... +#include // for SESSIO... +#include // for Host +#include +#include // for Respon... +#include // for Socket... +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::gateway; + +Session::Session(size_t _bufferSize) : bufferSize(_bufferSize) +{ + SESSION_LOG(INFO) << "[Session::Session] this=" << this; + m_recvBuffer.resize(bufferSize); + m_seq2Callback = std::make_shared>(); + m_idleCheckTimer = std::make_shared(m_idleTimeInterval, "idleChecker"); + m_idleCheckTimer->registerTimeoutHandler([this]() { checkNetworkStatus(); }); +} + +Session::~Session() +{ + SESSION_LOG(INFO) << "[Session::~Session] this=" << this; + try + { + if (m_socket) + { + bi::tcp::socket& socket = m_socket->ref(); + if (m_socket->isConnected()) + { + socket.close(); + } + } + if (m_idleCheckTimer) + { + m_idleCheckTimer->stop(); + } + } + catch (...) + { + SESSION_LOG(ERROR) << "Deconstruct Session exception"; + } +} + +NodeIPEndpoint Session::nodeIPEndpoint() const +{ + return m_socket->nodeIPEndpoint(); +} + +bool Session::actived() const +{ + auto server = m_server.lock(); + return m_actived && server && server->haveNetwork() && m_socket && m_socket->isConnected(); +} + +void Session::asyncSendMessage(Message::Ptr message, Options options, SessionCallbackFunc callback) +{ + auto server = m_server.lock(); + if (!actived()) + { + SESSION_LOG(WARNING) << "Session inactived"; + if (callback) + { + server->threadPool()->enqueue([callback] { + callback(NetworkException(-1, "Session inactived"), Message::Ptr()); + }); + } + return; + } + + auto session = shared_from_this(); + // checking before send the message + if (m_beforeMessageHandler && !m_beforeMessageHandler(session, message, callback)) + { + return; + } + + if (callback) + { + auto handler = std::make_shared(); + handler->callback = callback; + if (options.timeout > 0) + { + std::shared_ptr timeoutHandler = + server->asioInterface()->newTimer(options.timeout); + + auto session = std::weak_ptr(shared_from_this()); + auto seq = message->seq(); + timeoutHandler->async_wait([session, seq](const boost::system::error_code& _error) { + try + { + auto s = session.lock(); + if (!s) + { + return; + } + s->onTimeout(_error, seq); + } + catch (std::exception const& e) + { + SESSION_LOG(WARNING) << LOG_DESC("async_wait exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); + handler->timeoutHandler = timeoutHandler; + handler->m_startTime = utcSteadyTime(); + } + addSeqCallback(message->seq(), handler); + } + SESSION_LOG(TRACE) << LOG_DESC("Session asyncSendMessage") + << LOG_KV("endpoint", nodeIPEndpoint()); + + std::shared_ptr p_buffer = std::make_shared(); + message->encode(*p_buffer); + + send(p_buffer); +} + +void Session::send(std::shared_ptr _msg) +{ + if (!actived()) + { + return; + } + + if (!m_socket->isConnected()) + return; + + SESSION_LOG(TRACE) << "send" << LOG_KV("writeQueue size", m_writeQueue.size()); + { + Guard l(x_writeQueue); + + m_writeQueue.push(make_pair(_msg, u256(utcTime()))); + } + + write(); +} + +void Session::onWrite(boost::system::error_code ec, std::size_t, std::shared_ptr) +{ + if (!actived()) + { + return; + } + + try + { + m_lastWriteTime.store(utcSteadyTime()); + if (ec) + { + SESSION_LOG(WARNING) << LOG_DESC("onWrite error sending") + << LOG_KV("message", ec.message()) + << LOG_KV("endpoint", nodeIPEndpoint()); + drop(TCPError); + return; + } + { + if (m_writing) + { + m_writing = false; + } + } + + write(); + } + catch (std::exception& e) + { + SESSION_LOG(ERROR) << LOG_DESC("onWrite error") << LOG_KV("endpoint", nodeIPEndpoint()) + << LOG_KV("what", boost::diagnostic_information(e)); + drop(TCPError); + return; + } +} + +void Session::write() +{ + if (!actived()) + { + return; + } + + try + { + Guard l(x_writeQueue); + + if (m_writing) + { + return; + } + + m_writing = true; + + std::pair, u256> task; + u256 enter_time = u256(0); + + if (m_writeQueue.empty()) + { + m_writing = false; + return; + } + + task = m_writeQueue.top(); + m_writeQueue.pop(); + + enter_time = task.second; + auto buffer = task.first; + + auto server = m_server.lock(); + if (server && server->haveNetwork()) + { + if (m_socket->isConnected()) + { + // asio::buffer referecne buffer, so buffer need alive before + // asio::buffer be used + auto self = std::weak_ptr(shared_from_this()); + server->asioInterface()->asyncWrite(m_socket, boost::asio::buffer(*buffer), + [self, buffer](const boost::system::error_code _error, std::size_t _size) { + auto session = self.lock(); + if (!session) + { + return; + } + session->onWrite(_error, _size, buffer); + }); + } + else + { + SESSION_LOG(WARNING) + << "Error sending ssl socket is close!" << LOG_KV("endpoint", nodeIPEndpoint()); + drop(TCPError); + return; + } + } + else + { + SESSION_LOG(WARNING) << "Host has gone"; + drop(TCPError); + return; + } + } + catch (std::exception& e) + { + SESSION_LOG(ERROR) << LOG_DESC("write error") << LOG_KV("endpoint", nodeIPEndpoint()) + << LOG_KV("what", boost::diagnostic_information(e)); + drop(TCPError); + return; + } +} + +void Session::drop(DisconnectReason _reason) +{ + auto server = m_server.lock(); + if (!m_actived) + return; + m_actived = false; + + int errorCode = P2PExceptionType::Disconnect; + std::string errorMsg = "Disconnect"; + if (_reason == DuplicatePeer) + { + errorCode = P2PExceptionType::DuplicateSession; + errorMsg = "DuplicateSession"; + } + + SESSION_LOG(INFO) << "drop, call and erase all callback in this session!" + << LOG_KV("endpoint", nodeIPEndpoint()); + RecursiveGuard l(x_seq2Callback); + for (auto& it : *m_seq2Callback) + { + if (it.second->timeoutHandler) + { + it.second->timeoutHandler->cancel(); + } + if (it.second->callback) + { + SESSION_LOG(TRACE) << "drop, call callback by seq" << LOG_KV("seq", it.first); + if (server) + { + auto callback = it.second; + server->threadPool()->enqueue([callback, errorCode, errorMsg]() { + callback->callback(NetworkException(errorCode, errorMsg), Message::Ptr()); + }); + } + } + } + clearSeqCallback(); + + if (server && m_messageHandler) + { + auto handler = m_messageHandler; + auto self = std::weak_ptr(shared_from_this()); + server->threadPool()->enqueue([handler, self, errorCode, errorMsg]() { + auto session = self.lock(); + if (!session) + { + return; + } + handler(NetworkException(errorCode, errorMsg), session, Message::Ptr()); + }); + } + + if (m_socket->isConnected()) + { + try + { + if (_reason == DisconnectRequested || _reason == DuplicatePeer || + _reason == ClientQuit || _reason == UserReason) + { + SESSION_LOG(DEBUG) << "[drop] closing remote " << m_socket->remoteEndpoint() + << LOG_KV("reason", reasonOf(_reason)) + << LOG_KV("endpoint", m_socket->nodeIPEndpoint()); + } + else + { + SESSION_LOG(WARNING) << "[drop] closing remote " << m_socket->remoteEndpoint() + << LOG_KV("reason", reasonOf(_reason)) + << LOG_KV("endpoint", m_socket->nodeIPEndpoint()); + } + + /// if get Host object failed, close the socket directly + auto socket = m_socket; + auto server = m_server.lock(); + if (server && socket->isConnected()) + { + socket->close(); + } + auto shutdown_timer = std::make_shared( + *(socket->ioService()), boost::posix_time::milliseconds(m_shutDownTimeThres)); + /// async wait for shutdown + shutdown_timer->async_wait([socket](const boost::system::error_code& error) { + /// drop operation has been aborted + if (error == boost::asio::error::operation_aborted) + { + SESSION_LOG(DEBUG) << "[drop] operation aborted by async_shutdown" + << LOG_KV("errorValue", error.value()) + << LOG_KV("message", error.message()); + return; + } + /// shutdown timer error + if (error && error != boost::asio::error::operation_aborted) + { + SESSION_LOG(WARNING) + << "[drop] shutdown timer error" << LOG_KV("errorValue", error.value()) + << LOG_KV("message", error.message()); + } + /// force to shutdown when timeout + if (socket->ref().is_open()) + { + SESSION_LOG(WARNING) << "[drop] timeout, force close the socket" + << LOG_KV("remote endpoint", socket->nodeIPEndpoint()); + socket->close(); + } + }); + + /// async shutdown normally + socket->sslref().async_shutdown( + [socket, shutdown_timer](const boost::system::error_code& error) { + shutdown_timer->cancel(); + if (error) + { + SESSION_LOG(WARNING) + << "[drop] shutdown failed " << LOG_KV("errorValue", error.value()) + << LOG_KV("message", error.message()); + } + /// force to close the socket + if (socket->ref().is_open()) + { + SESSION_LOG(WARNING) << LOG_DESC("force to shutdown session") + << LOG_KV("endpoint", socket->nodeIPEndpoint()); + socket->close(); + } + }); + } + catch (...) + {} + } +} + +void Session::disconnect(DisconnectReason _reason) +{ + drop(_reason); +} + +void Session::start() +{ + if (!m_actived) + { + auto server = m_server.lock(); + if (server && server->haveNetwork()) + { + m_actived = true; + m_lastWriteTime.store(utcSteadyTime()); + m_lastReadTime.store(utcSteadyTime()); + server->asioInterface()->strandPost( + boost::bind(&Session::doRead, shared_from_this())); // doRead(); + } + } + if (m_idleCheckTimer) + { + m_idleCheckTimer->start(); + } +} + +void Session::doRead() +{ + auto server = m_server.lock(); + if (m_actived && server && server->haveNetwork()) + { + auto self = std::weak_ptr(shared_from_this()); + auto asyncRead = [self](boost::system::error_code ec, std::size_t bytesTransferred) { + auto s = self.lock(); + if (s) + { + if (ec) + { + SESSION_LOG(WARNING) + << LOG_DESC("doRead error") << LOG_KV("endpoint", s->nodeIPEndpoint()) + << LOG_KV("message", ec.message()); + s->drop(TCPError); + return; + } + s->m_lastReadTime.store(utcSteadyTime()); + s->m_data.insert(s->m_data.end(), s->m_recvBuffer.begin(), + s->m_recvBuffer.begin() + bytesTransferred); + + while (true) + { + Message::Ptr message = s->m_messageFactory->buildMessage(); + try + { + // Note: the decode function may throw exception + ssize_t result = + message->decode(bytesConstRef(s->m_data.data(), s->m_data.size())); + if (result > 0) + { + /// SESSION_LOG(TRACE) << "Decode success: " << result; + NetworkException e(P2PExceptionType::Success, "Success"); + s->onMessage(e, message); + s->m_data.erase(s->m_data.begin(), s->m_data.begin() + result); + } + else if (result == 0) + { + s->doRead(); + break; + } + else + { + SESSION_LOG(ERROR) + << LOG_DESC("Decode message error") << LOG_KV("result", result); + s->onMessage( + NetworkException(P2PExceptionType::ProtocolError, "ProtocolError"), + message); + break; + } + } + catch (std::exception const& e) + { + SESSION_LOG(ERROR) << LOG_DESC("Decode message exception") + << LOG_KV("error", boost::diagnostic_information(e)); + s->onMessage( + NetworkException(P2PExceptionType::ProtocolError, "ProtocolError"), + message); + break; + } + } + } + }; + + if (m_socket->isConnected()) + { + server->asioInterface()->asyncReadSome( + m_socket, boost::asio::buffer(m_recvBuffer, m_recvBuffer.size()), asyncRead); + } + else + { + SESSION_LOG(WARNING) << LOG_DESC("Error Reading ssl socket is close!"); + drop(TCPError); + return; + } + } + else + { + SESSION_LOG(ERROR) << LOG_DESC("callback doRead failed for session inactived") + << LOG_KV("active", m_actived) + << LOG_KV("haveNetwork", server->haveNetwork()); + } +} + +bool Session::checkRead(boost::system::error_code _ec) +{ + if (_ec && _ec.category() != boost::asio::error::get_misc_category() && + _ec.value() != boost::asio::error::eof) + { + SESSION_LOG(WARNING) << LOG_DESC("checkRead error") << LOG_KV("message", _ec.message()); + drop(TCPError); + + return false; + } + + return true; +} + + +void Session::onMessage(NetworkException const& e, Message::Ptr message) +{ + auto server = m_server.lock(); + if (!server) + { + return; + } + auto self = std::weak_ptr(shared_from_this()); + server->threadPool()->enqueue([e, message, self]() { + auto session = self.lock(); + if (!session) + { + return; + } + try + { + // the forwarding message + if (message->dstP2PNodeID().size() > 0 && + message->dstP2PNodeID() != session->m_hostNodeID) + { + session->m_messageHandler(e, session, message); + return; + } + auto server = session->m_server.lock(); + // in-activate session + if (!session->m_actived || !server || !server->haveNetwork()) + { + return; + } + auto callbackPtr = session->getCallbackBySeq(message->seq()); + // without callback, call default handler + if (!callbackPtr || !message->isRespPacket()) + { + session->m_messageHandler(e, session, message); + return; + } + // with callback + if (callbackPtr->timeoutHandler) + { + callbackPtr->timeoutHandler->cancel(); + } + auto callback = callbackPtr->callback; + session->removeSeqCallback(message->seq()); + if (!callback) + { + return; + } + callback(e, message); + } + catch (std::exception const& e) + { + SESSION_LOG(WARNING) << LOG_DESC("onMessage exception") + << LOG_KV("msg", boost::diagnostic_information(e)); + } + }); +} + +void Session::onTimeout(const boost::system::error_code& error, uint32_t seq) +{ + if (error) + { + SESSION_LOG(TRACE) << "timer cancel" << error; + return; + } + + auto server = m_server.lock(); + if (!server) + return; + ResponseCallback::Ptr callbackPtr = getCallbackBySeq(seq); + if (!callbackPtr) + return; + server->threadPool()->enqueue([=, this]() { + NetworkException e(P2PExceptionType::NetworkTimeout, "NetworkTimeout"); + callbackPtr->callback(e, Message::Ptr()); + removeSeqCallback(seq); + }); +} + +void Session::checkNetworkStatus() +{ + m_idleCheckTimer->restart(); + try + { + auto now = utcSteadyTime(); + // read idle + if ((m_lastReadTime + m_idleTimeInterval) < now) + { + SESSION_LOG(WARNING) << LOG_DESC( + "Long time without read operation, maybe session " + "inactivated, drop the session") + << LOG_KV("endpoint", m_socket->nodeIPEndpoint()); + drop(IdleWaitTimeout); + return; + } + // write idle + if ((m_lastWriteTime + m_idleTimeInterval) < now) + { + SESSION_LOG(WARNING) << LOG_DESC( + "Long time without write operation, maybe session " + "inactivated, drop the session") + << LOG_KV("endpoint", m_socket->nodeIPEndpoint()); + drop(IdleWaitTimeout); + return; + } + } + catch (std::exception const& e) + { + SESSION_LOG(WARNING) << LOG_DESC("checkNetworkStatus error") + << LOG_KV("msg", boost::diagnostic_information(e)); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Session.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Session.h" new file mode 100644 index 00000000..63ebbc90 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Session.h" @@ -0,0 +1,203 @@ + +/** @file Session.h + * @author monan <651932351@qq.com> + * @date 2018 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace gateway +{ +class Host; +class SocketFace; + +class Session : public SessionFace, public std::enable_shared_from_this +{ +public: + Session(size_t _bufferSize = 4096); + virtual ~Session(); + + using Ptr = std::shared_ptr; + + void start() override; + void disconnect(DisconnectReason _reason) override; + + void asyncSendMessage( + Message::Ptr, Options = Options(), SessionCallbackFunc = SessionCallbackFunc()) override; + + NodeIPEndpoint nodeIPEndpoint() const override; + + bool actived() const override; + + virtual std::weak_ptr host() { return m_server; } + virtual void setHost(std::weak_ptr host) { m_server = host; } + + std::shared_ptr socket() override { return m_socket; } + virtual void setSocket(std::shared_ptr socket) { m_socket = socket; } + + virtual MessageFactory::Ptr messageFactory() const { return m_messageFactory; } + virtual void setMessageFactory(MessageFactory::Ptr _messageFactory) + { + m_messageFactory = _messageFactory; + } + + virtual std::function messageHandler() + { + return m_messageHandler; + } + void setMessageHandler( + std::function messageHandler) + override + { + m_messageHandler = messageHandler; + } + + // handle before sending message, if the check fails, meaning false is returned, the message is + // not sent, and the SessionCallbackFunc will be performed + void setBeforeMessageHandler( + std::function handler) override + { + m_beforeMessageHandler = handler; + } + + void setHostNodeID(std::string const& _hostNodeID) { m_hostNodeID = _hostNodeID; } + +protected: + virtual void addSeqCallback(uint32_t seq, ResponseCallback::Ptr callback) + { + RecursiveGuard l(x_seq2Callback); + m_seq2Callback->insert(std::make_pair(seq, callback)); + } + virtual void removeSeqCallback(uint32_t seq) + { + RecursiveGuard l(x_seq2Callback); + m_seq2Callback->erase(seq); + } + virtual void clearSeqCallback() + { + RecursiveGuard l(x_seq2Callback); + m_seq2Callback->clear(); + } + + ResponseCallback::Ptr getCallbackBySeq(uint32_t seq) + { + RecursiveGuard l(x_seq2Callback); + auto it = m_seq2Callback->find(seq); + if (it != m_seq2Callback->end()) + { + return it->second; + } + else + { + return NULL; + } + } + + virtual void checkNetworkStatus(); + +private: + void send(std::shared_ptr _msg); + + void doRead(); + std::vector m_data; ///< Buffer for ingress packet data. + std::vector m_recvBuffer; + const size_t bufferSize; + + /// Drop the connection for the reason @a _r. + void drop(DisconnectReason _r); + + /// Check error code after reading and drop peer if error code. + bool checkRead(boost::system::error_code _ec); + + void onTimeout(const boost::system::error_code& error, uint32_t seq); + + /// Perform a single round of the write operation. This could end up calling + /// itself asynchronously. + void onWrite(boost::system::error_code ec, std::size_t length, std::shared_ptr buffer); + void write(); + + /// call by doRead() to deal with message + void onMessage(NetworkException const& e, Message::Ptr message); + + std::weak_ptr m_server; ///< The host that owns us. Never null. + std::shared_ptr m_socket; ///< Socket of peer's connection. + + MessageFactory::Ptr m_messageFactory; + + class QueueCompare + { + public: + bool operator()(const std::pair, u256>&, + const std::pair, u256>&) const + { + return false; + } + }; + + boost::heap::priority_queue, u256>, + boost::heap::compare, boost::heap::stable> + m_writeQueue; + std::atomic_bool m_writing = {false}; + bcos::Mutex x_writeQueue; + + mutable bcos::Mutex x_info; + + bool m_actived = false; + + ///< A call B, the function to call after the response is received by A. + mutable bcos::RecursiveMutex x_seq2Callback; + std::shared_ptr> m_seq2Callback; + + std::function m_messageHandler; + + std::function m_beforeMessageHandler; + + uint64_t m_shutDownTimeThres = 50000; + // 1min + uint64_t m_idleTimeInterval = 60 * 1000; + + // timer to check the connection + std::atomic m_lastReadTime; + std::atomic m_lastWriteTime; + std::shared_ptr m_idleCheckTimer; + + std::string m_hostNodeID; +}; + +class SessionFactory +{ +public: + SessionFactory(std::string const& _hostNodeID) : m_hostNodeID(_hostNodeID) {} + virtual ~SessionFactory(){}; + + virtual std::shared_ptr create_session(std::weak_ptr _server, + std::shared_ptr const& _socket, MessageFactory::Ptr _messageFactory) + { + std::shared_ptr session = std::make_shared(); + session->setHostNodeID(m_hostNodeID); + session->setHost(_server); + session->setSocket(_socket); + session->setMessageFactory(_messageFactory); + return session; + } + +private: + std::string m_hostNodeID; +}; + +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/SessionFace.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/SessionFace.h" new file mode 100644 index 00000000..222bfc88 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/SessionFace.h" @@ -0,0 +1,63 @@ + +/** @file Session.h + * @author Gav Wood + * @author Alex Leverington + * @date 2014 + * @author toxotguo + * @date 2018 + * + * @author: yujiechen + * @date: 2018-09-19 + * @modification: remove addNote interface + */ + +#pragma once +#include +#include +#include + +namespace bcos +{ +namespace gateway +{ +class SocketFace; + +using SessionCallbackFunc = std::function; +struct ResponseCallback : public std::enable_shared_from_this +{ + using Ptr = std::shared_ptr; + + uint64_t m_startTime; + SessionCallbackFunc callback; + std::shared_ptr timeoutHandler; +}; + +class SessionFace +{ +public: + virtual ~SessionFace(){}; + + using Ptr = std::shared_ptr; + + virtual void start() = 0; + virtual void disconnect(DisconnectReason) = 0; + + virtual void asyncSendMessage( + Message::Ptr, Options = Options(), SessionCallbackFunc = SessionCallbackFunc()) = 0; + + virtual std::shared_ptr socket() = 0; + + virtual void setMessageHandler( + std::function messageHandler) = 0; + + // handle before sending message, if the check fails, meaning false is returned, the message is + // not sent, and the SessionCallbackFunc will be performed + virtual void setBeforeMessageHandler( + std::function handler) = 0; + + virtual NodeIPEndpoint nodeIPEndpoint() const = 0; + + virtual bool actived() const = 0; +}; +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Socket.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Socket.h" new file mode 100644 index 00000000..bc205aa3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/Socket.h" @@ -0,0 +1,86 @@ +/** @file Socket.h + * @ author: yujiechen + * @ date: 2018-09-17 + * @ modification: rename RLPXSocket.h to Socket.h + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace gateway +{ +class Socket : public SocketFace, public std::enable_shared_from_this +{ +public: + Socket(std::shared_ptr _ioService, ba::ssl::context& _sslContext, + NodeIPEndpoint _nodeIPEndpoint) + : m_nodeIPEndpoint(_nodeIPEndpoint), m_ioService(_ioService) + { + try + { + m_sslSocket = + std::make_shared>(*_ioService, _sslContext); + } + catch (const std::exception& _e) + { + SESSION_LOG(ERROR) << "ERROR: " << boost::diagnostic_information(_e); + SESSION_LOG(ERROR) << "Ssl Socket Init Fail! Please Check CERTIFICATE!"; + } + } + ~Socket() { close(); } + + bool isConnected() const override { return m_sslSocket->lowest_layer().is_open(); } + + void close() override + { + try + { + boost::system::error_code ec; + m_sslSocket->lowest_layer().shutdown(bi::tcp::socket::shutdown_both, ec); + if (m_sslSocket->lowest_layer().is_open()) + m_sslSocket->lowest_layer().close(); + } + catch (...) + { + } + } + + bi::tcp::endpoint remoteEndpoint( + boost::system::error_code ec = boost::system::error_code()) override + { + return m_sslSocket->lowest_layer().remote_endpoint(ec); + } + + bi::tcp::endpoint localEndpoint( + boost::system::error_code ec = boost::system::error_code()) override + { + return m_sslSocket->lowest_layer().local_endpoint(ec); + } + + bi::tcp::socket& ref() override { return m_sslSocket->next_layer(); } + ba::ssl::stream& sslref() override { return *m_sslSocket; } + + const NodeIPEndpoint& nodeIPEndpoint() const override { return m_nodeIPEndpoint; } + void setNodeIPEndpoint(NodeIPEndpoint _nodeIPEndpoint) override + { + m_nodeIPEndpoint = _nodeIPEndpoint; + } + + std::shared_ptr ioService() override { return m_ioService; } + +protected: + NodeIPEndpoint m_nodeIPEndpoint; + std::shared_ptr m_ioService; + std::shared_ptr> m_sslSocket; +}; + +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/SocketFace.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/SocketFace.h" new file mode 100644 index 00000000..4912ac5a --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libnetwork/SocketFace.h" @@ -0,0 +1,38 @@ +/** + * @brief: Socket inteface + * @file SocketFace.h + * @author yujiechen + * @date 2018-09-17 + */ + +#pragma once +#include +#include +#include + +namespace bcos +{ +namespace gateway +{ +class SocketFace +{ +public: + SocketFace() = default; + + virtual ~SocketFace(){}; + virtual bool isConnected() const = 0; + virtual void close() = 0; + virtual bi::tcp::endpoint remoteEndpoint( + boost::system::error_code ec = boost::system::error_code()) = 0; + virtual bi::tcp::endpoint localEndpoint( + boost::system::error_code ec = boost::system::error_code()) = 0; + + virtual bi::tcp::socket& ref() = 0; + virtual ba::ssl::stream& sslref() = 0; + + virtual const NodeIPEndpoint& nodeIPEndpoint() const = 0; + virtual void setNodeIPEndpoint(NodeIPEndpoint _nodeIPEndpoint) = 0; + virtual std::shared_ptr ioService() = 0; +}; +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/Common.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/Common.h" new file mode 100644 index 00000000..d0db0fcb --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/Common.h" @@ -0,0 +1,28 @@ +/* + * Common.h + * + * Author: ancelmo + */ + +#pragma once + +#include +#include +#include + +namespace bcos +{ +namespace gateway +{ +#define P2PMSG_LOG(LEVEL) BCOS_LOG(LEVEL) << "[P2PService][P2PMessage]" +#define P2PSESSION_LOG(LEVEL) BCOS_LOG(LEVEL) << "[P2PService][P2PSession]" +#define SERVICE_LOG(LEVEL) BCOS_LOG(LEVEL) << "[P2PService][Service]" +#define SERVICE_ROUTER_LOG(LEVEL) BCOS_LOG(LEVEL) << "[P2PService][Router]" + +/// default compress threshold: 1KB +const uint64_t c_compressThreshold = 1024; +/// default zstd compress level: +const uint64_t c_zstdCompressLevel = 1; + +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PInterface.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PInterface.h" new file mode 100644 index 00000000..3a7dc0a8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PInterface.h" @@ -0,0 +1,104 @@ +/** @file P2PInterface.h + * @author chaychen + * @date 20180911 + */ + +#pragma once + +#include +#include +#include +#include + +namespace bcos +{ +namespace stat +{ +class NetworkStatHandler; +class ChannelNetworkStatHandler; +} // namespace stat + +namespace gateway +{ +class P2PMessage; +class MessageFactory; +class P2PSession; +using CallbackFuncWithSession = + std::function, std::shared_ptr)>; +using DisconnectCallbackFuncWithSession = + std::function)>; +using P2PResponseCallback = + std::function _data)>; +class P2PInterface +{ +public: + using Ptr = std::shared_ptr; + virtual ~P2PInterface(){}; + + virtual void start() = 0; + virtual void stop() = 0; + + virtual P2pID id() const = 0; + + virtual std::shared_ptr sendMessageByNodeID( + P2pID nodeID, std::shared_ptr message) = 0; + + virtual void asyncSendMessageByNodeID(P2pID nodeID, std::shared_ptr message, + CallbackFuncWithSession callback, Options options = Options()) = 0; + + virtual void asyncBroadcastMessage(std::shared_ptr message, Options options) = 0; + + virtual P2PInfos sessionInfos() = 0; + virtual P2PInfo localP2pInfo() = 0; + + virtual bool isConnected(P2pID const& _nodeID) const = 0; + virtual bool isReachable(P2pID const& _nodeID) const = 0; + virtual std::shared_ptr host() = 0; + + virtual std::shared_ptr messageFactory() = 0; + + virtual std::shared_ptr getP2PSessionByNodeId(P2pID const& _nodeID) = 0; + + + /** + * @brief send message to the given p2p nodes + * + * @param _type the message type + * @param _dstNodeID the dst node + * @param _payload the data + * @param options timeout option + * @param _callback called when receive response + */ + virtual void asyncSendMessageByP2PNodeID(int16_t _type, P2pID _dstNodeID, + bytesConstRef _payload, Options options = Options(), + P2PResponseCallback _callback = nullptr) = 0; + + /** + * @brief broadcast message to all p2p nodes + * + * @param _type the message type + * @param _payload the payload + */ + virtual void asyncBroadcastMessageToP2PNodes( + int16_t _type, uint16_t moduleID, bytesConstRef _payload, Options _options) = 0; + + /** + * @brief send message to the given nodeIDs + */ + virtual void asyncSendMessageByP2PNodeIDs(int16_t _type, const std::vector& _nodeIDs, + bytesConstRef _payload, Options _options) = 0; + + using MessageHandler = + std::function, P2PMessage::Ptr)>; + + virtual void registerHandlerByMsgType(int16_t _type, MessageHandler const& _msgHandler) = 0; + + virtual void eraseHandlerByMsgType(int16_t _type) = 0; + + virtual void sendRespMessageBySession(bytesConstRef _payload, P2PMessage::Ptr _p2pMessage, + std::shared_ptr _p2pSession) = 0; +}; + +} // namespace gateway + +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PMessage.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PMessage.cpp" new file mode 100644 index 00000000..0a4d41a6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PMessage.cpp" @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2021 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 P2PMessage.cpp + * @author: octopus + * @date 2021-05-04 + */ + +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::gateway; +using namespace bcos::crypto; + +bool P2PMessageOptions::encode(bytes& _buffer) +{ + // parameters check + if (m_groupID.size() > MAX_GROUPID_LENGTH) + { + P2PMSG_LOG(ERROR) << LOG_DESC("groupID length overflow") + << LOG_KV("groupID length", m_groupID.size()); + return false; + } + if (!m_srcNodeID || m_srcNodeID->empty() || (m_srcNodeID->size() > MAX_NODEID_LENGTH)) + { + P2PMSG_LOG(ERROR) << LOG_DESC("srcNodeID length valid") + << LOG_KV("srcNodeID length", (m_srcNodeID ? m_srcNodeID->size() : 0)); + return false; + } + if (m_dstNodeIDs.size() > MAX_DST_NODEID_COUNT) + { + P2PMSG_LOG(ERROR) << LOG_DESC("dstNodeID amount overfow") + << LOG_KV("dstNodeID size", m_dstNodeIDs.size()); + return false; + } + + for (auto dstNodeID : m_dstNodeIDs) + { + if (!dstNodeID || dstNodeID->empty() || (dstNodeID->size() > MAX_NODEID_LENGTH)) + { + P2PMSG_LOG(ERROR) << LOG_DESC("dstNodeID length valid") + << LOG_KV("dstNodeID length", (dstNodeID ? dstNodeID->size() : 0)); + return false; + } + } + + // groupID length + uint16_t groupIDLength = + boost::asio::detail::socket_ops::host_to_network_short((uint16_t)m_groupID.size()); + _buffer.insert(_buffer.end(), (byte*)&groupIDLength, (byte*)&groupIDLength + 2); + // groupID + _buffer.insert(_buffer.end(), m_groupID.begin(), m_groupID.end()); + + // nodeID length + uint16_t nodeIDLength = + boost::asio::detail::socket_ops::host_to_network_short((uint16_t)m_srcNodeID->size()); + _buffer.insert(_buffer.end(), (byte*)&nodeIDLength, (byte*)&nodeIDLength + 2); + // srcNodeID + _buffer.insert(_buffer.end(), m_srcNodeID->begin(), m_srcNodeID->end()); + + // dstNodeID count + uint8_t dstNodeIDCount = (uint8_t)m_dstNodeIDs.size(); + _buffer.insert(_buffer.end(), (byte*)&dstNodeIDCount, (byte*)&dstNodeIDCount + 1); + + // dstNodeIDs + for (const auto& nodeID : m_dstNodeIDs) + { + _buffer.insert(_buffer.end(), nodeID->begin(), nodeID->end()); + } + + // moduleID + uint16_t moduleID = boost::asio::detail::socket_ops::host_to_network_short(m_moduleID); + _buffer.insert(_buffer.end(), (byte*)&moduleID, (byte*)&moduleID + 2); + + return true; +} + +/// groupID length :1 bytes +/// groupID : bytes +/// nodeID length :2 bytes +/// src nodeID : bytes +/// src nodeID count :1 bytes +/// dst nodeIDs : bytes +ssize_t P2PMessageOptions::decode(bytesConstRef _buffer) +{ + size_t offset = 0; + size_t length = _buffer.size(); + + try + { + CHECK_OFFSET_WITH_THROW_EXCEPTION((offset + OPTIONS_MIN_LENGTH), length); + + // groupID length + uint16_t groupIDLength = + boost::asio::detail::socket_ops::network_to_host_short(*((uint16_t*)&_buffer[offset])); + offset += 2; + + // groupID + if (groupIDLength > 0) + { + CHECK_OFFSET_WITH_THROW_EXCEPTION(offset + groupIDLength, length); + m_groupID.assign(&_buffer[offset], &_buffer[offset] + groupIDLength); + offset += groupIDLength; + } + + // nodeID length + CHECK_OFFSET_WITH_THROW_EXCEPTION(offset + 2, length); + uint16_t nodeIDLength = + boost::asio::detail::socket_ops::network_to_host_short(*((uint16_t*)&_buffer[offset])); + offset += 2; + + CHECK_OFFSET_WITH_THROW_EXCEPTION(offset + nodeIDLength, length); + bytes emptyBuffer; + m_srcNodeID->swap(emptyBuffer); + m_srcNodeID->insert( + m_srcNodeID->begin(), (byte*)&_buffer[offset], (byte*)&_buffer[offset] + nodeIDLength); + offset += nodeIDLength; + + CHECK_OFFSET_WITH_THROW_EXCEPTION(offset + 1, length); + // dstNodeCount + uint8_t dstNodeCount = *((uint8_t*)&_buffer[offset]); + offset += 1; + + CHECK_OFFSET_WITH_THROW_EXCEPTION(offset + dstNodeCount * nodeIDLength, length); + // dstNodeIDs + m_dstNodeIDs.resize(dstNodeCount); + for (size_t i = 0; i < dstNodeCount; i++) + { + m_dstNodeIDs[i] = std::make_shared( + (byte*)&_buffer[offset], (byte*)&_buffer[offset] + nodeIDLength); + + offset += nodeIDLength; + } + + CHECK_OFFSET_WITH_THROW_EXCEPTION(offset + 2, length); + + uint16_t moduleID = + boost::asio::detail::socket_ops::network_to_host_short(*((uint16_t*)&_buffer[offset])); + offset += 2; + + m_moduleID = moduleID; + } + catch (const std::exception& e) + { + P2PMSG_LOG(ERROR) << LOG_DESC("decode message error") + << LOG_KV("e", boost::diagnostic_information(e)); + // invalid packet? + return MessageDecodeStatus::MESSAGE_ERROR; + } + + return offset; +} + +bool P2PMessage::encodeHeader(bytes& _buffer) +{ + // set length to zero first + uint32_t length = 0; + uint16_t version = boost::asio::detail::socket_ops::host_to_network_short(m_version); + uint16_t packetType = boost::asio::detail::socket_ops::host_to_network_short(m_packetType); + uint32_t seq = boost::asio::detail::socket_ops::host_to_network_long(m_seq); + uint16_t ext = boost::asio::detail::socket_ops::host_to_network_short(m_ext); + + _buffer.insert(_buffer.end(), (byte*)&length, (byte*)&length + 4); + _buffer.insert(_buffer.end(), (byte*)&version, (byte*)&version + 2); + _buffer.insert(_buffer.end(), (byte*)&packetType, (byte*)&packetType + 2); + _buffer.insert(_buffer.end(), (byte*)&seq, (byte*)&seq + 4); + _buffer.insert(_buffer.end(), (byte*)&ext, (byte*)&ext + 2); + return true; +} + +bool P2PMessage::encode(bytes& _buffer) +{ + bytes emptyBuffer; + _buffer.swap(emptyBuffer); + + // compress payload + std::shared_ptr compressData = std::make_shared(); + bool isCompressSuccess = false; + if (tryToCompressPayload(compressData)) + { + isCompressSuccess = true; + } + + if (!encodeHeader(_buffer)) + { + return false; + } + // encode options + if (hasOptions() && !m_options->encode(_buffer)) + { + return false; + } + + // encode payload + if (isCompressSuccess) + { + P2PMSG_LOG(TRACE) << LOG_DESC("compress payload success") + << LOG_KV("compressedData", (char*)compressData->data()) + << LOG_KV("packageType", m_packetType) << LOG_KV("ext", m_ext) + << LOG_KV("seq", m_seq); + _buffer.insert(_buffer.end(), compressData->begin(), compressData->end()); + } + else + { + _buffer.insert(_buffer.end(), m_payload->begin(), m_payload->end()); + } + + // calc total length and modify the length value in the buffer + auto length = boost::asio::detail::socket_ops::host_to_network_long((uint32_t)_buffer.size()); + + // update length + std::copy((byte*)&length, (byte*)&length + 4, _buffer.data()); + // set buffer size to m_length + m_length = _buffer.size(); + return true; +} + +/// compress the payload data to be sended +bool P2PMessage::tryToCompressPayload(std::shared_ptr compressData) +{ + if (m_payload->size() <= bcos::gateway::c_compressThreshold) + { + return false; + } + + if (m_version < (uint16_t)(bcos::protocol::ProtocolVersion::V2)) + { + return false; + } + + bool isCompressSuccess = + ZstdCompress::compress(ref(*m_payload), *compressData, bcos::gateway::c_zstdCompressLevel); + if (!isCompressSuccess) + { + return false; + } + // update compress flag + m_ext |= bcos::protocol::MessageExtFieldFlag::Compress; + return true; +} + +ssize_t P2PMessage::decodeHeader(bytesConstRef _buffer) +{ + int32_t offset = 0; + + // length field + m_length = + boost::asio::detail::socket_ops::network_to_host_long(*((uint32_t*)&_buffer[offset])); + offset += 4; + + // version + m_version = + boost::asio::detail::socket_ops::network_to_host_short(*((uint16_t*)&_buffer[offset])); + offset += 2; + + // packetType + m_packetType = + boost::asio::detail::socket_ops::network_to_host_short(*((uint16_t*)&_buffer[offset])); + offset += 2; + + // seq + m_seq = boost::asio::detail::socket_ops::network_to_host_long(*((uint32_t*)&_buffer[offset])); + offset += 4; + + // ext + m_ext = boost::asio::detail::socket_ops::network_to_host_short(*((uint16_t*)&_buffer[offset])); + offset += 2; + + return offset; +} + +ssize_t P2PMessage::decode(bytesConstRef _buffer) +{ + // check if packet header fully received + if (_buffer.size() < P2PMessage::MESSAGE_HEADER_LENGTH) + { + return MessageDecodeStatus::MESSAGE_INCOMPLETE; + } + + int32_t offset = decodeHeader(_buffer); + + // check if packet header fully received + if (_buffer.size() < m_length) + { + return MessageDecodeStatus::MESSAGE_INCOMPLETE; + } + if (m_length > P2PMessage::MAX_MESSAGE_LENGTH) + { + P2PMSG_LOG(WARNING) << LOG_DESC("Illegal p2p message packet") << LOG_KV("length", m_length) + << LOG_KV("maxLen", P2PMessage::MAX_MESSAGE_LENGTH); + return MessageDecodeStatus::MESSAGE_ERROR; + } + if (hasOptions()) + { + // encode options + auto optionsOffset = m_options->decode(_buffer.getCroppedData(offset)); + if (optionsOffset < 0) + { + return MessageDecodeStatus::MESSAGE_ERROR; + } + offset += optionsOffset; + } + + uint32_t length = _buffer.size(); + CHECK_OFFSET_WITH_THROW_EXCEPTION(m_length, length); + auto data = _buffer.getCroppedData(offset, m_length - offset); + // raw data cropped from buffer, maybe be compressed or not + auto rawData = std::make_shared(data.begin(), data.end()); + + // uncompress payload + // payload has been compressed + if ((m_ext & bcos::protocol::MessageExtFieldFlag::Compress) == + bcos::protocol::MessageExtFieldFlag::Compress) + { + bool isUncompressSuccess = ZstdCompress::uncompress(ref(*rawData), *m_payload); + if (!isUncompressSuccess) + { + P2PMSG_LOG(ERROR) << LOG_DESC("ZstdCompress decode message error, uncompress failed") + << LOG_KV("packageType", m_packetType) << LOG_KV("ext", m_ext) + << LOG_KV("seq", m_seq); + // invalid packet? + return MessageDecodeStatus::MESSAGE_ERROR; + } + P2PMSG_LOG(TRACE) << LOG_DESC("zstd uncompress success") + << LOG_KV("packetType", m_packetType) << LOG_KV("ext", m_ext) + << LOG_KV("rawData", (char*)(rawData->data())) << LOG_KV("seq", m_seq); + // reset ext + m_ext &= (~bcos::protocol::MessageExtFieldFlag::Compress); + } + else + { + m_payload = std::move(rawData); + } + + return m_length; +} diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PMessage.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PMessage.h" new file mode 100644 index 00000000..544b12ce --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PMessage.h" @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2021 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 P2PMessage.h + * @author: octopus + * @date 2021-05-04 + */ + +#pragma once + +#include +#include +#include +#include +#include + +#define CHECK_OFFSET_WITH_THROW_EXCEPTION(offset, length) \ + do \ + { \ + if ((offset) > (length)) \ + { \ + throw std::out_of_range("Out of range error, offset:" + std::to_string(offset) + \ + " ,length: " + std::to_string(length)); \ + } \ + } while (0); + +namespace bcos +{ +namespace gateway +{ +/// Options format definition +/// options(default version): +/// groupID length :1 bytes +/// groupID : bytes +/// nodeID length :2 bytes +/// src nodeID : bytes +/// src nodeID count :1 bytes +/// dst nodeIDs : bytes +/// moduleID : 2 bytes +class P2PMessageOptions +{ +public: + using Ptr = std::shared_ptr; + /// groupID length(2) + nodeID length(2) + dst nodeID count(1) + moduleID(2) + const static size_t OPTIONS_MIN_LENGTH = 7; + +public: + P2PMessageOptions() { m_srcNodeID = std::make_shared(); } + + virtual ~P2PMessageOptions() {} + + /// The maximum gateway transport protocol supported groupID length 65535 + const static size_t MAX_GROUPID_LENGTH = 65535; + /// The maximum gateway transport protocol supported nodeID length 65535 + const static size_t MAX_NODEID_LENGTH = 65535; + /// The maximum gateway transport protocol supported dst nodeID count 127 + const static size_t MAX_DST_NODEID_COUNT = 255; + + bool encode(bytes& _buffer); + ssize_t decode(bytesConstRef _buffer); + +public: + uint16_t moduleID() const { return m_moduleID; } + void setModuleID(uint16_t _moduleID) { m_moduleID = _moduleID; } + + std::string groupID() const { return m_groupID; } + void setGroupID(const std::string& _groupID) { m_groupID = _groupID; } + + std::shared_ptr srcNodeID() const { return m_srcNodeID; } + void setSrcNodeID(std::shared_ptr _srcNodeID) { m_srcNodeID = _srcNodeID; } + + std::vector>& dstNodeIDs() { return m_dstNodeIDs; } + void setDstNodeIDs(const std::vector>& _dstNodeIDs) + { + m_dstNodeIDs = _dstNodeIDs; + } + +protected: + std::string m_groupID; + std::shared_ptr m_srcNodeID; + std::vector> m_dstNodeIDs; + uint16_t m_moduleID; +}; + +/// Message format definition of gateway P2P network +/// +/// fields: +/// length :4 bytes +/// version :2 bytes +/// packet type :2 bytes +/// seq :4 bytes +/// ext :2 bytes +/// options(default version): +/// groupID length :1 bytes +/// groupID : bytes +/// nodeID length :2 bytes +/// src nodeID : bytes +/// src nodeID count :1 bytes +/// dst nodeIDs : bytes +/// moduleID : 2 bytes +/// payload :X bytes +class P2PMessage : public Message +{ +public: + using Ptr = std::shared_ptr; + + /// length(4) + version(2) + packetType(2) + seq(4) + ext(2) + const static size_t MESSAGE_HEADER_LENGTH = 14; + const static size_t MAX_MESSAGE_LENGTH = + 100 * 1024 * 1024; ///< The maximum length of data is 100M. +public: + P2PMessage() + { + m_payload = std::make_shared(); + m_options = std::make_shared(); + } + + ~P2PMessage() override {} + +public: + uint32_t length() const override + { + // The length value has been set + if (m_length > 0) + { + return m_length; + } + + // estimate the length of msg to be encoded + int64_t length = (int64_t)payload()->size() + (int64_t)P2PMessage::MESSAGE_HEADER_LENGTH; + if (hasOptions() && options() && options()->srcNodeID()) + { + length += P2PMessageOptions::OPTIONS_MIN_LENGTH; + length += + (int64_t)(options()->srcNodeID()->size() * (1 + options()->dstNodeIDs().size())); + } + return length; + } + virtual void setLength(uint32_t length) { m_length = length; } + + uint16_t version() const override { return m_version; } + virtual void setVersion(uint16_t version) { m_version = version; } + + uint16_t packetType() const override { return m_packetType; } + virtual void setPacketType(uint16_t packetType) { m_packetType = packetType; } + + uint32_t seq() const override { return m_seq; } + virtual void setSeq(uint32_t seq) { m_seq = seq; } + + uint16_t ext() const override { return m_ext; } + virtual void setExt(uint16_t _ext) { m_ext |= _ext; } + + P2PMessageOptions::Ptr options() const { return m_options; } + void setOptions(P2PMessageOptions::Ptr _options) { m_options = _options; } + + std::shared_ptr payload() const { return m_payload; } + void setPayload(std::shared_ptr _payload) { m_payload = _payload; } + + void setRespPacket() { m_ext |= bcos::protocol::MessageExtFieldFlag::Response; } + bool encode(bytes& _buffer) override; + ssize_t decode(bytesConstRef _buffer) override; + bool isRespPacket() const override + { + return (m_ext & bcos::protocol::MessageExtFieldFlag::Response) != 0; + } + + // compress payload if payload need to be compressed + bool tryToCompressPayload(std::shared_ptr compressData); + + bool hasOptions() const + { + return (m_packetType == GatewayMessageType::PeerToPeerMessage) || + (m_packetType == GatewayMessageType::BroadcastMessage); + } + + virtual void setSrcP2PNodeID(std::string const& _srcP2PNodeID) + { + m_srcP2PNodeID = _srcP2PNodeID; + } + virtual void setDstP2PNodeID(std::string const& _dstP2PNodeID) + { + m_dstP2PNodeID = _dstP2PNodeID; + } + + std::string const& srcP2PNodeID() const override { return m_srcP2PNodeID; } + std::string const& dstP2PNodeID() const override { return m_dstP2PNodeID; } + + virtual void setExtAttributes(MessageExtAttributes::Ptr _extAttr) { m_extAttr = _extAttr; } + MessageExtAttributes::Ptr extAttributes() override { return m_extAttr; } + +protected: + virtual ssize_t decodeHeader(bytesConstRef _buffer); + virtual bool encodeHeader(bytes& _buffer); + +protected: + uint32_t m_length = 0; + uint16_t m_version = (uint16_t)(bcos::protocol::ProtocolVersion::V0); + uint16_t m_packetType = 0; + uint32_t m_seq = 0; + uint16_t m_ext = 0; + + // the src p2pNodeID, for message forward, only encode into the P2PMessageV2 + std::string m_srcP2PNodeID; + // the dst p2pNodeID, for message forward, only encode into the P2PMessageV2 + std::string m_dstP2PNodeID; + + P2PMessageOptions::Ptr m_options; ///< options fields + + std::shared_ptr m_payload; ///< payload data + + MessageExtAttributes::Ptr m_extAttr = nullptr; ///< message additional attributes +}; + +class P2PMessageFactory : public MessageFactory +{ +public: + using Ptr = std::shared_ptr; + virtual ~P2PMessageFactory() {} + +public: + virtual Message::Ptr buildMessage() + { + auto message = std::make_shared(); + return message; + } +}; + +inline std::ostream& operator<<(std::ostream& _out, const P2PMessage _p2pMessage) +{ + _out << "P2PMessage {" + << " length: " << _p2pMessage.length() << " version: " << _p2pMessage.version() + << " packetType: " << _p2pMessage.packetType() << " seq: " << _p2pMessage.seq() + << " ext: " << _p2pMessage.ext() << " }"; + return _out; +} + +inline std::ostream& operator<<(std::ostream& _out, P2PMessage::Ptr _p2pMessage) +{ + _out << (*_p2pMessage.get()); + return _out; +} + +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PMessageV2.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PMessageV2.cpp" new file mode 100644 index 00000000..9ba5ef96 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PMessageV2.cpp" @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2021 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 P2PMessageV2.cpp + * @brief: extend srcNodeID and dstNodeID for message forward + * @author: yujiechen + * @date 2021-05-04 + */ + +#include "P2PMessageV2.h" +#include "Common.h" +using namespace bcos; +using namespace bcos::gateway; + +bool P2PMessageV2::encodeHeader(bytes& _buffer) +{ + auto ret = P2PMessage::encodeHeader(_buffer); + if (m_version <= (uint16_t)(bcos::protocol::ProtocolVersion::V0)) + { + return ret; + } + if (m_srcP2PNodeID.size() > P2PMessageOptions::MAX_NODEID_LENGTH) + { + P2PMSG_LOG(ERROR) << LOG_DESC("srcP2PNodeID length valid") + << LOG_KV("srcP2PNodeID length", m_srcP2PNodeID.size()); + return false; + } + if (m_dstP2PNodeID.size() > P2PMessageOptions::MAX_NODEID_LENGTH) + { + P2PMSG_LOG(ERROR) << LOG_DESC("dstP2PNodeID length valid") + << LOG_KV("dstP2PNodeID length", m_dstP2PNodeID.size()); + return false; + } + // ecode ttl + auto ttlData = boost::asio::detail::socket_ops::host_to_network_short(m_ttl); + _buffer.insert(_buffer.end(), (byte*)&ttlData, (byte*)&ttlData + 2); + + // encode srcP2PNodeID + auto srcP2PNodeIDLen = + boost::asio::detail::socket_ops::host_to_network_short(m_srcP2PNodeID.size()); + _buffer.insert(_buffer.end(), (byte*)&srcP2PNodeIDLen, (byte*)&srcP2PNodeIDLen + 2); + _buffer.insert(_buffer.end(), m_srcP2PNodeID.begin(), m_srcP2PNodeID.end()); + + // encode dstP2PNodeID + auto dstP2PNodeIDLen = + boost::asio::detail::socket_ops::host_to_network_short(m_dstP2PNodeID.size()); + _buffer.insert(_buffer.end(), (byte*)&dstP2PNodeIDLen, (byte*)&dstP2PNodeIDLen + 2); + _buffer.insert(_buffer.end(), m_dstP2PNodeID.begin(), m_dstP2PNodeID.end()); + return true; +} + +ssize_t P2PMessageV2::decodeHeader(bytesConstRef _buffer) +{ + auto offset = P2PMessage::decodeHeader(_buffer); + if (m_version <= bcos::protocol::ProtocolVersion::V0) + { + return offset; + } + ssize_t length = _buffer.size(); + // decode ttl + CHECK_OFFSET_WITH_THROW_EXCEPTION(offset + 2, length); + m_ttl = boost::asio::detail::socket_ops::network_to_host_short(*((uint16_t*)&_buffer[offset])); + + offset += 2; + CHECK_OFFSET_WITH_THROW_EXCEPTION(offset + 2, length); + // decode srcP2PNodeID, the length of srcP2PNodeID is 2-bytes + uint16_t srcP2PNodeIDLen = + boost::asio::detail::socket_ops::network_to_host_short(*((uint16_t*)&_buffer[offset])); + + offset += 2; + CHECK_OFFSET_WITH_THROW_EXCEPTION(offset + srcP2PNodeIDLen, length); + if (srcP2PNodeIDLen > 0) + { + m_srcP2PNodeID.assign(&_buffer[offset], &_buffer[offset] + srcP2PNodeIDLen); + } + offset += srcP2PNodeIDLen; + // decode dstP2PNodeID, the length of dstP2PNodeID is 2-bytes + uint16_t dstP2PNodeIDLen = + boost::asio::detail::socket_ops::network_to_host_short(*((uint16_t*)&_buffer[offset])); + offset += 2; + CHECK_OFFSET_WITH_THROW_EXCEPTION(offset + dstP2PNodeIDLen, length); + if (dstP2PNodeIDLen > 0) + { + m_dstP2PNodeID.assign(&_buffer[offset], &_buffer[offset] + dstP2PNodeIDLen); + } + offset += dstP2PNodeIDLen; + return offset; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PMessageV2.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PMessageV2.h" new file mode 100644 index 00000000..7a230179 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PMessageV2.h" @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021 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 P2PMessageV2.h + * @brief: extend srcNodeID and dstNodeID for message forward + * @author: yujiechen + * @date 2021-05-04 + */ +#pragma once +#include "P2PMessage.h" + +namespace bcos +{ +namespace gateway +{ +class P2PMessageV2 : public P2PMessage +{ +public: + using Ptr = std::shared_ptr; + P2PMessageV2() : P2PMessage() {} + ~P2PMessageV2() override {} + + virtual int16_t ttl() const { return m_ttl; } + virtual void setTTL(int16_t _ttl) { m_ttl = _ttl; } + +protected: + ssize_t decodeHeader(bytesConstRef _buffer) override; + bool encodeHeader(bytes& _buffer) override; + +protected: + int16_t m_ttl = 10; +}; + +class P2PMessageFactoryV2 : public MessageFactory +{ +public: + using Ptr = std::shared_ptr; + P2PMessageFactoryV2() = default; + ~P2PMessageFactoryV2() override {} + Message::Ptr buildMessage() override + { + auto message = std::make_shared(); + return message; + } +}; +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PSession.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PSession.cpp" new file mode 100644 index 00000000..86148a44 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PSession.cpp" @@ -0,0 +1,89 @@ +/** @file P2PSession.cpp + * @author monan + * @date 20181112 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace bcos; +using namespace bcos::gateway; + +P2PSession::P2PSession() + : m_p2pInfo(std::make_shared()), + m_protocolInfo(std::make_shared()) +{ + // init with the minVersion + m_protocolInfo->setVersion(m_protocolInfo->minVersion()); + P2PSESSION_LOG(INFO) << "[P2PSession::P2PSession] this=" << this; +} + +P2PSession::~P2PSession() +{ + P2PSESSION_LOG(INFO) << "[P2PSession::~P2PSession] this=" << this; +} + +void P2PSession::start() +{ + if (!m_run && m_session) + { + m_run = true; + + m_session->start(); + heartBeat(); + } +} + +void P2PSession::stop(DisconnectReason reason) +{ + if (m_run) + { + m_run = false; + if (m_session && m_session->actived()) + { + m_session->disconnect(reason); + } + } +} + +void P2PSession::heartBeat() +{ + auto service = m_service.lock(); + if (service && service->actived()) + { + if (m_session && m_session->actived()) + { + auto message = + std::dynamic_pointer_cast(service->messageFactory()->buildMessage()); + message->setPacketType(GatewayMessageType::Heartbeat); + P2PSESSION_LOG(TRACE) << LOG_DESC("P2PSession onHeartBeat") + << LOG_KV("p2pid", m_p2pInfo->p2pID) + << LOG_KV("endpoint", m_session->nodeIPEndpoint()); + + m_session->asyncSendMessage(message); + } + + auto self = std::weak_ptr(shared_from_this()); + m_timer = service->host()->asioInterface()->newTimer(HEARTBEAT_INTERVEL); + m_timer->async_wait([self](boost::system::error_code e) { + if (e) + { + P2PSESSION_LOG(TRACE) << "Timer canceled: " << e.message(); + return; + } + + auto s = self.lock(); + if (s) + { + s->heartBeat(); + } + }); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PSession.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PSession.h" new file mode 100644 index 00000000..8a2ef25c --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/P2PSession.h" @@ -0,0 +1,78 @@ +/** @file P2PSession.h + * @author monan + * @date 20181112 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace gateway +{ +class P2PMessage; +class Service; + +class P2PSession : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + + P2PSession(); + + virtual ~P2PSession(); + + virtual void start(); + virtual void stop(DisconnectReason reason); + virtual bool actived() { return m_run; } + virtual void heartBeat(); + + virtual SessionFace::Ptr session() { return m_session; } + virtual void setSession(std::shared_ptr session) { m_session = session; } + + virtual P2pID p2pID() { return m_p2pInfo->p2pID; } + // Note: the p2pInfo must be setted after session setted + virtual void setP2PInfo(P2PInfo const& p2pInfo) + { + *m_p2pInfo = p2pInfo; + m_p2pInfo->nodeIPEndpoint = m_session->nodeIPEndpoint(); + } + virtual P2PInfo const& p2pInfo() const& { return *m_p2pInfo; } + virtual std::shared_ptr mutableP2pInfo() { return m_p2pInfo; } + + virtual std::weak_ptr service() { return m_service; } + virtual void setService(std::weak_ptr service) { m_service = service; } + + virtual void setProtocolInfo(bcos::protocol::ProtocolInfo::ConstPtr _protocolInfo) + { + WriteGuard l(x_protocolInfo); + *m_protocolInfo = *_protocolInfo; + } + // empty when negotiate failed or negotiate unfinished + virtual bcos::protocol::ProtocolInfo::ConstPtr protocolInfo() const + { + ReadGuard l(x_protocolInfo); + return m_protocolInfo; + } + +private: + SessionFace::Ptr m_session; + /// gateway p2p info + std::shared_ptr m_p2pInfo; + std::weak_ptr m_service; + std::shared_ptr m_timer; + bool m_run = false; + const static uint32_t HEARTBEAT_INTERVEL = 5000; + + bcos::protocol::ProtocolInfo::Ptr m_protocolInfo = nullptr; + mutable bcos::SharedMutex x_protocolInfo; +}; + +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/Service.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/Service.cpp" new file mode 100644 index 00000000..1e08f020 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/Service.cpp" @@ -0,0 +1,685 @@ +/** @file Service.cpp + * @author chaychen + * @date 20180910 + */ + +#include +#include // for ASIOInterface +#include // for SocketFace +#include // for SocketFace +#include +#include // for SessionCallbackFunc... +#include +#include // for P2PSession +#include +#include + +using namespace bcos; +using namespace bcos::gateway; +using namespace bcos::protocol; + +static const uint32_t CHECK_INTERVEL = 10000; + +Service::Service(std::string const& _nodeID) : m_nodeID(_nodeID) +{ + m_localProtocol = g_BCOSConfig.protocolInfo(ProtocolModuleID::GatewayService); + m_codec = g_BCOSConfig.codec(); + // Process handshake packet logic, handshake protocol and determine + // the version, when handshake finished the version field of P2PMessage + // should be set + registerHandlerByMsgType(GatewayMessageType::Handshake, + boost::bind(&Service::onReceiveProtocol, this, boost::placeholders::_1, + boost::placeholders::_2, boost::placeholders::_3)); +} + +void Service::start() +{ + if (!m_run) + { + m_run = true; + + auto self = std::weak_ptr(shared_from_this()); + m_host->setConnectionHandler([self](NetworkException e, P2PInfo const& p2pInfo, + std::shared_ptr session) { + auto service = self.lock(); + if (service) + { + service->onConnect(e, p2pInfo, session); + } + }); + m_host->start(); + + heartBeat(); + } +} + +void Service::stop() +{ + if (m_run) + { + m_run = false; + if (m_timer) + { + m_timer->cancel(); + } + m_host->stop(); + + /// disconnect sessions + RecursiveGuard l(x_sessions); + for (auto session : m_sessions) + { + session.second->stop(ClientQuit); + } + + /// clear sessions + m_sessions.clear(); + } +} + +void Service::heartBeat() +{ + if (!m_run) + { + return; + } + + std::map staticNodes; + { + RecursiveGuard l(x_nodes); + staticNodes = m_staticNodes; + } + + // Reconnect all nodes + for (auto& it : staticNodes) + { + /// exclude myself + if (it.second == id()) + { + SERVICE_LOG(DEBUG) << LOG_DESC("heartBeat ignore myself p2pid same") + << LOG_KV("remote endpoint", it.first) + << LOG_KV("nodeid", it.second); + continue; + } + if (!it.second.empty() && isConnected(it.second)) + { + SERVICE_LOG(TRACE) << LOG_DESC("heartBeat ignore connected") + << LOG_KV("endpoint", it.first) << LOG_KV("nodeid", it.second); + continue; + } + SERVICE_LOG(DEBUG) << LOG_DESC("heartBeat try to reconnect") + << LOG_KV("endpoint", it.first); + m_host->asyncConnect( + it.first, std::bind(&Service::onConnect, shared_from_this(), std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3)); + } + { + RecursiveGuard l(x_sessions); + SERVICE_LOG(INFO) << METRIC << LOG_DESC("heartBeat") + << LOG_KV("connected count", m_sessions.size()); + } + + auto self = std::weak_ptr(shared_from_this()); + m_timer = m_host->asioInterface()->newTimer(CHECK_INTERVEL); + m_timer->async_wait([self](const boost::system::error_code& error) { + if (error) + { + SERVICE_LOG(WARNING) << "timer canceled" << LOG_KV("errorCode", error); + return; + } + auto service = self.lock(); + if (service && service->host()->haveNetwork()) + { + service->heartBeat(); + } + }); +} + +/// update the staticNodes +void Service::updateStaticNodes(std::shared_ptr const& _s, P2pID const& nodeID) +{ + NodeIPEndpoint endpoint(_s->nodeIPEndpoint()); + RecursiveGuard l(x_nodes); + auto it = m_staticNodes.find(endpoint); + // modify m_staticNodes(including accept cases, namely the client endpoint) + if (it != m_staticNodes.end()) + { + SERVICE_LOG(INFO) << LOG_DESC("updateStaticNodes") << LOG_KV("nodeid", nodeID) + << LOG_KV("endpoint", endpoint); + it->second = nodeID; + } + else + { + SERVICE_LOG(DEBUG) << LOG_DESC("updateStaticNodes can't find endpoint") + << LOG_KV("nodeid", nodeID) << LOG_KV("endpoint", endpoint); + } +} + +void Service::onConnect( + NetworkException e, P2PInfo const& p2pInfo, std::shared_ptr session) +{ + P2pID p2pID = p2pInfo.p2pID; + std::string peer = "unknown"; + if (session) + { + peer = session->nodeIPEndpoint().address(); + } + if (e.errorCode()) + { + SERVICE_LOG(WARNING) << LOG_DESC("onConnect") << LOG_KV("errorCode", e.errorCode()) + << LOG_KV("p2pid", p2pID) << LOG_KV("nodeName", p2pInfo.nodeName) + << LOG_KV("endpoint", peer) << LOG_KV("errorMessage", e.what()); + + return; + } + + SERVICE_LOG(INFO) << LOG_DESC("onConnect") << LOG_KV("p2pid", p2pID) + << LOG_KV("endpoint", peer); + + RecursiveGuard l(x_sessions); + auto it = m_sessions.find(p2pID); + if (it != m_sessions.end() && it->second->actived()) + { + SERVICE_LOG(INFO) << "Disconnect duplicate peer" << LOG_KV("p2pid", p2pID); + updateStaticNodes(session->socket(), p2pID); + session->disconnect(DuplicatePeer); + return; + } + + if (p2pID == id()) + { + SERVICE_LOG(TRACE) << "Disconnect self"; + updateStaticNodes(session->socket(), id()); + session->disconnect(DuplicatePeer); + return; + } + + auto p2pSession = std::make_shared(); + p2pSession->setSession(session); + p2pSession->setP2PInfo(p2pInfo); + p2pSession->setService(std::weak_ptr(shared_from_this())); + p2pSession->setProtocolInfo(m_localProtocol); + + auto p2pSessionWeakPtr = std::weak_ptr(p2pSession); + p2pSession->session()->setMessageHandler(std::bind(&Service::onMessage, shared_from_this(), + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, p2pSessionWeakPtr)); + p2pSession->session()->setBeforeMessageHandler(std::bind(&Service::onBeforeMessage, + shared_from_this(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + p2pSession->start(); + asyncSendProtocol(p2pSession); + updateStaticNodes(session->socket(), p2pID); + if (it != m_sessions.end()) + { + it->second = p2pSession; + } + else + { + m_sessions.insert(std::make_pair(p2pID, p2pSession)); + callNewSessionHandlers(p2pSession); + } + SERVICE_LOG(INFO) << LOG_DESC("Connection established") << LOG_KV("p2pid", p2pID) + << LOG_KV("endpoint", session->nodeIPEndpoint()); +} + +void Service::onDisconnect(NetworkException e, P2PSession::Ptr p2pSession) +{ + // handle all registered handlers + for (const auto& handler : m_disconnectionHandlers) + { + handler(e, p2pSession); + } + + RecursiveGuard l(x_sessions); + auto it = m_sessions.find(p2pSession->p2pID()); + if (it != m_sessions.end() && it->second == p2pSession) + { + SERVICE_LOG(TRACE) << "Service onDisconnect and remove from m_sessions" + << LOG_KV("p2pid", p2pSession->p2pID()) + << LOG_KV("endpoint", p2pSession->session()->nodeIPEndpoint()); + + m_sessions.erase(it); + callDeleteSessionHandlers(p2pSession); + + if (e.errorCode() == P2PExceptionType::DuplicateSession) + return; + SERVICE_LOG(WARNING) << LOG_DESC("onDisconnect") << LOG_KV("errorCode", e.errorCode()) + << LOG_KV("what", boost::diagnostic_information(e)); + RecursiveGuard l(x_nodes); + for (auto& it : m_staticNodes) + { + if (it.second == p2pSession->p2pID()) + { + it.second.clear(); // clear nodeid info when disconnect + break; + } + } + } + // heartBeat(); +} + +void Service::sendMessageToSession(P2PSession::Ptr _p2pSession, P2PMessage::Ptr _msg, + Options _options, CallbackFuncWithSession _callback) +{ + auto protocolVersion = _p2pSession->protocolInfo()->version(); + _msg->setVersion(protocolVersion); + if (!_callback) + { + _p2pSession->session()->asyncSendMessage(_msg, _options, nullptr); + return; + } + auto weakSession = std::weak_ptr(_p2pSession); + _p2pSession->session()->asyncSendMessage( + _msg, _options, [weakSession, _callback](NetworkException e, Message::Ptr message) { + auto session = weakSession.lock(); + if (!session) + { + return; + } + P2PMessage::Ptr p2pMessage = std::dynamic_pointer_cast(message); + if (_callback) + { + _callback(e, session, p2pMessage); + } + }); +} + +void Service::sendRespMessageBySession( + bytesConstRef _payload, P2PMessage::Ptr _p2pMessage, P2PSession::Ptr _p2pSession) +{ + auto respMessage = std::static_pointer_cast(messageFactory()->buildMessage()); + + respMessage->setSeq(_p2pMessage->seq()); + respMessage->setRespPacket(); + respMessage->setPayload(std::make_shared(_payload.begin(), _payload.end())); + + sendMessageToSession(_p2pSession, respMessage); + SERVICE_LOG(TRACE) << "sendRespMessageBySession" << LOG_KV("seq", _p2pMessage->seq()) + << LOG_KV("p2pid", _p2pSession->p2pID()) + << LOG_KV("payload size", _payload.size()); +} + +bool Service::onBeforeMessage( + SessionFace::Ptr _session, Message::Ptr _message, SessionCallbackFunc _callback) +{ + if (m_beforeMessageHandler) + { + return m_beforeMessageHandler(_session, _message, _callback); + } + + return true; +} + +void Service::onMessage(NetworkException e, SessionFace::Ptr session, Message::Ptr message, + std::weak_ptr p2pSessionWeakPtr) +{ + auto p2pSession = p2pSessionWeakPtr.lock(); + if (!p2pSession) + { + return; + } + + try + { + P2pID p2pID = id(); + NodeIPEndpoint nodeIPEndpoint(boost::asio::ip::address(), 0); + if (session && p2pSession) + { + p2pID = p2pSession->p2pID(); + nodeIPEndpoint = session->nodeIPEndpoint(); + } + + if (e.errorCode()) + { + SERVICE_LOG(WARNING) << LOG_DESC("disconnect error P2PSession") + << LOG_KV("p2pid", p2pID) << LOG_KV("endpoint", nodeIPEndpoint) + << LOG_KV("errorCode", e.errorCode()) + << LOG_KV("errorMessage", e.what()); + + if (p2pSession) + { + p2pSession->stop(UserReason); + onDisconnect(e, p2pSession); + } + return; + } + + // on message handler + if (m_onMessageHandler) + { + m_onMessageHandler(session, message); + } + + /// SERVICE_LOG(TRACE) << "Service onMessage: " << message->seq(); + auto p2pMessage = std::dynamic_pointer_cast(message); + SERVICE_LOG(TRACE) << LOG_DESC("onMessage receive message") << LOG_KV("p2pid", p2pID) + << LOG_KV("endpoint", nodeIPEndpoint) << LOG_KV("seq", p2pMessage->seq()) + << LOG_KV("version", p2pMessage->version()) + << LOG_KV("packetType", p2pMessage->packetType()); + + auto packetType = p2pMessage->packetType(); + auto handler = getMessageHandlerByMsgType(packetType); + if (handler) + { + // TODO: use thread pool here + handler(e, p2pSession, p2pMessage); + return; + } + switch (packetType) + { + case GatewayMessageType::Heartbeat: + break; + default: + { + SERVICE_LOG(ERROR) << LOG_DESC("Unrecognized message type") + << LOG_KV("packetType", packetType) + << LOG_KV("packetSeq", message->seq()); + } + break; + }; + } + catch (std::exception& e) + { + SERVICE_LOG(ERROR) << "onMessage error" << LOG_KV("what", boost::diagnostic_information(e)); + } +} + +P2PMessage::Ptr Service::sendMessageByNodeID(P2pID nodeID, P2PMessage::Ptr message) +{ + try + { + struct SessionCallback : public std::enable_shared_from_this + { + public: + using Ptr = std::shared_ptr; + + SessionCallback() { mutex.lock(); } + + void onResponse( + NetworkException _error, std::shared_ptr, P2PMessage::Ptr _message) + { + error = _error; + response = _message; + mutex.unlock(); + } + + NetworkException error; + P2PMessage::Ptr response; + std::mutex mutex; + }; + + SessionCallback::Ptr callback = std::make_shared(); + CallbackFuncWithSession fp = std::bind(&SessionCallback::onResponse, callback, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); + + asyncSendMessageByNodeID(nodeID, message, fp, Options()); + // lock to wait for async send + callback->mutex.lock(); + callback->mutex.unlock(); + SERVICE_LOG(DEBUG) << LOG_DESC("sendMessageByNodeID mutex unlock"); + + NetworkException error = callback->error; + if (error.errorCode() != 0) + { + SERVICE_LOG(ERROR) << LOG_DESC("asyncSendMessageByNodeID error") + << LOG_KV("nodeid", nodeID) << LOG_KV("errorCode", error.errorCode()) + << LOG_KV("what", error.what()); + BOOST_THROW_EXCEPTION(error); + } + + return callback->response; + } + catch (std::exception& e) + { + SERVICE_LOG(ERROR) << LOG_DESC("asyncSendMessageByNodeID error") << LOG_KV("nodeid", nodeID) + << LOG_KV("what", boost::diagnostic_information(e)); + BOOST_THROW_EXCEPTION(e); + } + + return P2PMessage::Ptr(); +} + +void Service::asyncSendMessageByEndPoint(NodeIPEndpoint const& _endPoint, P2PMessage::Ptr message, + CallbackFuncWithSession callback, Options options) +{ + RecursiveGuard l(x_sessions); + for (auto const& it : m_sessions) + { + if (it.second->session()->nodeIPEndpoint() == _endPoint) + { + sendMessageToSession(it.second, message, options, callback); + break; + } + } +} + +void Service::asyncSendMessageByNodeID( + P2pID nodeID, P2PMessage::Ptr message, CallbackFuncWithSession callback, Options options) +{ + try + { + if (nodeID == id()) + { + // ignore myself + return; + } + + RecursiveGuard l(x_sessions); + auto it = m_sessions.find(nodeID); + + if (it != m_sessions.end() && it->second->actived()) + { + if (message->seq() == 0) + { + message->setSeq(m_messageFactory->newSeq()); + } + auto session = it->second; + // for compatibility_version consideration + sendMessageToSession(session, message, options, callback); + } + else + { + if (callback) + { + NetworkException e(-1, "send message failed for no network established"); + callback(e, nullptr, nullptr); + } + SERVICE_LOG(WARNING) << "Node inactived" << LOG_KV("nodeid", nodeID); + } + } + catch (std::exception& e) + { + SERVICE_LOG(ERROR) << "asyncSendMessageByNodeID" << LOG_KV("nodeid", nodeID) + << LOG_KV("what", boost::diagnostic_information(e)); + + if (callback) + { + m_host->threadPool()->enqueue([callback, e] { + callback(NetworkException(Disconnect, "Disconnect"), P2PSession::Ptr(), + P2PMessage::Ptr()); + }); + } + } +} + +void Service::asyncBroadcastMessage(P2PMessage::Ptr message, Options options) +{ + try + { + std::unordered_map sessions; + { + RecursiveGuard l(x_sessions); + sessions = m_sessions; + } + + for (auto s : sessions) + { + asyncSendMessageByNodeID(s.first, message, CallbackFuncWithSession(), options); + } + } + catch (std::exception& e) + { + SERVICE_LOG(WARNING) << LOG_DESC("asyncBroadcastMessage") + << LOG_KV("what", boost::diagnostic_information(e)); + } +} + +P2PInfos Service::sessionInfos() +{ + P2PInfos infos; + try + { + RecursiveGuard l(x_sessions); + auto s = m_sessions; + for (auto const& i : s) + { + infos.push_back(i.second->p2pInfo()); + } + } + catch (std::exception& e) + { + SERVICE_LOG(WARNING) << LOG_DESC("sessionInfos") + << LOG_KV("what", boost::diagnostic_information(e)); + } + return infos; +} + +bool Service::isConnected(P2pID const& nodeID) const +{ + RecursiveGuard l(x_sessions); + auto it = m_sessions.find(nodeID); + + if (it != m_sessions.end() && it->second->actived()) + { + return true; + } + return false; +} + +std::shared_ptr Service::newP2PMessage(int16_t _type, bytesConstRef _payload) +{ + auto message = std::static_pointer_cast(messageFactory()->buildMessage()); + + message->setPacketType(_type); + message->setSeq(messageFactory()->newSeq()); + message->setPayload(std::make_shared(_payload.begin(), _payload.end())); + return message; +} + +void Service::asyncSendMessageByP2PNodeID(int16_t _type, P2pID _dstNodeID, bytesConstRef _payload, + Options _options, P2PResponseCallback _callback) +{ + if (!isReachable(_dstNodeID)) + { + if (_callback) + { + auto errorMsg = + "send message to " + _dstNodeID + " failed for no connection established"; + _callback(std::make_shared(-1, errorMsg), 0, nullptr); + } + return; + } + auto p2pMessage = newP2PMessage(_type, _payload); + asyncSendMessageByNodeID( + _dstNodeID, p2pMessage, + [_dstNodeID, _callback](NetworkException _e, std::shared_ptr, + std::shared_ptr _p2pMessage) { + auto packetType = _p2pMessage ? _p2pMessage->packetType() : 0; + if (_e.errorCode() != 0) + { + SERVICE_LOG(WARNING) << LOG_DESC("asyncSendMessageByP2PNodeID error") + << LOG_KV("code", _e.errorCode()) << LOG_KV("msg", _e.what()) + << LOG_KV("type", packetType) << LOG_KV("dst", _dstNodeID); + if (_callback) + { + _callback( + _e.toError(), packetType, _p2pMessage ? _p2pMessage->payload() : nullptr); + } + return; + } + if (_callback) + { + _callback(nullptr, packetType, _p2pMessage->payload()); + } + }, + _options); +} + +void Service::asyncBroadcastMessageToP2PNodes( + int16_t _type, uint16_t moduleID, bytesConstRef _payload, Options _options) +{ + auto p2pMessage = newP2PMessage(_type, _payload); + asyncBroadcastMessage(p2pMessage, _options); +} + +void Service::asyncSendMessageByP2PNodeIDs( + int16_t _type, const std::vector& _nodeIDs, bytesConstRef _payload, Options _options) +{ + for (auto const& nodeID : _nodeIDs) + { + asyncSendMessageByP2PNodeID(_type, nodeID, _payload, _options, nullptr); + } +} + +// send the protocolInfo +void Service::asyncSendProtocol(P2PSession::Ptr _session) +{ + auto payload = std::make_shared(); + m_codec->encode(m_localProtocol, *payload); + auto message = std::static_pointer_cast(messageFactory()->buildMessage()); + message->setPacketType(GatewayMessageType::Handshake); + auto seq = messageFactory()->newSeq(); + message->setSeq(seq); + message->setPayload(payload); + + SERVICE_LOG(INFO) << LOG_DESC("asyncSendProtocol") << LOG_KV("payload", payload->size()) + << LOG_KV("seq", seq); + sendMessageToSession(_session, message, Options(), nullptr); +} + +// receive the protocolInfo +void Service::onReceiveProtocol( + NetworkException _e, std::shared_ptr _session, P2PMessage::Ptr _message) +{ + if (_e.errorCode()) + { + SERVICE_LOG(WARNING) << LOG_DESC("onReceiveProtocol error") + << LOG_KV("errorCode", _e.errorCode()) << LOG_KV("errorMsg", _e.what()) + << LOG_KV("peer", _session ? _session->p2pID() : "unknown"); + return; + } + try + { + auto payload = _message->payload(); + auto protocolInfo = m_codec->decode(bytesConstRef(payload->data(), payload->size())); + // negotiated version + if (protocolInfo->minVersion() > m_localProtocol->maxVersion() || + protocolInfo->maxVersion() < m_localProtocol->minVersion()) + { + SERVICE_LOG(WARNING) + << LOG_DESC("onReceiveProtocol: protocolNegotiate failed, disconnect the session") + << LOG_KV("peer", _session->p2pID()) + << LOG_KV("minVersion", protocolInfo->minVersion()) + << LOG_KV("maxVersion", protocolInfo->maxVersion()) + << LOG_KV("supportMinVersion", m_localProtocol->minVersion()) + << LOG_KV("supportMaxVersion", m_localProtocol->maxVersion()); + _session->session()->disconnect(DisconnectReason::NegotiateFailed); + return; + } + auto version = std::min(m_localProtocol->maxVersion(), protocolInfo->maxVersion()); + protocolInfo->setVersion(version); + _session->setProtocolInfo(protocolInfo); + SERVICE_LOG(INFO) << LOG_DESC("onReceiveProtocol: protocolNegotiate success") + << LOG_KV("peer", _session->p2pID()) + << LOG_KV("minVersion", protocolInfo->minVersion()) + << LOG_KV("maxVersion", protocolInfo->maxVersion()) + << LOG_KV("supportMinVersion", m_localProtocol->minVersion()) + << LOG_KV("supportMaxVersion", m_localProtocol->maxVersion()) + << LOG_KV("negotiatedVersion", version); + } + catch (std::exception const& e) + { + SERVICE_LOG(WARNING) << LOG_DESC("onReceiveProtocol exception") + << LOG_KV("peer", _session ? _session->p2pID() : "unknown") + << LOG_KV("packetType", _message->packetType()) + << LOG_KV("seq", _message->seq()); + } +} diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/Service.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/Service.h" new file mode 100644 index 00000000..a6185d4c --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/Service.h" @@ -0,0 +1,266 @@ +/** @file Service.h + * @author monan + * @modify first draft + * @date 20180910 + * @author chaychen + * @modify realize encode and decode, add timeout, code format + * @date 20180911 + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace gateway +{ +class Host; +class P2PMessage; +class Gateway; + +class Service : public P2PInterface, public std::enable_shared_from_this +{ +public: + Service(std::string const& _nodeID); + virtual ~Service() { stop(); } + + using Ptr = std::shared_ptr; + + void start() override; + void stop() override; + virtual void heartBeat(); + + virtual bool actived() { return m_run; } + P2pID id() const override { return m_nodeID; } + + virtual void onConnect( + NetworkException e, P2PInfo const& p2pInfo, std::shared_ptr session); + virtual void onDisconnect(NetworkException e, P2PSession::Ptr p2pSession); + virtual void onMessage(NetworkException e, SessionFace::Ptr session, Message::Ptr message, + std::weak_ptr p2pSessionWeakPtr); + + virtual bool onBeforeMessage( + SessionFace::Ptr _session, Message::Ptr _message, SessionCallbackFunc _callback); + + void sendRespMessageBySession( + bytesConstRef _payload, P2PMessage::Ptr _p2pMessage, P2PSession::Ptr _p2pSession) override; + + std::shared_ptr sendMessageByNodeID( + P2pID nodeID, std::shared_ptr message) override; + + void asyncSendMessageByNodeID(P2pID nodeID, std::shared_ptr message, + CallbackFuncWithSession callback, Options options = Options()) override; + + void asyncBroadcastMessage(std::shared_ptr message, Options options) override; + + virtual std::map staticNodes() { return m_staticNodes; } + virtual void setStaticNodes(const std::set& staticNodes) + { + for (const auto& endpoint : staticNodes) + { + m_staticNodes.insert(std::make_pair(endpoint, "")); + } + } + + P2PInfos sessionInfos() override; ///< Only connected node + P2PInfo localP2pInfo() override + { + auto p2pInfo = m_host->p2pInfo(); + p2pInfo.p2pID = m_nodeID; + return p2pInfo; + } + bool isConnected(P2pID const& nodeID) const override; + bool isReachable(P2pID const& _nodeID) const override { return isConnected(_nodeID); } + + std::shared_ptr host() override { return m_host; } + virtual void setHost(std::shared_ptr host) { m_host = host; } + + std::shared_ptr messageFactory() override { return m_messageFactory; } + virtual void setMessageFactory(std::shared_ptr _messageFactory) + { + m_messageFactory = _messageFactory; + } + + std::shared_ptr keyFactory() { return m_keyFactory; } + + void setKeyFactory(std::shared_ptr _keyFactory) + { + m_keyFactory = _keyFactory; + } + void updateStaticNodes(std::shared_ptr const& _s, P2pID const& nodeId); + + void registerDisconnectHandler(std::function _handler) + { + m_disconnectionHandlers.push_back(_handler); + } + + std::shared_ptr getP2PSessionByNodeId(P2pID const& _nodeID) override + { + RecursiveGuard l(x_sessions); + auto it = m_sessions.find(_nodeID); + if (it != m_sessions.end()) + { + return it->second; + } + return nullptr; + } + + void asyncSendMessageByP2PNodeID(int16_t _type, P2pID _dstNodeID, bytesConstRef _payload, + Options options = Options(), P2PResponseCallback _callback = nullptr) override; + + void asyncBroadcastMessageToP2PNodes( + int16_t _type, uint16_t moduleID, bytesConstRef _payload, Options _options) override; + + void asyncSendMessageByP2PNodeIDs(int16_t _type, const std::vector& _nodeIDs, + bytesConstRef _payload, Options _options) override; + + void registerHandlerByMsgType(int16_t _type, MessageHandler const& _msgHandler) override + { + UpgradableGuard l(x_msgHandlers); + if (m_msgHandlers.count(_type) || !_msgHandler) + { + return; + } + UpgradeGuard ul(l); + m_msgHandlers[_type] = _msgHandler; + } + + MessageHandler getMessageHandlerByMsgType(int16_t _type) + { + ReadGuard l(x_msgHandlers); + if (m_msgHandlers.count(_type)) + { + return m_msgHandlers[_type]; + } + return nullptr; + } + + void eraseHandlerByMsgType(int16_t _type) override + { + UpgradableGuard l(x_msgHandlers); + if (!m_msgHandlers.count(_type)) + { + return; + } + UpgradeGuard ul(l); + m_msgHandlers.erase(_type); + } + + + void asyncSendMessageByEndPoint(NodeIPEndpoint const& _endPoint, P2PMessage::Ptr message, + CallbackFuncWithSession callback, Options options = Options()); + + // handle before sending message, if the check fails, meaning false is returned, the message is + // not sent, and the SessionCallbackFunc will be performed + void setBeforeMessageHandler( + std::function _handler) + { + m_beforeMessageHandler = _handler; + } + + void setOnMessageHandler(std::function _handler) + { + m_onMessageHandler = _handler; + } + +protected: + virtual void sendMessageToSession(P2PSession::Ptr _p2pSession, P2PMessage::Ptr _msg, + Options = Options(), CallbackFuncWithSession = CallbackFuncWithSession()); + + std::shared_ptr newP2PMessage(int16_t _type, bytesConstRef _payload); + // handshake protocol + void asyncSendProtocol(P2PSession::Ptr _session); + void onReceiveProtocol( + NetworkException _e, std::shared_ptr _session, P2PMessage::Ptr _message); + + // handlers called when new-session + void registerOnNewSession(std::function _handler) + { + m_newSessionHandlers.emplace_back(_handler); + } + // handlers called when delete-session + void registerOnDeleteSession(std::function _handler) + { + m_deleteSessionHandlers.emplace_back(_handler); + } + + + virtual void callNewSessionHandlers(P2PSession::Ptr _session) + { + try + { + for (auto const& handler : m_newSessionHandlers) + { + handler(_session); + } + } + catch (std::exception const& e) + { + SERVICE_LOG(WARNING) << LOG_DESC("callNewSessionHandlers exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + } + virtual void callDeleteSessionHandlers(P2PSession::Ptr _session) + { + try + { + for (auto const& handler : m_deleteSessionHandlers) + { + handler(_session); + } + } + catch (std::exception const& e) + { + SERVICE_LOG(WARNING) << LOG_DESC("callDeleteSessionHandlers exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + } + +protected: + std::vector> m_disconnectionHandlers; + + std::shared_ptr m_keyFactory; + + std::map m_staticNodes; + bcos::RecursiveMutex x_nodes; + + std::shared_ptr m_host; + + std::unordered_map m_sessions; + mutable bcos::RecursiveMutex x_sessions; + + std::shared_ptr m_messageFactory; + + P2pID m_nodeID; + + std::shared_ptr m_timer; + + bool m_run = false; + + std::map m_msgHandlers; + mutable SharedMutex x_msgHandlers; + + // the local protocol + bcos::protocol::ProtocolInfo::ConstPtr m_localProtocol; + bcos::protocol::ProtocolInfoCodec::ConstPtr m_codec; + + // handlers called when new-session + std::vector> m_newSessionHandlers; + // handlers called when delete-session + std::vector> m_deleteSessionHandlers; + + std::function m_beforeMessageHandler; + + std::function m_onMessageHandler; +}; + +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/ServiceV2.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/ServiceV2.cpp" new file mode 100644 index 00000000..e7055fa2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/ServiceV2.cpp" @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2021 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 ServiceV2.cpp + * @author: yujiechen + * @date 2022-5-24 + */ +#include "ServiceV2.h" +#include "Common.h" +#include "P2PMessageV2.h" + +using namespace bcos; +using namespace bcos::gateway; + +ServiceV2::ServiceV2(std::string const& _nodeID, RouterTableFactory::Ptr _routerTableFactory) + : Service(_nodeID), + m_routerTableFactory(_routerTableFactory), + m_routerTable(m_routerTableFactory->createRouterTable()) +{ + m_routerTable->setNodeID(m_nodeID); + m_routerTable->setUnreachableDistance(c_unreachableDistance); + // process router packet related logic + registerHandlerByMsgType(GatewayMessageType::RouterTableSyncSeq, + boost::bind(&ServiceV2::onReceiveRouterSeq, this, boost::placeholders::_1, + boost::placeholders::_2, boost::placeholders::_3)); + + registerHandlerByMsgType(GatewayMessageType::RouterTableResponse, + boost::bind(&ServiceV2::onReceivePeersRouterTable, this, boost::placeholders::_1, + boost::placeholders::_2, boost::placeholders::_3)); + + registerHandlerByMsgType(GatewayMessageType::RouterTableRequest, + boost::bind(&ServiceV2::onReceiveRouterTableRequest, this, boost::placeholders::_1, + boost::placeholders::_2, boost::placeholders::_3)); + registerOnNewSession([this](P2PSession::Ptr _session) { onNewSession(_session); }); + registerOnDeleteSession([this](P2PSession::Ptr _session) { onEraseSession(_session); }); + m_routerTimer = std::make_shared(3000, "routerSeqSync"); + m_routerTimer->registerTimeoutHandler([this]() { broadcastRouterSeq(); }); +} + +void ServiceV2::start() +{ + Service::start(); + if (m_routerTimer) + { + m_routerTimer->start(); + } +} + +void ServiceV2::stop() +{ + if (m_routerTimer) + { + m_routerTimer->stop(); + } + Service::stop(); +} + +// receive routerTable from peers +void ServiceV2::onReceivePeersRouterTable( + NetworkException _e, std::shared_ptr _session, P2PMessage::Ptr _message) +{ + if (_e.errorCode()) + { + SERVICE_ROUTER_LOG(WARNING) << LOG_DESC("onReceivePeersRouterTable error") + << LOG_KV("code", _e.errorCode()) << LOG_KV("msg", _e.what()); + return; + } + auto routerTable = m_routerTableFactory->createRouterTable(ref(*(_message->payload()))); + + SERVICE_ROUTER_LOG(INFO) << LOG_DESC("onReceivePeersRouterTable") + << LOG_KV("peer", _session->p2pID()) + << LOG_KV("entrySize", routerTable->routerEntries().size()); + joinRouterTable(_session->p2pID(), routerTable); +} + +void ServiceV2::joinRouterTable( + std::string const& _generatedFrom, RouterTableInterface::Ptr _routerTable) +{ + std::set unreachableNodes; + bool updated = false; + auto const& entries = _routerTable->routerEntries(); + for (auto const& it : entries) + { + auto entry = it.second; + if (m_routerTable->update(unreachableNodes, _generatedFrom, entry) && !updated) + { + updated = true; + } + } + SERVICE_ROUTER_LOG(INFO) << LOG_DESC("joinRouterTable: create router entry") + << LOG_KV("dst", _generatedFrom); + auto entry = m_routerTableFactory->createRouterEntry(); + entry->setDstNode(_generatedFrom); + entry->setDistance(0); + if (m_routerTable->update(unreachableNodes, m_nodeID, entry) && !updated) + { + updated = true; + } + if (!updated) + { + return; + } + onP2PNodesUnreachable(unreachableNodes); + m_statusSeq++; + broadcastRouterSeq(); +} + +// receive routerTable request from peer +void ServiceV2::onReceiveRouterTableRequest( + NetworkException _e, std::shared_ptr _session, P2PMessage::Ptr _message) +{ + if (_e.errorCode()) + { + SERVICE_ROUTER_LOG(WARNING) << LOG_DESC("onReceiveRouterTableRequest error") + << LOG_KV("code", _e.errorCode()) << LOG_KV("msg", _e.what()); + return; + } + SERVICE_ROUTER_LOG(INFO) << LOG_DESC("onReceiveRouterTableRequest and response routerTable") + << LOG_KV("peer", _session->p2pID()) + << LOG_KV("entrySize", m_routerTable->routerEntries().size()); + + auto routerTableData = std::make_shared(); + m_routerTable->encode(*routerTableData); + auto dstP2PNodeID = + (_message->srcP2PNodeID().size() > 0) ? _message->srcP2PNodeID() : _session->p2pID(); + asyncSendMessageByP2PNodeID(GatewayMessageType::RouterTableResponse, dstP2PNodeID, + bytesConstRef((byte*)routerTableData->data(), routerTableData->size())); +} + +void ServiceV2::broadcastRouterSeq() +{ + m_routerTimer->restart(); + auto message = std::static_pointer_cast(m_messageFactory->buildMessage()); + message->setPacketType(GatewayMessageType::RouterTableSyncSeq); + auto seq = m_statusSeq.load(); + auto statusSeq = boost::asio::detail::socket_ops::host_to_network_long(seq); + auto payload = std::make_shared((byte*)&statusSeq, (byte*)&statusSeq + 4); + message->setPayload(payload); + // the router table should only exchange between neighbor + asyncBroadcastMessageWithoutForward(message, Options()); +} + +void ServiceV2::onReceiveRouterSeq( + NetworkException _e, std::shared_ptr _session, P2PMessage::Ptr _message) +{ + if (_e.errorCode()) + { + SERVICE_ROUTER_LOG(WARNING) + << LOG_DESC("onReceiveRouterSeq error") << LOG_KV("code", _e.errorCode()) + << LOG_KV("message", _e.what()); + return; + } + auto statusSeq = boost::asio::detail::socket_ops::network_to_host_long( + *((uint32_t*)_message->payload()->data())); + if (!tryToUpdateSeq(_session->p2pID(), statusSeq)) + { + return; + } + SERVICE_ROUTER_LOG(INFO) << LOG_DESC("onReceiveRouterSeq and request routerTable") + << LOG_KV("peer", _session->p2pID()) << LOG_KV("seq", statusSeq); + // request router table to peer + auto dstP2PNodeID = + (_message->srcP2PNodeID().size() > 0) ? _message->srcP2PNodeID() : _session->p2pID(); + asyncSendMessageByP2PNodeID( + GatewayMessageType::RouterTableRequest, dstP2PNodeID, bytesConstRef()); +} + +void ServiceV2::onNewSession(P2PSession::Ptr _session) +{ + std::set unreachableNodes; + auto entry = m_routerTableFactory->createRouterEntry(); + entry->setDstNode(_session->p2pID()); + entry->setDistance(0); + if (!m_routerTable->update(unreachableNodes, m_nodeID, entry)) + { + SERVICE_ROUTER_LOG(INFO) << LOG_DESC("onNewSession: RouterTable not changed") + << LOG_KV("dst", _session->p2pID()); + return; + } + onP2PNodesUnreachable(unreachableNodes); + m_statusSeq++; + broadcastRouterSeq(); + SERVICE_ROUTER_LOG(INFO) << LOG_DESC("onNewSession: update routerTable") + << LOG_KV("dst", _session->p2pID()); +} + +void ServiceV2::onEraseSession(P2PSession::Ptr _session) +{ + eraseSeq(_session->p2pID()); + std::set unreachableNodes; + if (m_routerTable->erase(unreachableNodes, _session->p2pID())) + { + onP2PNodesUnreachable(unreachableNodes); + m_statusSeq++; + broadcastRouterSeq(); + } + SERVICE_ROUTER_LOG(INFO) << LOG_DESC("onEraseSession") << LOG_KV("dst", _session->p2pID()); +} + +bool ServiceV2::tryToUpdateSeq(std::string const& _p2pNodeID, uint32_t _seq) +{ + UpgradableGuard l(x_node2Seq); + if (m_node2Seq.count(_p2pNodeID) && m_node2Seq.at(_p2pNodeID) >= _seq) + { + return false; + } + UpgradeGuard ul(l); + m_node2Seq[_p2pNodeID] = _seq; + return true; +} + +bool ServiceV2::eraseSeq(std::string const& _p2pNodeID) +{ + UpgradableGuard l(x_node2Seq); + if (!m_node2Seq.count(_p2pNodeID)) + { + return false; + } + UpgradeGuard ul(l); + m_node2Seq.erase(_p2pNodeID); + return true; +} + +void ServiceV2::asyncSendMessageByNodeIDWithMsgForward( + std::shared_ptr _message, CallbackFuncWithSession _callback, Options _options) +{ + auto dstNodeID = _message->dstP2PNodeID(); + // without nextHop: maybe network unreachable or with distance equal to 1 + auto nextHop = m_routerTable->getNextHop(dstNodeID); + if (nextHop.size() == 0) + { + SERVICE_LOG(TRACE) << LOG_DESC("asyncSendMessageByNodeID: sendMessage to dstNode") + << LOG_KV("from", _message->srcP2PNodeID()) + << LOG_KV("to", _message->dstP2PNodeID()) + << LOG_KV("type", _message->packetType()) + << LOG_KV("rsp", _message->isRespPacket()); + return Service::asyncSendMessageByNodeID(dstNodeID, _message, _callback, _options); + } + // with nextHop, send the message to nextHop + SERVICE_LOG(TRACE) << LOG_DESC("asyncSendMessageByNodeID: forwardMessage to nextHop") + << LOG_KV("from", _message->srcP2PNodeID()) + << LOG_KV("to", _message->dstP2PNodeID()) << LOG_KV("nextHop", nextHop) + << LOG_KV("type", _message->packetType()) + << LOG_KV("rsp", _message->isRespPacket()); + return Service::asyncSendMessageByNodeID(nextHop, _message, _callback, _options); +} + +void ServiceV2::asyncSendMessageByNodeID(P2pID _nodeID, std::shared_ptr _message, + CallbackFuncWithSession _callback, Options _options) +{ + _message->setSrcP2PNodeID(m_nodeID); + _message->setDstP2PNodeID(_nodeID); + asyncSendMessageByNodeIDWithMsgForward(_message, _callback, _options); +} + +void ServiceV2::onMessage(NetworkException _e, SessionFace::Ptr _session, Message::Ptr _message, + std::weak_ptr _p2pSessionWeakPtr) +{ + if (_e.errorCode()) + { + SERVICE_LOG(WARNING) << LOG_DESC("onMessage error") << LOG_KV("code", _e.errorCode()) + << LOG_KV("msg", _e.what()); + // calls onMessage of Service to trigger disconnectHandler + Service::onMessage(_e, _session, _message, _p2pSessionWeakPtr); + return; + } + // v0 message or the dstP2PNodeID is the nodeSelf + auto p2pMsg = std::dynamic_pointer_cast(_message); + if (p2pMsg->dstP2PNodeID().size() == 0 || p2pMsg->dstP2PNodeID() == m_nodeID) + { + SERVICE_LOG(TRACE) << LOG_DESC("onMessage") << LOG_KV("from", p2pMsg->srcP2PNodeID()) + << LOG_KV("dst", p2pMsg->dstP2PNodeID()) + << LOG_KV("type", p2pMsg->packetType()) + << LOG_KV("rsp", p2pMsg->isRespPacket()) << LOG_KV("ttl", p2pMsg->ttl()) + << LOG_KV("payLoadSize", p2pMsg->payload()->size()); + Service::onMessage(_e, _session, _message, _p2pSessionWeakPtr); + return; + } + // forward the message again + auto ttl = p2pMsg->ttl(); + if (ttl <= 0) + { + SERVICE_LOG(WARNING) << LOG_DESC("onMessage: expired ttl") + << LOG_KV("from", p2pMsg->srcP2PNodeID()) + << LOG_KV("dst", p2pMsg->dstP2PNodeID()) + << LOG_KV("type", p2pMsg->packetType()) + << LOG_KV("rsp", p2pMsg->isRespPacket()) + << LOG_KV("payLoadSize", p2pMsg->payload()->size()) + << LOG_KV("ttl", ttl); + return; + } + p2pMsg->setTTL(ttl - 1); + SERVICE_LOG(TRACE) << LOG_DESC("onMessage: asyncSendMessageByNodeIDWithMsgForward") + << LOG_KV("from", p2pMsg->srcP2PNodeID()) + << LOG_KV("dst", p2pMsg->dstP2PNodeID()) + << LOG_KV("type", p2pMsg->packetType()) + << LOG_KV("rsp", p2pMsg->isRespPacket()) + << LOG_KV("payLoadSize", p2pMsg->payload()->size()) + << LOG_KV("ttl", p2pMsg->ttl()); + asyncSendMessageByNodeIDWithMsgForward(p2pMsg, nullptr); +} + +void ServiceV2::asyncBroadcastMessage(std::shared_ptr message, Options options) +{ + auto reachableNodes = m_routerTable->getAllReachableNode(); + try + { + std::unordered_map sessions; + { + RecursiveGuard l(x_sessions); + std::for_each(m_sessions.begin(), m_sessions.end(), + [&](std::unordered_map::value_type& _value) { + reachableNodes.insert(_value.first); + }); + } + for (auto const& node : reachableNodes) + { + message->setSrcP2PNodeID(m_nodeID); + message->setDstP2PNodeID(node); + asyncSendMessageByNodeID(node, message, CallbackFuncWithSession(), options); + } + } + catch (std::exception& e) + { + SERVICE_LOG(WARNING) << LOG_DESC("asyncBroadcastMessage") + << LOG_KV("what", boost::diagnostic_information(e)); + } +} + +// broadcast message without forward +void ServiceV2::asyncBroadcastMessageWithoutForward( + std::shared_ptr message, Options options) +{ + Service::asyncBroadcastMessage(message, options); +} + +bool ServiceV2::isReachable(P2pID const& _nodeID) const +{ + auto reachableNodes = m_routerTable->getAllReachableNode(); + return reachableNodes.count(_nodeID); +} + +void ServiceV2::sendRespMessageBySession( + bytesConstRef _payload, P2PMessage::Ptr _p2pMessage, P2PSession::Ptr _p2pSession) +{ + auto version = _p2pSession->protocolInfo()->version(); + if (version <= bcos::protocol::ProtocolVersion::V0) + { + Service::sendRespMessageBySession(_payload, _p2pMessage, _p2pSession); + return; + } + auto respMessage = std::dynamic_pointer_cast(messageFactory()->buildMessage()); + auto requestMsg = std::dynamic_pointer_cast(_p2pMessage); + respMessage->setDstP2PNodeID(requestMsg->srcP2PNodeID()); + respMessage->setSrcP2PNodeID(requestMsg->dstP2PNodeID()); + respMessage->setSeq(requestMsg->seq()); + respMessage->setRespPacket(); + respMessage->setPayload(std::make_shared(_payload.begin(), _payload.end())); + + asyncSendMessageByNodeID(respMessage->dstP2PNodeID(), respMessage, nullptr); + SERVICE_LOG(TRACE) << "sendRespMessageBySession" << LOG_KV("seq", requestMsg->seq()) + << LOG_KV("from", respMessage->srcP2PNodeID()) + << LOG_KV("dst", respMessage->dstP2PNodeID()) + << LOG_KV("payload size", _payload.size()); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/ServiceV2.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/ServiceV2.h" new file mode 100644 index 00000000..6406275d --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/ServiceV2.h" @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2021 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 ServiceV2.h + * @author: yujiechen + * @date 2022-5-24 + */ +#pragma once +#include "Service.h" +#include "router/RouterTableInterface.h" +namespace bcos +{ +namespace gateway +{ +class ServiceV2 : public Service +{ +public: + using Ptr = std::shared_ptr; + ServiceV2(std::string const& _nodeID, RouterTableFactory::Ptr _routerTableFactory); + ~ServiceV2() override {} + + void start() override; + void stop() override; + + void asyncSendMessageByNodeID(P2pID nodeID, std::shared_ptr message, + CallbackFuncWithSession callback, Options options = Options()) override; + + void onMessage(NetworkException e, SessionFace::Ptr session, Message::Ptr message, + std::weak_ptr p2pSessionWeakPtr) override; + void sendRespMessageBySession( + bytesConstRef _payload, P2PMessage::Ptr _p2pMessage, P2PSession::Ptr _p2pSession) override; + void asyncBroadcastMessage(std::shared_ptr message, Options options) override; + bool isReachable(P2pID const& _nodeID) const override; + + // handlers called when the node is unreachable + void registerUnreachableHandler(std::function _handler) + { + WriteGuard l(x_unreachableHandlers); + m_unreachableHandlers.emplace_back(_handler); + } + +protected: + // called when the nodes become unreachable + void onP2PNodesUnreachable(std::set const& _p2pNodeIDs) + { + std::vector> handlers; + { + ReadGuard l(x_unreachableHandlers); + handlers = m_unreachableHandlers; + } + // TODO: async here + for (auto const& node : _p2pNodeIDs) + { + for (auto const& it : m_unreachableHandlers) + { + it(node); + } + } + } + // router related + virtual void onReceivePeersRouterTable( + NetworkException _e, std::shared_ptr _session, P2PMessage::Ptr _message); + virtual void joinRouterTable( + std::string const& _generatedFrom, RouterTableInterface::Ptr _routerTable); + virtual void onReceiveRouterTableRequest( + NetworkException _e, std::shared_ptr _session, P2PMessage::Ptr _message); + virtual void broadcastRouterSeq(); + virtual void onReceiveRouterSeq( + NetworkException _e, std::shared_ptr _session, P2PMessage::Ptr _message); + + virtual void onNewSession(P2PSession::Ptr _session); + virtual void onEraseSession(P2PSession::Ptr _session); + bool tryToUpdateSeq(std::string const& _p2pNodeID, uint32_t _seq); + bool eraseSeq(std::string const& _p2pNodeID); + + virtual void asyncSendMessageByNodeIDWithMsgForward(std::shared_ptr _message, + CallbackFuncWithSession _callback, Options options = Options()); + + virtual void asyncBroadcastMessageWithoutForward( + std::shared_ptr message, Options options); + +protected: + // for message forward + std::shared_ptr m_routerTimer; + std::atomic m_statusSeq{1}; + + RouterTableFactory::Ptr m_routerTableFactory; + RouterTableInterface::Ptr m_routerTable; + + std::map m_node2Seq; + mutable SharedMutex x_node2Seq; + + const int c_unreachableDistance = 10; + + // called when the given node unreachable + std::vector> m_unreachableHandlers; + mutable SharedMutex x_unreachableHandlers; +}; +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/router/RouterTableImpl.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/router/RouterTableImpl.cpp" new file mode 100644 index 00000000..ce1873a9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/router/RouterTableImpl.cpp" @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2021 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 RouterTableImpl.cpp + * @author: yujiechen + * @date 2022-5-24 + */ +#include "RouterTableImpl.h" +#include "../Common.h" +#include "bcos-tars-protocol/Common.h" + +using namespace bcos; +using namespace bcos::gateway; + +void RouterTable::encode(bcos::bytes& _encodedData) +{ + WriteGuard l(x_routerEntries); + m_inner()->routerEntries.clear(); + // encode m_routerEntries + for (auto const& it : m_routerEntries) + { + auto entry = std::dynamic_pointer_cast(it.second); + m_inner()->routerEntries.emplace_back(entry->inner()); + } + tars::TarsOutputStream output; + m_inner()->writeTo(output); + output.getByteBuffer().swap(_encodedData); +} + +void RouterTable::decode(bcos::bytesConstRef _decodedData) +{ + WriteGuard l(x_routerEntries); + tars::TarsInputStream input; + input.setBuffer((const char*)_decodedData.data(), _decodedData.size()); + m_inner()->readFrom(input); + // decode into m_routerEntries + m_routerEntries.clear(); + for (auto& it : m_inner()->routerEntries) + { + auto entry = + std::make_shared([m_entry = it]() mutable { return &m_entry; }); + m_routerEntries.insert(std::make_pair(entry->dstNode(), entry)); + } +} + +bool RouterTable::erase(std::set& _unreachableNodes, std::string const& _p2pNodeID) +{ + bool updated = false; + WriteGuard l(x_routerEntries); + // erase router-entry of the _p2pNodeID + if (m_routerEntries.count(_p2pNodeID)) + { + // Note: reset the distance to m_unreachableDistance, to notify that the _p2pNodeID is + // unreachable + auto entry = m_routerEntries.at(_p2pNodeID); + entry->setDistance(m_unreachableDistance); + entry->clearNextHop(); + _unreachableNodes.insert(entry->dstNode()); + + SERVICE_ROUTER_LOG(INFO) << LOG_DESC("erase: make the router unreachable") + << LOG_KV("dst", _p2pNodeID) + << LOG_KV("distance", entry->distance()) + << LOG_KV("size", m_routerEntries.size()); + updated = true; + } + // update the router-entry with nextHop equal to _p2pNodeID to be unreachable + updateDistanceForAllRouterEntries(_unreachableNodes, _p2pNodeID, m_unreachableDistance); + return updated; +} + +void RouterTable::updateDistanceForAllRouterEntries( + std::set& _unreachableNodes, std::string const& _nextHop, int32_t _newDistance) +{ + for (auto& it : m_routerEntries) + { + auto entry = it.second; + if (entry->nextHop() == _nextHop) + { + auto oldDistance = entry->distance(); + entry->setDistance(_newDistance + (oldDistance - 1)); + if (entry->distance() >= m_unreachableDistance) + { + entry->clearNextHop(); + _unreachableNodes.insert(entry->dstNode()); + } + SERVICE_ROUTER_LOG(INFO) + << LOG_DESC("update entry since the nextHop distance has been updated") + << LOG_KV("dst", entry->dstNode()) << LOG_KV("nextHop", _nextHop) + << LOG_KV("distance", entry->distance()) << LOG_KV("oldDistance", oldDistance) + << LOG_KV("size", m_routerEntries.size()); + } + } +} + +bool RouterTable::update(std::set& _unreachableNodes, + std::string const& _generatedFrom, RouterTableEntryInterface::Ptr _entry) +{ + SERVICE_ROUTER_LOG(TRACE) << LOG_DESC("RouterTable: receive entry") + << LOG_KV("dst", _entry->dstNode()) + << LOG_KV("distance", _entry->distance()) + << LOG_KV("from", _generatedFrom); + auto ret = updateDstNodeEntry(_generatedFrom, _entry); + // the dst entry has not been updated + if (ret == false) + { + return false; + } + UpgradableGuard l(x_routerEntries); + if (!m_routerEntries.count(_entry->dstNode())) + { + return false; + } + // get the latest distance + auto currentEntry = m_routerEntries.at(_entry->dstNode()); + auto _newDistance = currentEntry->distance(); + if (_newDistance >= m_unreachableDistance) + { + currentEntry->clearNextHop(); + _unreachableNodes.insert(_entry->dstNode()); + } + // the dst entry has updated, update the distance of the router-entries with nextHop equal to + // dstNode + UpgradeGuard ul(l); + if (_newDistance == 1) + { + currentEntry->clearNextHop(); + } + updateDistanceForAllRouterEntries(_unreachableNodes, _entry->dstNode(), _newDistance); + return true; +} + +bool RouterTable::updateDstNodeEntry( + std::string const& _generatedFrom, RouterTableEntryInterface::Ptr _entry) +{ + UpgradableGuard l(x_routerEntries); + // the node self + if (_entry->dstNode() == m_nodeID) + { + return false; + } + // insert new entry + if (!m_routerEntries.count(_entry->dstNode())) + { + UpgradeGuard ul(l); + _entry->incDistance(1); + if (_generatedFrom != m_nodeID) + { + _entry->setNextHop(_generatedFrom); + } + m_routerEntries.insert(std::make_pair(_entry->dstNode(), _entry)); + SERVICE_ROUTER_LOG(INFO) << LOG_DESC( + "updateDstNodeEntry: insert new entry into the routerTable") + << LOG_KV("distance", _entry->distance()) + << LOG_KV("dst", _entry->dstNode()) + << LOG_KV("nextHop", _entry->nextHop()) + << LOG_KV("size", m_routerEntries.size()); + return true; + } + // discover smaller distance + auto currentEntry = m_routerEntries.at(_entry->dstNode()); + auto currentDistance = currentEntry->distance(); + auto distance = _entry->distance() + 1; + if (currentDistance > distance) + { + UpgradeGuard ul(l); + if (_generatedFrom != m_nodeID) + { + currentEntry->setNextHop(_generatedFrom); + } + currentEntry->setDistance(distance); + SERVICE_ROUTER_LOG(INFO) + << LOG_DESC("updateDstNodeEntry: Discover smaller distance, update entry") + << LOG_KV("distance", currentEntry->distance()) + << LOG_KV("oldDistance", currentDistance) << LOG_KV("dst", _entry->dstNode()) + << LOG_KV("nextHop", _entry->nextHop()) << LOG_KV("size", m_routerEntries.size()); + return true; + } + // the distance information for the nextHop changed + if (currentEntry->nextHop() == _generatedFrom) + { + // distance not updated + if (currentEntry->distance() == distance) + { + return false; + } + // unreachable condition + if (currentEntry->distance() >= m_unreachableDistance && distance >= m_unreachableDistance) + { + return false; + } + currentEntry->setDistance(distance); + if (currentEntry->distance() >= m_unreachableDistance) + { + currentEntry->clearNextHop(); + } + SERVICE_ROUTER_LOG(INFO) << LOG_DESC( + "updateDstNodeEntry: distance of the nextHop Entry " + "updated, update the current entry") + << LOG_KV("dst", currentEntry->dstNode()) + << LOG_KV("nextHop", currentEntry->nextHop()) + << LOG_KV("distance", currentEntry->distance()) + << LOG_KV("size", m_routerEntries.size()); + return true; + } + return false; +} + +std::string RouterTable::getNextHop(std::string const& _nodeID) +{ + std::string emptyNextHop; + ReadGuard l(x_routerEntries); + if (!m_routerEntries.count(_nodeID)) + { + return emptyNextHop; + } + auto entry = m_routerEntries.at(_nodeID); + if (entry->distance() >= m_unreachableDistance) + { + return emptyNextHop; + } + return entry->nextHop(); +} + +std::set RouterTable::getAllReachableNode() +{ + std::set reachableNodes; + ReadGuard l(x_routerEntries); + for (auto const& it : m_routerEntries) + { + auto entry = it.second; + if (entry->distance() < m_unreachableDistance) + { + reachableNodes.insert(entry->dstNode()); + } + } + return reachableNodes; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/router/RouterTableImpl.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/router/RouterTableImpl.h" new file mode 100644 index 00000000..721fefaa --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/router/RouterTableImpl.h" @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2021 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 RouterTableImpl.h + * @author: yujiechen + * @date 2022-5-24 + */ +#pragma once +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include "RouterTableInterface.h" +#include +#include + +namespace bcos +{ +namespace gateway +{ +class RouterTableEntry : public RouterTableEntryInterface +{ +public: + using Ptr = std::shared_ptr; + RouterTableEntry() + : m_inner([m_entry = bcostars::RouterTableEntry()]() mutable { return &m_entry; }) + {} + RouterTableEntry(std::function _inner) : m_inner(_inner) {} + ~RouterTableEntry() override {} + + void setDstNode(std::string const& _dstNode) override { m_inner()->dstNode = _dstNode; } + void setNextHop(std::string const& _nextHop) override { m_inner()->nextHop = _nextHop; } + void clearNextHop() override { m_inner()->nextHop = std::string(); } + void setDistance(int32_t _distance) override { m_inner()->distance = _distance; } + void incDistance(int32_t _deltaDistance) override { m_inner()->distance += _deltaDistance; } + + std::string const& dstNode() const override { return m_inner()->dstNode; } + std::string const& nextHop() const override { return m_inner()->nextHop; } + int32_t distance() const override { return m_inner()->distance; } + + bcostars::RouterTableEntry const& inner() const { return *(m_inner()); } + +private: + std::function m_inner; +}; + +class RouterTable : public RouterTableInterface +{ +public: + using Ptr = std::shared_ptr; + RouterTable() : m_inner([m_table = bcostars::RouterTable()]() mutable { return &m_table; }) {} + RouterTable(bytesConstRef _decodedData) : RouterTable() { decode(_decodedData); } + virtual ~RouterTable() {} + + void encode(bcos::bytes& _encodedData) override; + void decode(bcos::bytesConstRef _decodedData) override; + + std::map const& routerEntries() override + { + return m_routerEntries; + } + // append the unreachableNodes into param _unreachableNodes + bool update(std::set& _unreachableNodes, std::string const& _generatedFrom, + RouterTableEntryInterface::Ptr _entry) override; + // append the unreachableNodes into param _unreachableNodes + bool erase(std::set& _unreachableNodes, std::string const& _p2pNodeID) override; + + void setNodeID(std::string const& _nodeID) override { m_nodeID = _nodeID; } + std::string const& nodeID() const override { return m_nodeID; } + + void setUnreachableDistance(int _unreachableDistance) override + { + m_unreachableDistance = _unreachableDistance; + } + + std::string getNextHop(std::string const& _nodeID) override; + std::set getAllReachableNode() override; + +private: + bool updateDstNodeEntry( + std::string const& _generatedFrom, RouterTableEntryInterface::Ptr _entry); + void updateDistanceForAllRouterEntries(std::set& _unreachableNodes, + std::string const& _nextHop, int32_t _newDistance); + +private: + std::string m_nodeID; + std::function m_inner; + std::map m_routerEntries; + mutable SharedMutex x_routerEntries; + + int m_unreachableDistance = 10; +}; + +class RouterTableFactoryImpl : public RouterTableFactory +{ +public: + using Ptr = std::shared_ptr; + RouterTableFactoryImpl() = default; + ~RouterTableFactoryImpl() override {} + + RouterTableInterface::Ptr createRouterTable() override + { + return std::make_shared(); + } + RouterTableInterface::Ptr createRouterTable(bcos::bytesConstRef _decodedData) override + { + return std::make_shared(_decodedData); + } + + RouterTableEntryInterface::Ptr createRouterEntry() override + { + return std::make_shared(); + } +}; + +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/router/RouterTableInterface.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/router/RouterTableInterface.h" new file mode 100644 index 00000000..feedf62f --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libp2p/router/RouterTableInterface.h" @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2021 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 RouterTableInterface.h + * @author: yujiechen + * @date 2022-5-24 + */ +#pragma once +#include +#include +namespace bcos +{ +namespace gateway +{ +class RouterTableEntryInterface +{ +public: + using Ptr = std::shared_ptr; + RouterTableEntryInterface() = default; + virtual ~RouterTableEntryInterface() {} + + virtual void setDstNode(std::string const& _dstNode) = 0; + virtual void setNextHop(std::string const& _nextHop) = 0; + virtual void clearNextHop() = 0; + virtual void setDistance(int32_t _distance) = 0; + virtual void incDistance(int32_t _deltaDistance) = 0; + + virtual std::string const& dstNode() const = 0; + virtual std::string const& nextHop() const = 0; + virtual int32_t distance() const = 0; +}; + +class RouterTableInterface +{ +public: + using Ptr = std::shared_ptr; + RouterTableInterface() = default; + virtual ~RouterTableInterface() {} + + virtual bool update(std::set& _unreachableNodes, std::string const& _generatedFrom, + RouterTableEntryInterface::Ptr _entry) = 0; + virtual bool erase(std::set& _unreachableNodes, std::string const& _p2pNodeID) = 0; + + virtual std::map const& routerEntries() = 0; + + virtual void setNodeID(std::string const& _nodeID) = 0; + virtual std::string const& nodeID() const = 0; + virtual void setUnreachableDistance(int _unreachableDistance) = 0; + virtual std::string getNextHop(std::string const& _nodeID) = 0; + virtual std::set getAllReachableNode() = 0; + + virtual void encode(bcos::bytes& _encodedData) = 0; + virtual void decode(bcos::bytesConstRef _decodedData) = 0; +}; + +class RouterTableFactory +{ +public: + using Ptr = std::shared_ptr; + RouterTableFactory() = default; + virtual ~RouterTableFactory() {} + + virtual RouterTableInterface::Ptr createRouterTable() = 0; + virtual RouterTableInterface::Ptr createRouterTable(bcos::bytesConstRef _decodedData) = 0; + virtual RouterTableEntryInterface::Ptr createRouterEntry() = 0; +}; + +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/DistributedRateLimiter.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/DistributedRateLimiter.cpp" new file mode 100644 index 00000000..1e9eb556 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/DistributedRateLimiter.cpp" @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2021 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 DistributedRateLimiter.cpp + * @author: octopus + * @date 2022-06-30 + */ + +#include "bcos-gateway/Common.h" +#include "bcos-utilities/BoostLog.h" +#include +#include +#include + +using namespace bcos; +using namespace bcos::gateway; +using namespace bcos::gateway::ratelimiter; + +// lua script for redis distributed rate limit +const std::string DistributedRateLimiter::LUA_SCRIPT = R"( + -- 限流key + local key = KEYS[1] + -- 限流大小, 初始token数目 + local initialToken = tonumber(ARGV[1]) + -- 申请token数目 + local requestToken = tonumber(ARGV[2]) + -- 限流间隔,单位秒 + local interval = tonumber(ARGV[3]) + + -- key不存在, 未初始化或过期, 初始化 + local r = redis.call('get', key) + if not r then + -- 设置限流值 + redis.call('set', key, ARGV[1]) + -- 设置过期时间 + redis.call("EXPIRE", key, ARGV[3]) + end + + -- 剩余token + local curentToken = tonumber(redis.call('get', key) or '0') + + -- 是否超出限流 + if curentToken < requestToken then + -- 返回(拒绝) + return -1 + else + -- 更新 curentToken + redis.call("DECRBY", key, requestToken) + -- 返回(放行) + return requestToken + end + )"; + +/** + * @brief acquire permits + * + * @param _requiredPermits + * @return void + */ +void DistributedRateLimiter::acquire(int64_t _requiredPermits) +{ + // Note: This operation is not supported + std::ignore = _requiredPermits; +} + +/** + * @brief + * + * @param _requiredPermits + * @return true + * @return false + */ +bool DistributedRateLimiter::tryAcquire(int64_t _requiredPermits) +{ + // try local cache acquire first + if (m_enableLocalCache && tryAcquireLocalCache(_requiredPermits, true)) + { + return true; + } + + if (!m_enableLocalCache) + { + // local cache not enable, request redis directly + return requestRedis(_requiredPermits) >= 0; + } + + std::lock_guard lock(x_localCache); + // another thread update local cache again + if (tryAcquireLocalCache(_requiredPermits, false)) + { + return true; + } + + // the request acquire bigger than _requiredPermits has been failed + if (m_lastFailedPermit > 0 && _requiredPermits >= m_lastFailedPermit) + { + return false; + } + + // request redis to update local cache + int64_t permits = m_maxPermits / 100 * m_localCachePermits; + if (requestRedis(permits > _requiredPermits ? permits : _requiredPermits) >= 0) + { + // update local cache + m_localCachePermits += (permits - _requiredPermits); + return true; + } + + // update failed info + m_lastFailedPermit = permits; + if (permits == _requiredPermits) + { + return false; + } + + auto result = requestRedis(_requiredPermits); + if (result < 0) + { + m_lastFailedPermit = _requiredPermits; + } + + return result >= 0; +} + +/** + * @brief + * + * @return + */ +void DistributedRateLimiter::rollback(int64_t _requiredPermits) +{ + // Note: This operation is not supported + std::ignore = _requiredPermits; +} + +/** + * @brief + * + * @param _requiredPermits + * @param _withLock + * @return true + * @return false + */ +bool DistributedRateLimiter::tryAcquireLocalCache(int64_t _requiredPermits, bool _withLock) +{ + // enable local cache and local cache is not enough + if (m_localCachePermits < _requiredPermits) + { + return false; + } + + bool result = false; + if (_withLock) + { + x_localCache.lock(); + } + + if (m_localCachePermits >= _requiredPermits) + { + m_localCachePermits -= _requiredPermits; + result = true; + } + + if (_withLock) + { + x_localCache.unlock(); + } + + return result; +} + +/** + * @brief + * + * @param _requiredPermits + * @return true + * @return false + */ +int64_t DistributedRateLimiter::requestRedis(int64_t _requiredPermits) +{ + try + { + auto start = utcTimeUs(); + + auto keys = {m_rateLimiterKey}; + std::vector args = {std::to_string(m_maxPermits), + std::to_string(_requiredPermits), std::to_string(m_interval)}; + + auto result = m_redis->eval( + LUA_SCRIPT, keys.begin(), keys.end(), args.begin(), args.end()); + + auto end = utcTimeUs(); + + // update stat + result >= 0 ? m_stat.updateOk() : m_stat.updateFailed(); + + if ((end - start) > 1000) + { + m_stat.updateMore1MS(); + } + + m_stat.lastRequestTotalCostMS += (end - start); + + return result; + } + catch (const std::exception& e) + { + m_stat.updateExp(); + // TODO: statistics failure information + GATEWAY_LOG(DEBUG) << LOG_BADGE("DistributedRateLimiter") << LOG_DESC("requestRedis") + << LOG_KV("rateLimitKey", m_rateLimiterKey) + << LOG_KV("enableLocalCache", m_enableLocalCache) + << LOG_KV("error", e.what()); + + // exception throw, allow this acquire + return _requiredPermits; + } +} + +/** + * @brief + * + */ +void DistributedRateLimiter::stat() +{ + GATEWAY_LOG(DEBUG) << LOG_BADGE("DistributedRateLimiter") << LOG_BADGE("stat") + << LOG_BADGE(m_rateLimiterKey) << LOG_KV("totalC", m_stat.totalRequestRedis) + << LOG_KV("totalExpC", m_stat.totalRequestRedisExp) + << LOG_KV("totalFailedC", m_stat.totalRequestRedisFailed) + << LOG_KV("totalMore1MSC", m_stat.totalRequestRedisMore1MS) + << LOG_KV("lastC", m_stat.lastRequestRedis) + << LOG_KV("lastExpC", m_stat.lastRequestRedisExp) + << LOG_KV("lastFailedC", m_stat.lastRequestRedisFailed) + << LOG_KV("lastMore1MSC", m_stat.lastRequestRedisMore1MS) + << LOG_KV("lastTotalCostMS", m_stat.lastRequestTotalCostMS); + + m_stat.resetLast(); + m_statTimer->restart(); +} + +void DistributedRateLimiter::refreshLocalCache() +{ + { + std::lock_guard lock(x_localCache); + m_localCachePermits = 0; + m_localCachePercent = 0; + m_lastFailedPermit = 0; + } + + m_clearCacheTimer->restart(); +} diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/DistributedRateLimiter.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/DistributedRateLimiter.h" new file mode 100644 index 00000000..4cc0263c --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/DistributedRateLimiter.h" @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2021 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 DistributedRateLimiter.h + * @author: octopus + * @date 2022-06-30 + */ + +#pragma once + +#include "bcos-gateway/Common.h" +#include "bcos-utilities/BoostLog.h" +#include "bcos-utilities/Timer.h" +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace gateway +{ +namespace ratelimiter +{ + +class DistributedRateLimiter : public RateLimiterInterface, + public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + + const static std::string LUA_SCRIPT; + const static int32_t DEFAULT_LOCAL_CACHE_PERCENT = 15; + +public: + DistributedRateLimiter(std::shared_ptr _redis, + const std::string& _rateLimiterKey, int64_t _maxPermits, int32_t _interval = 1, + bool _enableLocalCache = true, int32_t _localCachePercent = DEFAULT_LOCAL_CACHE_PERCENT) + : m_redis(_redis), + m_rateLimiterKey(_rateLimiterKey), + m_maxPermits(_maxPermits), + m_interval(_interval), + m_enableLocalCache(_enableLocalCache), + m_localCachePercent(_localCachePercent) + + { + GATEWAY_LOG(INFO) << LOG_BADGE("DistributedRateLimiter::NEWOBJ") + << LOG_DESC("construct distributed rate limiter") + << LOG_KV("rateLimiterKey", _rateLimiterKey) + << LOG_KV("interval", _interval) << LOG_KV("maxPermits", _maxPermits) + << LOG_KV("enableLocalCache", _enableLocalCache) + << LOG_KV("localCachePercent", _localCachePercent); + + if (m_enableLocalCache) + { + m_clearCacheTimer = std::make_shared(_interval * 1000); + m_clearCacheTimer->registerTimeoutHandler([this]() { refreshLocalCache(); }); + m_clearCacheTimer->start(); + } + + m_statTimer = std::make_shared(60000); + m_statTimer->registerTimeoutHandler([this]() { stat(); }); + m_statTimer->start(); + } + + DistributedRateLimiter(DistributedRateLimiter&&) = delete; + DistributedRateLimiter(const DistributedRateLimiter&) = delete; + DistributedRateLimiter& operator=(const DistributedRateLimiter&) = delete; + DistributedRateLimiter& operator=(DistributedRateLimiter&&) = delete; + + ~DistributedRateLimiter() override + { + if (m_clearCacheTimer) + { + m_clearCacheTimer->stop(); + } + + if (m_statTimer) + { + m_statTimer->stop(); + } + } + +public: + struct Stat + { + std::atomic totalRequestRedis = 0; + std::atomic lastRequestRedis = 0; + + std::atomic totalRequestRedisFailed = 0; + std::atomic lastRequestRedisFailed = 0; + + std::atomic totalRequestRedisExp = 0; + std::atomic lastRequestRedisExp = 0; + + std::atomic totalRequestRedisMore1MS = 0; + std::atomic lastRequestRedisMore1MS = 0; + + std::atomic lastRequestTotalCostMS = 0; + + void resetLast() + { + lastRequestRedis = 0; + lastRequestRedisFailed = 0; + lastRequestRedisExp = 0; + lastRequestRedisMore1MS = 0; + lastRequestTotalCostMS = 0; + } + + void updateOk() + { + totalRequestRedis++; + lastRequestRedis++; + } + + void updateFailed() + { + totalRequestRedisFailed++; + lastRequestRedisFailed++; + } + + void updateExp() + { + totalRequestRedisExp++; + lastRequestRedisExp++; + } + + void updateMore1MS() + { + totalRequestRedisMore1MS++; + lastRequestRedisMore1MS++; + } + }; + +public: + /** + * @brief acquire permits + * + * @param _requiredPermits + * @return void + */ + void acquire(int64_t _requiredPermits) override; + + /** + * @brief + * + * @param _requiredPermits + * @return true + * @return false + */ + bool tryAcquire(int64_t _requiredPermits) override; + + + /** + * @brief + * + * @return + */ + void rollback(int64_t _requiredPermits) override; + + /** + * @brief + * + * @param _requiredPermits + * @param _withLock + * @return true + * @return false + */ + bool tryAcquireLocalCache(int64_t _requiredPermits, bool _withLock = true); + + /** + * @brief + * + * @param _requiredPermits + * @return true + * @return false + */ + int64_t requestRedis(int64_t _requiredPermits); + + /** + * @brief + * + */ + void refreshLocalCache(); + + /** + * @brief + * + */ + void stat(); + +public: + int64_t maxPermits() const { return m_maxPermits; } + int64_t interval() const { return m_interval; } + bool enableLocalCache() const { return m_enableLocalCache; } + int32_t localCachePercent() const { return m_localCachePercent; } + std::string rateLimitKey() const { return m_rateLimiterKey; } + std::shared_ptr redis() const { return m_redis; } + +private: + // stat statistics + Stat m_stat; + + // redis + std::shared_ptr m_redis; + // key for distributed rate limit + std::string m_rateLimiterKey; + // max token acquire in m_interval time + int64_t m_maxPermits; + // + int32_t m_interval = 1; + + // enable local cache for improve perf and reduce latency + bool m_enableLocalCache = false; + // lock for m_localCachePermits + std::mutex x_localCache; + // local cache percent of m_maxPermits + int32_t m_localCachePercent = DEFAULT_LOCAL_CACHE_PERCENT; + // local cache value + int64_t m_localCachePermits = 0; + // + int64_t m_lastFailedPermit = 0; + // clear local cache info periodically + std::shared_ptr m_clearCacheTimer = nullptr; + // stat info periodically + std::shared_ptr m_statTimer = nullptr; +}; + +} // namespace ratelimiter +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/GatewayRateLimiter.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/GatewayRateLimiter.cpp" new file mode 100644 index 00000000..5c3bde70 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/GatewayRateLimiter.cpp" @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2021 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 GatewayRateLimiter.cpp + * @author: octopus + * @date 2022-09-30 + */ + +#include + +using namespace bcos; +using namespace bcos::gateway; +using namespace bcos::gateway::ratelimiter; + +std::pair GatewayRateLimiter::checkOutGoing(const std::string& _endpoint, + const std::string& _groupID, uint16_t _moduleID, uint64_t _msgLength) +{ + // endpoint of the p2p connection + const std::string& endpoint = _endpoint; + // group of the message, empty string means the message is p2p's own message + const std::string& groupID = _groupID; + // moduleID of the message, zero means the message is p2p's own message + uint16_t moduleID = _moduleID; + // the length of the message + uint64_t msgLength = _msgLength; + + std::string errorMsg; + do + { + // total outgoing bandwidth + ratelimiter::RateLimiterInterface::Ptr totalOutGoingBWLimit = + m_rateLimiterManager->getRateLimiter( + ratelimiter::RateLimiterManager::TOTAL_OUTGOING_KEY); + + // connection outgoing bandwidth + ratelimiter::RateLimiterInterface::Ptr connOutGoingBWLimit = + m_rateLimiterManager->getConnRateLimiter(endpoint); + + // group outgoing bandwidth + ratelimiter::RateLimiterInterface::Ptr groupOutGoingBWLimit = nullptr; + if (!groupID.empty()) + { + groupOutGoingBWLimit = m_rateLimiterManager->getGroupRateLimiter(groupID); + } + + auto modulesWithoutLimit = m_rateLimiterManager->modulesWithoutLimit(); + + // if moduleID is zero, the P2P network itself's message, the ratelimiter does not limit + // P2P own's messages + if (moduleID == 0) + { + if (totalOutGoingBWLimit) + { + totalOutGoingBWLimit->tryAcquire(msgLength); + } + + if (connOutGoingBWLimit) + { + connOutGoingBWLimit->tryAcquire(msgLength); + } + } + // if moduleID is not zero, the message comes from the front + // There are two scenarios: + // 1. ulimit module message rate or + // 2. limit module message rate + else if (modulesWithoutLimit.contains(moduleID)) + { // case 1: ulimit module message rate or, just for statistic + + if (totalOutGoingBWLimit) + { + totalOutGoingBWLimit->tryAcquire(msgLength); + } + + if (connOutGoingBWLimit) + { + connOutGoingBWLimit->tryAcquire(msgLength); + } + + if (groupOutGoingBWLimit) + { + groupOutGoingBWLimit->tryAcquire(msgLength); + } + } + else + { // case 2: limit module message rate + + if (totalOutGoingBWLimit && !totalOutGoingBWLimit->tryAcquire(msgLength)) + { + // total outgoing bandwidth overflow + errorMsg = "the network total outgoing bandwidth overflow"; + break; + } + + if (connOutGoingBWLimit && !connOutGoingBWLimit->tryAcquire(msgLength)) + { + // connection outgoing bandwidth overflow + errorMsg = + "the network connection outgoing bandwidth overflow, endpoint: " + endpoint; + if (totalOutGoingBWLimit) + { + totalOutGoingBWLimit->rollback(msgLength); + } + + break; + } + + if (groupOutGoingBWLimit && !groupOutGoingBWLimit->tryAcquire(msgLength)) + { + // group outgoing bandwidth overflow + errorMsg = "the group outgoing bandwidth overflow, groupID: " + groupID; + if (totalOutGoingBWLimit) + { + totalOutGoingBWLimit->rollback(msgLength); + } + + if (connOutGoingBWLimit) + { + connOutGoingBWLimit->rollback(msgLength); + } + + break; + } + } + + m_rateLimiterStat->updateOutGoing(endpoint, msgLength, true); + m_rateLimiterStat->updateOutGoing(groupID, moduleID, msgLength, true); + + return {true, ""}; + } while (false); + + m_rateLimiterStat->updateOutGoing(endpoint, msgLength, false); + m_rateLimiterStat->updateOutGoing(groupID, moduleID, msgLength, false); + + return {false, errorMsg}; +} + +std::pair GatewayRateLimiter::checkInComing( + const std::string& _endpoint, uint64_t _msgLength) +{ + m_rateLimiterStat->updateInComing(_endpoint, _msgLength); + return {true, ""}; +} + +std::pair GatewayRateLimiter::checkInComing( + const std::string& _groupID, uint16_t _moduleID, uint64_t _msgLength) +{ + m_rateLimiterStat->updateInComing(_groupID, _moduleID, _msgLength); + return {true, ""}; +} diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/GatewayRateLimiter.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/GatewayRateLimiter.h" new file mode 100644 index 00000000..bb4cb896 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/GatewayRateLimiter.h" @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2021 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 GatewayRateLimiter.h + * @author: octopus + * @date 2022-09-30 + */ + +#pragma once + +#include "bcos-utilities/BoostLog.h" +#include "bcos-utilities/Timer.h" +#include +#include +#include +#include + +namespace bcos +{ +namespace gateway +{ +namespace ratelimiter +{ + +class GatewayRateLimiter +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + +public: + GatewayRateLimiter(ratelimiter::RateLimiterManager::Ptr& _rateLimiterManager, + ratelimiter::RateLimiterStat::Ptr& _rateLimiterStat) + : m_rateLimiterManager(_rateLimiterManager), m_rateLimiterStat(_rateLimiterStat) + {} + + GatewayRateLimiter(GatewayRateLimiter&&) = delete; + GatewayRateLimiter(const GatewayRateLimiter&) = delete; + GatewayRateLimiter& operator=(const GatewayRateLimiter&) = delete; + GatewayRateLimiter& operator=(GatewayRateLimiter&&) = delete; + + ~GatewayRateLimiter() { stop(); } + +public: + void start() + { + if (m_running) + { + RATELIMIT_LOG(INFO) << LOG_BADGE("GatewayRateLimiter") + << LOG_DESC("gateway ratelimiter is running"); + return; + } + m_running = true; + if (m_rateLimiterManager->rateLimiterConfig().enableRateLimit() && + m_rateLimiterStat) + { + m_rateLimiterStat->start(); + } + + RATELIMIT_LOG(INFO) << LOG_BADGE("GatewayRateLimiter") + << LOG_DESC("gateway ratelimiter start ok"); + } + + void stop() + { + if (!m_running) + { + RATELIMIT_LOG(INFO) << LOG_BADGE("GatewayRateLimiter") + << LOG_DESC("gateway ratelimiter has been stopped"); + return; + } + + m_running = false; + if (m_rateLimiterStat) + { + m_rateLimiterStat->stop(); + } + + RATELIMIT_LOG(INFO) << LOG_BADGE("GatewayRateLimiter") + << LOG_DESC("gateway ratelimiter stop end"); + } + +public: + std::pair checkOutGoing(const std::string& _endpoint, + const std::string& _groupID, uint16_t _moduleID, uint64_t _msgLength); + + + std::pair checkInComing(const std::string& _endpoint, uint64_t _msgLength); + std::pair checkInComing( + const std::string& _groupID, uint16_t _moduleID, uint64_t _msgLength); + +private: + bool m_running = false; + + ratelimiter::RateLimiterManager::Ptr m_rateLimiterManager; + ratelimiter::RateLimiterStat::Ptr m_rateLimiterStat; +}; + +} // namespace ratelimiter +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/ModuleWhiteList.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/ModuleWhiteList.h" new file mode 100644 index 00000000..06a608fa --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/ModuleWhiteList.h" @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2021 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 ModuleWhiteList.h + * @author: octopus + * @date 2022-06-30 + */ + +#pragma once + +#include +#include + +namespace bcos +{ +namespace gateway +{ +namespace ratelimiter +{ + +/** + * This module is used to record modules that do not limit bandwidth + */ + +/* +enum ModuleID +{ + PBFT = 1000, + Raft = 1001, + BlockSync = 2000, + TxsSync = 2001, + ConsTxsSync = 2002, + AMOP = 3000, + + LIGHTNODE_GETBLOCK = 4000, + LIGHTNODE_GETTRANSACTIONS, + LIGHTNODE_GETRECEIPTS, + LIGHTNODE_GETSTATUS, + LIGHTNODE_SENDTRANSACTION, + LIGHTNODE_CALL, + LIGHTNODE_END = 4999 +}; +*/ + +#define BIT_NUMBER_PER_UINT32 (32) + +class ModuleWhiteList +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + +public: + bool isModuleExist(uint16_t _moduleID) + { + unsigned int index = _moduleID / BIT_NUMBER_PER_UINT32; + unsigned temp = _moduleID % BIT_NUMBER_PER_UINT32; + + return (m_moduleIDsBitMap.at(index) & (1 << temp)) != 0U; + } + + bool addModuleID(uint16_t _moduleID) + { + unsigned index = _moduleID / BIT_NUMBER_PER_UINT32; + unsigned temp = _moduleID % BIT_NUMBER_PER_UINT32; + + if ((m_moduleIDsBitMap.at(index) & (1 << temp)) != 0U) + { // already exist + return false; + } + + m_moduleIDsBitMap.at(index) |= (1 << temp); + return true; + } + + + bool removeModuleID(uint16_t _moduleID) + { + unsigned index = _moduleID / BIT_NUMBER_PER_UINT32; + unsigned temp = _moduleID % BIT_NUMBER_PER_UINT32; + + if ((m_moduleIDsBitMap.at(index) & (1 << temp)) != 0U) + { + m_moduleIDsBitMap.at(index) &= ~(1 << temp); + return true; + } + + // not exist + return false; + } + +private: + std::array m_moduleIDsBitMap = {0}; +}; + +} // namespace ratelimiter +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterFactory.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterFactory.h" new file mode 100644 index 00000000..d45949a6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterFactory.h" @@ -0,0 +1,79 @@ + + +/* + * Copyright (C) 2021 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 RateLimiterFactory.h + * @author: octopus + * @date 2022-09-30 + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace gateway +{ +namespace ratelimiter +{ + +class RateLimiterFactory +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + +public: + RateLimiterFactory() = default; + RateLimiterFactory(std::shared_ptr _redis) : m_redis(_redis) {} + +public: + std::shared_ptr redis() const { return m_redis; } + +public: + static std::string toTokenKey(const std::string& _baseKey) + { + return "FISCO-BCOS 3.0 Gateway RateLimiter: " + _baseKey; + } + +public: + RateLimiterInterface::Ptr buildTokenBucketRateLimiter(int64_t _maxPermits) + { + auto rateLimiter = std::make_shared(_maxPermits); + return rateLimiter; + } + + RateLimiterInterface::Ptr buildRedisDistributedRateLimiter(const std::string& _key, + int64_t _maxPermits, int32_t _interval, bool _enableCache, int32_t _cachePercent) + { + auto rateLimiter = std::make_shared( + m_redis, _key, _maxPermits, _interval, _enableCache, _cachePercent); + return rateLimiter; + } + +private: + std::shared_ptr m_redis = nullptr; +}; + +} // namespace ratelimiter +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterInterface.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterInterface.h" new file mode 100644 index 00000000..10d1ba0b --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterInterface.h" @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2021 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 RateLimiterInterface.h + * @author: octopus + * @date 2022-06-30 + */ + +#pragma once + +#include + +namespace bcos +{ +namespace gateway +{ +namespace ratelimiter +{ + +class RateLimiterInterface +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + +public: + RateLimiterInterface() = default; + RateLimiterInterface(RateLimiterInterface&&) = default; + RateLimiterInterface(const RateLimiterInterface&) = default; + RateLimiterInterface& operator=(const RateLimiterInterface&) = default; + RateLimiterInterface& operator=(RateLimiterInterface&&) = default; + + virtual ~RateLimiterInterface() = default; + +public: + /** + * @brief acquire permits + * + * @param _requiredPermits + * @return void + */ + virtual void acquire(int64_t _requiredPermits) = 0; + + /** + * @brief + * + * @param _requiredPermits + * @return true + * @return false + */ + virtual bool tryAcquire(int64_t _requiredPermits) = 0; + + /** + * @brief + * + * @return + */ + virtual void rollback(int64_t _requiredPermits) = 0; +}; + +} // namespace ratelimiter +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterManager.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterManager.cpp" new file mode 100644 index 00000000..45866a23 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterManager.cpp" @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2021 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 RateLimiterManager.cpp + * @author: octopus + * @date 2022-06-30 + */ + +#include "bcos-gateway/Common.h" +#include "bcos-utilities/BoostLog.h" +#include + +using namespace bcos; +using namespace bcos::gateway; +using namespace bcos::gateway::ratelimiter; + +const std::string RateLimiterManager::TOTAL_OUTGOING_KEY = "total-outgoing-key"; + +RateLimiterInterface::Ptr RateLimiterManager::getRateLimiter(const std::string& _rateLimiterKey) +{ + std::shared_lock lock(x_rateLimiters); + auto it = m_rateLimiters.find(_rateLimiterKey); + if (it != m_rateLimiters.end()) + { + return it->second; + } + + return nullptr; +} + +bool RateLimiterManager::registerRateLimiter( + const std::string& _rateLimiterKey, RateLimiterInterface::Ptr _rateLimiter) +{ + std::unique_lock lock(x_rateLimiters); + auto result = m_rateLimiters.try_emplace(_rateLimiterKey, _rateLimiter); + + RATELIMIT_MGR_LOG(INFO) << LOG_BADGE("registerRateLimiter") + << LOG_KV("rateLimiterKey", _rateLimiterKey) + << LOG_KV("result", result.second); + return result.second; +} + +bool RateLimiterManager::removeRateLimiter(const std::string& _rateLimiterKey) +{ + RATELIMIT_MGR_LOG(INFO) << LOG_BADGE("removeRateLimiter") + << LOG_KV("rateLimiterKey", _rateLimiterKey); + + std::unique_lock lock(x_rateLimiters); + return m_rateLimiters.erase(_rateLimiterKey) > 0; +} + +RateLimiterInterface::Ptr RateLimiterManager::getGroupRateLimiter(const std::string& _group) +{ + if (!m_rateLimiterConfig.enableGroupRateLimit) + { + return nullptr; + } + + const std::string& rateLimiterKey = _group; + + auto rateLimiter = getRateLimiter(rateLimiterKey); + if (rateLimiter != nullptr) + { + return rateLimiter; + } + + // rete limiter not exist, create it + int64_t groupOutgoingBwLimit = -1; + + auto it = m_rateLimiterConfig.group2BwLimit.find(_group); + if (it != m_rateLimiterConfig.group2BwLimit.end()) + { + groupOutgoingBwLimit = it->second; + } + else if (m_rateLimiterConfig.groupOutgoingBwLimit > 0) + { + groupOutgoingBwLimit = m_rateLimiterConfig.groupOutgoingBwLimit; + } + + if (groupOutgoingBwLimit > 0) + { + RATELIMIT_MGR_LOG(INFO) << LOG_BADGE("getGroupRateLimiter") + << LOG_DESC("group rate limiter not exist") + << LOG_KV("rateLimiterKey", rateLimiterKey) + << LOG_KV("groupOutgoingBwLimit", groupOutgoingBwLimit) + << LOG_KV("enableDistributedRatelimit", + m_rateLimiterConfig.enableDistributedRatelimit); + + if (m_rateLimiterConfig.enableDistributedRatelimit) + { + // create ratelimiter + rateLimiter = m_rateLimiterFactory->buildRedisDistributedRateLimiter( + m_rateLimiterFactory->toTokenKey(_group), groupOutgoingBwLimit, 1, + m_rateLimiterConfig.enableDistributedRateLimitCache, + m_rateLimiterConfig.distributedRateLimitCachePercent); + } + else + { + // create ratelimiter + rateLimiter = m_rateLimiterFactory->buildTokenBucketRateLimiter(groupOutgoingBwLimit); + } + + registerRateLimiter(_group, rateLimiter); + } + + return rateLimiter; +} + +RateLimiterInterface::Ptr RateLimiterManager::getConnRateLimiter(const std::string& _connIP) +{ + if (!m_rateLimiterConfig.enableConRateLimit) + { + return nullptr; + } + + const std::string& rateLimiterKey = _connIP; + + auto rateLimiter = getRateLimiter(rateLimiterKey); + if (rateLimiter != nullptr) + { + return rateLimiter; + } + + int64_t connOutgoingBwLimit = -1; + + auto it = m_rateLimiterConfig.ip2BwLimit.find(_connIP); + if (it != m_rateLimiterConfig.ip2BwLimit.end()) + { + connOutgoingBwLimit = it->second; + } + else if (m_rateLimiterConfig.connOutgoingBwLimit > 0) + { + connOutgoingBwLimit = m_rateLimiterConfig.connOutgoingBwLimit; + } + + if (connOutgoingBwLimit > 0) + { + RATELIMIT_MGR_LOG(INFO) << LOG_BADGE("getConnRateLimiter") + << LOG_DESC("conn rate limiter not exist") + << LOG_KV("rateLimiterKey", rateLimiterKey) + << LOG_KV("connOutgoingBwLimit", connOutgoingBwLimit); + + // create ratelimiter + rateLimiter = m_rateLimiterFactory->buildTokenBucketRateLimiter(connOutgoingBwLimit); + + registerRateLimiter(rateLimiterKey, rateLimiter); + } + + return rateLimiter; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterManager.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterManager.h" new file mode 100644 index 00000000..3caeece6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterManager.h" @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2021 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 RateLimiterManager.h + * @author: octopus + * @date 2022-06-30 + */ + +#pragma once + +#include "bcos-gateway/libratelimit/ModuleWhiteList.h" +#include "bcos-gateway/libratelimit/RateLimiterFactory.h" +#include +#include +#include +#include + +namespace bcos +{ +namespace gateway +{ +namespace ratelimiter +{ + +class RateLimiterManager +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + +public: + const static std::string TOTAL_OUTGOING_KEY; + +public: + RateLimiterManager(const GatewayConfig::RateLimiterConfig& _rateLimiterConfig) + : m_rateLimiterConfig(_rateLimiterConfig) + {} + +public: + RateLimiterInterface::Ptr getRateLimiter(const std::string& _rateLimiterKey); + + bool registerRateLimiter( + const std::string& _rateLimiterKey, RateLimiterInterface::Ptr _rateLimiter); + bool removeRateLimiter(const std::string& _rateLimiterKey); + + RateLimiterInterface::Ptr getGroupRateLimiter(const std::string& _group); + RateLimiterInterface::Ptr getConnRateLimiter(const std::string& _connIP); + +public: + ratelimiter::RateLimiterFactory::Ptr rateLimiterFactory() const { return m_rateLimiterFactory; } + void setRateLimiterFactory(ratelimiter::RateLimiterFactory::Ptr& _rateLimiterFactory) + { + m_rateLimiterFactory = _rateLimiterFactory; + } + + const std::set& modulesWithoutLimit() const { return m_modulesWithoutLimit; } + void setModulesWithoutLimit(std::set _modulesWithoutLimit) + { + m_modulesWithoutLimit = std::move(_modulesWithoutLimit); + } + + void setRateLimiterConfig(const GatewayConfig::RateLimiterConfig& _rateLimiterConfig) + { + m_rateLimiterConfig = _rateLimiterConfig; + } + const GatewayConfig::RateLimiterConfig& rateLimiterConfig() const + { + return m_rateLimiterConfig; + } + +private: + // factory for RateLimiterInterface + ratelimiter::RateLimiterFactory::Ptr m_rateLimiterFactory; + + // lock for m_group2RateLimiter + mutable std::shared_mutex x_rateLimiters; + // group/ip => ratelimiter + std::unordered_map m_rateLimiters; + + // the message of modules that do not limit bandwidth + std::set m_modulesWithoutLimit; + + GatewayConfig::RateLimiterConfig m_rateLimiterConfig; +}; + +} // namespace ratelimiter +} // namespace gateway +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterStat.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterStat.cpp" new file mode 100644 index 00000000..17b9c226 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterStat.cpp" @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2021 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 RateLimiterStat.cpp + * @author: octopus + * @date 2022-06-30 + */ + +#include "bcos-gateway/Common.h" +#include "bcos-utilities/BoostLog.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::gateway; +using namespace bcos::gateway::ratelimiter; + +const std::string RateLimiterStat::TOTAL_INCOMING = " total "; +const std::string RateLimiterStat::TOTAL_OUTGOING = " total "; + +double Stat::calcAvgRate(uint64_t _data, uint32_t _periodMS) +{ + auto avgRate = (double)_data * 8 * 1000 / 1024 / 1024 / _periodMS; + return avgRate; +} + +std::optional Stat::toString(const std::string& _prefix, uint32_t _periodMS) +{ + if (lastDataSize.load() == 0) + { + return std::nullopt; + } + + auto avgRate = calcAvgRate(lastDataSize.load(), _periodMS); + + std::stringstream ss; + + ss << " \t[" << _prefix << "] " + << " \t" + << " |total data: " << totalDataSize.load() << " |last data: " << lastDataSize.load() + << " |total times: " << totalTimes.load() << " |last times: " << lastTimes.load() + << " |total failed times: " << totalFailedTimes.load() + << " |last failed times: " << lastFailedTimes.load() << " |avg rate(Mb/s): "; + + ss << std::fixed << std::setprecision(2) << avgRate; + + return ss.str(); +} + +void RateLimiterStat::start() +{ + if (m_running) + { + RATELIMIT_LOG(INFO) << LOG_BADGE("RateLimiterStat") + << LOG_DESC("ratelimiter stat is running"); + return; + } + m_running = true; + + m_statTimer = std::make_shared(m_statInterval, "ratelimiter_reporter"); + + auto statInterval = m_statInterval; + auto statTimer = m_statTimer; + auto rateLimiterStatWeakPtr = std::weak_ptr(shared_from_this()); + m_statTimer->registerTimeoutHandler([statTimer, rateLimiterStatWeakPtr, statInterval]() { + auto rateLimiterStat = rateLimiterStatWeakPtr.lock(); + if (!rateLimiterStat) + { + return; + } + + auto io = rateLimiterStat->inAndOutStat(statInterval); + GATEWAY_LOG(INFO) << LOG_DESC("\n [ratelimiter stat]") << LOG_DESC(io.first); + GATEWAY_LOG(INFO) << LOG_DESC("\n [ratelimiter stat]") << LOG_DESC(io.second); + rateLimiterStat->flushStat(); + statTimer->restart(); + }); + + m_statTimer->start(); + + RATELIMIT_LOG(INFO) << LOG_BADGE("RateLimiterStat") << LOG_DESC("ratelimiter stat start ok") + << LOG_KV("statInterval", statInterval); +} + +void RateLimiterStat::stop() +{ + if (!m_running) + { + RATELIMIT_LOG(INFO) << LOG_BADGE("RateLimiterStat") + << LOG_DESC("ratelimiter stat has been stopped"); + return; + } + + m_running = false; + if (m_statTimer) + { + m_statTimer->stop(); + } + + RATELIMIT_LOG(INFO) << LOG_BADGE("RateLimiterStat") << LOG_DESC("ratelimiter stat stop end"); +} + + +std::string RateLimiterStat::toGroupKey(const std::string& _groupID) +{ + return " group : " + _groupID; +} + +std::string RateLimiterStat::toModuleKey(uint16_t _moduleID) +{ + return " module : " + protocol::moduleIDToString((protocol::ModuleID)_moduleID); +} + +std::string RateLimiterStat::toEndPointKey(const std::string& _ep) +{ + return " endpoint: " + _ep; +} + +void RateLimiterStat::updateInComing(const std::string& _endpoint, uint64_t _dataSize) +{ + std::string epKey = toEndPointKey(_endpoint); + std::string totalKey = TOTAL_OUTGOING; + + // RATELIMIT_LOG(DEBUG) << LOG_BADGE("updateInComing") << LOG_KV("endpoint", _endpoint) + // << LOG_KV("dataSize", _dataSize); + + std::lock_guard lock(m_inLock); + + auto& totalInStat = m_inStat[totalKey]; + auto& epInStat = m_inStat[epKey]; + + // update total incoming + totalInStat.update(_dataSize); + + // update connection incoming + epInStat.update(_dataSize); +} + +void RateLimiterStat::updateOutGoing(const std::string& _endpoint, uint64_t _dataSize, bool suc) +{ + std::string epKey = toEndPointKey(_endpoint); + std::string totalKey = TOTAL_OUTGOING; + + std::lock_guard lock(m_outLock); + auto& totalOutStat = m_outStat[totalKey]; + auto& epOutStat = m_outStat[epKey]; + + if (suc) + { + // update total outgoing + totalOutStat.update(_dataSize); + + // update connection outgoing + epOutStat.update(_dataSize); + } + else + { + totalOutStat.updateFailed(); + + epOutStat.updateFailed(); + } +} + +void RateLimiterStat::updateInComing( + const std::string& _groupID, uint16_t _moduleID, uint64_t _dataSize) +{ + if (_groupID.empty()) + { // amop + if (_moduleID != 0) + { + std::string moduleKey = toModuleKey(_moduleID); + std::lock_guard lock(m_inLock); + + auto& moduleInStat = m_inStat[moduleKey]; + moduleInStat.update(_dataSize); + } + + return; + } + + // RATELIMIT_LOG(DEBUG) << LOG_BADGE("updateInComing") << LOG_KV("_groupID", _groupID) + // << LOG_KV("moduleID", _moduleID) << LOG_KV("dataSize", _dataSize); + + std::string groupKey = toGroupKey(_groupID); + std::lock_guard lock(m_inLock); + + auto& groupInStat = m_inStat[groupKey]; + groupInStat.update(_dataSize); +} + +void RateLimiterStat::updateOutGoing( + const std::string& _groupID, uint16_t _moduleID, uint64_t _dataSize, bool suc) +{ + if (_groupID.empty()) + { + if (_moduleID != 0) + { + std::string moduleKey = toModuleKey(_moduleID); + std::lock_guard lock(m_outLock); + + auto& moduleOutStat = m_outStat[moduleKey]; + moduleOutStat.update(_dataSize); + + if (suc) + { + // update total outgoing + moduleOutStat.update(_dataSize); + } + else + { + moduleOutStat.updateFailed(); + } + } + return; + } + + std::string groupKey = toGroupKey(_groupID); + std::lock_guard lock(m_outLock); + + auto& groupOutStat = m_outStat[groupKey]; + if (suc) + { + // update total outgoing + groupOutStat.update(_dataSize); + } + else + { + groupOutStat.updateFailed(); + } +} + +void RateLimiterStat::flushStat() +{ + { + std::lock_guard lock(m_inLock); + for (auto& [k, s] : m_inStat) + { + s.resetLast(); + } + } + + { + std::lock_guard lock(m_outLock); + for (auto& [k, s] : m_outStat) + { + s.resetLast(); + } + } +} + +std::pair RateLimiterStat::inAndOutStat(uint32_t _intervalMS) +{ + std::string in = " :"; + { + std::lock_guard lock(m_inLock); + for (auto& [k, s] : m_inStat) + { + in += "\t\n"; + + auto opt = s.toString(k, _intervalMS); + if (opt.has_value()) + { + in += opt.value(); + } + } + } + + std::string out = " :"; + { + std::lock_guard lock(m_outLock); + for (auto& [k, s] : m_outStat) + { + out += "\t\n"; + + auto opt = s.toString(k, _intervalMS); + if (opt.has_value()) + { + out += opt.value(); + } + } + } + + return {in, out}; +} diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterStat.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterStat.h" new file mode 100644 index 00000000..a8a70cf7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/RateLimiterStat.h" @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2021 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 RateLimiterStat.h + * @author: octopus + * @date 2022-06-30 + */ +#pragma once + +#include "bcos-gateway/Common.h" +#include "bcos-utilities/Timer.h" +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace gateway +{ +namespace ratelimiter +{ + +// +struct Stat +{ + std::atomic totalDataSize; + std::atomic lastDataSize; + + std::atomic totalTimes; + std::atomic lastTimes; + + std::atomic totalFailedTimes; + std::atomic lastFailedTimes; + +public: + void resetLast() + { + lastTimes = 0; + lastDataSize = 0; + lastFailedTimes = 0; + } + + void update(uint64_t _dataSize) + { + totalTimes++; + lastTimes++; + + totalDataSize += _dataSize; + lastDataSize += _dataSize; + } + + void updateFailed() + { + totalFailedTimes++; + lastFailedTimes++; + } + + double calcAvgRate(uint64_t _data, uint32_t _periodMS); + + std::optional toString(const std::string& _prefix, uint32_t _periodMS); +}; + +class RateLimiterStat : public std::enable_shared_from_this +{ +public: + const static std::string TOTAL_INCOMING; + const static std::string TOTAL_OUTGOING; + +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + +public: + void start(); + void stop(); + +public: + void updateInComing(const std::string& _endpoint, uint64_t _dataSize); + void updateInComing(const std::string& _groupID, uint16_t _moduleID, uint64_t _dataSize); + + void updateOutGoing(const std::string& _endpoint, uint64_t _dataSize, bool suc); + void updateOutGoing( + const std::string& _groupID, uint16_t _moduleID, uint64_t _dataSize, bool suc); + +public: + std::string toGroupKey(const std::string& _groupID); + std::string toModuleKey(uint16_t _moduleID); + std::string toEndPointKey(const std::string& _ep); + + void flushStat(); + + std::pair inAndOutStat(uint32_t _intervalMS); + +public: + const std::unordered_map& inStat() { return m_inStat; } + const std::unordered_map& outStat() { return m_outStat; } + + int32_t statInterval() const { return m_statInterval; } + void setStatInterval(int32_t _statInterval) { m_statInterval = _statInterval; } + +private: + bool m_running = false; + + // TODO: How to clean up the disconnected connections + std::mutex m_inLock; + std::mutex m_outLock; + std::unordered_map m_inStat; + std::unordered_map m_outStat; + + // report period, default 1 min + int32_t m_statInterval = 60000; + // the timer that periodically report the stat + std::shared_ptr m_statTimer; +}; + +} // namespace ratelimiter +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/TokenBucketRateLimiter.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/TokenBucketRateLimiter.cpp" new file mode 100644 index 00000000..b0e89e19 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/TokenBucketRateLimiter.cpp" @@ -0,0 +1,168 @@ +/* + * @CopyRight: + * FISCO-BCOS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FISCO-BCOS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FISCO-BCOS. If not, see + * (c) 2016-2020 fisco-dev contributors. + */ +/** + * @brief : Implement of TokenBucketRateLimiter + * @file: TokenBucketRateLimiter.cpp + * @author: yujiechen + * @date: 2020-04-15 + */ +#include +#include +#include + +using namespace bcos; +using namespace bcos::gateway; +using namespace bcos::gateway::ratelimiter; + +TokenBucketRateLimiter::TokenBucketRateLimiter(int64_t _maxQPS) + : m_maxQPS(_maxQPS), + m_permitsUpdateInterval((double)1000000 / (double)m_maxQPS), + m_lastPermitsUpdateTime(utcSteadyTimeUs()), + m_maxPermits(m_maxQPS), + m_futureBurstResetTime(m_lastPermitsUpdateTime + m_burstTimeInterval) +{ + RATELIMIT_LOG(INFO) << LOG_BADGE("[NEWOBJ][TokenBucketRateLimiter]") + << LOG_KV("permitsUpdateInterval", m_permitsUpdateInterval) + << LOG_KV("maxPermits", m_maxPermits); +} + +void TokenBucketRateLimiter::setMaxPermitsSize(int64_t const& _maxPermitsSize) +{ + m_maxPermits = _maxPermitsSize; + + RATELIMIT_LOG(INFO) << LOG_BADGE("setMaxPermitsSize") << LOG_DESC("setMaxPermitsSize") + << LOG_KV("maxPermitsSize", m_maxPermits); +} + +void TokenBucketRateLimiter::setBurstTimeInterval(int64_t const& _burstInterval) +{ + m_burstTimeInterval = _burstInterval; + + RATELIMIT_LOG(INFO) << LOG_BADGE("setBurstTimeInterval") + << LOG_KV("burstTimeInterval", m_burstTimeInterval); +} + +void TokenBucketRateLimiter::setMaxBurstReqNum(int64_t const& _maxBurstReqNum) +{ + m_maxBurstReqNum = _maxBurstReqNum; + + RATELIMIT_LOG(INFO) << LOG_BADGE("setMaxBurstReqNum") + << LOG_KV("maxBurstReqNum", m_maxBurstReqNum); +} + +bool TokenBucketRateLimiter::tryAcquire(int64_t _requiredPermits) +{ + int64_t waitTime = fetchPermitsAndGetWaitTime(_requiredPermits, false, utcSteadyTimeUs()); + return (waitTime == 0); +} + +void TokenBucketRateLimiter::acquire(int64_t _requiredPermits) +{ + int64_t waitTime = fetchPermitsAndGetWaitTime(_requiredPermits, false, utcSteadyTimeUs()); + if (waitTime > 0) + { + std::this_thread::sleep_for(std::chrono::microseconds(waitTime)); + } + return; +} + +void TokenBucketRateLimiter::rollback(int64_t _requiredPermits) +{ + Guard l(m_mutex); + m_currentStoredPermits += _requiredPermits; + if (m_currentStoredPermits > m_maxPermits) + { + m_currentStoredPermits = m_maxPermits; + } + return; +} + +int64_t TokenBucketRateLimiter::fetchPermitsAndGetWaitTime( + int64_t _requiredPermits, bool _fetchPermitsWhenRequireWait, int64_t _now) +{ + Guard l(m_mutex); + // has remaining permits, handle the request directly + if (m_currentStoredPermits > _requiredPermits) + { + m_currentStoredPermits -= _requiredPermits; + return 0; + } + + // update the permits + updatePermits(_now); + int64_t waitAvailableTime = m_lastPermitsUpdateTime - _now; + // _fetchPermitsWhenRequireWait is false: don't fetch permits after timeout + // _fetchPermitsWhenRequireWait is true: fetch permits after timeout + if (!_fetchPermitsWhenRequireWait) + { + if (waitAvailableTime > 0) + { + return waitAvailableTime; + } + // Only permits of m_maxQPS can be used in advance + if ((_requiredPermits - m_currentStoredPermits) >= m_maxQPS) + { + // Indicates that the permits was not obtained + return 1; + } + } + if ((waitAvailableTime > 0 || (_requiredPermits - m_currentStoredPermits) >= m_maxQPS) && + !_fetchPermitsWhenRequireWait) + { + return waitAvailableTime; + } + updateCurrentStoredPermits(_requiredPermits); + return std::max(waitAvailableTime, (int64_t)0); +} + +void TokenBucketRateLimiter::updateCurrentStoredPermits(int64_t _requiredPermits) +{ + double waitTime = 0; + + if (_requiredPermits > m_currentStoredPermits) + { + waitTime = (_requiredPermits - m_currentStoredPermits) * m_permitsUpdateInterval; + m_currentStoredPermits = 0; + } + else + { + m_currentStoredPermits -= _requiredPermits; + } + if (waitTime > 0) + { + m_lastPermitsUpdateTime += (int64_t)(waitTime); + } +} + +void TokenBucketRateLimiter::updatePermits(int64_t _now) +{ + if (_now <= m_lastPermitsUpdateTime) + { + return; + } + int64_t increasedPermits = (double)(_now - m_lastPermitsUpdateTime) / m_permitsUpdateInterval; + m_currentStoredPermits = std::min(m_maxPermits, m_currentStoredPermits + increasedPermits); + // update last permits update time + if (m_currentStoredPermits == m_maxPermits) + { + m_lastPermitsUpdateTime = _now; + } + else + { + m_lastPermitsUpdateTime += increasedPermits * m_permitsUpdateInterval; + } +} diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/TokenBucketRateLimiter.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/TokenBucketRateLimiter.h" new file mode 100644 index 00000000..2e681209 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/libratelimit/TokenBucketRateLimiter.h" @@ -0,0 +1,116 @@ +/* + * @CopyRight: + * FISCO-BCOS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FISCO-BCOS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FISCO-BCOS. If not, see + * (c) 2016-2020 fisco-dev contributors. + */ +/** + * @brief : Implement of TokenBucketRateLimiter + * @file: TokenBucketRateLimiter.h + * @author: yujiechen + * @date: 2020-04-15 + */ +#pragma once + +#include +#include + +namespace bcos +{ +namespace gateway +{ +namespace ratelimiter +{ + +class TokenBucketRateLimiter : public RateLimiterInterface +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + +public: + TokenBucketRateLimiter(int64_t _maxQPS); + + TokenBucketRateLimiter(TokenBucketRateLimiter&&) = delete; + TokenBucketRateLimiter(const TokenBucketRateLimiter&) = delete; + TokenBucketRateLimiter& operator=(const TokenBucketRateLimiter&) = delete; + TokenBucketRateLimiter& operator=(TokenBucketRateLimiter&&) = delete; + + ~TokenBucketRateLimiter() override = default; + +public: + /** + * @brief + * + * @param _requiredPermits + */ + void acquire(int64_t _requiredPermits) override; + + /** + * @brief + * + * @param _requiredPermits + * @return true + * @return false + */ + bool tryAcquire(int64_t _requiredPermits) override; + + /** + * @brief + * + * @return + */ + void rollback(int64_t _requiredPermits) override; + +public: + int64_t maxQPS() const { return m_maxQPS; } + + void setMaxPermitsSize(int64_t const& _maxPermitsSize); + void setBurstTimeInterval(int64_t const& _burstInterval); + void setMaxBurstReqNum(int64_t const& _maxBurstReqNum); + +protected: + int64_t fetchPermitsAndGetWaitTime( + int64_t _requiredPermits, bool _fetchPermitsWhenRequireWait, int64_t _now); + + void updatePermits(int64_t _now); + + void updateCurrentStoredPermits(int64_t _requiredPermits); + +private: + mutable bcos::Mutex m_mutex; + + // the max QPS + int64_t m_maxQPS; + + // stored permits + std::atomic m_currentStoredPermits = 0; + + // the interval time to update storedPermits + double m_permitsUpdateInterval; + int64_t m_lastPermitsUpdateTime; + int64_t m_maxPermits = 0; + + // the current burstReqNum, every m_burstTimeInterval is refreshed to 0 + std::atomic m_burstReqNum = {0}; + // the max burst num during m_burstTimeInterval + int64_t m_maxBurstReqNum = 0; + // default burst interval is 1s + uint64_t m_burstTimeInterval = 1000000; + std::atomic m_futureBurstResetTime; +}; + +} // namespace ratelimiter +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/protocol/GatewayNodeStatus.cpp" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/protocol/GatewayNodeStatus.cpp" new file mode 100644 index 00000000..d7ad55b4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/protocol/GatewayNodeStatus.cpp" @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2021 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 GatewayNodeStatus.cpp + * @author: yujiechen + * @date 2021-12-31 + */ +#include "GatewayNodeStatus.h" +#include +#include +using namespace bcos; +using namespace bcos::protocol; +using namespace bcos::gateway; + +GatewayNodeStatus::GatewayNodeStatus() + : m_tarsStatus(std::make_shared()) +{} + +bytesPointer GatewayNodeStatus::encode() +{ + // append groupInfos to m_tarsStatus + m_tarsStatus->nodeList.clear(); + for (auto const& it : m_groupNodeInfos) + { + auto groupNodeInfoImpl = + std::dynamic_pointer_cast(it); + m_tarsStatus->nodeList.emplace_back(groupNodeInfoImpl->inner()); + } + auto encodeData = std::make_shared(); + tars::TarsOutputStream output; + m_tarsStatus->writeTo(output); + output.getByteBuffer().swap(*encodeData); + return encodeData; +} + +void GatewayNodeStatus::decode(bytesConstRef _data) +{ + tars::TarsInputStream input; + input.setBuffer((const char*)_data.data(), _data.size()); + m_tarsStatus->readFrom(input); + // decode into m_groupNodeInfos + m_groupNodeInfos.clear(); + for (auto& it : m_tarsStatus->nodeList) + { + auto groupNodeInfo = std::make_shared( + [m_groupNodeInfo = it]() mutable { return &m_groupNodeInfo; }); + m_groupNodeInfos.emplace_back(groupNodeInfo); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/bcos-gateway/protocol/GatewayNodeStatus.h" "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/protocol/GatewayNodeStatus.h" new file mode 100644 index 00000000..36fa90c7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/bcos-gateway/protocol/GatewayNodeStatus.h" @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2021 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 GatewayNodeStatus.h + * @author: yujiechen + * @date 2021-12-31 + */ +#pragma once + +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include +#include +#include +#include +#include +namespace bcos +{ +namespace gateway +{ +class GatewayNodeStatus +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + GatewayNodeStatus(); + virtual ~GatewayNodeStatus() {} + + virtual void setUUID(std::string const& _uuid) { m_tarsStatus->uuid = _uuid; } + virtual void setSeq(uint32_t _seq) { m_tarsStatus->seq = _seq; } + virtual void setGroupNodeInfos(std::vector&& _groupNodeInfos) + { + m_groupNodeInfos = std::move(_groupNodeInfos); + } + + virtual bytesPointer encode(); + virtual void decode(bytesConstRef _data); + + virtual std::string const& uuid() const { return m_tarsStatus->uuid; } + virtual uint32_t seq() const { return m_tarsStatus->seq; } + // Note: externally ensure thread safety + virtual std::vector const& groupNodeInfos() const + { + return m_groupNodeInfos; + } + +private: + std::shared_ptr m_tarsStatus; + std::vector m_groupNodeInfos; +}; + +class GatewayNodeStatusFactory +{ +public: + using Ptr = std::shared_ptr; + GatewayNodeStatusFactory() = default; + virtual ~GatewayNodeStatusFactory() {} + + GatewayNodeStatus::Ptr createGatewayNodeStatus() + { + return std::make_shared(); + } + GroupNodeInfo::Ptr createGroupNodeInfo() + { + return std::make_shared(); + } +}; +} // namespace gateway +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-gateway/test/CMakeLists.txt" new file mode 100644 index 00000000..cf1604cd --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/CMakeLists.txt" @@ -0,0 +1,40 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of bcos-gateway +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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. +# ------------------------------------------------------------------------------ +if(BUILD_INTEG_TESTS) + file(GLOB_RECURSE SOURCES "unittests/*.cpp" "unittests/*.h" "integtests/*.cpp" "integtests/*.h" "unittests/*.sol") +else() + file(GLOB_RECURSE SOURCES "unittests/*.cpp" "unittests/*.h" "unittests/*.sol") +endif() + +# cmake settings +set(TEST_BINARY_NAME test-bcos-gateway) + +if (TOOLS) + add_subdirectory(main) +endif() + +add_executable(${TEST_BINARY_NAME} ${SOURCES}) +target_include_directories(${TEST_BINARY_NAME} PRIVATE .) + +target_compile_options(${TEST_BINARY_NAME} PRIVATE -Wno-unused-variable) + +find_package(Boost REQUIRED unit_test_framework) + +target_link_libraries(${TEST_BINARY_NAME} ${GATEWAY_TARGET} ${TARS_PROTOCOL_TARGET} ${FRONT_TARGET} bcos-crypto Boost::unit_test_framework) +# add_test(NAME test-gateway WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) +add_test(NAME ${TEST_BINARY_NAME} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bcos-gateway/test/unittests COMMAND ${TEST_BINARY_NAME}) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/test/common/FrontServiceBuilder.h" "b/BFPL\345\243\271/bcos-gateway/test/common/FrontServiceBuilder.h" new file mode 100644 index 00000000..753988d8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/common/FrontServiceBuilder.h" @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for Gateway + * @file FrontServiceBuilder.cpp + * @author: octopus + * @date 2021-05-21 + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +inline std::shared_ptr buildFrontService( + const std::string& _groupID, const std::string& _nodeID, const std::string& _configPath) +{ + auto keyFactory = std::make_shared(); + auto gatewayFactory = std::make_shared("", ""); + auto frontServiceFactory = std::make_shared(); + auto threadPool = std::make_shared("frontServiceTest", 16); + + // build gateway + auto gateway = gatewayFactory->buildGateway(_configPath, true, nullptr, "localGateway"); + + // create nodeID by nodeID str + auto nodeIDPtr = + keyFactory->createKey(bcos::bytesConstRef((bcos::byte*)_nodeID.data(), _nodeID.size())); + + frontServiceFactory->setGatewayInterface(gateway); + + // create frontService + auto frontService = frontServiceFactory->buildFrontService(_groupID, nodeIDPtr); + // register front service to gateway + gateway->gatewayNodeManager()->registerNode( + _groupID, nodeIDPtr, bcos::protocol::NodeType::CONSENSUS_NODE, frontService, nullptr); + // front service + frontService->start(); + // start gateway + gateway->start(); + + return frontService; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/test/integtests/GatewayTest.cpp" "b/BFPL\345\243\271/bcos-gateway/test/integtests/GatewayTest.cpp" new file mode 100644 index 00000000..e278c72f --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/integtests/GatewayTest.cpp" @@ -0,0 +1,155 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for Gateway + * @file GatewayTest.cpp + * @author: octopus + * @date 2021-05-21 + */ + +#include "../common/FrontServiceBuilder.h" +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::test; + +BOOST_FIXTURE_TEST_SUITE(GatewayTest, TestPromptFixture) + +static uint nodeCount = 3; + +std::vector buildFrontServiceVector() +{ + std::string groupID = "1"; + std::string nodeIDBase = "node"; + // ../test/unittests/data + std::string configPathBase = "./node"; + + std::vector frontServiceVector; + + for (uint i = 0; i < nodeCount; ++i) + { + auto frontService = buildFrontService(groupID, nodeIDBase + std::to_string(i), + configPathBase + std::to_string(i) + "/config.ini"); + auto frontServiceWeakptr = std::weak_ptr(frontService); + // register message dispatcher for front service + frontService->registerModuleMessageDispatcher( + bcos::protocol::ModuleID::AMOP, [frontServiceWeakptr](bcos::crypto::NodeIDPtr _nodeID, + const std::string _id, bytesConstRef _data) { + auto frontService = frontServiceWeakptr.lock(); + if (frontService) + { + frontService->asyncSendResponse( + _id, bcos::protocol::ModuleID::AMOP, _nodeID, _data, [](Error::Ptr) {}); + } + }); + frontServiceVector.push_back(frontService); + } + + std::this_thread::sleep_for(std::chrono::seconds(3)); + + return frontServiceVector; +} + +BOOST_AUTO_TEST_CASE(test_FrontServiceEcho) +{ + auto frontServiceVector = buildFrontServiceVector(); + auto keyFactory = std::make_shared(); + // echo test + for (const auto& frontService : frontServiceVector) + { + frontService->asyncGetGroupNodeInfo([frontService](Error::Ptr _error, + bcos::gateway::GroupNodeInfo::Ptr _groupNodeInfo) { + BOOST_CHECK(_error == nullptr); + auto const& nodeIDs = _groupNodeInfo->nodeIDList(); + BOOST_CHECK_EQUAL(nodeIDs.size(), nodeCount); + + for (const auto& nodeIDStr : nodeIDs) + { + auto nodeID = keyFactory->createKey(fromHex(nodeIDStr)); + std::string sendStr = boost::uuids::to_string(boost::uuids::random_generator()()); + + auto payload = bcos::bytesConstRef((bcos::byte*)sendStr.data(), sendStr.size()); + + std::promise p; + auto f = p.get_future(); + + frontService->asyncSendMessageByNodeID(bcos::protocol::ModuleID::AMOP, nodeID, + payload, 10000, + [sendStr, &p](Error::Ptr _error, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, const std::string& _id, + bcos::front::ResponseFunc _respFunc) { + p.set_value(true); + (void)_respFunc; + (void)_nodeID; + BOOST_CHECK(!_id.empty()); + BOOST_CHECK(_error == nullptr); + std::string retStr = std::string(_data.begin(), _data.end()); + BOOST_CHECK_EQUAL(sendStr, retStr); + }); + + f.get(); + } + }); + } +} + +BOOST_AUTO_TEST_CASE(test_FrontServiceTimeout) +{ + auto frontServiceVector = buildFrontServiceVector(); + auto keyFactory = std::make_shared(); + // echo test + for (const auto& frontService : frontServiceVector) + { + frontService->asyncGetGroupNodeInfo([frontService](Error::Ptr _error, + bcos::gateway::GroupNodeInfo::Ptr _groupNodeInfo) { + BOOST_CHECK(_error == nullptr); + auto const& nodeIDs = _groupNodeInfo->nodeIDList(); + BOOST_CHECK_EQUAL(nodeIDs.size(), nodeCount); + + for (const auto& nodeIDStr : nodeIDs) + { + auto nodeID = keyFactory->createKey(fromHex(nodeIDStr)); + std::string sendStr = boost::uuids::to_string(boost::uuids::random_generator()()); + + auto payload = bcos::bytesConstRef((bcos::byte*)sendStr.data(), sendStr.size()); + + std::promise p; + auto f = p.get_future(); + + frontService->asyncSendMessageByNodeID(bcos::protocol::ModuleID::AMOP + 1, nodeID, + payload, 10000, + [sendStr, &p](Error::Ptr _error, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, const std::string& _id, + bcos::front::ResponseFunc _respFunc) { + p.set_value(true); + (void)_respFunc; + (void)_nodeID; + (void)_data; + BOOST_CHECK(!_id.empty()); + BOOST_CHECK(_error != nullptr); + BOOST_CHECK_EQUAL( + _error->errorCode(), bcos::protocol::CommonError::TIMEOUT); + }); + + f.get(); + } + }); + } +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/test/main/CMakeLists.txt" "b/BFPL\345\243\271/bcos-gateway/test/main/CMakeLists.txt" new file mode 100644 index 00000000..26798cee --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/main/CMakeLists.txt" @@ -0,0 +1,7 @@ +file(GLOB SRC_LIST "*.cpp") +file(GLOB HEADERS "*.h") + +set(BCOS_GATE_WAY_EXEC_TARGET "gateway-exec-mini") +add_executable(${BCOS_GATE_WAY_EXEC_TARGET} ${SRC_LIST}) +target_link_libraries(${BCOS_GATE_WAY_EXEC_TARGET} PUBLIC ${GATEWAY_TARGET} ${UTILITIES_TARGET} ${FRONT_TARGET}) +target_compile_options(${BCOS_GATE_WAY_EXEC_TARGET} PRIVATE -Wno-unused-variable) diff --git "a/BFPL\345\243\271/bcos-gateway/test/main/main.cpp" "b/BFPL\345\243\271/bcos-gateway/test/main/main.cpp" new file mode 100644 index 00000000..11071042 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/main/main.cpp" @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2021 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 main.cpp + * @author: octopus + * @date 2021-05-25 + */ +#include +#include +#include + +#include "../common/FrontServiceBuilder.h" + +using namespace std; +using namespace bcos; +using namespace gateway; + +#define GATEWAY_MAIN_LOG(LEVEL) BCOS_LOG(LEVEL) << "[Gateway][MAIN]" + +int main(int argc, const char** argv) +{ + if ((argc == 2) && ((std::string(argv[1]) == "-h") || (std::string(argv[1]) == "--help"))) + { + std::cerr << "./gateway-exec-mini groupID nodeID ./config.ini" << std::endl; + return -1; + } + + if (argc <= 3) + { + std::cerr << "please input groupID、nodeID、config path" << std::endl; + return -1; + } + + std::string groupID = argv[1]; + std::string nodeID = argv[2]; + std::string configPath = argv[3]; + + try + { + // create frontService + auto frontService = buildFrontService(groupID, nodeID, configPath); + auto fsWeakptr = std::weak_ptr(frontService); + // register message dispatcher for front service + frontService->registerModuleMessageDispatcher( + bcos::protocol::ModuleID::AMOP, [fsWeakptr](bcos::crypto::NodeIDPtr _nodeID, + const std::string& _id, bytesConstRef _data) { + auto frontService = fsWeakptr.lock(); + if (frontService) + { + GATEWAY_MAIN_LOG(INFO) + << LOG_DESC("echo") << LOG_KV("to", _nodeID->hex()) + << LOG_KV("content", std::string(_data.begin(), _data.end())); + frontService->asyncSendResponse( + _id, bcos::protocol::ModuleID::AMOP, _nodeID, _data, [](Error::Ptr) {}); + } + }); + auto keyFactory = std::make_shared(); + while (true) + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + + frontService->asyncGetGroupNodeInfo( + [frontService, keyFactory]( + Error::Ptr _error, bcos::gateway::GroupNodeInfo::Ptr _groupNodeInfo) { + (void)_error; + if (!_groupNodeInfo || _groupNodeInfo->nodeIDList().empty()) + { + return; + } + auto const& nodeIDs = _groupNodeInfo->nodeIDList(); + for (const auto& nodeIDStr : nodeIDs) + { + auto nodeID = keyFactory->createKey(fromHex(nodeIDStr)); + std::string randStr = + boost::uuids::to_string(boost::uuids::random_generator()()); + GATEWAY_MAIN_LOG(INFO) << LOG_DESC("request") << LOG_KV("to", nodeID->hex()) + << LOG_KV("content", randStr); + + auto payload = bytesConstRef((bcos::byte*)randStr.data(), randStr.size()); + + frontService->asyncSendMessageByNodeID(bcos::protocol::ModuleID::AMOP, + nodeID, payload, 0, + [randStr](Error::Ptr _error, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, const std::string& _id, + bcos::front::ResponseFunc _respFunc) { + (void)_respFunc; + if (_error && (_error->errorCode() != 0)) + { + GATEWAY_MAIN_LOG(ERROR) + << LOG_DESC("request error") << LOG_KV("to", _nodeID->hex()) + << LOG_KV("id", _id); + return; + } + + std::string retMsg = std::string(_data.begin(), _data.end()); + if (retMsg == randStr) + { + GATEWAY_MAIN_LOG(INFO) + << LOG_DESC("response ok") << LOG_KV("from", _nodeID->hex()) + << LOG_KV("id", _id); + } + else + { + GATEWAY_MAIN_LOG(ERROR) + << LOG_DESC("response error") + << LOG_KV("from", _nodeID->hex()) << LOG_KV("id", _id) + << LOG_KV("req", randStr) << LOG_KV("rep", retMsg); + } + }); + } + }); + } + } + catch (const std::exception& e) + { + std::cerr << "exception throw, error: " << boost::diagnostic_information(e) << std::endl; + return -1; + } + + std::cout << "gateway program exit normally." << std::endl; + return 0; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/GatewayConfigTest.cpp" "b/BFPL\345\243\271/bcos-gateway/test/unittests/GatewayConfigTest.cpp" new file mode 100644 index 00000000..ebbcbe1e --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/GatewayConfigTest.cpp" @@ -0,0 +1,321 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for gateway + * @file GatewayConfigTest.cpp + * @author: octopus + * @date 2021-05-17 + */ + +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace gateway; +using namespace bcos::test; + +BOOST_FIXTURE_TEST_SUITE(GatewayConfigTest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(test_validPort) +{ + auto config = std::make_shared(); + BOOST_CHECK(!config->isValidPort(1024)); + BOOST_CHECK(!config->isValidPort(65536)); + BOOST_CHECK(config->isValidPort(30300)); +} + +BOOST_AUTO_TEST_CASE(test_validIP) +{ + auto config = std::make_shared(); + + BOOST_CHECK(!config->isValidIP("a")); + BOOST_CHECK(!config->isValidIP("127")); + BOOST_CHECK(!config->isValidIP("127.0")); + BOOST_CHECK(!config->isValidIP("127.0.0")); + BOOST_CHECK(!config->isValidIP("127.0.0.1.0")); + + // ipv4 + BOOST_CHECK(config->isValidIP("127.0.0.1")); + BOOST_CHECK(config->isValidIP("192.168.0.1")); + BOOST_CHECK(config->isValidIP("64.120.121.206")); + + // ipv6 + BOOST_CHECK(config->isValidIP("::1")); + BOOST_CHECK(config->isValidIP("fe80::58da:28ff:fe08:5d91")); + BOOST_CHECK(config->isValidIP("1111::1111:1111:1111:1111")); +} + +BOOST_AUTO_TEST_CASE(test_hostAndPort2Endpoint) +{ + auto config = std::make_shared(); + + { + NodeIPEndpoint endpoint; + BOOST_CHECK_NO_THROW(config->hostAndPort2Endpoint("127.0.0.1:1111", endpoint)); + BOOST_CHECK_EQUAL(endpoint.address(), "127.0.0.1"); + BOOST_CHECK_EQUAL(endpoint.port(), 1111); + BOOST_CHECK(!endpoint.isIPv6()); + } + + { + NodeIPEndpoint endpoint; + BOOST_CHECK_NO_THROW(config->hostAndPort2Endpoint("[::1]:1234", endpoint)); + BOOST_CHECK_EQUAL(endpoint.address(), "::1"); + BOOST_CHECK_EQUAL(endpoint.port(), 1234); + BOOST_CHECK(endpoint.isIPv6()); + } + + { + NodeIPEndpoint endpoint; + BOOST_CHECK_NO_THROW(config->hostAndPort2Endpoint("8.129.188.218:12345", endpoint)); + BOOST_CHECK_EQUAL(endpoint.address(), "8.129.188.218"); + BOOST_CHECK_EQUAL(endpoint.port(), 12345); + BOOST_CHECK(!endpoint.isIPv6()); + } + + { + NodeIPEndpoint endpoint; + BOOST_CHECK_NO_THROW( + config->hostAndPort2Endpoint("[fe80::1a9d:50ae:3207:80d9]:54321", endpoint)); + BOOST_CHECK_EQUAL(endpoint.address(), "fe80::1a9d:50ae:3207:80d9"); + BOOST_CHECK_EQUAL(endpoint.port(), 54321); + BOOST_CHECK(endpoint.isIPv6()); + } + + { + NodeIPEndpoint endpoint; + BOOST_CHECK_THROW(config->hostAndPort2Endpoint("abcdef:fff", endpoint), std::exception); + BOOST_CHECK_THROW(config->hostAndPort2Endpoint("127.0.0.1", endpoint), std::exception); + } +} + +BOOST_AUTO_TEST_CASE(test_nodesJsonParser) +{ + { + std::string json = + "{\"nodes\":[\"127.0.0.1:30300\",\"127.0.0.1:30301\"," + "\"127.0.0.1:30302\"]}"; + auto config = std::make_shared(); + std::set nodeIPEndpointSet; + config->parseConnectedJson(json, nodeIPEndpointSet); + BOOST_CHECK_EQUAL(nodeIPEndpointSet.size(), 3); + BOOST_CHECK_EQUAL(config->threadPoolSize(), 16); + } + + { + std::string json = "{\"nodes\":[]}"; + auto config = std::make_shared(); + std::set nodeIPEndpointSet; + config->parseConnectedJson(json, nodeIPEndpointSet); + BOOST_CHECK_EQUAL(nodeIPEndpointSet.size(), 0); + BOOST_CHECK_EQUAL(config->threadPoolSize(), 16); + } + + { + std::string json = + "{\"nodes\":[\"[" + "fe80::1a9d:50ae:3207:80d9]:30302\"," + "\"[fe80::1a9d:50ae:3207:80d9]:30303\"]}"; + auto config = std::make_shared(); + std::set nodeIPEndpointSet; + config->parseConnectedJson(json, nodeIPEndpointSet); + BOOST_CHECK_EQUAL(nodeIPEndpointSet.size(), 2); + BOOST_CHECK_EQUAL(config->threadPoolSize(), 16); + } +} + +BOOST_AUTO_TEST_CASE(test_initConfig) +{ + { + // std::string + // configIni("../../../bcos-gateway/test/unittests/data/config/config_ipv4.ini"); + std::string configIni("data/config/config_ipv4.ini"); + auto config = std::make_shared(); + config->initConfig(configIni); + config->loadP2pConnectedNodes(); + + BOOST_CHECK_EQUAL(config->listenIP(), "127.0.0.1"); + BOOST_CHECK_EQUAL(config->listenPort(), 12345); + BOOST_CHECK_EQUAL(config->smSSL(), false); + BOOST_CHECK_EQUAL(config->connectedNodes().size(), 3); + + auto certConfig = config->certConfig(); + BOOST_CHECK(!certConfig.caCert.empty()); + BOOST_CHECK(!certConfig.nodeCert.empty()); + BOOST_CHECK(!certConfig.nodeKey.empty()); + } +} + +BOOST_AUTO_TEST_CASE(test_initSMConfig) +{ + { + // std::string + // configIni("../../../bcos-gateway/test/unittests/data/config/config_ipv6.ini"); + std::string configIni("data/config/config_ipv6.ini"); + + auto config = std::make_shared(); + config->initConfig(configIni); + config->loadP2pConnectedNodes(); + + BOOST_CHECK_EQUAL(config->listenIP(), "0.0.0.0"); + BOOST_CHECK_EQUAL(config->listenPort(), 54321); + BOOST_CHECK_EQUAL(config->smSSL(), true); + BOOST_CHECK_EQUAL(config->connectedNodes().size(), 1); + + auto smCertConfig = config->smCertConfig(); + BOOST_CHECK(!smCertConfig.caCert.empty()); + BOOST_CHECK(!smCertConfig.nodeCert.empty()); + BOOST_CHECK(!smCertConfig.nodeKey.empty()); + BOOST_CHECK(!smCertConfig.enNodeCert.empty()); + BOOST_CHECK(!smCertConfig.enNodeKey.empty()); + } +} + +BOOST_AUTO_TEST_CASE(test_initRateLimiterConfig) +{ + { + bcos::gateway::GatewayConfig::RateLimiterConfig rateLimiterConfig; + BOOST_CHECK(!rateLimiterConfig.enableRateLimit()); + } + + { + std::string configIni("data/config/config_ipv4.ini"); + + boost::property_tree::ptree pt; + boost::property_tree::ini_parser::read_ini(configIni, pt); + + auto config = std::make_shared(); + config->initRateLimitConfig(pt); + + auto rateLimiterConfig = config->rateLimiterConfig(); + + BOOST_CHECK(rateLimiterConfig.enableDistributedRatelimit); + BOOST_CHECK(rateLimiterConfig.enableDistributedRateLimitCache); + BOOST_CHECK_EQUAL(rateLimiterConfig.distributedRateLimitCachePercent, 13); + BOOST_CHECK_EQUAL(rateLimiterConfig.statInterval, 12345); + + BOOST_CHECK(rateLimiterConfig.enableRateLimit()); + BOOST_CHECK(rateLimiterConfig.enableConRateLimit); + BOOST_CHECK(rateLimiterConfig.enableGroupRateLimit); + + BOOST_CHECK_EQUAL(rateLimiterConfig.totalOutgoingBwLimit, 10 * 1024 * 1024 / 8); + BOOST_CHECK_EQUAL(rateLimiterConfig.connOutgoingBwLimit, 2 * 1024 * 1024 / 8); + BOOST_CHECK_EQUAL(rateLimiterConfig.groupOutgoingBwLimit, 5 * 1024 * 1024 / 8); + + BOOST_CHECK_EQUAL(rateLimiterConfig.modulesWithoutLimit.size(), 4); + BOOST_CHECK(rateLimiterConfig.modulesWithoutLimit.find(bcos::protocol::ModuleID::Raft) != + rateLimiterConfig.modulesWithoutLimit.end()); + BOOST_CHECK(rateLimiterConfig.modulesWithoutLimit.find(bcos::protocol::ModuleID::PBFT) != + rateLimiterConfig.modulesWithoutLimit.end()); + BOOST_CHECK(rateLimiterConfig.modulesWithoutLimit.find(bcos::protocol::ModuleID::TxsSync) != + rateLimiterConfig.modulesWithoutLimit.end()); + BOOST_CHECK(rateLimiterConfig.modulesWithoutLimit.find(bcos::protocol::ModuleID::AMOP) != + rateLimiterConfig.modulesWithoutLimit.end()); + + BOOST_CHECK_EQUAL(rateLimiterConfig.ip2BwLimit.size(), 3); + BOOST_CHECK_EQUAL( + rateLimiterConfig.ip2BwLimit.find("192.108.0.1")->second, 1 * 1024 * 1024 / 8); + BOOST_CHECK_EQUAL( + rateLimiterConfig.ip2BwLimit.find("192.108.0.2")->second, 2 * 1024 * 1024 / 8); + BOOST_CHECK_EQUAL( + rateLimiterConfig.ip2BwLimit.find("192.108.0.3")->second, 3 * 1024 * 1024 / 8); + + BOOST_CHECK_EQUAL(rateLimiterConfig.group2BwLimit.size(), 3); + BOOST_CHECK_EQUAL( + rateLimiterConfig.group2BwLimit.find("group0")->second, 2 * 1024 * 1024 / 8); + BOOST_CHECK_EQUAL( + rateLimiterConfig.group2BwLimit.find("group1")->second, 2 * 1024 * 1024 / 8); + BOOST_CHECK_EQUAL( + rateLimiterConfig.group2BwLimit.find("group2")->second, 2 * 1024 * 1024 / 8); + } + + { + std::string configIni("data/config/config_ipv6.ini"); + + boost::property_tree::ptree pt; + boost::property_tree::ini_parser::read_ini(configIni, pt); + + auto config = std::make_shared(); + config->initRateLimitConfig(pt); + + auto rateLimiterConfig = config->rateLimiterConfig(); + + BOOST_CHECK(rateLimiterConfig.enableRateLimit()); + + BOOST_CHECK(!rateLimiterConfig.enableDistributedRatelimit); + BOOST_CHECK(rateLimiterConfig.enableDistributedRateLimitCache); + BOOST_CHECK_EQUAL(rateLimiterConfig.distributedRateLimitCachePercent, 20); + BOOST_CHECK_EQUAL(rateLimiterConfig.statInterval, 60000); + + BOOST_CHECK(rateLimiterConfig.enableRateLimit()); + BOOST_CHECK(rateLimiterConfig.enableConRateLimit); + BOOST_CHECK(rateLimiterConfig.enableGroupRateLimit); + + BOOST_CHECK_EQUAL(rateLimiterConfig.totalOutgoingBwLimit, 3 * 1024 * 1024 / 8); + BOOST_CHECK_EQUAL(rateLimiterConfig.connOutgoingBwLimit, 2 * 1024 * 1024 / 8); + BOOST_CHECK_EQUAL(rateLimiterConfig.groupOutgoingBwLimit, 1 * 1024 * 1024 / 8); + + BOOST_CHECK_EQUAL(rateLimiterConfig.modulesWithoutLimit.size(), 3); + BOOST_CHECK(rateLimiterConfig.modulesWithoutLimit.find(bcos::protocol::ModuleID::Raft) != + rateLimiterConfig.modulesWithoutLimit.end()); + BOOST_CHECK(rateLimiterConfig.modulesWithoutLimit.find(bcos::protocol::ModuleID::PBFT) != + rateLimiterConfig.modulesWithoutLimit.end()); + BOOST_CHECK( + rateLimiterConfig.modulesWithoutLimit.find(bcos::protocol::ModuleID::ConsTxsSync) != + rateLimiterConfig.modulesWithoutLimit.end()); + + BOOST_CHECK_EQUAL(rateLimiterConfig.ip2BwLimit.size(), 0); + BOOST_CHECK_EQUAL(rateLimiterConfig.group2BwLimit.size(), 0); + } +} + +BOOST_AUTO_TEST_CASE(test_doubleMBToBit) +{ + auto config = std::make_shared(); + BOOST_CHECK_EQUAL(config->doubleMBToBit(1.0), 1024 * 1024 / 8); + + BOOST_CHECK_EQUAL(config->doubleMBToBit(2.5), 25 * 1024 * 1024 / 8 / 10); + + // BOOST_CHECK_EQUAL(config->doubleMBToBit(10.0), 10 * 1024 * 1024 / 8 / 10); + + BOOST_CHECK_EQUAL(config->doubleMBToBit(25.5), 255 * 1024 * 1024 / 8 / 10); + + BOOST_CHECK_EQUAL(config->doubleMBToBit(100), 100 * 1024 * 1024 / 8); +} + +BOOST_AUTO_TEST_CASE(test_RedisConfig) +{ + auto config = std::make_shared(); + std::string configIni("data/config/config_ipv6.ini"); + + boost::property_tree::ptree pt; + boost::property_tree::ini_parser::read_ini(configIni, pt); + + config->initRedisConfig(pt); + + BOOST_CHECK_EQUAL(config->redisConfig().host, "127.127.127.127"); + BOOST_CHECK_EQUAL(config->redisConfig().port, 12345); + BOOST_CHECK_EQUAL(config->redisConfig().connectionPoolSize, 111); + BOOST_CHECK_EQUAL(config->redisConfig().timeout, 54321); + BOOST_CHECK_EQUAL(config->redisConfig().password, "abc"); + BOOST_CHECK_EQUAL(config->redisConfig().db, 12); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/GatewayFactoryTest.cpp" "b/BFPL\345\243\271/bcos-gateway/test/unittests/GatewayFactoryTest.cpp" new file mode 100644 index 00000000..90e7cc14 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/GatewayFactoryTest.cpp" @@ -0,0 +1,96 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for GatewayFactory + * @file GatewayFactoryTest.cpp + * @author: octopus + * @date 2021-05-17 + */ + +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace gateway; +using namespace bcos::test; + +BOOST_FIXTURE_TEST_SUITE(GatewayFactoryTest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(test_certPubHexHandler) +{ + auto factory = std::make_shared("", ""); + { + // sm cert + std::string cert = "../../../bcos-gateway/test/unittests/data/sm_ca/sm_node.crt"; + std::string pubHex; + auto r = factory->certPubHexHandler()(cert, pubHex); + BOOST_CHECK(r); + BOOST_CHECK_EQUAL(pubHex, + R"(045a0d065954bbc96dba0e9eea163d970a9187c3e5f1a6329daf2898acb888ac2d668f4e3b34b538dcd1be7839d86a0869ca6478913cfd4e46c1517586f9c0b3c0)"); + } + + { + // RSA cert + std::string cert("../../../bcos-gateway/test/unittests/data/ca/node.crt"); + std::string pubHex; + auto r = factory->certPubHexHandler()(cert, pubHex); + BOOST_CHECK(r); + } +} + +BOOST_AUTO_TEST_CASE(test_buildSSLContext) +{ + auto factory = std::make_shared("", ""); + + { + // SM SSLContext + std::string configIni("../../../bcos-gateway/test/unittests/data/config/config_ipv6.ini"); + auto config = std::make_shared(); + config->initConfig(configIni); + + { + auto context = factory->buildSSLContext(true, config->smCertConfig()); + BOOST_CHECK(context); + } + + { + auto context = factory->buildSSLContext(false, config->smCertConfig()); + BOOST_CHECK(context); + } + } + + { + // SSLContext + std::string configIni("../../../bcos-gateway/test/unittests/data/config/config_ipv4.ini"); + auto config = std::make_shared(); + config->initConfig(configIni); + + { + auto context = factory->buildSSLContext(true, config->certConfig()); + BOOST_CHECK(context); + } + + { + auto context = factory->buildSSLContext(false, config->certConfig()); + BOOST_CHECK(context); + } + } +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/GatewayMessageTest.cpp" "b/BFPL\345\243\271/bcos-gateway/test/unittests/GatewayMessageTest.cpp" new file mode 100644 index 00000000..9adffd3b --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/GatewayMessageTest.cpp" @@ -0,0 +1,488 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for gateway + * @file GatewayMessageTest.cpp + * @author: octopus + * @date 2021-04-26 + */ + +#include "bcos-gateway/gateway/GatewayMessageExtAttributes.h" +#include +#define BOOST_TEST_MAIN + +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::gateway; +using namespace bcos::test; + +BOOST_FIXTURE_TEST_SUITE(GatewayMessageTest, TestPromptFixture) + +void testP2PMessageHasOptions(std::shared_ptr factory, uint32_t _version = 0) +{ + // default P2PMessage object + auto msg = std::static_pointer_cast(factory->buildMessage()); + msg->setVersion(_version); + msg->setPacketType(GatewayMessageType::Heartbeat); + BOOST_CHECK_EQUAL(msg->hasOptions(), false); + msg->setPacketType(GatewayMessageType::Handshake); + BOOST_CHECK_EQUAL(msg->hasOptions(), false); + msg->setPacketType(GatewayMessageType::RequestNodeStatus); + BOOST_CHECK_EQUAL(msg->hasOptions(), false); + msg->setPacketType(GatewayMessageType::ResponseNodeStatus); + BOOST_CHECK_EQUAL(msg->hasOptions(), false); + msg->setPacketType(GatewayMessageType::PeerToPeerMessage); + BOOST_CHECK_EQUAL(msg->hasOptions(), true); + msg->setPacketType(GatewayMessageType::BroadcastMessage); + BOOST_CHECK_EQUAL(msg->hasOptions(), true); + msg->setPacketType(0x1111); + BOOST_CHECK_EQUAL(msg->hasOptions(), false); + + BOOST_CHECK_EQUAL(msg->length(), 14); +} + +BOOST_AUTO_TEST_CASE(test_P2PMessage_hasOptions) +{ + auto factory = std::make_shared(); + testP2PMessageHasOptions(factory); +} + +BOOST_AUTO_TEST_CASE(test_P2PMessageV2_hasOptions) +{ + auto factory = std::make_shared(); + testP2PMessageHasOptions(factory, 1); +} + +void testP2PMessage(std::shared_ptr factory, uint32_t _version = 0) +{ + // default P2PMessage object + auto encodeMsg = std::static_pointer_cast(factory->buildMessage()); + encodeMsg->setVersion(_version); + auto buffer = std::make_shared(); + auto r = encodeMsg->encode(*buffer.get()); + + BOOST_CHECK_EQUAL(r, true); + + // decode default + auto decodeMsg = std::static_pointer_cast(factory->buildMessage()); + auto ret = decodeMsg->decode(bytesConstRef(buffer->data(), buffer->size())); + auto version = decodeMsg->version(); + if (version == 0) + { + BOOST_CHECK_EQUAL(ret, 14); + BOOST_CHECK_EQUAL(decodeMsg->length(), 14); + } + else + { + BOOST_CHECK_EQUAL(ret, 20); + BOOST_CHECK_EQUAL(decodeMsg->length(), 20); + } + BOOST_CHECK_EQUAL(decodeMsg->packetType(), 0); + BOOST_CHECK_EQUAL(decodeMsg->seq(), 0); + BOOST_CHECK_EQUAL(decodeMsg->ext(), 0); + BOOST_CHECK_EQUAL(decodeMsg->payload()->size(), 0); + + auto decodeMsg1 = std::static_pointer_cast(factory->buildMessage()); + // decode with less length + + if (_version > 0) + { + BOOST_CHECK_THROW(decodeMsg1->decode(bytesConstRef(buffer->data(), buffer->size() - 1)), + std::out_of_range); + } + else + { + auto ret1 = decodeMsg1->decode(bytesConstRef(buffer->data(), buffer->size() - 1)); + BOOST_CHECK_EQUAL(ret1, MessageDecodeStatus::MESSAGE_INCOMPLETE); + } + + { + auto factory = std::make_shared(); + // default P2PMessage object + auto encodeMsg = std::static_pointer_cast(factory->buildMessage()); + encodeMsg->setVersion(_version); + encodeMsg->setPacketType(GatewayMessageType::PeerToPeerMessage); + + auto buffer = std::make_shared(); + auto r = encodeMsg->encode(*buffer.get()); + BOOST_CHECK_EQUAL(r, false); + } + // test invalid message + std::string invalidMessage = + "GET / HTTP/1.1\r\nHost: 127.0.0.1:20200\r\nUpgrade: websocket\r\nConnection: " + "upgrade\r\nSec-WebSocket-Key: lkBb9dFFu4tuMNJyXAWIfQ==\r\nSec-WebSocket-Version: " + "13\r\n\r\n"; + auto invalidMsgBytes = asBytes(invalidMessage); + auto p2pMsg = std::static_pointer_cast(factory->buildMessage()); + p2pMsg->setVersion(_version); + if (_version > 0) + { + BOOST_CHECK_THROW(p2pMsg->decode(ref(invalidMsgBytes)), std::out_of_range); + } + else + { + BOOST_CHECK_EQUAL( + p2pMsg->decode(ref(invalidMsgBytes)), MessageDecodeStatus::MESSAGE_INCOMPLETE); + } +} + +BOOST_AUTO_TEST_CASE(test_P2PMessage) +{ + auto factory = std::make_shared(); + testP2PMessage(factory); +} + +BOOST_AUTO_TEST_CASE(test_P2PMessageV2) +{ + auto factory = std::make_shared(); + testP2PMessage(factory, 1); +} + +void test_P2PMessageWithoutOptions(std::shared_ptr factory, uint32_t _version = 0) +{ + // default P2PMessage object + auto encodeMsg = std::static_pointer_cast(factory->buildMessage()); + encodeMsg->setVersion(_version); + uint32_t seq = 0x12345678; + uint16_t packetType = 0x4321; + uint16_t ext = 0x1101; + auto payload = std::make_shared(10000, 'a'); + + auto version = encodeMsg->version(); + int16_t headerLen = 14; + if (version > 0) + { + headerLen = 20; + } + + encodeMsg->setSeq(seq); + encodeMsg->setPacketType(packetType); + encodeMsg->setExt(ext); + encodeMsg->setPayload(payload); + + auto buffer = std::make_shared(); + auto r = encodeMsg->encode(*buffer.get()); + BOOST_CHECK_EQUAL(r, true); + + // decode default + auto decodeMsg = std::static_pointer_cast(factory->buildMessage()); + auto ret = decodeMsg->decode(bytesConstRef(buffer->data(), buffer->size())); + BOOST_CHECK_EQUAL(ret, headerLen + payload->size()); + BOOST_CHECK_EQUAL(decodeMsg->length(), headerLen + payload->size()); + BOOST_CHECK_EQUAL(decodeMsg->packetType(), packetType); + BOOST_CHECK_EQUAL(decodeMsg->seq(), seq); + BOOST_CHECK_EQUAL(decodeMsg->ext(), ext); + BOOST_CHECK_EQUAL(decodeMsg->payload()->size(), payload->size()); + + // test invalid message + std::string invalidMessage = + "GET / HTTP/1.1\r\nHost: 127.0.0.1:20200\r\nUpgrade: websocket\r\nConnection: " + "upgrade\r\nSec-WebSocket-Key: lkBb9dFFu4tuMNJyXAWIfQ==\r\nSec-WebSocket-Version: " + "13\r\n\r\n"; + auto invalidMsgBytes = asBytes(invalidMessage); + auto p2pMsg = std::static_pointer_cast(factory->buildMessage()); + p2pMsg->setVersion(_version); + if (_version > 0) + { + BOOST_CHECK_THROW(p2pMsg->decode(ref(invalidMsgBytes)), std::out_of_range); + } + else + { + BOOST_CHECK_EQUAL( + p2pMsg->decode(ref(invalidMsgBytes)), MessageDecodeStatus::MESSAGE_INCOMPLETE); + } +} + +BOOST_AUTO_TEST_CASE(test_P2PMessage_withoutOptions) +{ + auto factory = std::make_shared(); + test_P2PMessageWithoutOptions(factory); +} + +BOOST_AUTO_TEST_CASE(test_P2PMessageV2_withoutOptions) +{ + auto factory = std::make_shared(); + test_P2PMessageWithoutOptions(factory, 1); +} + + +BOOST_AUTO_TEST_CASE(test_P2PMessage_optionsCodec) +{ + { + auto options = std::make_shared(); + auto buffer = std::make_shared(); + auto r = options->encode(*buffer.get()); + BOOST_CHECK(!r); + } + + { + auto options = std::make_shared(); + std::string groupID = "group"; + options->setGroupID(groupID); + auto buffer = std::make_shared(); + auto r = options->encode(*buffer.get()); + BOOST_CHECK(!r); + } + + { + auto options = std::make_shared(); + std::string groupID = std::string(100000, 'a'); + std::string srcNodeID = "nodeID"; + + options->setGroupID(groupID); + auto srcNodeIDPtr = std::make_shared(srcNodeID.begin(), srcNodeID.end()); + options->setSrcNodeID(srcNodeIDPtr); + auto buffer = std::make_shared(); + auto r = options->encode(*buffer.get()); + BOOST_CHECK(!r); // groupID overflow + } + + { + auto options = std::make_shared(); + std::string groupID = "group"; + std::string srcNodeID = std::string(100000, 'a'); + options->setGroupID(groupID); + auto srcNodeIDPtr = std::make_shared(srcNodeID.begin(), srcNodeID.end()); + options->setSrcNodeID(srcNodeIDPtr); + auto buffer = std::make_shared(); + auto r = options->encode(*buffer.get()); + BOOST_CHECK(!r); // srcNodeID overflow + } + + { + auto options = std::make_shared(); + std::string groupID = "group"; + std::string srcNodeID = "nodeID"; + std::string dstNodeID = std::string(100000, 'a'); + + auto srcNodeIDPtr = std::make_shared(srcNodeID.begin(), srcNodeID.end()); + auto dstNodeIDPtr = std::make_shared(dstNodeID.begin(), dstNodeID.end()); + + options->setGroupID(groupID); + options->setSrcNodeID(srcNodeIDPtr); + options->dstNodeIDs().push_back(dstNodeIDPtr); + + auto buffer = std::make_shared(); + auto r = options->encode(*buffer.get()); + BOOST_CHECK(!r); // srcNodeID overflow + } + + { + auto options = std::make_shared(); + std::string groupID = "group"; + std::string srcNodeID = "nodeID"; + uint16_t moduleID = 12345; + + options->setModuleID(moduleID); + options->setGroupID(groupID); + auto srcNodeIDPtr = std::make_shared(srcNodeID.begin(), srcNodeID.end()); + options->setSrcNodeID(srcNodeIDPtr); + auto buffer = std::make_shared(); + auto r = options->encode(*buffer.get()); + BOOST_CHECK(r); + + auto decodeOptions = std::make_shared(); + auto ret = decodeOptions->decode(bytesConstRef(buffer->data(), buffer->size())); + BOOST_CHECK(ret > 0); + BOOST_CHECK_EQUAL(groupID, decodeOptions->groupID()); + BOOST_CHECK_EQUAL(moduleID, decodeOptions->moduleID()); + BOOST_CHECK_EQUAL(srcNodeID, + std::string(decodeOptions->srcNodeID()->begin(), decodeOptions->srcNodeID()->end())); + BOOST_CHECK_EQUAL(0, decodeOptions->dstNodeIDs().size()); + } + + { + auto options = std::make_shared(); + std::string groupID = "group"; + std::string srcNodeID = "nodeID"; + std::string dstNodeID = "nodeID"; + uint16_t moduleID = 11; + + auto srcNodeIDPtr = std::make_shared(srcNodeID.begin(), srcNodeID.end()); + auto dstNodeIDPtr = std::make_shared(dstNodeID.begin(), dstNodeID.end()); + + options->setModuleID(moduleID); + options->setGroupID(groupID); + options->setSrcNodeID(srcNodeIDPtr); + auto& dstNodeIDS = options->dstNodeIDs(); + dstNodeIDS.push_back(dstNodeIDPtr); + dstNodeIDS.push_back(dstNodeIDPtr); + dstNodeIDS.push_back(dstNodeIDPtr); + + auto buffer = std::make_shared(); + auto r = options->encode(*buffer.get()); + BOOST_CHECK(r); + + auto decodeOptions = std::make_shared(); + auto ret = decodeOptions->decode(bytesConstRef(buffer->data(), buffer->size())); + BOOST_CHECK(ret > 0); + BOOST_CHECK_EQUAL(groupID, decodeOptions->groupID()); + BOOST_CHECK_EQUAL(moduleID, decodeOptions->moduleID()); + BOOST_CHECK_EQUAL(srcNodeID, + std::string(decodeOptions->srcNodeID()->begin(), decodeOptions->srcNodeID()->end())); + BOOST_CHECK_EQUAL(3, decodeOptions->dstNodeIDs().size()); + for (size_t i = 0; i < 3; ++i) + { + BOOST_CHECK_EQUAL(dstNodeID, std::string(decodeOptions->dstNodeIDs()[i]->begin(), + decodeOptions->dstNodeIDs()[i]->end())); + } + } +} + +void testP2PMessageCodec(std::shared_ptr factory, uint32_t _version = 0) +{ + auto encodeMsg = std::static_pointer_cast(factory->buildMessage()); + encodeMsg->setVersion(_version); + + uint16_t version = 0x1234; + uint32_t seq = 0x12345678; + uint16_t packetType = GatewayMessageType::PeerToPeerMessage; + uint16_t ext = 0x1101; + auto payload = std::make_shared(10000, 'a'); + + encodeMsg->setVersion(version); + encodeMsg->setSeq(seq); + encodeMsg->setPacketType(packetType); + encodeMsg->setExt(ext); + encodeMsg->setPayload(payload); + + auto options = std::make_shared(); + std::string groupID = "group"; + std::string srcNodeID = "nodeID"; + std::string dstNodeID = "nodeID"; + + auto srcNodeIDPtr = std::make_shared(srcNodeID.begin(), srcNodeID.end()); + auto dstNodeIDPtr = std::make_shared(dstNodeID.begin(), dstNodeID.end()); + + options->setGroupID(groupID); + options->setSrcNodeID(srcNodeIDPtr); + auto& dstNodeIDS = options->dstNodeIDs(); + dstNodeIDS.push_back(dstNodeIDPtr); + dstNodeIDS.push_back(dstNodeIDPtr); + + encodeMsg->setOptions(options); + + auto buffer = std::make_shared(); + auto r = encodeMsg->encode(*buffer.get()); + BOOST_CHECK(r); + + auto decodeMsg = std::static_pointer_cast(factory->buildMessage()); + auto ret = decodeMsg->decode(bytesConstRef(buffer->data(), buffer->size())); + BOOST_CHECK(ret > 0); + + BOOST_CHECK_EQUAL(decodeMsg->version(), version); + BOOST_CHECK_EQUAL(decodeMsg->packetType(), packetType); + BOOST_CHECK_EQUAL(decodeMsg->seq(), seq); + BOOST_CHECK_EQUAL((decodeMsg->ext() & ext), ext); + BOOST_CHECK_EQUAL(decodeMsg->payload()->size(), payload->size()); + + auto decodeOptions = decodeMsg->options(); + BOOST_CHECK_EQUAL(groupID, decodeOptions->groupID()); + BOOST_CHECK_EQUAL(srcNodeID, + std::string(decodeOptions->srcNodeID()->begin(), decodeOptions->srcNodeID()->end())); + BOOST_CHECK_EQUAL(2, decodeOptions->dstNodeIDs().size()); + for (size_t i = 0; i < 2; ++i) + { + BOOST_CHECK_EQUAL(dstNodeID, std::string(decodeOptions->dstNodeIDs()[i]->begin(), + decodeOptions->dstNodeIDs()[i]->end())); + } +} + +BOOST_AUTO_TEST_CASE(test_P2PMessage_codec) +{ + auto factory = std::make_shared(); + testP2PMessageCodec(factory); +} + +BOOST_AUTO_TEST_CASE(test_P2PMessageV2_codec) +{ + auto factory = std::make_shared(); + testP2PMessageCodec(factory, 1); +} + +BOOST_AUTO_TEST_CASE(test_P2PMessage_compress) +{ + auto factory = std::make_shared(); + auto encodeMsg = std::static_pointer_cast(factory->buildMessage()); + auto encodeMsgWithoutCompress = std::static_pointer_cast(factory->buildMessage()); + + // only version >= V2 support p2p network compress + uint16_t version = 2; + uint32_t seq = 0x12345678; + uint16_t packetType = GatewayMessageType::PeerToPeerMessage; + uint16_t ext = 0x1101; + auto payload = std::make_shared(10000, 'a'); + auto smallPayload = std::make_shared(1, 'a'); + + encodeMsg->setVersion(version); + encodeMsg->setSeq(seq); + encodeMsg->setPacketType(packetType); + encodeMsg->setExt(ext); + encodeMsg->setPayload(payload); + + auto options = std::make_shared(); + std::string groupID = "group"; + std::string srcNodeID = "nodeID"; + std::string dstNodeID = "nodeID"; + + auto srcNodeIDPtr = std::make_shared(srcNodeID.begin(), srcNodeID.end()); + auto dstNodeIDPtr = std::make_shared(dstNodeID.begin(), dstNodeID.end()); + + options->setGroupID(groupID); + options->setSrcNodeID(srcNodeIDPtr); + auto& dstNodeIDS = options->dstNodeIDs(); + dstNodeIDS.push_back(dstNodeIDPtr); + dstNodeIDS.push_back(dstNodeIDPtr); + + encodeMsg->setOptions(options); + + // compress payload + auto compressData = std::make_shared(); + auto r = encodeMsg->tryToCompressPayload(compressData); + BOOST_CHECK(r); + BOOST_CHECK_EQUAL((encodeMsg->ext() & bcos::protocol::MessageExtFieldFlag::Compress), + bcos::protocol::MessageExtFieldFlag::Compress); + + // uncompress payload that don't compress + // size of payload smaller than 1kb, so payload don't be compressed + encodeMsg->setPayload(smallPayload); + auto buffer = std::make_shared(); + auto retWithoutCompress = encodeMsg->encode(*buffer.get()); + BOOST_CHECK(retWithoutCompress); + auto decodeMsg = std::static_pointer_cast(factory->buildMessage()); + auto ret = decodeMsg->decode(bytesConstRef(buffer->data(), buffer->size())); + BOOST_CHECK_EQUAL(ret, MessageDecodeStatus::MESSAGE_ERROR); +} + +BOOST_AUTO_TEST_CASE(test_P2PMessage_attr) +{ + auto attr = std::make_shared(); + std::string group = "group0"; + uint16_t moduleID = 1001; + attr->setGroupID(group); + attr->setModuleID(moduleID); + + BOOST_CHECK_EQUAL(attr->groupID(), group); + BOOST_CHECK_EQUAL(attr->moduleID(), moduleID); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/GatewayNodeManagerTest.cpp" "b/BFPL\345\243\271/bcos-gateway/test/unittests/GatewayNodeManagerTest.cpp" new file mode 100644 index 00000000..a3640679 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/GatewayNodeManagerTest.cpp" @@ -0,0 +1,449 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for GatewayNodeManager + * @file GatewayNodeManagerTest.cpp + * @author: octopus + * @date 2021-05-14 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::gateway; +using namespace bcos::test; +using namespace bcos::protocol; + +BOOST_FIXTURE_TEST_SUITE(GatewayNodeManagerTest, TestPromptFixture) + +class FakeGatewayNodeManager : public GatewayNodeManager +{ +public: + FakeGatewayNodeManager() : GatewayNodeManager("", nullptr, nullptr) + { + m_keyFactory = std::make_shared(); + } + ~FakeGatewayNodeManager() override {} + + bool statusChanged(std::string const& _p2pNodeID, uint32_t _seq) + { + return GatewayNodeManager::statusChanged(_p2pNodeID, _seq); + } + uint32_t statusSeq() { return GatewayNodeManager::statusSeq(); } + + bytesPointer generateNodeStatus() override { return GatewayNodeManager::generateNodeStatus(); } + void updatePeerStatus(std::string const& _p2pID, GatewayNodeStatus::Ptr _status) override + { + return GatewayNodeManager::updatePeerStatus(_p2pID, _status); + } + void setStatusSeq(std::string const& _nodeID, uint32_t _seq) { m_p2pID2Seq[_nodeID] = _seq; } + void start() override {} + void stop() override {} +}; + +inline GatewayNodeStatus::Ptr createGatewayNodeStatus( + int32_t _seq, std::string const& _uuid, std::vector _groupInfos) +{ + auto gatewayNodeStatus = std::make_shared(); + gatewayNodeStatus->setSeq(_seq); + gatewayNodeStatus->setUUID(_uuid); + gatewayNodeStatus->setGroupNodeInfos(std::move(_groupInfos)); + return gatewayNodeStatus; +} + +inline GroupNodeInfo::Ptr createGroupNodeInfo( + std::string const& _groupID, std::vector _nodeIDList) +{ + auto groupNodeInfo = std::make_shared(); + groupNodeInfo->setGroupID(_groupID); + for (auto const& nodeID : _nodeIDList) + { + auto protocolInfo = std::make_shared( + ProtocolModuleID::NodeService, ProtocolVersion::V1, ProtocolVersion::V1); + groupNodeInfo->appendProtocol(protocolInfo); + } + groupNodeInfo->setNodeIDList(std::move(_nodeIDList)); + return groupNodeInfo; +} + +class FakeGateway : public Gateway +{ +public: + FakeGateway() : Gateway() {} + ~FakeGateway() override {} + + void start() override {} + void stop() override {} +}; + +BOOST_AUTO_TEST_CASE(test_P2PMessage_statusSeqChanged) +{ + auto gatewayNodeManager = std::make_shared(); + std::string p2pID = "1"; + bool changed = false; + changed = gatewayNodeManager->statusChanged(p2pID, 1); + BOOST_CHECK(changed); +} + +BOOST_AUTO_TEST_CASE(test_GatewayNodeManager_registerFrontService) +{ + auto gatewayNodeManager = std::make_shared(); + std::string groupID = "group"; + std::string strNodeID = "nodeID"; + auto keyFactory = std::make_shared(); + + auto nodeID = + keyFactory->createKey(bytesConstRef((bcos::byte*)strNodeID.data(), strNodeID.size())); + + auto frontServiceFactory = std::make_shared(); + frontServiceFactory->setGatewayInterface(std::make_shared()); + + auto frontService = frontServiceFactory->buildFrontService(groupID, nodeID); + + bool r = false; + auto seq = gatewayNodeManager->statusSeq(); + r = gatewayNodeManager->registerNode(groupID, nodeID, bcos::protocol::NodeType::CONSENSUS_NODE, + frontService, g_BCOSConfig.protocolInfo(ProtocolModuleID::NodeService)); + BOOST_CHECK_EQUAL(r, true); + BOOST_CHECK_EQUAL(seq + 1, gatewayNodeManager->statusSeq()); + + auto s = gatewayNodeManager->localRouterTable()->getGroupFrontServiceList(groupID); + BOOST_CHECK(!s.empty()); + + seq = gatewayNodeManager->statusSeq(); + r = gatewayNodeManager->registerNode(groupID, nodeID, bcos::protocol::NodeType::CONSENSUS_NODE, + nullptr, g_BCOSConfig.protocolInfo(ProtocolModuleID::NodeService)); + BOOST_CHECK_EQUAL(r, false); + BOOST_CHECK_EQUAL(seq, gatewayNodeManager->statusSeq()); + + seq = gatewayNodeManager->statusSeq(); + r = gatewayNodeManager->unregisterNode(groupID, nodeID->hex()); + BOOST_CHECK_EQUAL(r, true); + BOOST_CHECK_EQUAL(seq + 1, gatewayNodeManager->statusSeq()); + + s = gatewayNodeManager->localRouterTable()->getGroupFrontServiceList(groupID); + BOOST_CHECK(s.empty()); + + seq = gatewayNodeManager->statusSeq(); + r = gatewayNodeManager->registerNode(groupID, nodeID, bcos::protocol::NodeType::CONSENSUS_NODE, + nullptr, g_BCOSConfig.protocolInfo(ProtocolModuleID::NodeService)); + BOOST_CHECK_EQUAL(r, true); + BOOST_CHECK_EQUAL(seq + 1, gatewayNodeManager->statusSeq()); + + s = gatewayNodeManager->localRouterTable()->getGroupFrontServiceList(groupID); + BOOST_CHECK(!s.empty()); + + seq = gatewayNodeManager->statusSeq(); + r = gatewayNodeManager->registerNode(groupID, nodeID, bcos::protocol::NodeType::CONSENSUS_NODE, + nullptr, g_BCOSConfig.protocolInfo(ProtocolModuleID::NodeService)); + BOOST_CHECK_EQUAL(r, false); + BOOST_CHECK_EQUAL(seq, gatewayNodeManager->statusSeq()); + s = gatewayNodeManager->localRouterTable()->getGroupFrontServiceList(groupID); + BOOST_CHECK(!s.empty()); +} + +BOOST_AUTO_TEST_CASE(test_GatewayNodeManager_registerFrontService_loop) +{ + auto gatewayNodeManager = std::make_shared(); + size_t loopCount = 100; + auto keyFactory = std::make_shared(); + + for (size_t i = 0; i < loopCount; i++) + { + std::string strNodeID = "nodeID" + std::to_string(i); + std::string groupID = "group" + std::to_string(i); + + auto nodeID = + keyFactory->createKey(bytesConstRef((bcos::byte*)strNodeID.data(), strNodeID.size())); + + auto seq = gatewayNodeManager->statusSeq(); + bool r = gatewayNodeManager->registerNode(groupID, nodeID, + bcos::protocol::NodeType::CONSENSUS_NODE, nullptr, + g_BCOSConfig.protocolInfo(ProtocolModuleID::NodeService)); + BOOST_CHECK_EQUAL(r, true); + BOOST_CHECK_EQUAL(seq + 1, gatewayNodeManager->statusSeq()); + + seq = gatewayNodeManager->statusSeq(); + r = gatewayNodeManager->registerNode(groupID, nodeID, + bcos::protocol::NodeType::CONSENSUS_NODE, nullptr, + g_BCOSConfig.protocolInfo(ProtocolModuleID::NodeService)); + BOOST_CHECK_EQUAL(r, false); + BOOST_CHECK_EQUAL(seq, gatewayNodeManager->statusSeq()); + + auto statusData = gatewayNodeManager->generateNodeStatus(); + BOOST_CHECK(!statusData->empty()); + + seq = gatewayNodeManager->statusSeq(); + r = gatewayNodeManager->unregisterNode(groupID, nodeID->hex()); + BOOST_CHECK_EQUAL(r, true); + BOOST_CHECK_EQUAL(seq + 1, gatewayNodeManager->statusSeq()); + + seq = gatewayNodeManager->statusSeq(); + r = gatewayNodeManager->unregisterNode(groupID, nodeID->hex()); + BOOST_CHECK_EQUAL(r, false); + BOOST_CHECK_EQUAL(seq, gatewayNodeManager->statusSeq()); + } +} + +BOOST_AUTO_TEST_CASE(test_GatewayNodeManager_onRequestNodeStatus) +{ + auto gatewayNodeManager = std::make_shared(); + auto keyFactory = std::make_shared(); + + for (size_t i = 0; i < 100; i++) + { + std::string groupID = "group" + std::to_string(i); + std::string strNodeID = "nodeID" + std::to_string(i); + + auto nodeID = + keyFactory->createKey(bytesConstRef((bcos::byte*)strNodeID.data(), strNodeID.size())); + + bool r = false; + auto seq = gatewayNodeManager->statusSeq(); + r = gatewayNodeManager->registerNode(groupID, nodeID, + bcos::protocol::NodeType::CONSENSUS_NODE, nullptr, + g_BCOSConfig.protocolInfo(ProtocolModuleID::NodeService)); + BOOST_CHECK(r); + BOOST_CHECK_EQUAL(seq + 1, gatewayNodeManager->statusSeq()); + + auto nodeStatusData = gatewayNodeManager->generateNodeStatus(); + BOOST_CHECK(!nodeStatusData->empty()); + + uint32_t statusSeq; + auto gatewayStatus = std::make_shared(); + gatewayStatus->decode(bytesConstRef(nodeStatusData->data(), nodeStatusData->size())); + BOOST_CHECK_EQUAL(seq + 1, gatewayStatus->seq()); + } +} + +BOOST_AUTO_TEST_CASE(test_GatewayNodeManager_statusEncodeDecode) +{ + auto gatewayNodeManager = std::make_shared(); + auto gatewayNodeStatus = std::make_shared(); + gatewayNodeStatus->setSeq(110); + gatewayNodeStatus->setUUID("testuuid"); + std::vector groupNodeInfos; + // group1 + auto group1Info = std::make_shared(); + group1Info->setGroupID("group1"); + std::vector nodeIDList = {"a0", "b0", "c0"}; + group1Info->setNodeIDList(std::move(nodeIDList)); + groupNodeInfos.emplace_back(group1Info); + + // group2 + auto group2Info = std::make_shared(); + group2Info->setGroupID("group2"); + std::vector nodeIDList2 = {"a1", "b1", "c1"}; + group2Info->setNodeIDList(std::move(nodeIDList2)); + groupNodeInfos.emplace_back(group2Info); + + // group3 + auto group3Info = std::make_shared(); + group3Info->setGroupID("group3"); + std::vector nodeIDList3 = {"a2", "b2", "c2"}; + group3Info->setNodeIDList(std::move(nodeIDList3)); + groupNodeInfos.emplace_back(group3Info); + + gatewayNodeStatus->setGroupNodeInfos(std::move(groupNodeInfos)); + + // encode + auto encodeData = gatewayNodeStatus->encode(); + + // decode + auto decodedStatus = std::make_shared(); + decodedStatus->decode(bytesConstRef(encodeData->data(), encodeData->size())); + + // check + BOOST_CHECK_EQUAL(decodedStatus->seq(), 110); + BOOST_CHECK_EQUAL(decodedStatus->uuid(), "testuuid"); + auto const& groupInfos = decodedStatus->groupNodeInfos(); + BOOST_CHECK(groupInfos.size() == 3); + BOOST_CHECK(groupInfos[0]->groupID() == "group1"); + BOOST_CHECK(groupInfos[0]->nodeIDList().size() == 3); + BOOST_CHECK(groupInfos[0]->nodeIDList()[0] == "a0"); + BOOST_CHECK(groupInfos[0]->nodeIDList()[1] == "b0"); + BOOST_CHECK(groupInfos[0]->nodeIDList()[2] == "c0"); + + BOOST_CHECK(groupInfos[1]->groupID() == "group2"); + BOOST_CHECK(groupInfos[1]->nodeIDList().size() == 3); + BOOST_CHECK(groupInfos[1]->nodeIDList()[0] == "a1"); + BOOST_CHECK(groupInfos[1]->nodeIDList()[1] == "b1"); + BOOST_CHECK(groupInfos[1]->nodeIDList()[2] == "c1"); + + BOOST_CHECK(groupInfos[2]->groupID() == "group3"); + BOOST_CHECK(groupInfos[2]->nodeIDList().size() == 3); + BOOST_CHECK(groupInfos[2]->nodeIDList()[0] == "a2"); + BOOST_CHECK(groupInfos[2]->nodeIDList()[1] == "b2"); + BOOST_CHECK(groupInfos[2]->nodeIDList()[2] == "c2"); +} + +BOOST_AUTO_TEST_CASE(test_GatewayNodeManager_onReceiveGroupNodeInfo) +{ + auto gatewayNodeManager = std::make_shared(); + auto gatewayNodeStatus = + createGatewayNodeStatus(110, "testUUID", std::vector()); + std::string p2pID = "xxxxxxxxxxxxxxxxxxxxx"; + + gatewayNodeManager->updatePeerStatus(p2pID, gatewayNodeStatus); + bool changed = false; + + changed = gatewayNodeManager->statusChanged(p2pID, 110); + BOOST_CHECK(!changed); + gatewayNodeManager->setStatusSeq(p2pID, 110); + + changed = gatewayNodeManager->statusChanged(p2pID, 111); + BOOST_CHECK(changed); + + changed = gatewayNodeManager->statusChanged(p2pID, 109); + BOOST_CHECK(!changed); +} + + +BOOST_AUTO_TEST_CASE(test_GatewayNodeManager_query) +{ + auto gatewayNodeManager = std::make_shared(); + std::vector groupInfos; + std::string group1 = "group1"; + auto group1Info = createGroupNodeInfo(group1, {"a0", "b0", "c0"}); + groupInfos.emplace_back(group1Info); + + std::string group2 = "group2"; + auto group2Info = createGroupNodeInfo(group2, {"a1", "b1", "c1"}); + groupInfos.emplace_back(group2Info); + + std::string group3 = "group3"; + auto group3Info = createGroupNodeInfo(group3, {"a2", "b2", "c2"}); + groupInfos.emplace_back(group3Info); + + auto status = createGatewayNodeStatus(110, "testUUID", groupInfos); + + std::string p2pID1 = "xxxxx"; + std::string p2pID2 = "yyyyy"; + std::string p2pID3 = "zzzzz"; + + gatewayNodeManager->updatePeerStatus(p2pID1, status); + + auto p2pIDs1 = gatewayNodeManager->peersRouterTable()->queryP2pIDsByGroupID(group1); + BOOST_CHECK_EQUAL(p2pIDs1.size(), 1); + BOOST_CHECK_EQUAL(*p2pIDs1.begin(), p2pID1); + + auto groupInfo = gatewayNodeManager->getGroupNodeInfoList(group1); + auto const& nodeIDList = groupInfo->nodeIDList(); + BOOST_CHECK_EQUAL(nodeIDList.size(), 3); + + auto p2pIDs2 = gatewayNodeManager->peersRouterTable()->queryP2pIDs(group1, "a0"); + BOOST_CHECK_EQUAL(p2pIDs2.size(), 1); + BOOST_CHECK_EQUAL(*p2pIDs2.begin(), p2pID1); + + gatewayNodeManager->updatePeerStatus(p2pID2, status); + + auto p2pIDs3 = gatewayNodeManager->peersRouterTable()->queryP2pIDsByGroupID(group2); + BOOST_CHECK_EQUAL(p2pIDs3.size(), 2); + + auto p2pIDs4 = gatewayNodeManager->peersRouterTable()->queryP2pIDs(group2, "a1"); + BOOST_CHECK_EQUAL(p2pIDs4.size(), 2); + + gatewayNodeManager->updatePeerStatus(p2pID3, status); + + auto p2pIDs5 = gatewayNodeManager->peersRouterTable()->queryP2pIDsByGroupID(group3); + BOOST_CHECK_EQUAL(p2pIDs5.size(), 3); + + auto p2pIDs6 = gatewayNodeManager->peersRouterTable()->queryP2pIDs(group3, "a2"); + BOOST_CHECK_EQUAL(p2pIDs6.size(), 3); +} + + +BOOST_AUTO_TEST_CASE(test_GatewayNodeManager_remove) +{ + auto gatewayNodeManager = std::make_shared(); + + std::vector groupInfos; + std::string group1 = "group1"; + auto group1Info = createGroupNodeInfo(group1, {"a0", "b0", "c0"}); + groupInfos.emplace_back(group1Info); + + std::string group2 = "group2"; + auto group2Info = createGroupNodeInfo(group2, {"a1", "b1", "c1"}); + groupInfos.emplace_back(group2Info); + + std::string group3 = "group3"; + auto group3Info = createGroupNodeInfo(group3, {"a2", "b2", "c2"}); + groupInfos.emplace_back(group3Info); + + auto status = createGatewayNodeStatus(110, "testUUID", groupInfos); + + std::string p2pID1 = "xxxxx"; + std::string p2pID2 = "yyyyy"; + std::string p2pID3 = "zzzzz"; + + gatewayNodeManager->updatePeerStatus(p2pID1, status); + gatewayNodeManager->updatePeerStatus(p2pID2, status); + gatewayNodeManager->updatePeerStatus(p2pID3, status); + + { + auto p2pIDs1 = gatewayNodeManager->peersRouterTable()->queryP2pIDsByGroupID(group1); + BOOST_CHECK_EQUAL(p2pIDs1.size(), 3); + BOOST_CHECK(p2pIDs1.find(p2pID2) != p2pIDs1.end()); + BOOST_CHECK(p2pIDs1.find(p2pID3) != p2pIDs1.end()); + BOOST_CHECK(p2pIDs1.find(p2pID1) != p2pIDs1.end()); + + auto p2pIDs2 = gatewayNodeManager->peersRouterTable()->queryP2pIDs(group1, "a0"); + BOOST_CHECK_EQUAL(p2pIDs2.size(), 3); + BOOST_CHECK(p2pIDs2.find(p2pID2) != p2pIDs2.end()); + BOOST_CHECK(p2pIDs2.find(p2pID3) != p2pIDs2.end()); + BOOST_CHECK(p2pIDs2.find(p2pID1) != p2pIDs2.end()); + } + + gatewayNodeManager->onRemoveNodeIDs(p2pID1); + { + auto p2pIDs1 = gatewayNodeManager->peersRouterTable()->queryP2pIDsByGroupID(group1); + BOOST_CHECK_EQUAL(p2pIDs1.size(), 2); + BOOST_CHECK(p2pIDs1.find(p2pID2) != p2pIDs1.end()); + BOOST_CHECK(p2pIDs1.find(p2pID3) != p2pIDs1.end()); + + auto p2pIDs2 = gatewayNodeManager->peersRouterTable()->queryP2pIDs(group1, "a0"); + BOOST_CHECK_EQUAL(p2pIDs2.size(), 2); + BOOST_CHECK(p2pIDs2.find(p2pID2) != p2pIDs2.end()); + BOOST_CHECK(p2pIDs2.find(p2pID3) != p2pIDs2.end()); + } + + gatewayNodeManager->onRemoveNodeIDs(p2pID2); + { + auto p2pIDs1 = gatewayNodeManager->peersRouterTable()->queryP2pIDsByGroupID(group1); + BOOST_CHECK_EQUAL(p2pIDs1.size(), 1); + BOOST_CHECK(p2pIDs1.find(p2pID3) != p2pIDs1.end()); + + auto p2pIDs2 = gatewayNodeManager->peersRouterTable()->queryP2pIDs(group1, "a0"); + BOOST_CHECK_EQUAL(p2pIDs2.size(), 1); + BOOST_CHECK(p2pIDs2.find(p2pID3) != p2pIDs2.end()); + } + + gatewayNodeManager->onRemoveNodeIDs(p2pID3); + { + auto p2pIDs1 = gatewayNodeManager->peersRouterTable()->queryP2pIDsByGroupID(group1); + BOOST_CHECK(p2pIDs1.empty()); + + auto p2pIDs2 = gatewayNodeManager->peersRouterTable()->queryP2pIDs(group1, "a0"); + BOOST_CHECK(p2pIDs2.empty()); + } +} +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/ModuleWhiteListTest.cpp" "b/BFPL\345\243\271/bcos-gateway/test/unittests/ModuleWhiteListTest.cpp" new file mode 100644 index 00000000..0f97924f --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/ModuleWhiteListTest.cpp" @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for ModuleWhiteList + * @file GatewayFactoryTest.cpp + * @author: octopus + * @date 2021-05-17 + */ + +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace gateway; +using namespace bcos::test; + +BOOST_FIXTURE_TEST_SUITE(ModuleWhiteListTest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(test_moduleWhiteList) +{ + bcos::gateway::ratelimiter::ModuleWhiteList moduleWhiteList; + + BOOST_CHECK(!moduleWhiteList.isModuleExist(1001)); + + BOOST_CHECK(moduleWhiteList.addModuleID(1001)); + BOOST_CHECK(moduleWhiteList.isModuleExist(1001)); + + BOOST_CHECK(!moduleWhiteList.addModuleID(1001)); + BOOST_CHECK(moduleWhiteList.isModuleExist(1001)); + + moduleWhiteList.removeModuleID(1001); + BOOST_CHECK(!moduleWhiteList.isModuleExist(1001)); + BOOST_CHECK(!moduleWhiteList.removeModuleID(1001)); + BOOST_CHECK(!moduleWhiteList.isModuleExist(1001)); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/RateLimiterManagerTest.cpp" "b/BFPL\345\243\271/bcos-gateway/test/unittests/RateLimiterManagerTest.cpp" new file mode 100644 index 00000000..f4498ce5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/RateLimiterManagerTest.cpp" @@ -0,0 +1,330 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for RateLimiterManager + * @file RateLimiterManagerTest.cpp + * @author: octopus + * @date 2021-05-17 + */ + +#include "bcos-gateway/libratelimit/RateLimiterManager.h" +#include "bcos-gateway/libratelimit/DistributedRateLimiter.h" +#include "bcos-gateway/libratelimit/RateLimiterFactory.h" +#include "bcos-gateway/libratelimit/TokenBucketRateLimiter.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace gateway; +using namespace bcos::test; + +BOOST_FIXTURE_TEST_SUITE(RateLimiterManagerTest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(test_rateLimiterManager) +{ + auto gatewayFactory = std::make_shared("", ""); + bcos::gateway::GatewayConfig::RateLimiterConfig rateLimiterConfig; + bcos::gateway::GatewayConfig::RedisConfig redisConfig; + auto rateLimiterManager = gatewayFactory->buildRateLimiterManager(rateLimiterConfig, nullptr); + auto rateLimiterFactory = rateLimiterManager->rateLimiterFactory(); + + BOOST_CHECK(!rateLimiterConfig.enableRateLimit()); + BOOST_CHECK(!rateLimiterConfig.enableDistributedRatelimit); + BOOST_CHECK(rateLimiterConfig.enableDistributedRateLimitCache); + + BOOST_CHECK(!rateLimiterConfig.enableConRateLimit); + BOOST_CHECK(!rateLimiterConfig.enableGroupRateLimit); + BOOST_CHECK(!rateLimiterConfig.enableDistributedRatelimit); + + BOOST_CHECK(rateLimiterManager->getRateLimiter("192.108.0.0") == nullptr); + + BOOST_CHECK(rateLimiterManager->registerRateLimiter( + "192.108.0.0", rateLimiterFactory->buildTokenBucketRateLimiter(10))); + BOOST_CHECK(!rateLimiterManager->registerRateLimiter( + "192.108.0.0", rateLimiterFactory->buildTokenBucketRateLimiter(10))); + + BOOST_CHECK(rateLimiterManager->getRateLimiter("192.108.0.0") != nullptr); + BOOST_CHECK(rateLimiterManager->removeRateLimiter("192.108.0.0")); + BOOST_CHECK(!rateLimiterManager->removeRateLimiter("192.108.0.0")); + + BOOST_CHECK(rateLimiterManager->getRateLimiter("192.108.0.0") == nullptr); + + BOOST_CHECK(rateLimiterManager->registerRateLimiter( + "192.108.0.0", rateLimiterFactory->buildTokenBucketRateLimiter(10))); + BOOST_CHECK(rateLimiterManager->getRateLimiter("192.108.0.0") != nullptr); +} + +BOOST_AUTO_TEST_CASE(test_rateLimiterManagerDefaultConfig) +{ + auto gatewayFactory = std::make_shared("", ""); + + bcos::gateway::GatewayConfig::RateLimiterConfig rateLimiterConfig; + auto rateLimiterManager = gatewayFactory->buildRateLimiterManager(rateLimiterConfig, nullptr); + + BOOST_CHECK(!rateLimiterConfig.enableRateLimit()); + BOOST_CHECK(!rateLimiterConfig.enableConRateLimit); + BOOST_CHECK(!rateLimiterConfig.enableGroupRateLimit); + BOOST_CHECK(!rateLimiterConfig.enableDistributedRatelimit); + BOOST_CHECK(rateLimiterConfig.enableDistributedRateLimitCache); + + BOOST_CHECK(rateLimiterManager->getRateLimiter( + bcos::gateway::ratelimiter::RateLimiterManager::TOTAL_OUTGOING_KEY) == nullptr); + + BOOST_CHECK(rateLimiterManager->getGroupRateLimiter("group0") == nullptr); + BOOST_CHECK(!rateLimiterManager->removeRateLimiter(("group0"))); + + BOOST_CHECK(rateLimiterManager->getConnRateLimiter("192.108.0.1") == nullptr); + BOOST_CHECK(!rateLimiterManager->removeRateLimiter("192.108.0.1")); +} + +BOOST_AUTO_TEST_CASE(test_rateLimiterManagerConfigIPv4) +{ + std::string configIni("data/config/config_ipv4.ini"); + + boost::property_tree::ptree pt; + boost::property_tree::ini_parser::read_ini(configIni, pt); + + auto config = std::make_shared(); + config->initRateLimitConfig(pt); + + BOOST_CHECK(config->rateLimiterConfig().enableRateLimit()); + BOOST_CHECK(config->rateLimiterConfig().enableConRateLimit); + BOOST_CHECK(config->rateLimiterConfig().enableGroupRateLimit); + BOOST_CHECK(config->rateLimiterConfig().enableDistributedRatelimit); + BOOST_CHECK(config->rateLimiterConfig().enableDistributedRateLimitCache); + BOOST_CHECK_EQUAL(config->rateLimiterConfig().distributedRateLimitCachePercent, 13); + + auto rateLimiterConfig = config->rateLimiterConfig(); + auto gatewayFactory = std::make_shared("", ""); + auto rateLimiterManager = gatewayFactory->buildRateLimiterManager(rateLimiterConfig, nullptr); + + auto rateLimiterFactory = rateLimiterManager->rateLimiterFactory(); + + BOOST_CHECK(rateLimiterFactory != nullptr); + + { + /* + ; default bandwidth limit for the group + group_outgoing_bw_limit=5 + ; specify group to limit bandwidth, group_groupName=n + group_outgoing_bw_limit_group0=2.0 + group_outgoing_bw_limit_group1 = 2.0 + group_outgoing_bw_limit_group2= 2.0 + */ + + BOOST_CHECK_EQUAL(config->rateLimiterConfig().groupOutgoingBwLimit, 5 * 1024 * 1024 / 8); + BOOST_CHECK_EQUAL( + config->rateLimiterConfig().group2BwLimit.find("group0")->second, 2 * 1024 * 1024 / 8); + BOOST_CHECK_EQUAL( + config->rateLimiterConfig().group2BwLimit.find("group1")->second, 2 * 1024 * 1024 / 8); + BOOST_CHECK_EQUAL( + config->rateLimiterConfig().group2BwLimit.find("group2")->second, 2 * 1024 * 1024 / 8); + BOOST_CHECK(config->rateLimiterConfig().group2BwLimit.find("group3") == + config->rateLimiterConfig().group2BwLimit.end()); + + + BOOST_CHECK(rateLimiterManager->getGroupRateLimiter("group0") != nullptr); + BOOST_CHECK(rateLimiterManager->getGroupRateLimiter("group1") != nullptr); + BOOST_CHECK(rateLimiterManager->getGroupRateLimiter("group2") != nullptr); + BOOST_CHECK(rateLimiterManager->getGroupRateLimiter("group3") != nullptr); + BOOST_CHECK(rateLimiterManager->getGroupRateLimiter("group4") != nullptr); + + BOOST_CHECK(rateLimiterManager->removeRateLimiter("group3")); + BOOST_CHECK(!rateLimiterManager->removeRateLimiter("group3")); + BOOST_CHECK(rateLimiterManager->removeRateLimiter("group2")); + BOOST_CHECK(!rateLimiterManager->removeRateLimiter("group2")); + BOOST_CHECK(rateLimiterManager->getGroupRateLimiter("group2") != nullptr); + BOOST_CHECK(!rateLimiterManager->registerRateLimiter( + "group2", rateLimiterFactory->buildTokenBucketRateLimiter(10))); + BOOST_CHECK(rateLimiterManager->getGroupRateLimiter("group2") != nullptr); + BOOST_CHECK(rateLimiterManager->removeRateLimiter("group2")); + BOOST_CHECK(rateLimiterManager->getGroupRateLimiter("group2") != nullptr); + + { + auto rateLimiterManager = + gatewayFactory->buildRateLimiterManager(rateLimiterConfig, nullptr); + + BOOST_CHECK(rateLimiterConfig.enableGroupRateLimit); + BOOST_CHECK(rateLimiterConfig.enableConRateLimit); + BOOST_CHECK(rateLimiterConfig.enableDistributedRatelimit); + BOOST_CHECK(rateLimiterConfig.enableDistributedRateLimitCache); + + auto distributedRateLimiter0 = + std::dynamic_pointer_cast( + rateLimiterManager->getGroupRateLimiter("group0")); + + BOOST_CHECK_EQUAL( + distributedRateLimiter0->rateLimitKey(), rateLimiterFactory->toTokenKey("group0")); + BOOST_CHECK_EQUAL(distributedRateLimiter0->enableLocalCache(), true); + BOOST_CHECK_EQUAL(distributedRateLimiter0->localCachePercent(), 13); + BOOST_CHECK_EQUAL(distributedRateLimiter0->interval(), 1); + BOOST_CHECK_EQUAL(distributedRateLimiter0->maxPermits(), 2 * 1024 * 1024 / 8); + + auto distributedRateLimiter1 = + std::dynamic_pointer_cast( + rateLimiterManager->getGroupRateLimiter("group1")); + + BOOST_CHECK_EQUAL( + distributedRateLimiter1->rateLimitKey(), rateLimiterFactory->toTokenKey("group1")); + BOOST_CHECK_EQUAL(distributedRateLimiter1->enableLocalCache(), true); + BOOST_CHECK_EQUAL(distributedRateLimiter1->localCachePercent(), 13); + BOOST_CHECK_EQUAL(distributedRateLimiter1->interval(), 1); + BOOST_CHECK_EQUAL(distributedRateLimiter1->maxPermits(), 2 * 1024 * 1024 / 8); + + auto distributedRateLimiter2 = + std::dynamic_pointer_cast( + rateLimiterManager->getGroupRateLimiter("group3")); + + BOOST_CHECK_EQUAL( + distributedRateLimiter2->rateLimitKey(), rateLimiterFactory->toTokenKey("group3")); + BOOST_CHECK_EQUAL(distributedRateLimiter2->enableLocalCache(), true); + BOOST_CHECK_EQUAL(distributedRateLimiter2->localCachePercent(), 13); + BOOST_CHECK_EQUAL(distributedRateLimiter2->interval(), 1); + BOOST_CHECK_EQUAL(distributedRateLimiter2->maxPermits(), 5 * 1024 * 1024 / 8); + } + } + + { + /* + conn_outgoing_bw_limit=2 + ; specify IP to limit bandwidth, format: conn_outgoing_bw_limit_x.x.x.x=n + conn_outgoing_bw_limit_192.108.0.1=1.0 + conn_outgoing_bw_limit_192.108.0.2 =2.0 + conn_outgoing_bw_limit_192.108.0.3= 3.0 + */ + BOOST_CHECK_EQUAL(config->rateLimiterConfig().connOutgoingBwLimit, 2 * 1024 * 1024 / 8); + BOOST_CHECK_EQUAL(config->rateLimiterConfig().ip2BwLimit.find("192.108.0.1")->second, + 1 * 1024 * 1024 / 8); + BOOST_CHECK_EQUAL(config->rateLimiterConfig().ip2BwLimit.find("192.108.0.2")->second, + 2 * 1024 * 1024 / 8); + BOOST_CHECK_EQUAL(config->rateLimiterConfig().ip2BwLimit.find("192.108.0.3")->second, + 3 * 1024 * 1024 / 8); + BOOST_CHECK(config->rateLimiterConfig().ip2BwLimit.find("192.108.0.0") == + config->rateLimiterConfig().ip2BwLimit.end()); + + BOOST_CHECK(rateLimiterManager->getConnRateLimiter("192.108.0.1") != nullptr); + BOOST_CHECK(rateLimiterManager->getConnRateLimiter("192.108.0.2") != nullptr); + BOOST_CHECK(rateLimiterManager->getConnRateLimiter("192.108.0.3") != nullptr); + BOOST_CHECK(rateLimiterManager->getConnRateLimiter("192.108.0.0") != nullptr); + + BOOST_CHECK(!rateLimiterManager->registerRateLimiter( + "192.108.0.0", rateLimiterFactory->buildTokenBucketRateLimiter(10))); + BOOST_CHECK(rateLimiterManager->getConnRateLimiter("192.108.0.0") != nullptr); + + BOOST_CHECK(!rateLimiterManager->registerRateLimiter( + "192.108.0.0", rateLimiterFactory->buildTokenBucketRateLimiter(10))); + + BOOST_CHECK(rateLimiterManager->removeRateLimiter("192.108.0.2")); + BOOST_CHECK(rateLimiterManager->getConnRateLimiter("192.108.0.2") != nullptr); + BOOST_CHECK(rateLimiterManager->removeRateLimiter("192.108.0.2")); + + BOOST_CHECK(rateLimiterManager->registerRateLimiter( + "192.108.0.2", rateLimiterFactory->buildTokenBucketRateLimiter(10))); + BOOST_CHECK(rateLimiterManager->getConnRateLimiter("192.108.0.2") != nullptr); + + { + // rate limiter factory + auto rateLimiterManager = + gatewayFactory->buildRateLimiterManager(rateLimiterConfig, nullptr); + + BOOST_CHECK(rateLimiterConfig.enableGroupRateLimit); + BOOST_CHECK(rateLimiterConfig.enableConRateLimit); + BOOST_CHECK(rateLimiterConfig.enableDistributedRatelimit); + BOOST_CHECK(rateLimiterConfig.enableDistributedRateLimitCache); + + auto rateLimiter0 = std::dynamic_pointer_cast( + rateLimiterManager->getConnRateLimiter("192.108.0.1")); + + BOOST_CHECK_EQUAL(rateLimiter0->maxQPS(), 1 * 1024 * 1024 / 8); + + auto rateLimiter1 = std::dynamic_pointer_cast( + rateLimiterManager->getConnRateLimiter("192.108.0.2")); + + BOOST_CHECK_EQUAL(rateLimiter1->maxQPS(), 2 * 1024 * 1024 / 8); + + auto rateLimiter2 = std::dynamic_pointer_cast( + rateLimiterManager->getConnRateLimiter("192.108.0.3")); + + BOOST_CHECK_EQUAL(rateLimiter2->maxQPS(), 3 * 1024 * 1024 / 8); + + auto rateLimiter3 = std::dynamic_pointer_cast( + rateLimiterManager->getConnRateLimiter("192.108.0.4")); + + BOOST_CHECK_EQUAL(rateLimiter3->maxQPS(), 2 * 1024 * 1024 / 8); + } + } +} + +BOOST_AUTO_TEST_CASE(test_rateLimiterManagerConfigIPv6) +{ + std::string configIni("data/config/config_ipv6.ini"); + + boost::property_tree::ptree pt; + boost::property_tree::ini_parser::read_ini(configIni, pt); + + auto config = std::make_shared(); + config->initRateLimitConfig(pt); + + auto rateLimiterConfig = config->rateLimiterConfig(); + auto gatewayFactory = std::make_shared("", ""); + auto rateLimiterManager = gatewayFactory->buildRateLimiterManager(rateLimiterConfig, nullptr); + + auto rateLimiterFactory = rateLimiterManager->rateLimiterFactory(); + + BOOST_CHECK(rateLimiterFactory != nullptr); + + BOOST_CHECK(rateLimiterConfig.totalOutgoingBwLimit > 0); + BOOST_CHECK(rateLimiterConfig.connOutgoingBwLimit > 0); + BOOST_CHECK(rateLimiterConfig.groupOutgoingBwLimit > 0); + + BOOST_CHECK(rateLimiterConfig.enableRateLimit()); + BOOST_CHECK(rateLimiterConfig.enableConRateLimit); + BOOST_CHECK(rateLimiterConfig.enableGroupRateLimit); + BOOST_CHECK(!rateLimiterConfig.enableDistributedRatelimit); + BOOST_CHECK_EQUAL(rateLimiterConfig.enableDistributedRateLimitCache, true); + BOOST_CHECK_EQUAL(rateLimiterConfig.distributedRateLimitCachePercent, 20); + + { + // rate limiter manager + auto rateLimiterManager = + gatewayFactory->buildRateLimiterManager(rateLimiterConfig, nullptr); + + auto tokenBucketRateLimiter0 = + std::dynamic_pointer_cast( + rateLimiterManager->getGroupRateLimiter("group0")); + + BOOST_CHECK_EQUAL(tokenBucketRateLimiter0->maxQPS(), 1024 * 1024 / 8); + + auto tokenBucketRateLimiter1 = + std::dynamic_pointer_cast( + rateLimiterManager->getConnRateLimiter("127.0.0.1")); + + BOOST_CHECK_EQUAL(tokenBucketRateLimiter1->maxQPS(), 2 * 1024 * 1024 / 8); + + auto tokenBucketRateLimiter2 = + std::dynamic_pointer_cast( + rateLimiterManager->getRateLimiter( + bcos::gateway::ratelimiter::RateLimiterManager::TOTAL_OUTGOING_KEY)); + + BOOST_CHECK_EQUAL(tokenBucketRateLimiter2->maxQPS(), 3 * 1024 * 1024 / 8); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/amop/AMOPMessageTest.cpp" "b/BFPL\345\243\271/bcos-gateway/test/unittests/amop/AMOPMessageTest.cpp" new file mode 100644 index 00000000..c290edc9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/amop/AMOPMessageTest.cpp" @@ -0,0 +1,104 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for AMOPMessage + * @file AMOPMessageTest.cpp + * @author: octopus + * @date 2021-06-21 + */ + +#include +#include +#include + +using namespace bcos; +using namespace bcos::amop; +using namespace bcos::test; + +BOOST_FIXTURE_TEST_SUITE(AMOPMessageTest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(test_initAMOPMessage) +{ + auto messageFactory = std::make_shared(); + { + auto message = messageFactory->buildMessage(); + auto buffer = std::make_shared(); + message->encode(*buffer.get()); + BOOST_CHECK_EQUAL(buffer->size(), AMOPMessage::HEADER_LENGTH); + + auto decodeMessage = messageFactory->buildMessage(); + auto r = decodeMessage->decode(bytesConstRef(buffer->data(), buffer->size())); + BOOST_CHECK(r > 0); + BOOST_CHECK_EQUAL(decodeMessage->type(), 0); + BOOST_CHECK_EQUAL(decodeMessage->data().size(), 0); + } + + { + uint16_t type = 111; + std::string topic = "topic"; + std::string data = "Hello, FISCO-BCOS 3.0"; + auto message = messageFactory->buildMessage(); + message->setType(type); + message->setData(data); + auto buffer = std::make_shared(); + message->encode(*buffer.get()); + + auto decodeMessage = messageFactory->buildMessage(); + auto r = decodeMessage->decode(bytesConstRef(buffer->data(), buffer->size())); + BOOST_CHECK(r > 0); + BOOST_CHECK_EQUAL(decodeMessage->type(), type); + BOOST_CHECK_EQUAL( + data, std::string(decodeMessage->data().begin(), decodeMessage->data().end())); + } + + { + uint16_t type = 1234; + std::string topic(65535, '1'); + std::string data(10000, '1'); + auto message = messageFactory->buildMessage(); + message->setType(type); + message->setData(bytesConstRef((byte*)data.data(), data.size())); + auto buffer = std::make_shared(); + message->encode(*buffer.get()); + + auto decodeMessage = messageFactory->buildMessage(); + auto r = decodeMessage->decode(bytesConstRef(buffer->data(), buffer->size())); + BOOST_CHECK(r > 0); + BOOST_CHECK_EQUAL(decodeMessage->type(), type); + BOOST_CHECK_EQUAL( + data, std::string(decodeMessage->data().begin(), decodeMessage->data().end())); + } + + { + auto decodeMessage = messageFactory->buildMessage(); + auto r = decodeMessage->decode(bytesConstRef("")); + BOOST_CHECK(r < 0); + } +} + +BOOST_AUTO_TEST_CASE(test_AMOPMessageTopicOverflow) +{ + auto messageFactory = std::make_shared(); + + uint16_t type = 1234; + std::string data(10000, '1'); + auto message = messageFactory->buildMessage(); + message->setType(type); + message->setData(bytesConstRef((byte*)data.data(), data.size())); + auto buffer = std::make_shared(); + auto r = message->encode(*buffer.get()); + BOOST_CHECK(r); +} +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/amop/TopicManagerTest.cpp" "b/BFPL\345\243\271/bcos-gateway/test/unittests/amop/TopicManagerTest.cpp" new file mode 100644 index 00000000..9af23ce5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/amop/TopicManagerTest.cpp" @@ -0,0 +1,139 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for TopicManager + * @file TopicManagerTest.cpp + * @author: octopus + * @date 2021-06-21 + */ +#include "bcos-gateway/libamop/AirTopicManager.h" +#include +#include +#include + +using namespace bcos; +using namespace bcos::amop; +using namespace bcos::test; + +BOOST_FIXTURE_TEST_SUITE(TopicManagerTest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(test_initTopicManager) +{ + auto topicManager = std::make_shared("", nullptr); + { + auto jsonValue = topicManager->queryTopicsSubByClient(); + + uint32_t topicSeq; + TopicItems topicItems; + auto b = topicManager->parseTopicItemsJson(topicSeq, topicItems, jsonValue); + BOOST_CHECK(b); + BOOST_CHECK(topicSeq == 1); + BOOST_CHECK(topicItems.empty()); + } + + { + auto seq = topicManager->topicSeq(); + BOOST_CHECK(seq == 1); + topicManager->incTopicSeq(); + BOOST_CHECK(topicManager->topicSeq() == (seq + 1)); + topicManager->incTopicSeq(); + BOOST_CHECK(topicManager->topicSeq() == (seq + 2)); + } +} + +BOOST_AUTO_TEST_CASE(test_parseTopicItemsJson) +{ + auto topicManager = std::make_shared("", nullptr); + { + uint32_t topicSeq; + TopicItems topicItems; + std::string json = ""; + auto r = topicManager->parseTopicItemsJson(topicSeq, topicItems, json); + BOOST_CHECK(!r); + } + { + uint32_t topicSeq; + TopicItems topicItems; + std::string json = "{\"topicSeq\":111,\"topicItems\":[]}"; + auto r = topicManager->parseTopicItemsJson(topicSeq, topicItems, json); + BOOST_CHECK(r); + BOOST_CHECK(topicSeq == 111); + BOOST_CHECK(topicItems.size() == 0); + } + { + uint32_t topicSeq; + TopicItems topicItems; + std::string json = "{\"topicSeq\":123,\"topicItems\":[\"a\",\"b\",\"c\"]}"; + auto r = topicManager->parseTopicItemsJson(topicSeq, topicItems, json); + BOOST_CHECK(r); + BOOST_CHECK(topicSeq == 123); + BOOST_CHECK(topicItems.size() == 3); + auto a = std::string("a"); + BOOST_CHECK(std::find_if(topicItems.begin(), topicItems.end(), + [a](const TopicItem& _topicItem) { return _topicItem.topicName() == a; }) != + topicItems.end()); + auto b = std::string("b"); + BOOST_CHECK(std::find_if(topicItems.begin(), topicItems.end(), + [b](const TopicItem& _topicItem) { return _topicItem.topicName() == b; }) != + topicItems.end()); + auto c = std::string("c"); + BOOST_CHECK(std::find_if(topicItems.begin(), topicItems.end(), + [c](const TopicItem& _topicItem) { return _topicItem.topicName() == c; }) != + topicItems.end()); + } +} + +BOOST_AUTO_TEST_CASE(test_subTopics) +{ + auto topicManager = std::make_shared("", nullptr); + + std::string clientID = "client"; + { + TopicItems topicItems; + auto r = topicManager->queryTopicItemsByClient(clientID, topicItems); + BOOST_CHECK(!r); + BOOST_CHECK(topicItems.empty()); + } + + + { + TopicItems topicItems; + std::vector topics{"topic0", "topic1", "topic2", "topic3"}; + for (const auto& topic : topics) + { + topicItems.insert(TopicItem(topic)); + } + auto seq = topicManager->topicSeq(); + // sub topics + topicManager->subTopic(clientID, topicItems); + topicItems.clear(); + auto r = topicManager->queryTopicItemsByClient(clientID, topicItems); + BOOST_CHECK(r); + BOOST_CHECK(topicItems.size() == topics.size()); + BOOST_CHECK(topicManager->topicSeq() == (seq + 1)); + + auto jsonValue = topicManager->queryTopicsSubByClient(); + BOOST_CHECK(!jsonValue.empty()); + + uint32_t topicSeqFromJson; + TopicItems topicItemsFromJson; + auto b = topicManager->parseTopicItemsJson(topicSeqFromJson, topicItemsFromJson, jsonValue); + BOOST_CHECK(b); + BOOST_CHECK(topicSeqFromJson = topicManager->topicSeq()); + BOOST_CHECK(topicItemsFromJson.size() == topicItems.size()); + } +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/data/ca/ca.crt" "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/ca/ca.crt" new file mode 100644 index 00000000..0fd06e32 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/ca/ca.crt" @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFVTCCAz2gAwIBAgIUQ6VUY9KboNNT74eeimEXBT/WoF4wDQYJKoZIhvcNAQEL +BQAwOjETMBEGA1UEAwwKRklTQ08tQkNPUzETMBEGA1UECgwKRklTQ08tQkNPUzEO +MAwGA1UECwwFY2hhaW4wHhcNMjEwNTIwMTE0NzI1WhcNMzEwNTE4MTE0NzI1WjA6 +MRMwEQYDVQQDDApGSVNDTy1CQ09TMRMwEQYDVQQKDApGSVNDTy1CQ09TMQ4wDAYD +VQQLDAVjaGFpbjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAO4KcrDe +Zo7xwM8fw0x73At+7x49xm2B35/PLKcZGteSOP/+0FhUXuDWr+TuFtCYtyRTlvlj +WeCu2W3RsaQ80iBEU/GZ/mD9eDcoI8J/AiYN049BfXi+AEtM6geLKdaQWT7QAsyP +vTNu5x7wIzA6K55q1m7oGFEvIcxIJqifC8Nx5EuNo2znOPEZD2n+4lWQCRV8wOWD +huhtx9j6Caek291b3FdesIh+JsrVvly3rQU1wk0Vw6/e9GV5nJ4nVgTCXuitsOnL ++VAP2p7UwHQ02YOFR3S7p+kDImNjWhNdRjmUObkdRy9B/SryVplEys1kD3hQ5S6K +q9Ygze8KLPx1QkhxQs6Tog86s0Zg+po5jEISwH7rty6L56RZMd4a/6YvCiaAWT7l +RrTKEKBBtjX+uD9Oo/FQfpjNNRYZHp+Bl1a0eQ9z9poD3OPtp2cDHCVtBUUAWk3g +QI3PmO2RQ+Te90jWrWv4dOdh5bccSgWISXofwcOTm+aDjUjFd+EEzpdk7TcXZy7Z +/LQEdCH4g9BPR/0Cytf4DK3fLMw3iscxD+5hQWoyfL7I01RVA47hckNU1wsR57bS +6UjIoITMiMTZU9Wn34mz8bQO9rV6lirKCm7W7+7nCDdaCowJWBDG+9hLaDWVxodq +Thn/Zxj3jZcu47DQgpeGs5Ob0gDWFkzr3N4NAgMBAAGjUzBRMB0GA1UdDgQWBBT9 +CvGcCNbgVQoB/JwjWwU8wEjbJTAfBgNVHSMEGDAWgBT9CvGcCNbgVQoB/JwjWwU8 +wEjbJTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQDGmlAsQaiM ++zLMadnPtbXs0Pjy0jamOrQ0pkfZmpSWLQ6hX6PPDw8evdqHs3U5nmdqAmizf9FO +OREaNQeR/UYexiH+98MBEXDXRcoXCnH5CpcANlI5tY90WG+lLXwNIjSZPz8jkcAu +7mnEUywGCHVrJgTLULD6Xr2piQycb5rz7Ljrt1v+GsMci4Jl6pX4wCIpEgdmXOMf +87iSSgaK2ub/hnTnlWBvuIOz8IdlrOzK129JO4Eao3pHTH4J3hRreXb5eKWP9vUI +6vz7ZqOUrYwo56pdP+u6i830qpM14VWYxHjJWWUdReM0Cfs5kn0yhHQDHxTT3pcB +wk1ACM6MJW0gpGuj3XfDFMAtPAj4ncvn237NKfTM4daDY/x+vPmyyclMFO6F8Qvq +S7B2mA9m/fcOCZqg1JTC8kND8+dv81B4DsX732XenFc80pSc5kh32RWzG4kRzDcu +2dIFWwdzLXrmYEl3HleG6daE3BX/X0TOzfTgneRTtZcOSQsbODf1wQkKpmDSOx+k +qQP5iZMWNDI45Ba/NBBxcdcL+hOzwlDzNcPf6OtDWnFFr8uaaK9MTuSQG3Z6kRpA +/E3mnheLTBsEmsY62NTytQBn2Ye2DQ1rnPztrGkgQfSfPheY1fHTNMJjiakmtiKy +g7b+mYIq6Wt4WDum5EGP9s+3nAFeYajNqg== +-----END CERTIFICATE----- diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/data/ca/node.crt" "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/ca/node.crt" new file mode 100644 index 00000000..d56e1456 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/ca/node.crt" @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFEzCCAvugAwIBAgIUOXkrcknudy8U2eev1i08W0WihEEwDQYJKoZIhvcNAQEL +BQAwOjETMBEGA1UEAwwKRklTQ08tQkNPUzETMBEGA1UECgwKRklTQ08tQkNPUzEO +MAwGA1UECwwFY2hhaW4wHhcNMjEwNTIwMTE0NzMwWhcNMzEwNTE4MTE0NzMwWjA7 +MRMwEQYDVQQDDApGSVNDTy1CQ09TMRMwEQYDVQQKDApmaXNjby1iY29zMQ8wDQYD +VQQLDAZhZ2VuY3kwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDPYhXC +lguLP3xQosIqs0RfQb/xYxdAOiKHNoEYkko/lnRyqTQt+8TXl6oNefMFs4bn3C02 +3jSF8LSRWNQKLqgnLFqo/VpW5zHZOLvhQNps+QRE0koJqsyxdIC011D7NYUf+U1R +Vl5JRcwQ2MDvj84GbdkfI5vrSWVCJUAIkElzaqNwrn5i16aHvEM9WtPzT5gG/qDw +E4iX9LmVwOu4phHZF6xBra6D2bmfOY5HQFM++TBQFwzUZXiVEHRzIuXkr6OfQKE1 +xCXdrN26J9MDwINa27PKZJz24Jt/e3J/JfMp77ThLjoJnd9vnmw5DjIz79QTfF6Z +Y61IztTzw0ZmEsoPCAeqJUZ2XnhbT//a/o31kZLthkITbod37cPiUc+2tpV5KlU5 +8JOX7LqtEdqccoJZ2nhOSXRu1VqFn02TMfORErSnVZSJCT5/5ZKhAi9I//uZ2pnS +0qeQMjX7zxioMlkspT4QjtaIR3lnYW5siUMbegZyjpuIPy4Zr2lFT5bhoAkGF+J1 +Pq0xK15I1SSF9PZpz2+juswfnXButhrXunHF8ABe0q7kr4kpochVo59SzFWp6qgk +iwkqxJLeZ2avAr5iTVRrsjvxwts1IeDuGsHSvOAYzICWU00MMFW5tiN0LIgtnwhp +4v+l5YvXCYx+xuwhMalc8ae/UiXKFg6g8LwwdwIDAQABoxAwDjAMBgNVHRMEBTAD +AQH/MA0GCSqGSIb3DQEBCwUAA4ICAQC4pqWoKp3XO/+Xh5FPYxvIGd4FsDiCoDTJ ++k8GCyu8gsHZ79bMJKUo/J2UpackKoDAB3nVUueuGj4ZmTJVNeU8a1oBl8RPkMet +pniHQQ2zK5FEac3mG89N75uKmRQUfudaTzL2h89N3EmeaVKVdAggYrqUU5FrSFG3 +3Lp/r4pFsSGJaq5ylX5gQ4u9mP+BS4LNgKWjPiBsSIiubiZjzcvMMI+ObEeIX/zS +lpRrpIH1aeEgbumTfNR+g0jveC9wGDOKcIH75kC5rwCXSi6VvgHEXxdo5+Ika129 +yu9AXMSYuH0Ed89fX53UrwBCKQiogJoG+j8qPRoWJJFDKt+nzvyrpda+8LooDbi1 +/zOska0TmzWUrhTi6erpKn3suY1u1sFy0OD9fhL87K53Y3tr47AadkwIGyUiened +CbIHWxhdRMCGKQTrMSInDqeQHP/U7Z29kYiru/oDZDrfHIXgFo4Q07aGftlGG5Wb +ZI/XrLZKkX1U8FmDs/9A7nYHV71uVZXA6I95TGIKmdpT4WOwXAVbG3KGcBIxrV07 +Pq3ClLwg4sLXadGoYRKW+P5PNqbLzbdSf3GPhZehChRDGJCCOhpRTlzB67kKtyUM +UPLjH2rZHeGnFb9MyOBUpazZMIH0knHJieD6sZSPwVL7ukehsNMA22aqOvL71fY/ +baXis2i1KA== +-----END CERTIFICATE----- diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/data/ca/node.key" "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/ca/node.key" new file mode 100644 index 00000000..9b2f75ef --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/ca/node.key" @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDPYhXClguLP3xQ +osIqs0RfQb/xYxdAOiKHNoEYkko/lnRyqTQt+8TXl6oNefMFs4bn3C023jSF8LSR +WNQKLqgnLFqo/VpW5zHZOLvhQNps+QRE0koJqsyxdIC011D7NYUf+U1RVl5JRcwQ +2MDvj84GbdkfI5vrSWVCJUAIkElzaqNwrn5i16aHvEM9WtPzT5gG/qDwE4iX9LmV +wOu4phHZF6xBra6D2bmfOY5HQFM++TBQFwzUZXiVEHRzIuXkr6OfQKE1xCXdrN26 +J9MDwINa27PKZJz24Jt/e3J/JfMp77ThLjoJnd9vnmw5DjIz79QTfF6ZY61IztTz +w0ZmEsoPCAeqJUZ2XnhbT//a/o31kZLthkITbod37cPiUc+2tpV5KlU58JOX7Lqt +EdqccoJZ2nhOSXRu1VqFn02TMfORErSnVZSJCT5/5ZKhAi9I//uZ2pnS0qeQMjX7 +zxioMlkspT4QjtaIR3lnYW5siUMbegZyjpuIPy4Zr2lFT5bhoAkGF+J1Pq0xK15I +1SSF9PZpz2+juswfnXButhrXunHF8ABe0q7kr4kpochVo59SzFWp6qgkiwkqxJLe +Z2avAr5iTVRrsjvxwts1IeDuGsHSvOAYzICWU00MMFW5tiN0LIgtnwhp4v+l5YvX +CYx+xuwhMalc8ae/UiXKFg6g8LwwdwIDAQABAoICAQCRUTbUQlXWfmb7DgGm8DUx +1p3MZNYvEE2Pl9bARAo5IpF4oy5IJorZU5I6nUB4t0MKB5O7RdtiG7g7vRXgCK+V +u5PPpLCAAsNAZmvovIwSHjAqlXyB56hFlNB3aAV78hXVwNi37SBmkb0b9PbFJ2yD +vseM9WPVbHnC7t0+4vRFJu1eETfIxToRFQ+81397mEN8a0KU1+s4J6k/0Y6bter9 +x4PSBgUqqQ9UDn2vWdi7YNvE01IPJwdm1L/0yfhz1ct+1UfhqA+slOxN/If1shmZ +Oihy4yvGJr9vhi5GAG6y7SA/RigvAuxFh6poFJWDJUPjX5veOiV7xkscgLI85l+l +AppIGZR792E+OGjnEdz8HdhBv/FOMehls8M+SUhpfRO8hE1/UCXH/bBnV+9AAHo9 +piHvan9H23dNSsBNhiLpO6Neks1pUFPdrSCXcpCTEXUCOlJ/NVDPZ+nKsu/Uh3NJ +4UusLG51GBHWM0MR5SUYFTF6WUqhA16HrSHS2wIpUDJpCgLLPBw7Fp3eD7Dn5Rd2 +mrWAbbbZcx2sDcG91muJaFHgou/lyuFbOQp88SntJqgT7JttT2Uz8U4TZFPnY38R +nfX6VUM+sRCk7MHnJM3W/v5mOKoxoiechahsUnZ3XRB5+kIbsk038VvKZxZhwBqR +R1k8GiPPlKCqFSYTfj92AQKCAQEA+73ORrbo2ihUD8i91NxxCvspvQSez8PSe59B +5bGS+vP0hGbbMKTZPo5o33seri3ISsf5GTRF2tKgyfbjkt3qfNM+8HAKZ00I0AE9 +R0HUjOpBp9ihmjxSAq8CzopdqNN8hHBvv+cIibeVrizJly5OKRhHq7+NaRega/Ac +rc2SuCUEkyGNpOiHWigpTsxBdcXo3QQk+ySrfodvDNnh5nf6BkP48HgcgKSBhCOT +Zv1uGlJybUfqeMPJHy+cteV8nbEbZMLLrZEusA/hVozjqA5eBb3ECNE9hOnXPh/0 +hHOW20Zjd43ODhUXP7e+o0ObqaZ0Ov95ilAmP+506431N0DTdwKCAQEA0uQuReu4 +Jtk7OWtSjTctwkQrzloDYKqB2fsBvWhQ2FPT85Q1okOqr/oUG9wTxLVDloaIIFTW +9MV9CPM2VWGL0DyyB0GPSQRbd80W/AhXzTxTeB1eu2drmmELrD+80zUmkTu0xpqI +zARmaXKnEz6K/+xnJ2YD3feYk+OQ1oQTfBYDjncfyX1E3O3q9ZGA9sc8UVZPoaDH +qqyvys1yXM/nnoznL7Prwtz5cj0tmsavAO4gABdt5LOpLQLEuIycCpXlu5cPAZNV +GkG2zTPJenMgciK+D8q9Etu8Zo0Z7YihvqzU+GbCDGwolfFI9F1L5um4qM5+bJl2 +eRquXYxBt5TLAQKCAQEAql+A9XbhDJRyn+QaJa+zid0GGHjCCpbbIvNbo9qUQOdO +OzVpbviCVsYG0AkBcJxni8TfH2GzTS9zxnwi9Mjf4+8MD6mkQNlv92Z/VHSHJ397 +Q99nL31Xe516ZtJaJOJMyU1XNCdmLd5jnOeO35RlLYbTKrePOurUlXiB0Fbqz1mu +SO1ScaM1x5yaqEuwmcaBnOMrLBVbQ1zhmW70ZggY3JiwJ/8CO0YaqZVyMyedlo7q +Bm+/jk/jFAojIy/XMNomUgFL24IAeQOmW+8qPBjNJVGTFOyXmBayp8b0s5ePJ4Px +2X3NUNaRT3xJtzEQbrbKvwsb9LHd0TLPSoReyzBCbQKCAQEAmRDS4R8AjvnWeYuC +5EorZTfzj5dXoj6/dsYvchkXrJvTV5S4BOkWJxncpIfstTZXMxa8ELNjPU9lvCxC +wF/HicGz+X5FEFsgRGjQCOfJSoZBkwnGK0EaIXfUcBXm6GlIb9slD400QtfiuSBl +UZtwaeZczITHw8CktppSEtDUD5kuxaWCpczNQYlRoyETuInNJr/9ljNLGH60LP9G +xUSFOVfNqJrvQIUAbEEpK5CPjp5HDanzsi4QWUIMJGKyEyDPGIPAeYVFHISbuH6g ++sY6w3yh9HZTGy/vo4NAUV58/xcUkKKMr1WFc6coK2zX3WbAB42wxwPvsGCENBPL +0wIlAQKCAQAImv0u0+BlBEQwkCbzkOQ60MjAQ7CncElzB+WwjcDZhfoolYyu3FTn +F4Hlr2O4U5GreAoQiHSYVa9ciQBMDt7zTovt1Fo7/KWqQTEKzgosCz2pnaSNmFYS +sNu2bWHcino3o/SY+h9ZTCsaVjiIZCi1pOanH569QXVw1xzt/95iq4b9BOl0vbZ1 +pvB5Ejhvw8wgKG8FCHw8ClFCqPPTsnYYzyhVzFOUQyvQu4fimrleaoPIyB+wzeHe +AyAmitHYInNRMzrfiqIxeOhlhjK3jpyjhPESsdrqoqe5aJrl7Ga0K3prESDGv7eU +489TEvuAazj7512RODaWSseV5TrJ27Qt +-----END PRIVATE KEY----- diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/data/config/config_ipv4.ini" "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/config/config_ipv4.ini" new file mode 100644 index 00000000..b70a523e --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/config/config_ipv4.ini" @@ -0,0 +1,57 @@ +[p2p] + ; ssl or sm ssl + sm_ssl=false + listen_ip=127.0.0.1 + listen_port=12345 + nodes_path=data/config/json/ + nodes_file=nodes_ipv4.json + +[cert] + ; directory the certificates located in + ca_path=data/ca/ + ; the ca certificate file + ca_cert=ca.crt + ; the node private key file + node_key=node.key + ; the node certificate file + node_cert=node.crt + +[redis] + server_ip=127.127.127.127 + server_port=12345 + request_timeout=54321 + connection_pool_size=111 + password=abc + db=12 + + [flow_control] + enable_distributed_ratelimit=true + enable_distributed_ratelimit_cache=true + distributed_ratelimit_cache_percent=13 + stat_reporter_interval=12345 + + ; the module that does not limit bandwidth + ; list of all modules: raft,pbft,amop,block_sync,txs_sync + ; + modules_without_bw_limit=raft,pbft,txs_sync,amop + + ; restrict the outgoing bandwidth of the node + ; both integer and decimal is support, unit: Mb + ; + total_outgoing_bw_limit=10 + + ; restrict the outgoing bandwidth of the the connection + ; both integer and decimal is support, unit: Mb + ; + conn_outgoing_bw_limit=2 + ; specify IP to limit bandwidth, format: conn_outgoing_bw_limit_x.x.x.x=n + conn_outgoing_bw_limit_192.108.0.1=1.0 + conn_outgoing_bw_limit_192.108.0.2 =2.0 + conn_outgoing_bw_limit_192.108.0.3= 3.0 + + ; default bandwidth limit for the group + group_outgoing_bw_limit=5 + ; specify group to limit bandwidth, group_groupName=n + group_outgoing_bw_limit_group0=2.0 + group_outgoing_bw_limit_group1 = 2.0 + group_outgoing_bw_limit_group2= 2.0 \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/data/config/config_ipv6.ini" "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/config/config_ipv6.ini" new file mode 100644 index 00000000..2461b4f8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/config/config_ipv6.ini" @@ -0,0 +1,49 @@ +[p2p] + ; ssl or sm ssl + sm_ssl=true + listen_ip=0.0.0.0 + listen_port=54321 + nodes_path=../../../bcos-gateway/test/unittests/data/config/json/ + nodes_file=nodes_ipv6.json +[cert] + ; directory the certificates located in + ca_path=../../../bcos-gateway/test/unittests/data/sm_ca/ + ; the ca certificate file + sm_ca_cert=sm_ca.crt + ; the node private key file + sm_node_key=sm_node.key + ; the node certificate file + sm_node_cert=sm_node.crt + ; the node private key file + sm_ennode_key=sm_ennode.key + ; the node certificate file + sm_ennode_cert=sm_ennode.crt + +[flow_control] + ; the module that does not limit bandwidth + ; list of all modules: raft,pbft,amop,block_sync,txs_sync + ; + ; modules_without_bw_limit=raft,pbft,txs_sync + + ; restrict the outgoing bandwidth of the node + ; both integer and decimal is support, unit: Mb + ; + total_outgoing_bw_limit=3.0 + + ; restrict the outgoing bandwidth of the the connection + ; both integer and decimal is support, unit: Mb + ; + conn_outgoing_bw_limit=2.0 + ; specify IP to limit bandwidth, format: ip_x.x.x.x=n + + ; default bandwidth limit for the group + group_outgoing_bw_limit=1.0 + ; specify group to limit bandwidth, group_groupName=n + +[redis] + server_ip=127.127.127.127 + server_port=12345 + request_timeout=54321 + connection_pool_size=111 + password=abc + db=12 \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/data/config/json/nodes_ipv4.json" "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/config/json/nodes_ipv4.json" new file mode 100644 index 00000000..6edd586f --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/config/json/nodes_ipv4.json" @@ -0,0 +1 @@ +{"nodes":["127.0.0.1:30300","127.0.0.1:30301","127.0.0.1:30302"]} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/data/config/json/nodes_ipv6.json" "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/config/json/nodes_ipv6.json" new file mode 100644 index 00000000..535925a4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/config/json/nodes_ipv6.json" @@ -0,0 +1 @@ +{"nodes":["[fe80::1a9d:50ae:3207:80d9]:30300"]} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_ca.crt" "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_ca.crt" new file mode 100644 index 00000000..4dea0fad --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_ca.crt" @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBwzCCAWqgAwIBAgIJALH5EDzvxroQMAoGCCqBHM9VAYN1MDcxEDAOBgNVBAMM +B2dtY2hhaW4xEzARBgNVBAoMCmZpc2NvLWJjb3MxDjAMBgNVBAsMBWNoYWluMCAX +DTIxMDUyMDEwNDQzM1oYDzIxMjEwNDI2MTA0NDMzWjA3MRAwDgYDVQQDDAdnbWNo +YWluMRMwEQYDVQQKDApmaXNjby1iY29zMQ4wDAYDVQQLDAVjaGFpbjBZMBMGByqG +SM49AgEGCCqBHM9VAYItA0IABFiVCiTx3zgl1SqZb2xFfFWl2SANx6/yCqfifQCT +x+JRvGustdpx1vVlMEuVUWr8qNR60eobopi83ygYdwds5WOjXTBbMB0GA1UdDgQW +BBQeINDcgl/xfjSC5QAuVb0yqYiyRjAfBgNVHSMEGDAWgBQeINDcgl/xfjSC5QAu +Vb0yqYiyRjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjAKBggqgRzPVQGDdQNH +ADBEAiBvqT4UITuUz7tYu6zNh6BPiQgWuwRPiNDAjwIP6V9oAQIgOs+XfvSr4Ca2 +5Mu7qfUBZR26D7Dht93N6kxqYnPdk2Q= +-----END CERTIFICATE----- diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_ennode.crt" "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_ennode.crt" new file mode 100644 index 00000000..b999e172 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_ennode.crt" @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBgzCCASqgAwIBAgIJAOy1CxhahqUnMAoGCCqBHM9VAYN1MDsxEzARBgNVBAMM +CmFnZW5jeV9zb24xEzARBgNVBAoMCmZpc2NvLWJjb3MxDzANBgNVBAsMBmFnZW5j +eTAgFw0yMTA1MjAxMDQ0MzRaGA8yMTIxMDQyNjEwNDQzNFowNjEOMAwGA1UEAwwF +bm9kZTAxEzARBgNVBAoMCmZpc2NvLWJjb3MxDzANBgNVBAsMBmVubm9kZTBZMBMG +ByqGSM49AgEGCCqBHM9VAYItA0IABM+CEHxscS6uhB1qjP/1ZBioa0WvDBm2OIn9 +DoDlRA0bi9O90uKfQufh89WFma/JKIaeHdznnbRViNKMSO8BtKqjGjAYMAkGA1Ud +EwQCMAAwCwYDVR0PBAQDAgM4MAoGCCqBHM9VAYN1A0cAMEQCID3xo0UfLPeVSPsU +tmNaA5w9QHQbin91ocT3tw/KVc+MAiBE68Ibp6HScfG+eGiJTHIMgPDx9B6OeJIL +qK45DyDd4Q== +-----END CERTIFICATE----- diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_ennode.key" "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_ennode.key" new file mode 100644 index 00000000..c1bcefbb --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_ennode.key" @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgPXQvW/xzm/S9w2np +OXpUZsj4Z7Gpf3feUhsHrZ3GNBShRANCAATPghB8bHEuroQdaoz/9WQYqGtFrwwZ +tjiJ/Q6A5UQNG4vTvdLin0Ln4fPVhZmvySiGnh3c5520VYjSjEjvAbSq +-----END PRIVATE KEY----- diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_node.crt" "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_node.crt" new file mode 100644 index 00000000..b5cba963 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_node.crt" @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +MIIBgzCCASigAwIBAgIJAOy1CxhahqUmMAoGCCqBHM9VAYN1MDsxEzARBgNVBAMM +CmFnZW5jeV9zb24xEzARBgNVBAoMCmZpc2NvLWJjb3MxDzANBgNVBAsMBmFnZW5j +eTAgFw0yMTA1MjAxMDQ0MzRaGA8yMTIxMDQyNjEwNDQzNFowNDEOMAwGA1UEAwwF +bm9kZTAxEzARBgNVBAoMCmZpc2NvLWJjb3MxDTALBgNVBAsMBG5vZGUwWTATBgcq +hkjOPQIBBggqgRzPVQGCLQNCAARaDQZZVLvJbboOnuoWPZcKkYfD5fGmMp2vKJis +uIisLWaPTjs0tTjc0b54OdhqCGnKZHiRPP1ORsFRdYb5wLPAoxowGDAJBgNVHRME +AjAAMAsGA1UdDwQEAwIGwDAKBggqgRzPVQGDdQNJADBGAiEA6N06YOh+bCdjsoxA +M8XhOTZ/V1oOOieEwZ97ThzX8zcCIQDTL+Xo+s5X72VSP+970x1k3gJ1vgxksYRt +YCnkMs0hwQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIBxjCCAWygAwIBAgIJALwX68WuI706MAoGCCqBHM9VAYN1MDcxEDAOBgNVBAMM +B2dtY2hhaW4xEzARBgNVBAoMCmZpc2NvLWJjb3MxDjAMBgNVBAsMBWNoYWluMB4X +DTIxMDUyMDEwNDQzM1oXDTMxMDUxODEwNDQzM1owOzETMBEGA1UEAwwKYWdlbmN5 +X3NvbjETMBEGA1UECgwKZmlzY28tYmNvczEPMA0GA1UECwwGYWdlbmN5MFkwEwYH +KoZIzj0CAQYIKoEcz1UBgi0DQgAE7n72jlrYJ0sAoDLFffStPZ7G4TsAgoCF4J/w +koSaoKt4/H2lSUINqVZTZb5O50uewnsHliGdrrsrzbmBwmXFIKNdMFswHQYDVR0O +BBYEFOS57Fr5XPgg1qIjWJDQiQZ2SMCMMB8GA1UdIwQYMBaAFB4g0NyCX/F+NILl +AC5VvTKpiLJGMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMAoGCCqBHM9VAYN1 +A0gAMEUCIQDbfPxt13kaz8ey5v3C23yODyMn1ThP/QtW4jQQvEHLqwIgfUt9NPzY +XGD3x8BzGIdpJuXZIgjKzfYw0+5wTN/m9b8= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIBwzCCAWqgAwIBAgIJALH5EDzvxroQMAoGCCqBHM9VAYN1MDcxEDAOBgNVBAMM +B2dtY2hhaW4xEzARBgNVBAoMCmZpc2NvLWJjb3MxDjAMBgNVBAsMBWNoYWluMCAX +DTIxMDUyMDEwNDQzM1oYDzIxMjEwNDI2MTA0NDMzWjA3MRAwDgYDVQQDDAdnbWNo +YWluMRMwEQYDVQQKDApmaXNjby1iY29zMQ4wDAYDVQQLDAVjaGFpbjBZMBMGByqG +SM49AgEGCCqBHM9VAYItA0IABFiVCiTx3zgl1SqZb2xFfFWl2SANx6/yCqfifQCT +x+JRvGustdpx1vVlMEuVUWr8qNR60eobopi83ygYdwds5WOjXTBbMB0GA1UdDgQW +BBQeINDcgl/xfjSC5QAuVb0yqYiyRjAfBgNVHSMEGDAWgBQeINDcgl/xfjSC5QAu +Vb0yqYiyRjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjAKBggqgRzPVQGDdQNH +ADBEAiBvqT4UITuUz7tYu6zNh6BPiQgWuwRPiNDAjwIP6V9oAQIgOs+XfvSr4Ca2 +5Mu7qfUBZR26D7Dht93N6kxqYnPdk2Q= +-----END CERTIFICATE----- diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_node.key" "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_node.key" new file mode 100644 index 00000000..48dff27a --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_node.key" @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgTMhT81W740vO5KzH +axpV+sfA0ApMBCvv9pDXuRNYqsqhRANCAARaDQZZVLvJbboOnuoWPZcKkYfD5fGm +Mp2vKJisuIisLWaPTjs0tTjc0b54OdhqCGnKZHiRPP1ORsFRdYb5wLPA +-----END PRIVATE KEY----- diff --git "a/BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_node.nodeid" "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_node.nodeid" new file mode 100644 index 00000000..c56b7da7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-gateway/test/unittests/data/sm_ca/sm_node.nodeid" @@ -0,0 +1 @@ +5a0d065954bbc96dba0e9eea163d970a9187c3e5f1a6329daf2898acb888ac2d668f4e3b34b538dcd1be7839d86a0869ca6478913cfd4e46c1517586f9c0b3c0 diff --git "a/BFPL\345\243\271/bcos-leader-election/CMakeLists.txt" "b/BFPL\345\243\271/bcos-leader-election/CMakeLists.txt" new file mode 100644 index 00000000..499cd1c8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-leader-election/CMakeLists.txt" @@ -0,0 +1,31 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for bcos-leader-election +# ------------------------------------------------------------------------------ +# Copyright (C) 2022 bcos-leader-election +# 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. +#------------------------------------------------------------------------------ + +cmake_minimum_required(VERSION 3.10) +set(CMAKE_OSX_DEPLOYMENT_TARGET "11.3" CACHE STRING "Minimum OS X deployment version") + +if(WITH_TIKV) + include(Version) + project(bcos-leader-election VERSION ${VERSION}) + + file(GLOB_RECURSE SRCS src/*.cpp) + find_package(etcd-cpp-api CONFIG REQUIRED) + find_package(gRPC REQUIRED) + add_library(${LEADER_ELECTION_TARGET} ${SRCS}) + target_link_libraries(${LEADER_ELECTION_TARGET} PUBLIC bcos-utilities bcos-framework etcd-cpp-api) +endif() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-leader-election/src/CampaignConfig.cpp" "b/BFPL\345\243\271/bcos-leader-election/src/CampaignConfig.cpp" new file mode 100644 index 00000000..4bddbbe7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-leader-election/src/CampaignConfig.cpp" @@ -0,0 +1,145 @@ +/** + * 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. + * + * @brief the configuration of campaign leader + * @file CampaignConfig.cpp + * @author: yujiechen + * @date 2022-04-26 + */ + +#include "CampaignConfig.h" + +using namespace bcos; +using namespace bcos::election; + +bcos::protocol::MemberInterface::Ptr CampaignConfig::fetchLeader() +{ + auto leader = getLeader(); + if (leader) + { + return leader; + } + // TODO: check the mode is sync or async + fetchLeaderInfoFromEtcd(); + return m_leader; +} + +bcos::protocol::MemberInterface::Ptr CampaignConfig::getLeader() +{ + ReadGuard l(x_leader); + return m_leader; +} + +void CampaignConfig::fetchLeaderInfoFromEtcd() +{ + try + { + ELECTION_LOG(INFO) << LOG_DESC("fetchLeaderInfoFromEtcd") << LOG_KV("key", m_leaderKey); + auto response = m_etcdClient->get(m_leaderKey).get(); + if (!checkAndUpdateLeaderKey(response)) + { + ELECTION_LOG(INFO) << LOG_DESC("fetchLeaderInfoFromEtcd failed") + << LOG_KV("key", m_leaderKey); + return; + } + ELECTION_LOG(INFO) << LOG_DESC("fetchLeaderInfoFromEtcd success") + << LOG_KV("leaderKey", m_leaderKey) + << LOG_KV("leaderID", m_leader->memberID()); + } + catch (std::exception const& e) + { + ELECTION_LOG(WARNING) << LOG_DESC("fetchLeaderInfoFromEtcd exception") + << LOG_KV("leaderKey", m_leaderKey) + << LOG_KV("error", boost::diagnostic_information(e)); + } +} + +void CampaignConfig::resetLeader(bcos::protocol::MemberInterface::Ptr _leader) +{ + WriteGuard l(x_leader); + m_leader = _leader; +} + +bool CampaignConfig::checkAndUpdateLeaderKey(etcd::Response _response) +{ + if (!_response.is_ok()) + { + if (_response.error_code() != etcdv3::ERROR_KEY_NOT_FOUND) + { + ELECTION_LOG(WARNING) << LOG_DESC("checkAndUpdateLeaderKey error") + << LOG_KV("code", _response.error_code()) + << LOG_KV("msg", _response.error_message()) + << LOG_KV("key", m_leaderKey); + return false; + } + resetLeader(nullptr); + // the key has already been cleared or not has been set, calls m_triggerCampaign + if (m_triggerCampaign) + { + ELECTION_LOG(INFO) << LOG_DESC( + "checkAndUpdateLeaderKey: the leader key is not accupied " + "now, trigger campaign") + << LOG_KV("key", m_leaderKey); + m_triggerCampaign(); + } + return false; + } + auto valueVersion = _response.value().version(); + if (valueVersion == 0) + { + resetLeader(nullptr); + auto ret = m_triggerCampaign(); + ELECTION_LOG(WARNING) << LOG_DESC("The leader key is released now, trigger campaign") + << LOG_KV("key", m_leaderKey) << LOG_KV("success", ret); + return false; + } + auto leader = m_memberFactory->createMember(_response.value().as_string()); + auto seq = _response.value().modified_index(); + leader->setSeq(seq); + leader->setLeaseID(_response.value().lease()); + resetLeader(leader); + // calls campaignLeader try to tryToSwitchToBackup if the leader is not the node-self + if (m_triggerCampaign) + { + m_triggerCampaign(); + } + ELECTION_LOG(INFO) << LOG_DESC("checkAndUpdateLeaderKey success") + << LOG_KV("leaderKey", m_leaderKey) << LOG_KV("leader", m_leader->memberID()) + << LOG_KV("version", valueVersion) << LOG_KV("modifiedIndex", seq) + << LOG_KV("lease", leader->leaseID()); + return true; +} + +// Note: this handler is triggered when leaderKey changed +void CampaignConfig::onLeaderKeyChanged(etcd::Response _response) +{ + ELECTION_LOG(INFO) << LOG_DESC("onLeaderKeyChanged, checkAndUpdateLeaderKey") + << LOG_KV("leaderKey", m_leaderKey); + checkAndUpdateLeaderKey(_response); +} + + +void CampaignConfig::onElectionClusterRecover() +{ + if (m_electionClusterOk.load()) + { + return; + } + resetLeader(nullptr); + // fetch leader and trigger campaign again(Note: checkAndUpdateLeaderKey of fetchLeader will + // trigger leader campaign) + fetchLeader(); + ElectionConfig::onElectionClusterRecover(); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-leader-election/src/CampaignConfig.h" "b/BFPL\345\243\271/bcos-leader-election/src/CampaignConfig.h" new file mode 100644 index 00000000..208dd548 --- /dev/null +++ "b/BFPL\345\243\271/bcos-leader-election/src/CampaignConfig.h" @@ -0,0 +1,118 @@ +/** + * 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. + * + * @brief the configuration of campaign leader + * @file CampaignConfig.h + * @author: yujiechen + * @date 2022-04-26 + */ +#pragma once +#include "ElectionConfig.h" + +namespace bcos +{ +namespace election +{ +class CampaignConfig : public ElectionConfig +{ +public: + using Ptr = std::shared_ptr; + CampaignConfig(bcos::protocol::MemberInterface::Ptr _self, std::string const& _etcdEndPoint, + bcos::protocol::MemberFactoryInterface::Ptr _memberFactory, std::string const& _leaderKey, + std::string const& _purpose, unsigned _leaseTTL = 3, const std::string& _caPath = "", + const std::string& _certPath = "", const std::string& _keyPath = "") + : ElectionConfig(_etcdEndPoint, _memberFactory, _purpose, _caPath, _certPath, _keyPath) + { + m_leaderKey = _leaderKey; + m_leaseTTL = _leaseTTL; + m_self = _self; + m_self->encode(m_leaderValue); + ELECTION_LOG(INFO) << LOG_DESC("CampaignConfig") << LOG_KV("selfID", m_self->memberID()) + << LOG_KV("key", m_leaderKey) << LOG_KV("leaderValue", m_leaderValue); + } + + ~CampaignConfig() override {} + + std::string const& leaderKey() const { return m_leaderKey; } + virtual bcos::protocol::MemberInterface::Ptr fetchLeader(); + virtual bcos::protocol::MemberInterface::Ptr getLeader(); + + virtual void setLeaderToSelf(int64_t _leaseID, int64_t _seq) + { + bcos::WriteGuard l(x_leader); + bcos::ReadGuard lock(x_self); + m_leader = m_memberFactory->createMember(); + m_leader->setMemberID(m_self->memberID()); + m_leader->setMemberConfig(m_self->memberConfig()); + m_leader->setSeq(_seq); + // update the lease + m_leader->setLeaseID(_leaseID); + } + + std::string const& leaderValue() const + { + ReadGuard l(x_self); + return m_leaderValue; + } + unsigned leaseTTL() const { return m_leaseTTL; } + bcos::protocol::MemberInterface::Ptr self() + { + ReadGuard l(x_self); + return m_self; + } + + void registerTriggerCampaignHandler(std::function _triggerCampaign) + { + m_triggerCampaign = _triggerCampaign; + } + + void updateSelf(bcos::protocol::MemberInterface::Ptr _self) + { + WriteGuard l(x_self); + m_self = _self; + m_self->encode(m_leaderValue); + } + +protected: + virtual void fetchLeaderInfoFromEtcd(); + virtual void onLeaderKeyChanged(etcd::Response _response); + bool checkAndUpdateLeaderKey(etcd::Response _response); + void onElectionClusterRecover() override; + void reCreateWatcher() override + { + m_watcher = std::make_shared(*m_etcdClient, m_leaderKey, + boost::bind(&CampaignConfig::onLeaderKeyChanged, this, boost::placeholders::_1)); + } + + void resetLeader(bcos::protocol::MemberInterface::Ptr _leader); + +protected: + // the leader key that multiple workers compete for, eg: "/consensus" + std::string m_leaderKey; + // default lease ttl is 3 seconds + unsigned m_leaseTTL = 5; + + // the campaign leader info, eg: the grpc/tars endpoint address + bcos::protocol::MemberInterface::Ptr m_self; + std::string m_leaderValue; + mutable SharedMutex x_self; + + bcos::protocol::MemberInterface::Ptr m_leader; + mutable bcos::SharedMutex x_leader; + + std::function m_triggerCampaign; +}; +} // namespace election +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-leader-election/src/Common.h" "b/BFPL\345\243\271/bcos-leader-election/src/Common.h" new file mode 100644 index 00000000..d3313bee --- /dev/null +++ "b/BFPL\345\243\271/bcos-leader-election/src/Common.h" @@ -0,0 +1,21 @@ +/** + * 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: yujiechen + * @date 2022-04-26 + */ +#include +#define ELECTION_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("Election") \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-leader-election/src/ElectionConfig.cpp" "b/BFPL\345\243\271/bcos-leader-election/src/ElectionConfig.cpp" new file mode 100644 index 00000000..9fe2964b --- /dev/null +++ "b/BFPL\345\243\271/bcos-leader-election/src/ElectionConfig.cpp" @@ -0,0 +1,92 @@ +/** + * 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. + * + * @brief the election config + * @file ElectionConfig.cpp + * @author: yujiechen + * @date 2022-04-26 + */ +#include "ElectionConfig.h" + +using namespace bcos; +using namespace bcos::election; + +void ElectionConfig::start() +{ + reCreateWatcher(); + if (!m_watcher) + { + return; + } + m_watcherTimer = std::make_shared(5000); + auto self = std::weak_ptr(shared_from_this()); + m_watcherTimer->registerTimeoutHandler([self]() { + auto config = self.lock(); + if (!config) + { + return; + } + config->refreshWatcher(); + }); + m_watcherTimer->start(); +} + +void ElectionConfig::onElectionClusterRecover() +{ + if (m_electionClusterOk.load()) + { + return; + } + m_electionClusterOk.store(true); + if (m_onElectionClusterRecover) + { + m_onElectionClusterRecover(); + } +} + +void ElectionConfig::onElectionClusterDown() +{ + if (!m_electionClusterOk.load()) + { + return; + } + m_electionClusterOk.store(false); + if (m_onElectionClusterException) + { + m_onElectionClusterException(); + } +} + +void ElectionConfig::refreshWatcher() +{ + if (m_etcdClient->head().get().is_ok()) + { + onElectionClusterRecover(); + m_watcherTimer->restart(); + return; + } + m_watcherTimer->stop(); + ELECTION_LOG(WARNING) << LOG_DESC("The client disconnect, wait for reconnect success"); + onElectionClusterDown(); + // wait until the client connects to etcd server + while (!m_etcdClient->head().get().is_ok()) + { + boost::this_thread::sleep_for(boost::chrono::milliseconds(100)); + } + onElectionClusterRecover(); + ELECTION_LOG(WARNING) << LOG_DESC("The client reconnect success, refreshWatcher"); + reCreateWatcher(); + m_watcherTimer->start(); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-leader-election/src/ElectionConfig.h" "b/BFPL\345\243\271/bcos-leader-election/src/ElectionConfig.h" new file mode 100644 index 00000000..8db0c7d5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-leader-election/src/ElectionConfig.h" @@ -0,0 +1,110 @@ +/** + * 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. + * + * @brief the election config + * @file ElectionConfig.h + * @author: yujiechen + * @date 2022-04-26 + */ +#pragma once +#include "Common.h" +#include +#include +#include +#include +#include + +#include +namespace bcos +{ +namespace election +{ +class ElectionConfig : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + ElectionConfig(std::string const& _etcdEndPoint, + bcos::protocol::MemberFactoryInterface::Ptr _memberFactory, std::string const& _purpose, + std::string const& _caPath = "", std::string const& _certPath = "", + std::string const& _keyPath = "") + : m_memberFactory(_memberFactory), m_purpose(_purpose) + { + if (!_caPath.empty() && !_certPath.empty() && !_keyPath.empty()) + { + m_etcdClient = std::make_shared( + _etcdEndPoint, _caPath, _certPath, _keyPath, "", "round_robin"); + } + else + { + m_etcdClient = std::make_shared(_etcdEndPoint); + } + } + + virtual void start(); + virtual void stop() + { + if (m_watcherTimer) + { + m_watcherTimer->stop(); + } + if (m_watcher) + { + m_watcher->Cancel(); + } + } + + virtual ~ElectionConfig() { stop(); } + + std::string const purpose() const { return m_purpose; } + std::shared_ptr etcdClient() { return m_etcdClient; } + bcos::protocol::MemberFactoryInterface::Ptr memberFactory() { return m_memberFactory; } + + bool electionClusterOk() const { return m_electionClusterOk.load(); } + void registerOnElectionClusterException(std::function _handler) + { + m_onElectionClusterException = _handler; + } + + void registerOnElectionClusterRecover(std::function _handler) + { + m_onElectionClusterRecover = _handler; + } + +protected: + virtual void refreshWatcher(); + virtual void reCreateWatcher() = 0; + virtual void onElectionClusterRecover(); + void onElectionClusterDown(); + +protected: + std::shared_ptr m_etcdClient; + std::shared_ptr m_watcher; + + bcos::protocol::MemberFactoryInterface::Ptr m_memberFactory; + std::string m_purpose; + + // regularly check the etcdClient inventory, and reset the watcher after disconnection and + // reconnection + std::shared_ptr m_watcherTimer; + + std::atomic_bool m_electionClusterOk = {true}; + + // called when the election-cluster down + std::function m_onElectionClusterException; + // called when the election-cluster recover + std::function m_onElectionClusterRecover; +}; +} // namespace election +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-leader-election/src/LeaderElection.cpp" "b/BFPL\345\243\271/bcos-leader-election/src/LeaderElection.cpp" new file mode 100644 index 00000000..0921ded3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-leader-election/src/LeaderElection.cpp" @@ -0,0 +1,249 @@ +/** + * 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. + * + * @brief leader-election + * @file LeaderElection.cpp + * @author: yujiechen + * @date 2022-04-26 + */ +#include "LeaderElection.h" +#include +#include + +using namespace bcos; +using namespace bcos::election; + +void LeaderElection::start() +{ + auto self = std::weak_ptr(shared_from_this()); + m_campaignTimer->registerTimeoutHandler([self]() { + auto leaderElection = self.lock(); + if (!leaderElection) + { + return; + } + leaderElection->campaignLeader(); + }); + if (m_config) + { + m_config->start(); + } + auto leader = m_config->fetchLeader(); + if (!leader) + { + return; + } + campaignLeader(); +} + +void LeaderElection::stop() +{ + if (m_campaignTimer) + { + m_campaignTimer->stop(); + } + if (m_config) + { + m_config->stop(); + } +} + +std::pair LeaderElection::grantLease() +{ + auto response = m_etcdClient->leasegrant(m_config->leaseTTL()).get(); + if (!response.is_ok()) + { + ELECTION_LOG(ERROR) << LOG_DESC("grantLease error") + << LOG_KV("msg", response.error_message()) + << LOG_KV("code", response.error_code()); + return std::make_pair(false, 0); + } + auto leaseID = response.value().lease(); + ELECTION_LOG(INFO) << LOG_DESC("grantLease success") << LOG_KV("id", leaseID) + << LOG_KV("ttl", response.value().ttl()) + << LOG_KV("purpose", m_config->purpose()); + return std::make_pair(true, leaseID); +} + +bool LeaderElection::campaignLeader() +{ + try + { + RecursiveGuard l(m_mutex); + // already has leader + if (m_config->getLeader()) + { + // tryToSwitchToBackup in case of the leader is not the node-self + tryToSwitchToBackup(); + return false; + } + auto ret = grantLease(); + if (!ret.first) + { + tryToSwitchToBackup(); + m_campaignTimer->restart(); + return false; + } + auto leaseID = ret.second; + auto tx = std::make_shared(m_config->leaderKey()); + tx->init_compare(0, etcdv3::CompareResult::EQUAL, etcdv3::CompareTarget::MOD); + tx->setup_basic_failure_operation(m_config->leaderKey()); + tx->setup_basic_create_sequence(m_config->leaderKey(), m_config->leaderValue(), leaseID); + auto response = m_etcdClient->txn(*tx).get(); + if (!response.is_ok()) + { + // failed for non-compare-failed error, restart campaign + if (response.error_code() != etcdv3::ERROR_COMPARE_FAILED) + { + m_campaignTimer->restart(); + } + else + { + // failed for compare-failed error, stop campaign + m_campaignTimer->stop(); + } + ELECTION_LOG(INFO) << LOG_DESC("campaignLeader error") + << LOG_KV("msg", response.error_message()) + << LOG_KV("code", response.error_code()) + << LOG_KV("purpose", m_config->purpose()) + << LOG_KV("lease", leaseID); + tryToSwitchToBackup(); + return false; + } + m_campaignTimer->stop(); + ELECTION_LOG(INFO) << LOG_DESC("campaignLeader success") + << LOG_KV("leaderKey", m_config->leaderKey()) + << LOG_KV("purpose", m_config->purpose()) << LOG_KV("lease", leaseID) + << LOG_KV("version", response.value().version()) + << LOG_KV("msg", response.error_message()) + << LOG_KV("value", response.value().as_string()) + << LOG_KV("key", response.value().key()); + // cancel the old keepAlive + if (m_keepAlive) + { + ELECTION_LOG(INFO) << LOG_DESC("campaignLeader: cancel keepAlive thread") + << LOG_KV("lease", m_keepAlive->Lease()) + << LOG_KV("leaderKey", m_config->leaderKey()); + m_keepAlive->Cancel(); + } + // establish new keepAlive + auto keepAliveTTL = m_config->leaseTTL() - 1; + m_keepAlive = std::make_shared(*(m_config->etcdClient()), + boost::bind(&LeaderElection::onKeepAliveException, this, boost::placeholders::_1), + keepAliveTTL, leaseID); + m_config->setLeaderToSelf(leaseID, response.value().modified_index()); + auto leader = m_config->getLeader(); + if (m_onCampaignHandler) + { + m_onCampaignHandler(true, leader); + } + ELECTION_LOG(INFO) + << LOG_DESC("campaignLeader: establish new keepAlive thread and switch to master-node") + << LOG_KV("ttl", keepAliveTTL) << LOG_KV("lease", leaseID) + << LOG_KV("leaderKey", m_config->leaderKey()); + return true; + } + catch (std::exception const& e) + { + ELECTION_LOG(WARNING) << LOG_DESC("campaignLeader exception") + << LOG_KV("error", boost::diagnostic_information(e)); + // release the leaderKey when exception + if (m_keepAlive) + { + ELECTION_LOG(INFO) << LOG_DESC("campaignLeader: cancel keepAlive thread for exception") + << LOG_KV("lease", m_keepAlive->Lease()) + << LOG_KV("leaderKey", m_config->leaderKey()); + m_keepAlive->Cancel(); + } + } + return false; +} + +void LeaderElection::onKeepAliveException(std::exception_ptr _exception) +{ + try + { + if (_exception) + { + std::rethrow_exception(_exception); + } + } + catch (const std::exception& e) + { + ELECTION_LOG(WARNING) << LOG_DESC("onKeepAliveException, restart campaign") + << LOG_KV("error", boost::diagnostic_information(e)); + } + if (m_campaignTimer) + { + m_campaignTimer->restart(); + } + if (m_onKeepAliveException) + { + m_onKeepAliveException(_exception); + } +} + +void LeaderElection::tryToSwitchToBackup() +{ + if (!m_onCampaignHandler) + { + return; + } + auto leader = m_config->getLeader(); + if (leader && m_config->self()->memberID() == leader->memberID()) + { + ELECTION_LOG(INFO) << LOG_DESC("tryToSwitchToBackup failed for the node-self is leader") + << LOG_KV("id", leader->memberID()); + return; + } + ELECTION_LOG(INFO) << LOG_DESC("tryToSwitchToBackup") + << LOG_KV("memberID", m_config->self()->memberID()) + << LOG_KV("leader", leader ? leader->memberID() : "no-leader"); + m_onCampaignHandler(false, leader); +} + +void LeaderElection::updateSelfConfig(bcos::protocol::MemberInterface::Ptr _self) +{ + RecursiveGuard l(m_mutex); + + m_config->updateSelf(_self); + ELECTION_LOG(INFO) << LOG_DESC("updateSelfConfig") << LOG_KV("ID", _self->memberID()); + // update the configuration if the node is leader + auto leader = m_config->getLeader(); + if (!leader || leader->memberID() != _self->memberID()) + { + ELECTION_LOG(INFO) << LOG_DESC("updateSelfConfig return for the node is not leader") + << LOG_KV("leaderID", leader ? leader->memberID() : "None"); + return; + } + auto leaseID = leader->leaseID(); + ELECTION_LOG(INFO) + << LOG_DESC("updateSelfConfig, the node-self is leader, sync the modified memberConfig") + << LOG_KV("lease", leaseID); + auto tx = std::make_shared(m_config->leaderKey()); + tx->init_lease_compare(leaseID, etcdv3::CompareResult::EQUAL, etcdv3::CompareTarget::LEASE); + tx->setup_basic_failure_operation(m_config->leaderKey()); + tx->setup_compare_and_swap_sequence(m_config->leaderValue(), leaseID); + auto response = m_etcdClient->txn(*tx).get(); + if (!response.is_ok()) + { + ELECTION_LOG(WARNING) << LOG_DESC("sync the modified memberConfig to storage error") + << LOG_KV("code", response.error_code()) + << LOG_KV("msg", response.error_message()) << LOG_KV("lease", leaseID) + << LOG_KV("leaderKey", m_config->leaderKey()); + return; + } + ELECTION_LOG(INFO) << LOG_DESC("updateSelfConfig success"); +} diff --git "a/BFPL\345\243\271/bcos-leader-election/src/LeaderElection.h" "b/BFPL\345\243\271/bcos-leader-election/src/LeaderElection.h" new file mode 100644 index 00000000..3e3acd13 --- /dev/null +++ "b/BFPL\345\243\271/bcos-leader-election/src/LeaderElection.h" @@ -0,0 +1,95 @@ +/** + * 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. + * + * @brief leader-election + * @file LeaderElection.h + * @author: yujiechen + * @date 2022-04-26 + */ +#pragma once +#include "CampaignConfig.h" +#include +#include +#include +namespace bcos +{ +namespace election +{ +class LeaderElection : public LeaderElectionInterface, + public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + LeaderElection(CampaignConfig::Ptr _config) + : m_config(_config), m_etcdClient(_config->etcdClient()) + { + m_config->registerTriggerCampaignHandler( + boost::bind(&LeaderElection::campaignLeader, this)); + m_campaignTimer = std::make_shared(m_config->leaseTTL() * 1000); + } + ~LeaderElection() override { stop(); } + void start() override; + + void stop() override; + void updateSelfConfig(bcos::protocol::MemberInterface::Ptr _self) override; + bool electionClusterOk() const override { return m_config->electionClusterOk(); } + + // called when campaign success, this logic should start to work when campaign success + void registerOnCampaignHandler( + std::function _onCampaignHandler) override + { + // Note: m_onCampaignHandler can't been executed in threadPool + m_onCampaignHandler = _onCampaignHandler; + } + + // called when keep-alive exception + void registerKeepAliveExceptionHandler( + std::function _handler) override + { + m_onKeepAliveException = _handler; + } + + // handler called when the election cluster down + void registerOnElectionClusterException(std::function _handler) override + { + m_config->registerOnElectionClusterException(_handler); + } + // handler called when the election cluster recover + void registerOnElectionClusterRecover(std::function _handler) override + { + m_config->registerOnElectionClusterRecover(_handler); + } + +protected: + // campaign leader + virtual bool campaignLeader(); + // grant lease with given ttl + std::pair grantLease(); + virtual void onKeepAliveException(std::exception_ptr _exception); + virtual void tryToSwitchToBackup(); + +protected: + CampaignConfig::Ptr m_config; + std::shared_ptr m_etcdClient; + std::shared_ptr m_keepAlive; + std::function m_onKeepAliveException; + std::function m_onCampaignHandler; + mutable RecursiveMutex m_mutex; + + // for trigger campaign after disconnect + std::shared_ptr m_campaignTimer; +}; +} // namespace election +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-leader-election/src/LeaderElectionFactory.h" "b/BFPL\345\243\271/bcos-leader-election/src/LeaderElectionFactory.h" new file mode 100644 index 00000000..2cd84fae --- /dev/null +++ "b/BFPL\345\243\271/bcos-leader-election/src/LeaderElectionFactory.h" @@ -0,0 +1,63 @@ +/** + * 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. + * + * @brief factory to create leaderElection + * @file LeaderElectionFactory.h + * @author: yujiechen + * @date 2022-04-26 + */ +#pragma once +#include "LeaderElection.h" +#include +#include +#include + +namespace bcos +{ +namespace election +{ +class LeaderElectionFactory : public LeaderElectionFactoryInterface +{ +public: + using Ptr = std::shared_ptr; + LeaderElectionFactory(bcos::protocol::MemberFactoryInterface::Ptr _memberFactory) + : m_memberFactory(_memberFactory) + {} + ~LeaderElectionFactory() override {} + + LeaderElectionInterface::Ptr createLeaderElection(std::string const& _memberID, + std::string const& _memberConfig, std::string const& _etcdEndPoint, + std::string const& _leaderKey, std::string const& _purpose, unsigned _leaseTTL, + const std::string& _caPath, const std::string& _certPath, + const std::string& _keyPath) override + { + auto member = m_memberFactory->createMember(); + member->setMemberID(_memberID); + member->setMemberConfig(_memberConfig); + + auto config = std::make_shared(member, _etcdEndPoint, m_memberFactory, + _leaderKey, _purpose, _leaseTTL, _caPath, _certPath, _keyPath); + ELECTION_LOG(INFO) << LOG_DESC("createLeaderElection") << LOG_KV("memberID", _memberID) + << LOG_KV("etcdEndPoint", _etcdEndPoint) + << LOG_KV("leaderKey", _leaderKey) << LOG_KV("purpose", _purpose) + << LOG_KV("leaseTTL", _leaseTTL); + return std::make_shared(config); + } + +private: + bcos::protocol::MemberFactoryInterface::Ptr m_memberFactory; +}; +} // namespace election +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-leader-election/src/LeaderEntryPoint.h" "b/BFPL\345\243\271/bcos-leader-election/src/LeaderEntryPoint.h" new file mode 100644 index 00000000..556f3046 --- /dev/null +++ "b/BFPL\345\243\271/bcos-leader-election/src/LeaderEntryPoint.h" @@ -0,0 +1,91 @@ +/** + * 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. + * + * @brief the election config + * @file LeaderEntryPoint.h + * @author: yujiechen + * @date 2022-04-26 + */ +#pragma once +#include "WatcherConfig.h" +#include +#include + +namespace bcos +{ +namespace election +{ +class LeaderEntryPoint : public LeaderEntryPointInterface +{ +public: + using Ptr = std::shared_ptr; + LeaderEntryPoint(WatcherConfig::Ptr _config) : m_config(_config) {} + ~LeaderEntryPoint() {} + + void start() override { m_config->start(); } + void stop() override { m_config->stop(); } + bcos::protocol::MemberInterface::Ptr getLeaderByKey(std::string const& _leaderKey) override + { + return m_config->leader(_leaderKey); + } + std::map getAllLeaders() override + { + return m_config->keyToLeader(); + } + + void addMemberChangeNotificationHandler( + std::function _handler) + override + { + m_config->addMemberChangeNotificationHandler(_handler); + } + + void addMemberDeleteNotificationHandler( + std::function _handler) + override + { + m_config->addMemberDeleteNotificationHandler(_handler); + } + +private: + WatcherConfig::Ptr m_config; +}; + +class LeaderEntryPointFactoryImpl : public LeaderEntryPointFactory +{ +public: + using Ptr = std::shared_ptr(); + LeaderEntryPointFactoryImpl(bcos::protocol::MemberFactoryInterface::Ptr _memberFactory) + : m_memberFactory(_memberFactory) + {} + ~LeaderEntryPointFactoryImpl() override {} + + LeaderEntryPointInterface::Ptr createLeaderEntryPoint(std::string const& _etcdEndPoint, + std::string const& _watchDir, std::string const& _purpose, const std::string& _caPath, + const std::string& _certPath, const std::string& _keyPath) override + { + auto config = std::make_shared( + _etcdEndPoint, _watchDir, m_memberFactory, _purpose, _caPath, _certPath, _keyPath); + ELECTION_LOG(INFO) << LOG_DESC("createLeaderEntryPoint") + << LOG_KV("etcdAddr", _etcdEndPoint) << LOG_KV("watchDir", _watchDir) + << LOG_KV("purpose", _purpose); + return std::make_shared(config); + } + +private: + bcos::protocol::MemberFactoryInterface::Ptr m_memberFactory; +}; +} // namespace election +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-leader-election/src/WatcherConfig.cpp" "b/BFPL\345\243\271/bcos-leader-election/src/WatcherConfig.cpp" new file mode 100644 index 00000000..53bb4c11 --- /dev/null +++ "b/BFPL\345\243\271/bcos-leader-election/src/WatcherConfig.cpp" @@ -0,0 +1,150 @@ +/** + * 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. + * + * @brief the config for the watcher + * @file WatcherConfig.cpp + * @author: yujiechen + * @date 2022-04-28 + */ + +#include "WatcherConfig.h" + +using namespace bcos; +using namespace bcos::election; + +void WatcherConfig::reCreateWatcher() +{ + ELECTION_LOG(INFO) << LOG_DESC("reCreateWatcher"); + // Note: set recursive to watch subdirectory change + m_watcher = std::make_shared(*m_etcdClient, m_watchDir, + boost::bind(&WatcherConfig::onWatcherKeyChanged, this, boost::placeholders::_1), true); + // fetchLeadersInfo when reCreateWatcher + fetchLeadersInfo(); +} + +void WatcherConfig::fetchLeadersInfo() +{ + ELECTION_LOG(INFO) << LOG_DESC("fetchLeadersInfo") << LOG_KV("watchDir", m_watchDir); + auto response = m_etcdClient->ls(m_watchDir).get(); + if (!response.is_ok()) + { + ELECTION_LOG(WARNING) << LOG_DESC("fetchLeadersInfo failed") + << LOG_KV("watchDir", m_watchDir); + return; + } + auto const& values = response.values(); + for (auto const& value : values) + { + updateLeaderInfo(value); + } + ELECTION_LOG(INFO) << LOG_DESC("fetchLeadersInfo success") << LOG_KV("watchDir", m_watchDir) + << LOG_KV("nodesSize", values.size()); +} + +void WatcherConfig::updateLeaderInfo(etcd::Value const& _value) +{ + try + { + auto version = _value.version(); + if (version == 0) + { + ELECTION_LOG(INFO) << LOG_DESC("updateLeaderInfo: the leaderKey has been released") + << LOG_KV("leaderKey", _value.key()); + { + auto const& leaderKey = _value.key(); + UpgradableGuard l(x_keyToLeader); + if (!m_keyToLeader.count(leaderKey)) + { + return; + } + auto member = m_keyToLeader.at(leaderKey); + UpgradeGuard ul(l); + m_keyToLeader.erase(leaderKey); + onMemberDeleted(leaderKey, member); + } + return; + } + auto const& leaderKey = _value.key(); + auto member = m_memberFactory->createMember(_value.as_string()); + auto seq = _value.modified_index(); + member->setSeq(seq); + ELECTION_LOG(INFO) << LOG_DESC("updateLeaderInfo: update leader") + << LOG_KV("leaderKey", leaderKey) << LOG_KV("member", member->memberID()) + << LOG_KV("modifiedIndex", seq); + { + WriteGuard l(x_keyToLeader); + m_keyToLeader[leaderKey] = member; + } + callNotificationHandlers(leaderKey, member); + } + catch (std::exception const& e) + { + ELECTION_LOG(WARNING) << LOG_DESC("updateLeaderInfo exception") + << LOG_KV("watchDir", m_watchDir) << LOG_KV("key", _value.key()) + << LOG_KV("value", _value.as_string()) + << LOG_KV("error", boost::diagnostic_information(e)); + } +} + +void WatcherConfig::onWatcherKeyChanged(etcd::Response _response) +{ + if (!_response.is_ok()) + { + ELECTION_LOG(WARNING) << LOG_DESC("onWatcherKeyChanged error") + << LOG_KV("code", _response.error_code()) + << LOG_KV("msg", _response.error_message()); + } + ELECTION_LOG(INFO) << LOG_DESC("onWatcherKeyChanged") << LOG_KV("key", _response.value().key()) + << LOG_KV("version", _response.value().version()); + updateLeaderInfo(_response.value()); +} + +void WatcherConfig::callNotificationHandlers( + std::string const& _key, bcos::protocol::MemberInterface::Ptr _member) +{ + ReadGuard l(x_notificationHandlers); + for (auto const& handler : m_notificationHandlers) + { + try + { + handler(_key, _member); + } + catch (std::exception const& e) + { + ELECTION_LOG(ERROR) << LOG_DESC("callNotificationHandlers exception") + << LOG_KV("key", _key) << LOG_KV("memberID", _member->memberID()) + << LOG_KV("error", boost::diagnostic_information(e)); + } + } +} + +void WatcherConfig::onMemberDeleted( + std::string const& _key, bcos::protocol::MemberInterface::Ptr _member) +{ + ReadGuard l(x_onMemberDeleted); + for (auto const& handler : m_onMemberDeleted) + { + try + { + handler(_key, _member); + } + catch (std::exception const& e) + { + ELECTION_LOG(ERROR) << LOG_DESC("onMemberDeleted exception") << LOG_KV("key", _key) + << LOG_KV("memberID", _member->memberID()) + << LOG_KV("error", boost::diagnostic_information(e)); + } + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-leader-election/src/WatcherConfig.h" "b/BFPL\345\243\271/bcos-leader-election/src/WatcherConfig.h" new file mode 100644 index 00000000..4a28e4dc --- /dev/null +++ "b/BFPL\345\243\271/bcos-leader-election/src/WatcherConfig.h" @@ -0,0 +1,107 @@ +/** + * 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. + * + * @brief the config for the watcher + * @file WatcherConfig.h + * @author: yujiechen + * @date 2022-04-28 + */ +#pragma once +#include "ElectionConfig.h" + +namespace bcos +{ +namespace election +{ +class WatcherConfig : public ElectionConfig +{ +public: + using Ptr = std::shared_ptr; + WatcherConfig(std::string const& _etcdEndPoint, std::string const& _watchDir, + bcos::protocol::MemberFactoryInterface::Ptr _memberFactory, std::string const& _purpose, + const std::string& _caPath = "", const std::string& _certPath = "", + const std::string& _keyPath = "") + : ElectionConfig(_etcdEndPoint, _memberFactory, _purpose, _caPath, _certPath, _keyPath) + { + m_watchDir = _watchDir; + ELECTION_LOG(INFO) << LOG_DESC("WatcherConfig") << LOG_KV("watchDir", _watchDir); + } + + ~WatcherConfig() override {} + + void start() override + { + ElectionConfig::start(); + fetchLeadersInfo(); + } + + std::string const& watchDir() const { return m_watchDir; } + std::map keyToLeader() const + { + ReadGuard l(x_keyToLeader); + return m_keyToLeader; + } + + bcos::protocol::MemberInterface::Ptr leader(std::string const& _key) const + { + ReadGuard l(x_keyToLeader); + if (!m_keyToLeader.count(_key)) + { + return nullptr; + } + return m_keyToLeader.at(_key); + } + + void addMemberChangeNotificationHandler( + std::function _handler) + { + ReadGuard l(x_notificationHandlers); + m_notificationHandlers.emplace_back(_handler); + } + + void addMemberDeleteNotificationHandler( + std::function _handler) + { + ReadGuard l(x_onMemberDeleted); + m_onMemberDeleted.emplace_back(_handler); + } + +protected: + virtual void fetchLeadersInfo(); + void updateLeaderInfo(etcd::Value const& _value); + + void reCreateWatcher() override; + virtual void onWatcherKeyChanged(etcd::Response _response); + + virtual void callNotificationHandlers( + std::string const& _key, bcos::protocol::MemberInterface::Ptr _member); + virtual void onMemberDeleted( + std::string const& _key, bcos::protocol::MemberInterface::Ptr _member); + +private: + std::string m_watchDir; + std::map m_keyToLeader; + mutable SharedMutex x_keyToLeader; + + std::vector> + m_notificationHandlers; + mutable SharedMutex x_notificationHandlers; + + std::vector> + m_onMemberDeleted; + mutable SharedMutex x_onMemberDeleted; +}; +} // namespace election +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-ledger/CMakeLists.txt" "b/BFPL\345\243\271/bcos-ledger/CMakeLists.txt" new file mode 100644 index 00000000..c3a366ca --- /dev/null +++ "b/BFPL\345\243\271/bcos-ledger/CMakeLists.txt" @@ -0,0 +1,41 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for bcos-ledger +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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. +# ------------------------------------------------------------------------------ + +cmake_minimum_required(VERSION 3.10) +set(CMAKE_OSX_DEPLOYMENT_TARGET "11.1" CACHE STRING "Minimum OS X deployment version") + +aux_source_directory(src/libledger SRCS) +aux_source_directory(src/libledger/utilities SRCS) + +find_package(Boost REQUIRED serialization) + +add_library(${LEDGER_TARGET} ${SRCS}) +target_link_libraries(${LEDGER_TARGET} PUBLIC ${CODEC_TARGET} ${TABLE_TARGET} ${PROTOCOL_TARGET} bcos-concepts Boost::serialization) + +# test related +if (TESTS) + enable_testing() + set(CTEST_OUTPUT_ON_FAILURE TRUE) + add_subdirectory(test) +endif() + +# for code coverage +if (COVERAGE) + include(Coverage) + config_coverage("ledger-coverage" "'/usr*' '${CMAKE_CURRENT_SOURCE_DIR}/bcos-cmake-scripts*' '${CMAKE_SOURCE_DIR}/test/mock**' '${CMAKE_SOURCE_DIR}/test/main**'") +endif () diff --git "a/BFPL\345\243\271/bcos-ledger/src/libledger/Ledger.cpp" "b/BFPL\345\243\271/bcos-ledger/src/libledger/Ledger.cpp" new file mode 100644 index 00000000..7f97a574 --- /dev/null +++ "b/BFPL\345\243\271/bcos-ledger/src/libledger/Ledger.cpp" @@ -0,0 +1,1769 @@ +/** + * Copyright (C) 2021 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 Ledger.cpp + * @author: kyonRay + * @date 2021-04-13 + * @file Ledger.cpp + * @author: ancelmo + * @date 2021-09-06 + */ + +#include "Ledger.h" +#include "bcos-tool/VersionConverter.h" +#include "utilities/Common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::ledger; +using namespace bcos::protocol; +using namespace bcos::storage; +using namespace bcos::crypto; +using namespace bcos::tool; + + +void Ledger::asyncPreStoreBlockTxs(bcos::protocol::TransactionsPtr _blockTxs, + bcos::protocol::Block::ConstPtr block, std::function _callback) +{ + auto txsToSaveResult = needStoreUnsavedTxs(_blockTxs, block); + bool shouldStoreTxs = std::get<0>(txsToSaveResult); + if (!shouldStoreTxs) + { + _callback(nullptr); + return; + } + auto startT = utcTime(); + auto blockTxsSize = _blockTxs->size(); + auto unstoredTxsHash = std::get<1>(txsToSaveResult); + auto blockNumber = block->blockHeaderConst()->number(); + asyncStoreTransactions(std::get<2>(txsToSaveResult), unstoredTxsHash, + [startT, _callback, _blockTxs, blockNumber, unstoredTxsHash, blockTxsSize]( + Error::Ptr _error) { + LEDGER_LOG(INFO) << LOG_DESC("asyncPreStoreBlockTxs: store uncommitted txs") + << LOG_KV("blockNumber", blockNumber) + << LOG_KV("blockTxsSize", blockTxsSize) + << LOG_KV("unStoredTxs", unstoredTxsHash->size()) + << LOG_KV("msg", _error ? _error->errorMessage() : "success") + << LOG_KV("code", _error ? _error->errorCode() : 0) + << LOG_KV("timeCost", (utcTime() - startT)); + + if (_error) + { + _callback(std::make_unique(*_error)); + return; + } + // set the flag when store success + for (auto const& tx : *_blockTxs) + { + tx->setStoreToBackend(true); + } + _callback(nullptr); + return; + }); +} + +void Ledger::asyncPrewriteBlock(bcos::storage::StorageInterface::Ptr storage, + bcos::protocol::TransactionsPtr _blockTxs, bcos::protocol::Block::ConstPtr block, + std::function callback) +{ + if (!block) + { + callback( + BCOS_ERROR_PTR(LedgerError::ErrorArgument, "asyncPrewriteBlock failed, empty block")); + return; + } + + if (isSysContractDeploy(block->blockHeaderConst()->number()) && block->transactionsSize() > 0) + { + // sys contract deploy + /// NOTE: write block number for 2pc storage + Entry numberEntry; + numberEntry.importFields({"0"}); + storage->asyncSetRow(SYS_CURRENT_STATE, SYS_KEY_CURRENT_NUMBER, std::move(numberEntry), + [callback](Error::Ptr&& error) { + if (error) + { + LEDGER_LOG(ERROR) << "System contract write ledger storage error " + << LOG_KV("msg", error->errorMessage()) + << LOG_KV("code", error->errorCode()); + } + callback(std::forward(error)); + }); + return; + } + auto header = block->blockHeaderConst(); + + auto blockNumberStr = boost::lexical_cast(header->number()); + + // 9 storage callbacks and write hash=>receipt + size_t TOTAL_CALLBACK = 9 + block->receiptsSize(); + auto setRowCallback = [total = std::make_shared>(TOTAL_CALLBACK), + failed = std::make_shared(false), + callback = std::move(callback)]( + Error::UniquePtr&& error, size_t count = 1) { + *total -= count; + if (error) + { + LEDGER_LOG(ERROR) << "Prewrite block error!" << boost::diagnostic_information(*error); + *failed = true; + } + + if (*total == 0) + { + // all finished + if (*failed) + { + LEDGER_LOG(ERROR) << "PrewriteBlock error"; + callback( + BCOS_ERROR_PTR(LedgerError::CollectAsyncCallbackError, "PrewriteBlock error")); + return; + } + + callback(nullptr); + } + }; + + // number 2 entry + Entry numberEntry; + numberEntry.importFields({blockNumberStr}); + storage->asyncSetRow(SYS_CURRENT_STATE, SYS_KEY_CURRENT_NUMBER, std::move(numberEntry), + [setRowCallback](auto&& error) { setRowCallback(std::forward(error)); }); + + // number 2 hash + Entry hashEntry; + hashEntry.importFields({header->hash().asBytes()}); + storage->asyncSetRow(SYS_NUMBER_2_HASH, blockNumberStr, std::move(hashEntry), + [setRowCallback](auto&& error) { setRowCallback(std::forward(error)); }); + + // hash 2 number + Entry hash2NumberEntry; + hash2NumberEntry.importFields({blockNumberStr}); + storage->asyncSetRow(SYS_HASH_2_NUMBER, bcos::concepts::bytebuffer::toView(header->hash()), + std::move(hash2NumberEntry), + [setRowCallback](auto&& error) { setRowCallback(std::forward(error)); }); + + // number 2 header + bytes headerBuffer; + header->encode(headerBuffer); + + Entry number2HeaderEntry; + number2HeaderEntry.importFields({std::move(headerBuffer)}); + storage->asyncSetRow(SYS_NUMBER_2_BLOCK_HEADER, blockNumberStr, std::move(number2HeaderEntry), + [setRowCallback](auto&& error) { setRowCallback(std::forward(error)); }); + + // number 2 nonce + auto nonceBlock = m_blockFactory->createBlock(); + nonceBlock->setNonceList(block->nonceList()); + bytes nonceBuffer; + nonceBlock->encode(nonceBuffer); + + Entry number2NonceEntry; + number2NonceEntry.importFields({std::move(nonceBuffer)}); + storage->asyncSetRow(SYS_BLOCK_NUMBER_2_NONCES, blockNumberStr, std::move(number2NonceEntry), + [setRowCallback](auto&& error) { setRowCallback(std::forward(error)); }); + + // number 2 transactions + auto transactionsBlock = m_blockFactory->createBlock(); + if (block->transactionsMetaDataSize() > 0) + { + for (size_t i = 0; i < block->transactionsMetaDataSize(); ++i) + { + auto originTransactionMetaData = block->transactionMetaData(i); + auto transactionMetaData = m_blockFactory->createTransactionMetaData( + originTransactionMetaData->hash(), std::string(originTransactionMetaData->to())); + transactionsBlock->appendTransactionMetaData(std::move(transactionMetaData)); + } + } + else if (block->transactionsSize() > 0) + { + for (size_t i = 0; i < block->transactionsSize(); ++i) + { + auto transaction = block->transaction(i); + auto transactionMetaData = m_blockFactory->createTransactionMetaData( + transaction->hash(), std::string(transaction->to())); + transactionsBlock->appendTransactionMetaData(std::move(transactionMetaData)); + } + } + else if (header->number() > 0) + { + LEDGER_LOG(WARNING) << "Empty transactions and metadata, empty block?" + << LOG_KV("blockNumber", blockNumberStr); + } + bytes transactionsBuffer; + transactionsBlock->encode(transactionsBuffer); + + Entry number2TransactionHashesEntry; + number2TransactionHashesEntry.importFields({std::move(transactionsBuffer)}); + storage->asyncSetRow(SYS_NUMBER_2_TXS, blockNumberStr, std::move(number2TransactionHashesEntry), + [setRowCallback](auto&& error) { setRowCallback(std::forward(error)); }); + + // hash 2 receipts + std::atomic_int64_t totalCount = 0; + std::atomic_int64_t failedCount = 0; + + std::vector> receiptDatas(block->receiptsSize()); + tbb::parallel_for(tbb::blocked_range(0, block->receiptsSize()), + [&transactionsBlock, &block, &failedCount, &totalCount, &receiptDatas]( + const tbb::blocked_range& range) { + for (size_t i = range.begin(); i < range.end(); ++i) + { + auto& [hash, entry] = receiptDatas[i]; + hash = transactionsBlock->transactionHash(i); + + auto receipt = block->receipt(i); + if (receipt->status() != 0) + { + failedCount++; + } + totalCount++; + + bytes receiptBuffer; + receipt->encode(receiptBuffer); + + entry.importFields({std::move(receiptBuffer)}); + } + }); + + for (auto& [hash, entry] : receiptDatas) + { + storage->asyncSetRow(SYS_HASH_2_RECEIPT, bcos::concepts::bytebuffer::toView(hash), + std::move(entry), [setRowCallback](auto&& error) { + setRowCallback(std::forward(error)); + }); + } + + LEDGER_LOG(DEBUG) << LOG_DESC("Calculate tx counts in block") + << LOG_KV("number", blockNumberStr) << LOG_KV("totalCount", totalCount) + << LOG_KV("failedCount", failedCount); + + // total transaction count + asyncGetTotalTransactionCount( + [storage, block, &setRowCallback, &totalCount, &failedCount]( + Error::Ptr error, int64_t total, int64_t failed, bcos::protocol::BlockNumber) { + if (error) + { + setRowCallback(std::make_unique(*error), 2); + return; + } + auto totalTxsCount = total + totalCount; + Entry totalEntry; + totalEntry.importFields({boost::lexical_cast(totalTxsCount)}); + storage->asyncSetRow(SYS_CURRENT_STATE, SYS_KEY_TOTAL_TRANSACTION_COUNT, + std::move(totalEntry), [setRowCallback](auto&& error) { + setRowCallback(std::forward(error)); + }); + auto failedTxs = failed + failedCount; + if (failedCount != 0) + { + Entry failedEntry; + failedEntry.importFields({boost::lexical_cast(failedTxs)}); + storage->asyncSetRow(SYS_CURRENT_STATE, SYS_KEY_TOTAL_FAILED_TRANSACTION, + std::move(failedEntry), [setRowCallback](auto&& error) { + setRowCallback(std::forward(error)); + }); + } + else + { + setRowCallback({}, true); + } + LEDGER_LOG(INFO) << METRIC << LOG_DESC("asyncPrewriteBlock") + << LOG_KV("number", block->blockHeaderConst()->number()) + << LOG_KV("totalTxs", totalTxsCount) << LOG_KV("failedTxs", failedTxs) + << LOG_KV("incTxs", totalCount) << LOG_KV("incFailedTxs", failedCount); + }); + asyncPreStoreBlockTxs(_blockTxs, block, setRowCallback); +} + +std::tuple>> +Ledger::needStoreUnsavedTxs( + bcos::protocol::TransactionsPtr _blockTxs, bcos::protocol::Block::ConstPtr _block) +{ + // Note: in the case of block-sync, no-need to save transactions when prewriteBlock + if (!_blockTxs || _blockTxs->size() == 0) + { + LEDGER_LOG(INFO) << LOG_DESC("asyncPreStoreBlockTxs: needStoreUnsavedTxs: empty txs") + << LOG_KV("number", _block->blockHeaderConst()->number()); + return std::make_tuple(false, nullptr, nullptr); + } + // supplement the unsaved hash_2_txs + auto txsToStore = std::make_shared>(); + size_t unstoredTxs = 0; + auto txsHash = std::make_shared(); + for (auto const& tx : (*_blockTxs)) + { + if (tx->storeToBackend()) + { + continue; + } + bcos::bytes encodeData; + tx->encode(encodeData); + unstoredTxs++; + txsHash->emplace_back(tx->hash()); + txsToStore->emplace_back(std::make_shared(std::move(encodeData))); + } + LEDGER_LOG(INFO) << LOG_DESC("asyncPreStoreBlockTxs: needStoreUnsavedTxs") + << LOG_KV("txsSize", _blockTxs->size()) << LOG_KV("unstoredTxs", unstoredTxs) + << LOG_KV("number", _block->blockHeaderConst()->number()); + if (txsToStore->size() == 0) + { + return std::make_tuple(false, nullptr, nullptr); + } + return std::make_tuple(true, txsHash, txsToStore); +} + +void Ledger::asyncStoreTransactions(std::shared_ptr> _txToStore, + crypto::HashListPtr _txHashList, std::function _onTxStored) +{ + if (!_txToStore || !_txHashList || _txToStore->size() != _txHashList->size()) + { + LEDGER_LOG(ERROR) << "StoreTransactions argument error"; + _onTxStored( + BCOS_ERROR_PTR(LedgerError::ErrorArgument, "asyncStoreTransactions argument error!")); + return; + } + + auto total = _txToStore->size(); + std::vector keys(total); + std::vector values(total); + tbb::parallel_for(tbb::blocked_range(0, _txHashList->size()), + [&](const tbb::blocked_range& range) { + for (size_t i = range.begin(); i < range.end(); ++i) + { + keys[i] = bcos::concepts::bytebuffer::toView((*_txHashList)[i]); + values[i] = bcos::concepts::bytebuffer::toView((*(*_txToStore)[i])); + } + }); + // Note: transactions must be submitted serially, because transaction submissions are + // transactional, preventing write conflicts + RecursiveGuard l(m_mutex); + auto error = m_storage->setRows(SYS_HASH_2_TX, std::move(keys), std::move(values)); + _onTxStored(error); +} + +void Ledger::asyncGetBlockDataByNumber(bcos::protocol::BlockNumber _blockNumber, int32_t _blockFlag, + std::function _onGetBlock) +{ + LEDGER_LOG(TRACE) << "GetBlockDataByNumber request" << LOG_KV("blockNumber", _blockNumber) + << LOG_KV("blockFlag", _blockFlag); + if (_blockNumber < 0 || _blockFlag < 0) + { + LEDGER_LOG(INFO) << "GetBlockDataByNumber, wrong argument"; + _onGetBlock(BCOS_ERROR_PTR(LedgerError::ErrorArgument, "Wrong argument"), nullptr); + return; + } + + std::list> fetchers; + auto block = m_blockFactory->createBlock(); + auto total = std::make_shared(0); + auto result = std::make_shared, std::atomic>>(0, 0); + + auto finally = [_blockNumber, total, result, block, _onGetBlock](Error::Ptr&& error) { + if (error) + ++std::get<1>(*result); + else + ++std::get<0>(*result); + + if (std::get<0>(*result) + std::get<1>(*result) == *total) + { + // All finished + if (std::get<0>(*result) != *total) + { + LEDGER_LOG(DEBUG) << "GetBlockDataByNumber request failed!" + << LOG_KV("number", _blockNumber); + _onGetBlock(BCOS_ERROR_PTR(LedgerError::CollectAsyncCallbackError, + "Get block failed with errors!"), + nullptr); + return; + } + + _onGetBlock(nullptr, std::move(block)); + } + }; + + if (_blockFlag & HEADER) + { + ++(*total); + + fetchers.push_back([this, _blockNumber, block, finally]() { + asyncGetBlockHeader( + block, _blockNumber, [finally](Error::Ptr&& error) { finally(std::move(error)); }); + }); + } + if (_blockFlag & TRANSACTIONS) + ++(*total); + if (_blockFlag & RECEIPTS) + ++(*total); + + if ((_blockFlag & TRANSACTIONS) || (_blockFlag & RECEIPTS)) + { + fetchers.push_back([this, block, _blockNumber, finally, _blockFlag]() { + asyncGetBlockTransactionHashes(_blockNumber, [this, _blockFlag, block, finally]( + Error::Ptr&& error, + std::vector&& hashes) { + if (error) + { + if (_blockFlag & TRANSACTIONS) + finally(std::move(error)); + if (_blockFlag & RECEIPTS) + finally(std::move(error)); + return; + } + + LEDGER_LOG(TRACE) << "Get transactions hash list success, size:" << hashes.size(); + + auto hashesPtr = std::make_shared>(std::move(hashes)); + if (_blockFlag & TRANSACTIONS) + { + asyncBatchGetTransactions( + hashesPtr, [block, finally](Error::Ptr&& error, + std::vector&& transactions) { + if (error) + { + LEDGER_LOG(ERROR) + << LOG_DESC( + "asyncGetBlockDataByNumber batch getTransactions error") + << LOG_KV("code", error->errorCode()) + << LOG_KV("msg", error->errorMessage()); + } + for (auto& it : transactions) + { + block->appendTransaction(it); + } + finally(std::move(error)); + }); + } + if (_blockFlag & RECEIPTS) + { + asyncBatchGetReceipts( + hashesPtr, [block, finally](Error::Ptr&& error, + std::vector&& receipts) { + for (auto& it : receipts) + { + block->appendReceipt(it); + } + finally(std::move(error)); + }); + } + }); + }); + } + + for (auto& it : fetchers) + { + it(); + } +} + +void Ledger::asyncGetBlockNumber( + std::function _onGetBlock) +{ + asyncGetSystemTableEntry(SYS_CURRENT_STATE, SYS_KEY_CURRENT_NUMBER, + [callback = std::move(_onGetBlock)]( + Error::Ptr&& error, std::optional&& entry) { + if (error) + { + LEDGER_LOG(DEBUG) << "GetBlockNumber failed" + << boost::diagnostic_information(error); + callback(BCOS_ERROR_WITH_PREV_PTR(LedgerError::GetStorageError, + "Get block number storage failed", *error), + -1); + return; + } + + bcos::protocol::BlockNumber blockNumber = -1; + try + { + blockNumber = boost::lexical_cast(entry->getField(0)); + } + catch (boost::bad_lexical_cast& e) + { + // Ignore the exception + LEDGER_LOG(INFO) << "Cast blockNumber failed, may be empty, set to default value -1" + << LOG_KV("blockNumber str", entry->getField(0)); + } + + LEDGER_LOG(TRACE) << "GetBlockNumber success" << LOG_KV("blockNumber", blockNumber); + callback(nullptr, blockNumber); + }); +} + +void Ledger::asyncGetBlockHashByNumber(bcos::protocol::BlockNumber _blockNumber, + std::function _onGetBlock) +{ + LEDGER_LOG(TRACE) << "GetBlockHashByNumber request" << LOG_KV("blockNumber", _blockNumber); + if (_blockNumber < 0) + { + _onGetBlock(BCOS_ERROR_PTR( + LedgerError::ErrorArgument, "GetBlockHashByNumber error, wrong argument"), + bcos::crypto::HashType()); + return; + } + + auto key = boost::lexical_cast(_blockNumber); + asyncGetSystemTableEntry(SYS_NUMBER_2_HASH, key, + [callback = std::move(_onGetBlock)]( + Error::Ptr&& error, std::optional&& entry) { + try + { + if (error) + { + LEDGER_LOG(DEBUG) + << "GetBlockHashByNumber error" << boost::diagnostic_information(error); + callback(BCOS_ERROR_WITH_PREV_PTR(LedgerError::GetStorageError, + "GetBlockHashByNumber error", *error), + bcos::crypto::HashType()); + return; + } + + auto hashStr = entry->getField(0); + bcos::crypto::HashType hash( + std::string(hashStr), bcos::crypto::HashType::FromBinary); + + callback(nullptr, std::move(hash)); + } + catch (std::exception& e) + { + callback(BCOS_ERROR_WITH_PREV_PTR(LedgerError::UnknownError, "Unknown error", e), + bcos::crypto::HashType()); + return; + } + }); +} + +void Ledger::asyncGetBlockNumberByHash(const crypto::HashType& _blockHash, + std::function _onGetBlock) +{ + auto key = _blockHash; + LEDGER_LOG(TRACE) << "GetBlockNumberByHash request" << LOG_KV("hash", key.hex()); + + asyncGetSystemTableEntry(SYS_HASH_2_NUMBER, bcos::concepts::bytebuffer::toView(key), + [callback = std::move(_onGetBlock)]( + Error::Ptr&& error, std::optional&& entry) { + try + { + if (error) + { + LEDGER_LOG(DEBUG) + << "GetBlockNumberByHash error " << boost::diagnostic_information(*error); + callback(BCOS_ERROR_WITH_PREV_PTR(LedgerError::GetStorageError, + "GetBlockNumberByHash error ", *error), + -1); + return; + } + + bcos::protocol::BlockNumber blockNumber = -1; + try + { + blockNumber = + boost::lexical_cast(entry->getField(0)); + } + catch (boost::bad_lexical_cast& e) + { + // Ignore the exception + LEDGER_LOG(INFO) + << "Cast blockNumber failed, may be empty, set to default value -1" + << LOG_KV("blockNumber str", entry->getField(0)); + } + callback(nullptr, blockNumber); + } + catch (std::exception& e) + { + LEDGER_LOG(INFO) << "GetBlockNumberByHash failed " + << boost::diagnostic_information(e); + callback(BCOS_ERROR_WITH_PREV_PTR(LedgerError::GetStorageError, + "GetBlockNumberByHash failed ", std::move(e)), + -1); + } + }); +} + +void Ledger::asyncGetBatchTxsByHashList(crypto::HashListPtr _txHashList, bool _withProof, + std::function>)> + _onGetTx) +{ + if (!_txHashList) + { + LEDGER_LOG(ERROR) << "GetBatchTxsByHashList error, wrong argument"; + _onGetTx(BCOS_ERROR_PTR(LedgerError::ErrorArgument, "Wrong argument"), nullptr, nullptr); + return; + } + + LEDGER_LOG(TRACE) << "GetBatchTxsByHashList request" << LOG_KV("hashes", _txHashList->size()) + << LOG_KV("withProof", _withProof); + + auto hexList = std::make_shared>(); + hexList->reserve(_txHashList->size()); + + for (auto& it : *_txHashList) + { + std::string hex(it.begin(), it.end()); + hexList->emplace_back(std::move(hex)); + } + + asyncBatchGetTransactions( + hexList, [this, callback = std::move(_onGetTx), _txHashList, _withProof]( + Error::Ptr&& error, std::vector&& transactions) { + if (error) + { + LEDGER_LOG(DEBUG) << "GetBatchTxsByHashList failed: " << error->errorMessage(); + callback(BCOS_ERROR_WITH_PREV_PTR( + LedgerError::GetStorageError, "GetBatchTxsByHashList error", *error), + nullptr, nullptr); + return; + } + + bcos::protocol::TransactionsPtr results = + std::make_shared(std::move(transactions)); + + if (_withProof) + { + auto con_proofMap = + std::make_shared>(); + auto count = std::make_shared(0); + auto counter = [_txList = results, _txHashList, count, con_proofMap, + callback = callback]() { + count->fetch_add(1); + if (count->load() == _txHashList->size()) + { + auto proofMap = std::make_shared>( + con_proofMap->begin(), con_proofMap->end()); + LEDGER_LOG(TRACE) << LOG_BADGE("GetBatchTxsByHashList success") + << LOG_KV("txHashListSize", _txHashList->size()) + << LOG_KV("proofMapSize", proofMap->size()); + callback(nullptr, _txList, proofMap); + } + }; + + tbb::parallel_for(tbb::blocked_range(0, _txHashList->size()), + [this, _txHashList, counter, con_proofMap]( + const tbb::blocked_range& range) { + for (size_t i = range.begin(); i < range.end(); ++i) + { + auto txHash = _txHashList->at(i); + getTxProof(txHash, [con_proofMap, txHash, counter]( + Error::Ptr _error, MerkleProofPtr _proof) { + if (!_error && _proof) + { + con_proofMap->insert(std::make_pair(txHash.hex(), _proof)); + } + counter(); + }); + } + }); + } + else + { + LEDGER_LOG(TRACE) << LOG_BADGE("GetBatchTxsByHashList success") + << LOG_KV("txHashListSize", _txHashList->size()) + << LOG_KV("withProof", _withProof); + callback(nullptr, results, nullptr); + } + }); +} + +void Ledger::asyncGetTransactionReceiptByHash(bcos::crypto::HashType const& _txHash, + bool _withProof, + std::function + _onGetTx) +{ + auto key = _txHash; + + LEDGER_LOG(TRACE) << "GetTransactionReceiptByHash" << LOG_KV("hash", key); + + asyncGetSystemTableEntry(SYS_HASH_2_RECEIPT, bcos::concepts::bytebuffer::toView(key), + [this, callback = std::move(_onGetTx), _withProof]( + Error::Ptr&& error, std::optional&& entry) { + if (error) + { + LEDGER_LOG(DEBUG) << "GetTransactionReceiptByHash: " + << boost::diagnostic_information(error); + callback(BCOS_ERROR_WITH_PREV_PTR( + LedgerError::GetStorageError, "GetTransactionReceiptByHash", *error), + nullptr, nullptr); + + return; + } + + auto value = entry->getField(0); + auto receipt = m_blockFactory->receiptFactory()->createReceipt( + bcos::bytesConstRef((bcos::byte*)value.data(), value.size())); + + if (_withProof) + { + getReceiptProof(receipt, + [receipt, _onGetTx = callback](Error::Ptr _error, MerkleProofPtr _proof) { + if (_error) + { + LEDGER_LOG(DEBUG) << "GetTransactionReceiptByHash" + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()) + << boost::diagnostic_information(_error); + _onGetTx(std::move(_error), receipt, nullptr); + return; + } + + _onGetTx(nullptr, receipt, std::move(_proof)); + }); + } + else + { + callback(nullptr, receipt, nullptr); + } + }); +} + +void Ledger::asyncGetTotalTransactionCount( + std::function _callback) +{ + static std::string_view keys[] = { + SYS_KEY_TOTAL_TRANSACTION_COUNT, SYS_KEY_TOTAL_FAILED_TRANSACTION, SYS_KEY_CURRENT_NUMBER}; + + m_storage->asyncOpenTable(SYS_CURRENT_STATE, [this, callback = std::move(_callback)]( + auto&& error, std::optional
&& table) { + auto tableError = + checkTableValid(std::forward(error), table, SYS_CURRENT_STATE); + if (tableError) + { + LEDGER_LOG(DEBUG) << "GetTotalTransactionCount" + << boost::diagnostic_information(*tableError); + callback(std::move(tableError), -1, -1, -1); + return; + } + + table->asyncGetRows(keys, [callback = std::move(callback)]( + auto&& error, std::vector>&& entries) { + if (error) + { + LEDGER_LOG(DEBUG) << "GetTotalTransactionCount" + << boost::diagnostic_information(*error); + callback( + BCOS_ERROR_WITH_PREV_PTR(LedgerError::GetStorageError, "Get row error", *error), + -1, -1, -1); + return; + } + + int64_t totalCount = 0, failedCount = 0, blockNumber = 0; + size_t i = 0; + for (auto& entry : entries) + { + int64_t value = 0; + if (!entry) + { + LEDGER_LOG(WARNING) + << "GetTotalTransactionCount error" << LOG_KV("index", i) << " empty"; + } + else + { + try + { + value = boost::lexical_cast(entry->getField(0)); + } + catch (boost::bad_lexical_cast& e) + { + LEDGER_LOG(ERROR) << "Lexical cast transaction count failed, entry value: " + << entry->get(); + BOOST_THROW_EXCEPTION(e); + } + } + switch (i++) + { + case 0: + totalCount = value; + break; + case 1: + failedCount = value; + break; + case 2: + blockNumber = value; + break; + } + } + + LEDGER_LOG(TRACE) << "GetTotalTransactionCount success" + << LOG_KV("totalCount", totalCount) + << LOG_KV("failedCount", failedCount) + << LOG_KV("blockNumber", blockNumber); + callback(nullptr, totalCount, failedCount, blockNumber); + }); + }); +} + +void Ledger::asyncGetSystemConfigByKey(const std::string_view& _key, + std::function _onGetConfig) +{ + LEDGER_LOG(TRACE) << "GetSystemConfigByKey request" << LOG_KV("key", _key); + + asyncGetBlockNumber([this, callback = std::move(_onGetConfig), _key]( + Error::Ptr error, bcos::protocol::BlockNumber blockNumber) { + if (error) + { + LEDGER_LOG(DEBUG) << "GetSystemConfigByKey, " << boost::diagnostic_information(*error); + callback(std::move(error), "", -1); + return; + } + + asyncGetSystemTableEntry(SYS_CONFIG, _key, + [blockNumber, callback = std::move(callback)]( + Error::Ptr&& error, std::optional&& entry) { + try + { + // Note: should considerate the case that the compatibility_version is not set + if (error) + { + LEDGER_LOG(DEBUG) + << "GetSystemConfigByKey, " << boost::diagnostic_information(*error); + callback(std::move(error), "", -1); + return; + } + + if (!entry) + { + LEDGER_LOG(WARNING) << "asyncGetSystemTableEntry: entry doesn't exists"; + callback( + BCOS_ERROR_PTR(-1, "asyncGetSystemTableEntry failed for empty entry"), + "", -1); + return; + } + + LEDGER_LOG(TRACE) << "Entry value: " << toHex(entry->get()); + + auto [value, number] = entry->getObject(); + + // The param was reset at height getLatestBlockNumber(), and takes effect in + // next block. So we query the status of getLatestBlockNumber() + 1. + auto effectNumber = blockNumber + 1; + if (number > effectNumber) + { + LEDGER_LOG(INFO) << "GetSystemConfigByKey, config not available" + << LOG_KV("currentBlockNumber", effectNumber) + << LOG_KV("available number", number); + callback(BCOS_ERROR_PTR(LedgerError::ErrorArgument, "Config not available"), + "", -1); + return; + } + + LEDGER_LOG(TRACE) << "GetSystemConfigByKey success" << LOG_KV("value", value) + << LOG_KV("number", number); + callback(nullptr, std::move(value), number); + } + catch (std::exception& e) + { + LEDGER_LOG(ERROR) + << "GetSystemConfigByKey error, " << boost::diagnostic_information(e); + callback( + BCOS_ERROR_WITH_PREV_PTR(LedgerError::GetStorageError, "error", e), "", -1); + } + }); + }); +} + +void Ledger::asyncGetNonceList(bcos::protocol::BlockNumber _startNumber, int64_t _offset, + std::function>)> + _onGetList) +{ + LEDGER_LOG(TRACE) << "GetNonceList request" << LOG_KV("startNumber", _startNumber) + << LOG_KV("offset", _offset); + + if (_startNumber < 0 || _offset < 0) + { + LEDGER_LOG(ERROR) << "GetNonceList error arguments" << LOG_KV("startNumber", _startNumber) + << LOG_KV("offset", _offset); + _onGetList(BCOS_ERROR_PTR(LedgerError::ErrorArgument, "Wrong argument"), nullptr); + return; + } + + m_storage->asyncOpenTable(SYS_BLOCK_NUMBER_2_NONCES, [this, callback = std::move(_onGetList), + _startNumber, _offset](auto&& error, + std::optional
&& table) { + auto tableError = + checkTableValid(std::forward(error), table, SYS_BLOCK_NUMBER_2_NONCES); + if (tableError) + { + callback(std::move(tableError), nullptr); + return; + } + + auto numberList = std::vector(); + for (BlockNumber i = _startNumber; i <= _startNumber + _offset; ++i) + { + numberList.push_back(boost::lexical_cast(i)); + } + + table->asyncGetRows(numberList, [this, numberList, callback = std::move(callback)]( + auto&& error, + std::vector>&& entries) { + if (error) + { + LEDGER_LOG(ERROR) << "GetNonceList error" << boost::diagnostic_information(*error); + callback( + BCOS_ERROR_WITH_PREV_PTR(LedgerError::GetStorageError, "GetNonceList", *error), + nullptr); + return; + } + + auto retMap = + std::make_shared>(); + + for (size_t i = 0; i < numberList.size(); ++i) + { + try + { + auto number = numberList[i]; + auto entry = entries[i]; + if (!entry) + { + continue; + } + + auto value = entry->getField(0); + auto block = m_blockFactory->createBlock( + bcos::bytesConstRef((bcos::byte*)value.data(), value.size()), false, false); + + auto nonceList = std::make_shared(block->nonceList()); + retMap->emplace( + std::make_pair(boost::lexical_cast(number), nonceList)); + } + catch (std::exception const& e) + { + LEDGER_LOG(WARNING) + << "Parse nonce list error" << boost::diagnostic_information(e); + continue; + } + } + + LEDGER_LOG(TRACE) << "GetNonceList success" << LOG_KV("retMap size", retMap->size()); + callback(nullptr, std::move(retMap)); + }); + }); +} + +void Ledger::asyncGetNodeListByType(const std::string_view& _type, + std::function _onGetConfig) +{ + LEDGER_LOG(DEBUG) << "GetNodeListByType request" << LOG_KV("type", _type); + + asyncGetBlockNumber([this, type = std::move(_type), callback = std::move(_onGetConfig)]( + Error::Ptr&& error, bcos::protocol::BlockNumber blockNumber) { + if (error) + { + LEDGER_LOG(DEBUG) << "GetNodeListByType" << boost::diagnostic_information(*error); + callback(BCOS_ERROR_WITH_PREV_PTR( + LedgerError::GetStorageError, "GetNodeListByType error", *error), + nullptr); + return; + } + + LEDGER_LOG(DEBUG) << "Get nodeList from" << LOG_KV("blockNumber", blockNumber); + + m_storage->asyncGetRow(SYS_CONSENSUS, "key", + [callback = std::move(callback), type = type, this, blockNumber]( + Error::UniquePtr error, std::optional entry) { + if (error) + { + callback(std::move(error), nullptr); + return; + } + + auto nodeList = decodeConsensusList(entry->getField(0)); + auto nodes = std::make_shared(); + + auto effectNumber = blockNumber + 1; + for (auto& it : nodeList) + { + if (it.type == type && boost::lexical_cast( + it.enableNumber) <= effectNumber) + { + crypto::NodeIDPtr nodeID = + m_blockFactory->cryptoSuite()->keyFactory()->createKey( + fromHex(it.nodeID)); + // Note: use try-catch to handle the exception case + nodes->emplace_back(std::make_shared( + nodeID, it.weight.convert_to())); + } + } + + LEDGER_LOG(DEBUG) << "GetNodeListByType success" << LOG_KV("type", type) + << LOG_KV("nodes size", nodes->size()); + callback(nullptr, std::move(nodes)); + }); + }); +} + +Error::Ptr Ledger::checkTableValid(Error::UniquePtr&& error, + const std::optional& table, const std::string_view& tableName) +{ + if (error) + { + std::stringstream ss; + ss << "Open table: " << tableName << " failed!"; + LEDGER_LOG(DEBUG) << ss.str() << boost::diagnostic_information(*error); + + return BCOS_ERROR_WITH_PREV_PTR(LedgerError::OpenTableFailed, ss.str(), *error); + } + + if (!table) + { + std::stringstream ss; + ss << "Table: " << tableName << " does not exists!"; + LEDGER_LOG(DEBUG) << ss.str(); + return BCOS_ERROR_PTR(LedgerError::OpenTableFailed, ss.str()); + } + + return nullptr; +} + +Error::Ptr Ledger::checkEntryValid(Error::UniquePtr&& error, + const std::optional& entry, const std::string_view& key) +{ + if (error) + { + std::stringstream ss; + ss << "Get row: " << key << " failed!"; + LEDGER_LOG(DEBUG) << ss.str() << boost::diagnostic_information(*error); + + return BCOS_ERROR_WITH_PREV_PTR(LedgerError::GetStorageError, ss.str(), *error); + } + + if (!entry) + { + std::stringstream ss; + ss << "Entry: " << key << " does not exists!"; + return BCOS_ERROR_PTR(LedgerError::GetStorageError, ss.str()); + } + + return nullptr; +} + +void Ledger::asyncGetBlockHeader(bcos::protocol::Block::Ptr block, + bcos::protocol::BlockNumber blockNumber, std::function callback) +{ + m_storage->asyncOpenTable(SYS_NUMBER_2_BLOCK_HEADER, + [this, blockNumber, block, callback](auto&& error, std::optional
&& table) { + auto validError = checkTableValid(std::move(error), table, SYS_NUMBER_2_BLOCK_HEADER); + if (validError) + { + callback(std::move(validError)); + return; + } + + table->asyncGetRow(boost::lexical_cast(blockNumber), + [this, blockNumber, block, callback](auto&& error, std::optional&& entry) { + auto validError = checkEntryValid( + std::move(error), entry, boost::lexical_cast(blockNumber)); + if (validError) + { + callback(std::move(validError)); + return; + } + + auto field = entry->getField(0); + auto headerPtr = m_blockFactory->blockHeaderFactory()->createBlockHeader( + bcos::bytesConstRef((bcos::byte*)field.data(), field.size())); + + block->setBlockHeader(std::move(headerPtr)); + callback(nullptr); + }); + }); +} + +void Ledger::asyncGetBlockTransactionHashes(bcos::protocol::BlockNumber blockNumber, + std::function&&)> callback) +{ + m_storage->asyncOpenTable(SYS_NUMBER_2_TXS, + [this, blockNumber, callback](auto&& error, std::optional
&& table) { + auto validError = checkTableValid(std::move(error), table, SYS_NUMBER_2_BLOCK_HEADER); + if (validError) + { + callback(std::move(validError), std::vector()); + return; + } + + table->asyncGetRow(boost::lexical_cast(blockNumber), + [this, blockNumber, callback](auto&& error, std::optional&& entry) { + auto validError = checkEntryValid( + std::move(error), entry, boost::lexical_cast(blockNumber)); + if (validError) + { + callback(std::move(validError), std::vector()); + return; + } + + auto txs = entry->getField(0); + auto blockWithTxs = m_blockFactory->createBlock( + bcos::bytesConstRef((bcos::byte*)txs.data(), txs.size())); + + std::vector hashList(blockWithTxs->transactionsHashSize()); + for (size_t i = 0; i < blockWithTxs->transactionsHashSize(); ++i) + { + auto hash = blockWithTxs->transactionHash(i); + hashList[i].assign(hash.begin(), hash.end()); + // hashList[i] = hash.hex(); + } + + callback(nullptr, std::move(hashList)); + }); + }); +} + +void Ledger::asyncBatchGetTransactions(std::shared_ptr> hashes, + std::function&&)> callback) +{ + m_storage->asyncOpenTable( + SYS_HASH_2_TX, [this, hashes, callback](auto&& error, std::optional
&& table) { + auto validError = + checkTableValid(std::forward(error), table, SYS_HASH_2_TX); + if (validError) + { + callback(std::move(validError), std::vector()); + return; + } + + std::vector hashesView; + hashesView.reserve(hashes->size()); + for (auto& hash : *hashes) + { + hashesView.push_back(hash); + } + + table->asyncGetRows(hashesView, [this, hashes, callback](auto&& error, + std::vector>&& entries) { + if (error) + { + LEDGER_LOG(DEBUG) + << "Batch get transaction failed " << boost::diagnostic_information(*error); + callback(BCOS_ERROR_WITH_PREV_PTR(LedgerError::GetStorageError, + "Batch get transaction failed ", *error), + std::vector()); + + return; + } + + std::vector transactions; + size_t i = 0; + for (auto& entry : entries) + { + if (!entry.has_value()) + { + LEDGER_LOG(TRACE) + << "Get transaction failed: " << (*hashes)[i] << " not found"; + } + else + { + auto field = entry->getField(0); + auto transaction = m_blockFactory->transactionFactory()->createTransaction( + bcos::bytesConstRef((bcos::byte*)field.data(), field.size())); + transactions.push_back(std::move(transaction)); + } + + ++i; + } + if (transactions.size() != hashes->size()) + { + LEDGER_LOG(DEBUG) + << "Batch get transaction failed, transactions size not match hashesSize" + << LOG_KV("txsSize", transactions.size()) + << LOG_KV("hashesSize", hashes->size()); + callback(BCOS_ERROR_PTR(LedgerError::CollectAsyncCallbackError, + "Batch get transaction failed, transactions size not match " + "hashesSize, txsSize: " + + std::to_string(transactions.size()) + + ", hashesSize: " + std::to_string(hashes->size())), + std::move(transactions)); + return; + } + + callback(nullptr, std::move(transactions)); + }); + }); +} + +void Ledger::asyncBatchGetReceipts(std::shared_ptr> hashes, + std::function&&)> callback) +{ + m_storage->asyncOpenTable( + SYS_HASH_2_RECEIPT, [this, hashes, callback](auto&& error, std::optional
&& table) { + auto validError = checkTableValid(std::move(error), table, SYS_HASH_2_RECEIPT); + if (validError) + { + callback(std::move(validError), std::vector()); + return; + } + + table->asyncGetRows(*hashes, [this, hashes, callback](auto&& error, + std::vector>&& entries) { + if (error) + { + LEDGER_LOG(DEBUG) + << "Batch get receipt error!" << boost::diagnostic_information(*error); + callback(BCOS_ERROR_WITH_PREV_PTR( + LedgerError::GetStorageError, "Batch get receipt error!", *error), + std::vector()); + + return; + } + + size_t i = 0; + std::vector receipts; + receipts.reserve(hashes->size()); + for (auto& entry : entries) + { + if (!entry.has_value()) + { + LEDGER_LOG(DEBUG) << "Get receipt with empty entry: " << (*hashes)[i]; + callback(BCOS_ERROR_PTR( + LedgerError::GetStorageError, "Batch get transaction failed"), + std::vector()); + return; + } + + auto field = entry->getField(0); + auto receipt = m_blockFactory->receiptFactory()->createReceipt( + bcos::bytesConstRef((bcos::byte*)field.data(), field.size())); + receipts.push_back(std::move(receipt)); + + ++i; + } + + callback(nullptr, std::move(receipts)); + }); + }); +} + +void Ledger::asyncGetSystemTableEntry(const std::string_view& table, const std::string_view& key, + std::function&&)> callback) +{ + m_storage->asyncOpenTable(table, [this, key = std::string(key), callback = std::move(callback)]( + auto&& error, std::optional
&& table) { + auto tableError = + checkTableValid(std::forward(error), table, SYS_CURRENT_STATE); + if (tableError) + { + callback(std::move(tableError), {}); + return; + } + + table->asyncGetRow(key, [this, key, callback = std::move(callback)]( + auto&& error, std::optional&& entry) { + auto entryError = checkEntryValid(std::move(error), entry, key); + if (entryError) + { + callback(std::move(entryError), {}); + return; + } + + callback(nullptr, std::move(entry)); + }); + }); +} + +void Ledger::getTxProof( + const HashType& _txHash, std::function _onGetProof) +{ + // txHash->receipt receipt->number number->txHash + asyncGetTransactionReceiptByHash(_txHash, false, + [this, _txHash, _onGetProof = std::move(_onGetProof)]( + Error::Ptr _error, TransactionReceipt::ConstPtr _receipt, const MerkleProofPtr&) { + if (_error || !_receipt) + { + LEDGER_LOG(DEBUG) << LOG_BADGE("getTxProof") + << LOG_DESC("getReceiptByTxHash from storage failed") + << LOG_KV("txHash", _txHash.hex()); + _onGetProof(std::forward(_error), nullptr); + return; + } + asyncGetBlockTransactionHashes(_receipt->blockNumber(), + [this, _onGetProof, _txHash = std::move(_txHash)]( + Error::Ptr&& _error, std::vector&& _hashList) { + if (_error || _hashList.empty()) + { + LEDGER_LOG(DEBUG) + << LOG_BADGE("getTxProof") + << LOG_DESC("asyncGetBlockTransactionHashes from storage failed") + << LOG_KV("txHash", _txHash.hex()); + _onGetProof(std::forward(_error), nullptr); + return; + } + asyncBatchGetTransactions(std::make_shared>(_hashList), + [cryptoSuite = m_blockFactory->cryptoSuite(), _onGetProof, + _txHash = std::move(_txHash)]( + Error::Ptr&& _error, std::vector&& _txList) { + if (_error || _txList.empty()) + { + LEDGER_LOG(DEBUG) + << LOG_BADGE("getTxProof") << LOG_DESC("getTxs callback failed") + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + _onGetProof(std::forward(_error), nullptr); + return; + } + + auto merkleProofPtr = std::make_shared(); + auto merkleProofUtility = std::make_shared(); + + merkleProofUtility->getMerkleProof(_txHash, + std::forward(_txList), cryptoSuite, + merkleProofPtr); + LEDGER_LOG(TRACE) + << LOG_BADGE("getTxProof") << LOG_DESC("get merkle proof success") + << LOG_KV("txHash", _txHash.hex()); + _onGetProof(nullptr, std::move(merkleProofPtr)); + }); + }); + }); +} + +void Ledger::getReceiptProof(protocol::TransactionReceipt::Ptr _receipt, + std::function _onGetProof) +{ + // receipt->number number->txs txs->receipts + asyncGetBlockTransactionHashes(_receipt->blockNumber(), + [this, _onGetProof = std::move(_onGetProof), receiptHash = _receipt->hash()]( + Error::Ptr&& _error, std::vector&& _hashList) { + if (_error) + { + _onGetProof(std::forward(_error), nullptr); + return; + } + + asyncBatchGetReceipts(std::make_shared>(_hashList), + [cryptoSuite = this->m_blockFactory->cryptoSuite(), _onGetProof, + receiptHash = receiptHash](Error::Ptr&& _error, + std::vector&& _receiptList) { + if (_error || _receiptList.empty()) + { + LEDGER_LOG(DEBUG) << LOG_BADGE("getReceiptProof") + << LOG_DESC("asyncBatchGetReceipts callback failed"); + _onGetProof(std::forward(_error), nullptr); + return; + } + auto merkleProof = std::make_shared(); + auto merkleProofUtility = std::make_shared(); + merkleProofUtility->getMerkleProof(receiptHash, + std::forward(_receiptList), cryptoSuite, + merkleProof); + _onGetProof(nullptr, std::move(merkleProof)); + }); + }); +} + +// sync method +bool Ledger::buildGenesisBlock(LedgerConfig::Ptr _ledgerConfig, size_t _gasLimit, + const std::string_view& _genesisData, std::string const& _compatibilityVersion) +{ + LEDGER_LOG(INFO) << LOG_DESC("[#buildGenesisBlock]"); + if (_gasLimit < TX_GAS_LIMIT_MIN) + { + LEDGER_LOG(FATAL) << LOG_BADGE("buildGenesisBlock") + << LOG_DESC("gas limit too low, return false") + << LOG_KV("gasLimit", _gasLimit) + << LOG_KV("gasLimitMin", TX_GAS_LIMIT_MIN); + return false; + } + + std::promise> getBlockPromise; + asyncGetBlockHashByNumber(0, [&](Error::Ptr error, bcos::crypto::HashType hash) { + getBlockPromise.set_value({std::move(error), hash}); + }); + + auto getBlockResult = getBlockPromise.get_future().get(); + if (std::get<0>(getBlockResult) && + std::get<0>(getBlockResult)->errorCode() != LedgerError::GetStorageError) + { + BOOST_THROW_EXCEPTION(*(std::get<0>(getBlockResult))); + } + + if (std::get<1>(getBlockResult)) + { + // genesis block exists, quit + LEDGER_LOG(INFO) << LOG_DESC("[#buildGenesisBlock] success, block exists"); + std::promise blockHeaderFuture; + // get genesisBlockHeader + asyncGetBlockDataByNumber( + 0, HEADER, [&blockHeaderFuture](Error::Ptr error, Block::Ptr block) { + if (error) + { + LEDGER_LOG(INFO) << "Get genesisBlockHeader from storage failed"; + blockHeaderFuture.set_value(nullptr); + } + else + { + blockHeaderFuture.set_value(block->blockHeader()); + } + }); + bcos::protocol::BlockHeader::Ptr m_genesisBlockHeader = + blockHeaderFuture.get_future().get(); + auto initialGenesisData = m_genesisBlockHeader->extraData().toString(); + // check genesisData whether inconsistent with initialGenesisData + if (initialGenesisData == _genesisData) + { + auto version = bcos::tool::toVersionNumber(_compatibilityVersion); + if (version > (uint32_t)protocol::BlockVersion::MAX_VERSION || + version < (uint32_t)protocol::BlockVersion::MIN_VERSION) + { + BOOST_THROW_EXCEPTION(bcos::tool::InvalidVersion() << errinfo_comment( + "Current genesis compatibilityVersion is " + + _compatibilityVersion + ", No support this version")); + } + else + { + return true; + } + } + else + { + // GetBlockDataByNumber success but not consistent with initialGenesisData + if (m_genesisBlockHeader) + { + std::cout << "The Genesis Data is inconsistent with the initial Genesis Data. " + "Initial Genesis Data is :" + << std::endl + << initialGenesisData << std::endl; + BOOST_THROW_EXCEPTION( + bcos::tool::InvalidConfig() << errinfo_comment( + "The Genesis Data is inconsistent with the initial Genesis Data")); + } + else + { + LEDGER_LOG(INFO) << "error! initialGenesisDate is null"; + } + } + } + auto versionNumber = bcos::tool::toVersionNumber(_compatibilityVersion); + // clang-format off + std::vector tables { + SYS_CONFIG, SYS_VALUE_AND_ENABLE_BLOCK_NUMBER, + SYS_CONSENSUS, SYS_VALUE, + SYS_CURRENT_STATE, SYS_VALUE, + SYS_HASH_2_TX, SYS_VALUE, + SYS_HASH_2_NUMBER, SYS_VALUE, + SYS_NUMBER_2_HASH, SYS_VALUE, + SYS_NUMBER_2_BLOCK_HEADER, SYS_VALUE, + SYS_NUMBER_2_TXS, SYS_VALUE, + SYS_HASH_2_RECEIPT, SYS_VALUE, + SYS_BLOCK_NUMBER_2_NONCES, SYS_VALUE, + }; + + if (versionNumber >= (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION) + { + std::vector moreTables{ + SYS_CODE_BINARY, SYS_VALUE, + SYS_CONTRACT_ABI, SYS_VALUE + }; + + for (auto v : moreTables) + { + tables.push_back(v); + } + } + // clang-format on + + size_t total = tables.size(); + + for (size_t i = 0; i < total; i += 2) + { + std::promise> createTablePromise; + m_storage->asyncCreateTable(std::string(tables[i]), std::string(tables[i + 1]), + [&createTablePromise](auto&& error, std::optional
&&) { + createTablePromise.set_value({std::move(error)}); + }); + auto createTableResult = createTablePromise.get_future().get(); + if (std::get<0>(createTableResult)) + { + BOOST_THROW_EXCEPTION(*(std::get<0>(createTableResult))); + } + } + + + createFileSystemTables(versionNumber); + if (versionNumber > (uint32_t)protocol::BlockVersion::MAX_VERSION) + { + BOOST_THROW_EXCEPTION(bcos::tool::InvalidVersion() << errinfo_comment( + "The genesis compatibilityVersion is " + _compatibilityVersion + + ", high than support maxVersion")); + } + + auto txLimit = _ledgerConfig->blockTxCountLimit(); + LEDGER_LOG(INFO) << LOG_DESC("Commit the genesis block") << LOG_KV("txLimit", txLimit) + << LOG_KV("leaderSwitchPeriod", _ledgerConfig->leaderSwitchPeriod()) + << LOG_KV("blockTxCountLimit", _ledgerConfig->blockTxCountLimit()) + << LOG_KV("compatibilityVersion", _compatibilityVersion) + << LOG_KV("minSupportedVersion", g_BCOSConfig.minSupportedVersion()) + << LOG_KV("maxSupportedVersion", g_BCOSConfig.maxSupportedVersion()); + + // build a block + auto header = m_blockFactory->blockHeaderFactory()->createBlockHeader(); + header->setNumber(0); + if (versionNumber >= (uint32_t)protocol::BlockVersion::V3_1_VERSION) + { + header->setVersion(versionNumber); + } + header->setExtraData(bcos::bytes(_genesisData.begin(), _genesisData.end())); + + auto block = m_blockFactory->createBlock(); + block->setBlockHeader(header); + + std::promise genesisBlockPromise; + asyncPrewriteBlock(m_storage, nullptr, block, [&genesisBlockPromise](Error::Ptr&& error) { + genesisBlockPromise.set_value(std::move(error)); + }); + + auto error = genesisBlockPromise.get_future().get(); + if (error) + { + BOOST_THROW_EXCEPTION(*error); + } + + // write sys config + std::promise>> sysTablePromise; + m_storage->asyncOpenTable( + SYS_CONFIG, [&sysTablePromise](auto&& error, std::optional
&& table) { + sysTablePromise.set_value({std::move(error), std::move(table)}); + }); + + auto [tableError, sysTable] = sysTablePromise.get_future().get(); + if (tableError) + { + BOOST_THROW_EXCEPTION(*tableError); + } + + if (!sysTable) + { + BOOST_THROW_EXCEPTION(BCOS_ERROR(LedgerError::OpenTableFailed, "Open SYS_CONFIG failed!")); + } + + // tx count limit + Entry txLimitEntry; + txLimitEntry.setObject( + SystemConfigEntry{boost::lexical_cast(_ledgerConfig->blockTxCountLimit()), 0}); + sysTable->setRow(SYSTEM_KEY_TX_COUNT_LIMIT, std::move(txLimitEntry)); + + // tx gas limit + Entry gasLimitEntry; + gasLimitEntry.setObject(SystemConfigEntry{boost::lexical_cast(_gasLimit), 0}); + sysTable->setRow(SYSTEM_KEY_TX_GAS_LIMIT, std::move(gasLimitEntry)); + + // consensus leader period + Entry leaderPeriodEntry; + leaderPeriodEntry.setObject(SystemConfigEntry{ + boost::lexical_cast(_ledgerConfig->leaderSwitchPeriod()), 0}); + sysTable->setRow(SYSTEM_KEY_CONSENSUS_LEADER_PERIOD, std::move(leaderPeriodEntry)); + + LEDGER_LOG(INFO) << LOG_DESC("init the compatibilityVersion") + << LOG_KV("versionNumber", versionNumber); + // write compatibility version + Entry compatibilityVersionEntry; + compatibilityVersionEntry.setObject(SystemConfigEntry{_compatibilityVersion, 0}); + sysTable->setRow(SYSTEM_KEY_COMPATIBILITY_VERSION, std::move(compatibilityVersionEntry)); + + // write consensus node list + std::promise>> consensusTablePromise; + m_storage->asyncOpenTable(SYS_CONSENSUS, [&consensusTablePromise]( + auto&& error, std::optional
&& table) { + consensusTablePromise.set_value({std::forward(error), std::move(table)}); + }); + + auto [consensusError, consensusTable] = consensusTablePromise.get_future().get(); + if (consensusError) + { + BOOST_THROW_EXCEPTION(*consensusError); + } + + if (!consensusTable) + { + BOOST_THROW_EXCEPTION( + BCOS_ERROR(LedgerError::OpenTableFailed, "Open SYS_CONSENSUS failed!")); + } + + ConsensusNodeList consensusNodeList; + + for (const auto& node : _ledgerConfig->consensusNodeList()) + { + consensusNodeList.emplace_back( + node->nodeID()->hex(), node->weight(), std::string{CONSENSUS_SEALER}, "0"); + } + + for (const auto& node : _ledgerConfig->observerNodeList()) + { + consensusNodeList.emplace_back( + node->nodeID()->hex(), node->weight(), std::string{CONSENSUS_OBSERVER}, "0"); + } + + Entry consensusNodeListEntry; + consensusNodeListEntry.importFields({encodeConsensusList(consensusNodeList)}); + + std::promise setConsensusNodeListPromise; + consensusTable->asyncSetRow("key", std::move(consensusNodeListEntry), + [&setConsensusNodeListPromise]( + Error::UniquePtr&& error) { setConsensusNodeListPromise.set_value(std::move(error)); }); + + auto setConsensusNodeListError = setConsensusNodeListPromise.get_future().get(); + if (setConsensusNodeListError) + { + BOOST_THROW_EXCEPTION(BCOS_ERROR_WITH_PREV( + LedgerError::CallbackError, "Write genesis consensus node list error!", *error)); + } + + // write current state + std::promise>> stateTablePromise; + m_storage->asyncOpenTable( + SYS_CURRENT_STATE, [&stateTablePromise](auto&& error, std::optional
&& table) { + stateTablePromise.set_value({std::forward(error), std::move(table)}); + }); + + auto [stateError, stateTable] = stateTablePromise.get_future().get(); + if (stateError) + { + BOOST_THROW_EXCEPTION(*stateError); + } + + if (!stateTable) + { + BOOST_THROW_EXCEPTION( + BCOS_ERROR(LedgerError::OpenTableFailed, "Open SYS_CURRENT_STATE failed!")); + } + + Entry currentNumber; + currentNumber.importFields({"0"}); + stateTable->setRow(SYS_KEY_CURRENT_NUMBER, std::move(currentNumber)); + + Entry txNumber; + txNumber.importFields({"0"}); + stateTable->setRow(SYS_KEY_TOTAL_TRANSACTION_COUNT, std::move(txNumber)); + + Entry txFailedNumber; + txFailedNumber.importFields({"0"}); + stateTable->setRow(SYS_KEY_TOTAL_FAILED_TRANSACTION, std::move(txFailedNumber)); + + return true; +} + +void Ledger::createFileSystemTables(uint32_t blockVersion) +{ + std::array rootSubNames = { + tool::FS_APPS, tool::FS_USER, tool::FS_USER_TABLE, tool::FS_SYS_BIN}; + + /// blockVersion >= 3.1.0, use executor build + if (blockVersion >= (uint32_t)BlockVersion::V3_1_VERSION) + { + return; + } + buildDir(tool::FS_ROOT, blockVersion); + // root table must exist + + Entry rootSubEntry; + std::map rootSubMap; + for (const auto& sub : rootSubNames | RANGES::views::transform( + [](std::string_view const& sub) -> std::string_view { + return sub.substr(1); + })) + { + rootSubMap.insert(std::make_pair(sub, FS_TYPE_DIR)); + } + rootSubEntry.importFields({asString(codec::scale::encode(rootSubMap))}); + std::promise setPromise; + m_storage->asyncSetRow( + FS_ROOT, FS_KEY_SUB, std::move(rootSubEntry), [&setPromise](auto&& error) { + setPromise.set_value(std::forward(error)); + }); + auto setError = setPromise.get_future().get(); + if (setError) + { + BOOST_THROW_EXCEPTION(*setError); + } + + buildDir(tool::FS_USER, blockVersion); + buildDir(tool::FS_APPS, blockVersion); + buildDir(tool::FS_USER_TABLE, blockVersion); + auto sysTable = buildDir(tool::FS_SYS_BIN, blockVersion); + Entry sysSubEntry; + std::map sysSubMap; + for (const auto& contract : + precompiled::BFS_SYS_SUBS | + RANGES::views::transform([](std::string_view const& sub) -> std::string_view { + return sub.substr(tool::FS_SYS_BIN.length() + 1); + })) + { + sysSubMap.insert(std::make_pair(contract, FS_TYPE_CONTRACT)); + } + sysSubEntry.importFields({asString(codec::scale::encode(sysSubMap))}); + sysTable->setRow(FS_KEY_SUB, std::move(sysSubEntry)); +} + +std::optional Ledger::buildDir( + const std::string_view& _absoluteDir, uint32_t blockVersion, std::string valueField) +{ + std::promise>> createPromise; + m_storage->asyncCreateTable(std::string(_absoluteDir), std::move(valueField), + [&createPromise](auto&& error, std::optional
&& _table) { + createPromise.set_value({std::forward(error), std::move(_table)}); + }); + auto [createError, table] = createPromise.get_future().get(); + if (createError) + { + BOOST_THROW_EXCEPTION(*createError); + } + if (blockVersion >= (uint32_t)BlockVersion::V3_1_VERSION) + { + // >= 3.1.0 logic + return table; + } + // 3.0.0 logic + Entry tEntry; + Entry newSubEntry; + Entry aclTypeEntry; + Entry aclWEntry; + Entry aclBEntry; + Entry extraEntry; + std::map newSubMap; + newSubEntry.importFields({asString(codec::scale::encode(newSubMap))}); + tEntry.importFields({std::string(FS_TYPE_DIR)}); + aclTypeEntry.importFields({"0"}); + aclWEntry.importFields({""}); + aclBEntry.importFields({""}); + extraEntry.importFields({""}); + table->setRow(FS_KEY_TYPE, std::move(tEntry)); + table->setRow(FS_KEY_SUB, std::move(newSubEntry)); + table->setRow(FS_ACL_TYPE, std::move(aclTypeEntry)); + table->setRow(FS_ACL_WHITE, std::move(aclWEntry)); + table->setRow(FS_ACL_BLACK, std::move(aclBEntry)); + table->setRow(FS_KEY_EXTRA, std::move(extraEntry)); + return table; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-ledger/src/libledger/Ledger.h" "b/BFPL\345\243\271/bcos-ledger/src/libledger/Ledger.h" new file mode 100644 index 00000000..771f7394 --- /dev/null +++ "b/BFPL\345\243\271/bcos-ledger/src/libledger/Ledger.h" @@ -0,0 +1,152 @@ +/** + * Copyright (C) 2021 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 Ledger.h + * @author: kyonRay + * @date 2021-04-13 + */ +#pragma once +#include "bcos-framework/ledger/LedgerInterface.h" +#include "bcos-framework/ledger/LedgerTypeDef.h" +#include "bcos-framework/protocol/BlockFactory.h" +#include "bcos-framework/protocol/BlockHeaderFactory.h" +#include "bcos-framework/protocol/ProtocolTypeDef.h" +#include "bcos-framework/storage/Common.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "utilities/MerkleProofUtility.h" +#include +#include +#include +#include + +#define LEDGER_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("LEDGER") + +namespace bcos::ledger +{ +class Ledger : public LedgerInterface, public std::enable_shared_from_this +{ +public: + Ledger(bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::storage::StorageInterface::Ptr _storage) + : m_blockFactory(std::move(_blockFactory)), m_storage(std::move(_storage)) + { + assert(m_blockFactory); + assert(m_storage); + }; + + ~Ledger() override = default; + + void asyncPreStoreBlockTxs(bcos::protocol::TransactionsPtr _blockTxs, + bcos::protocol::Block::ConstPtr block, + std::function _callback) override; + void asyncPrewriteBlock(bcos::storage::StorageInterface::Ptr storage, + bcos::protocol::TransactionsPtr _blockTxs, bcos::protocol::Block::ConstPtr block, + std::function callback) override; + + void asyncStoreTransactions(std::shared_ptr> _txToStore, + crypto::HashListPtr _txHashList, std::function _onTxStored) override; + + void asyncGetBlockDataByNumber(bcos::protocol::BlockNumber _blockNumber, int32_t _blockFlag, + std::function _onGetBlock) override; + + void asyncGetBlockNumber( + std::function _onGetBlock) override; + + void asyncGetBlockHashByNumber(bcos::protocol::BlockNumber _blockNumber, + std::function _onGetBlock) override; + + void asyncGetBlockNumberByHash(const crypto::HashType& _blockHash, + std::function _onGetBlock) override; + + void asyncGetBatchTxsByHashList(crypto::HashListPtr _txHashList, bool _withProof, + std::function>)> + _onGetTx) override; + + void asyncGetTransactionReceiptByHash(bcos::crypto::HashType const& _txHash, bool _withProof, + std::function + _onGetTx) override; + + void asyncGetTotalTransactionCount( + std::function _callback) + override; + + void asyncGetSystemConfigByKey(const std::string_view& _key, + std::function _onGetConfig) + override; + + void asyncGetNonceList(bcos::protocol::BlockNumber _startNumber, int64_t _offset, + std::function>)> + _onGetList) override; + + void asyncGetNodeListByType(const std::string_view& _type, + std::function _onGetConfig) override; + + /****** init ledger ******/ + bool buildGenesisBlock(LedgerConfig::Ptr _ledgerConfig, size_t _gasLimit, + const std::string_view& _genesisData, std::string const& _compatibilityVersion); + +private: + Error::Ptr checkTableValid(Error::UniquePtr&& error, + const std::optional& table, const std::string_view& tableName); + + Error::Ptr checkEntryValid(Error::UniquePtr&& error, + const std::optional& entry, const std::string_view& key); + + void asyncGetBlockHeader(bcos::protocol::Block::Ptr block, + bcos::protocol::BlockNumber blockNumber, std::function callback); + + void asyncGetBlockTransactionHashes(bcos::protocol::BlockNumber blockNumber, + std::function&&)> callback); + + void asyncBatchGetTransactions(std::shared_ptr> hashes, + std::function&&)> callback); + + void asyncBatchGetReceipts(std::shared_ptr> hashes, + std::function&&)> + callback); + + void asyncGetSystemTableEntry(const std::string_view& table, const std::string_view& key, + std::function&&)> callback); + + void getTxProof(const crypto::HashType& _txHash, + std::function _onGetProof); + + void getReceiptProof(protocol::TransactionReceipt::Ptr _receipt, + std::function _onGetProof); + + void createFileSystemTables(uint32_t blockVersion); + + std::optional buildDir(const std::string_view& _absoluteDir, + uint32_t blockVersion, std::string valueField = SYS_VALUE); + + // only for /sys/ + inline std::string getSysBaseName(const std::string& _s) + { + return _s.substr(_s.find_last_of('/') + 1); + } + + std::tuple>> + needStoreUnsavedTxs( + bcos::protocol::TransactionsPtr _blockTxs, bcos::protocol::Block::ConstPtr _block); + + bcos::protocol::BlockFactory::Ptr m_blockFactory; + bcos::storage::StorageInterface::Ptr m_storage; + + mutable RecursiveMutex m_mutex; +}; +} // namespace bcos::ledger \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-ledger/src/libledger/utilities/Common.h" "b/BFPL\345\243\271/bcos-ledger/src/libledger/utilities/Common.h" new file mode 100644 index 00000000..d11e3024 --- /dev/null +++ "b/BFPL\345\243\271/bcos-ledger/src/libledger/utilities/Common.h" @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2021 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: kyonRay + * @date 2021-04-13 + */ +#pragma once +#include +#include +#include +#include + +#define LEDGER_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("LEDGER") + +namespace bcos::ledger +{ +// parent=>children +using Parent2ChildListMap = std::map>; +// child=>parent +using Child2ParentMap = tbb::concurrent_unordered_map; + +static const char* const SYS_VALUE = "value"; +static const char* const SYS_CONFIG_ENABLE_BLOCK_NUMBER = "enable_number"; +static const char* const SYS_VALUE_AND_ENABLE_BLOCK_NUMBER = "value,enable_number"; + +// FileSystem paths +static const char* const FS_ROOT = "/"; +static const char* const FS_APPS = "/apps"; +static const char* const FS_USER = "/usr"; +static const char* const FS_SYS_BIN = "/sys"; +static const char* const FS_USER_TABLE = "/tables"; + +enum LedgerError : int32_t +{ + SUCCESS = 0, + OpenTableFailed = 3001, + CallbackError = 3002, + ErrorArgument = 3003, + DecodeError = 3004, + ErrorCommitBlock = 3005, + CollectAsyncCallbackError = 3006, + LedgerLockError = 3007, + GetStorageError = 3008, + EmptyEntry = 3009, + UnknownError = 3010, +}; + +} // namespace bcos::ledger diff --git "a/BFPL\345\243\271/bcos-ledger/src/libledger/utilities/MerkleProofUtility.cpp" "b/BFPL\345\243\271/bcos-ledger/src/libledger/utilities/MerkleProofUtility.cpp" new file mode 100644 index 00000000..e6094db2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-ledger/src/libledger/utilities/MerkleProofUtility.cpp" @@ -0,0 +1,93 @@ +/** + * Copyright (C) 2021 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 MerkleProofUtility.cpp + * @author: kyonRay + * @date 2021-05-24 + */ + +#include "MerkleProofUtility.h" + +using namespace bcos; +using namespace bcos::ledger; + +namespace bcos +{ +namespace ledger +{ +void MerkleProofUtility::makeMerkleProof(const crypto::HashType& _txHash, + const std::shared_ptr& parent2ChildList, + const std::shared_ptr& child2Parent, const std::shared_ptr& merkleProof) +{ + std::string merkleNode = _txHash.hex(); + // get child=>parent info + auto itChild2Parent = child2Parent->find(merkleNode); + while (itChild2Parent != child2Parent->end()) + { + // find parent=>childrenList info + auto itParent2ChildList = parent2ChildList->find(itChild2Parent->second); + if (itParent2ChildList == parent2ChildList->end()) + { + break; + } + // get index from itParent2ChildList->second by merkleNode + auto itChildList = std::find( + itParent2ChildList->second.begin(), itParent2ChildList->second.end(), merkleNode); + if (itChildList == itParent2ChildList->second.end()) + { + break; + } + // leftPath = [childrenList.begin, index) + std::vector leftPath{}; + // rightPath = (index, childrenList.end] + std::vector rightPath{}; + leftPath.insert(leftPath.end(), itParent2ChildList->second.begin(), itChildList); + rightPath.insert(rightPath.end(), std::next(itChildList), itParent2ChildList->second.end()); + + auto singleTree = std::make_pair(std::move(leftPath), std::move(rightPath)); + merkleProof->emplace_back(singleTree); + + // node=parent + merkleNode = itChild2Parent->second; + itChild2Parent = child2Parent->find(merkleNode); + } +} + +std::shared_ptr MerkleProofUtility::getChild2Parent( + const std::shared_ptr& _parent2Child) +{ + auto child2Parent = std::make_shared(); + + // trans parent2ChildList into child2Parent concurrently + tbb::parallel_for_each( + _parent2Child->begin(), _parent2Child->end(), [&](auto const& _childListIterator) { + tbb::parallel_for(tbb::blocked_range(0, _childListIterator.second.size()), + [&](const tbb::blocked_range& range) { + for (size_t i = range.begin(); i < range.end(); i++) + { + std::string child = _childListIterator.second[i]; + if (!child.empty()) + { + child2Parent->insert({child, _childListIterator.first}); + } + } + }); + }); + + return child2Parent; +} + +} // namespace ledger +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-ledger/src/libledger/utilities/MerkleProofUtility.h" "b/BFPL\345\243\271/bcos-ledger/src/libledger/utilities/MerkleProofUtility.h" new file mode 100644 index 00000000..9c06ea53 --- /dev/null +++ "b/BFPL\345\243\271/bcos-ledger/src/libledger/utilities/MerkleProofUtility.h" @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2021 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 MerkleProofUtility.h + * @author: kyonRay + * @date 2021-04-29 + */ + +#pragma once + +#include "Common.h" +#include +#include +#include +#include +#include +#include + +#include + +namespace bcos::ledger +{ +class MerkleProofUtility +{ +public: + template + void getMerkleProof(const crypto::HashType& _txHash, T _ts, crypto::CryptoSuite::Ptr _crypto, + const std::shared_ptr& merkleProof) + { + auto parent2Child = getParent2ChildList(_crypto, _ts); + auto child2Parent = getChild2Parent(parent2Child); + makeMerkleProof(_txHash, std::move(parent2Child), std::move(child2Parent), merkleProof); + } + + template + std::shared_ptr getParent2ChildList( + crypto::CryptoSuite::Ptr _crypto, T _ts) + { + auto merklePath = std::make_shared(); + tbb::concurrent_vector tsVector; + tsVector.resize(_ts.size()); + tbb::parallel_for(tbb::blocked_range(0, _ts.size()), + [_ts = std::move(_ts), &tsVector](const tbb::blocked_range& _r) { + for (uint32_t i = _r.begin(); i < _r.end(); ++i) + { + crypto::HashType hash = ((_ts)[i])->hash(); + tsVector[i] = hash.asBytes(); + } + }); + auto tsList = std::vector(tsVector.begin(), tsVector.end()); + protocol::calculateMerkleProof(std::move(_crypto), tsList, merklePath); + return merklePath; + } + + void makeMerkleProof(const crypto::HashType& _txHash, + const std::shared_ptr& parent2ChildList, + const std::shared_ptr& child2Parent, + const std::shared_ptr& merkleProof); + + std::shared_ptr getChild2Parent( + const std::shared_ptr& _parent2Child); +}; + + +} // namespace bcos::ledger \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-ledger/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-ledger/test/CMakeLists.txt" new file mode 100644 index 00000000..5e6b08df --- /dev/null +++ "b/BFPL\345\243\271/bcos-ledger/test/CMakeLists.txt" @@ -0,0 +1,32 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of bcos-ledger +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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(GLOB_RECURSE SOURCES "*.cpp") + +# cmake settings +list(APPEND CMAKE_MODULE_PATH ${BCOS_CMAKE_SCRIPTS_DIR}) +set(TEST_BINARY_NAME test-bcos-ledger) + +add_executable(${TEST_BINARY_NAME} ${SOURCES}) +target_include_directories(${TEST_BINARY_NAME} PRIVATE ${CMAKE_SOURCE_DIR}) +target_include_directories(${TEST_BINARY_NAME} PRIVATE ${CMAKE_BINARY_DIR}/bcos-framework) + +find_package(Boost REQUIRED unit_test_framework) + +target_link_libraries(${TEST_BINARY_NAME} ${LEDGER_TARGET} ${CRYPTO_TARGET} ${TARS_PROTOCOL_TARGET} Boost::unit_test_framework) +target_compile_definitions(${TEST_BINARY_NAME} PUBLIC _TESTS_) +add_test(NAME test-ledger WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-ledger/test/main/main.cpp" "b/BFPL\345\243\271/bcos-ledger/test/main/main.cpp" new file mode 100644 index 00000000..833452c8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-ledger/test/main/main.cpp" @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2021 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 main.cpp + * @author: kyonRay + * @date 2021-05-14 + */ + +#define BOOST_TEST_NO_MAIN + +#include +#include + +int main(int argc, const char* argv[]) +{ + auto fakeInit = [](int, char**) -> boost::unit_test::test_suite* { return nullptr; }; + int result = boost::unit_test::unit_test_main(fakeInit, argc, const_cast(argv)); + return result; +} diff --git "a/BFPL\345\243\271/bcos-ledger/test/mock/MockKeyFactor.h" "b/BFPL\345\243\271/bcos-ledger/test/mock/MockKeyFactor.h" new file mode 100644 index 00000000..0ae0ebfd --- /dev/null +++ "b/BFPL\345\243\271/bcos-ledger/test/mock/MockKeyFactor.h" @@ -0,0 +1,102 @@ +/** + * Copyright (C) 2021 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 MockKeyFactor.h + * @author: kyonRay + * @date 2021-05-14 + */ + +#pragma once + +#include + +using namespace bcos; +using namespace bcos::crypto; + +namespace bcos +{ +namespace test +{ +DERIVE_BCOS_EXCEPTION(InvalidKey); +class MockKey : public bcos::crypto::KeyInterface +{ +public: + using Ptr = std::shared_ptr; + explicit MockKey(size_t _keySize) : m_keyData(std::make_shared(_keySize)) {} + explicit MockKey(bytesConstRef _data) : m_keyData(std::make_shared()) { decode(_data); } + explicit MockKey(bytes const& _data) : MockKey(ref(_data)) {} + explicit MockKey(size_t _keySize, std::shared_ptr _data) + : m_keyData(std::make_shared()) + { + if (_data->size() < _keySize) + { + BOOST_THROW_EXCEPTION(InvalidKey() << errinfo_comment( + "invalidKey, the key size: " + std::to_string(_data->size()) + + ", expected size:" + std::to_string(_keySize))); + } + *m_keyData = *_data; + } + + bool operator==(MockKey const& _comparedKey) { return (*m_keyData == _comparedKey.data()); } + bool operator!=(MockKey const& _comparedKey) { return !operator==(_comparedKey); } + + ~MockKey() override {} + + const bytes& data() const override { return *m_keyData; } + size_t size() const override { return m_keyData->size(); } + char* mutableData() override { return (char*)m_keyData->data(); } + const char* constData() const override { return (const char*)m_keyData->data(); } + std::shared_ptr encode() const override { return m_keyData; } + void decode(bytesConstRef _data) override { *m_keyData = _data.toBytes(); } + + void decode(bytes&& _data) override { *m_keyData = std::move(_data); } + + std::string shortHex() override + { + auto startIt = m_keyData->begin(); + auto endIt = m_keyData->end(); + if (m_keyData->size() > 4) + { + endIt = startIt + 4 * sizeof(byte); + } + return *toHexString(startIt, endIt) + "..."; + } + + std::string hex() override { return *toHexString(*m_keyData); } + +private: + std::shared_ptr m_keyData; +}; + +class MockKeyFactory : public bcos::crypto::KeyFactory +{ +public: + using Ptr = std::shared_ptr; + MockKeyFactory() = default; + ~MockKeyFactory() override {} + + KeyInterface::Ptr createKey(bytesConstRef _keyData) override + { + return std::make_shared(_keyData); + } + + KeyInterface::Ptr createKey(bytes const& _keyData) override + { + return std::make_shared(_keyData); + } +}; + +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-ledger/test/mock/MockStorage.h" "b/BFPL\345\243\271/bcos-ledger/test/mock/MockStorage.h" new file mode 100644 index 00000000..263c82bc --- /dev/null +++ "b/BFPL\345\243\271/bcos-ledger/test/mock/MockStorage.h" @@ -0,0 +1,460 @@ +/** + * Copyright (C) 2021 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 MockStorage.h + * @author: kyonRay + * @date 2021-05-06 + */ + +#pragma once + +#include "bcos-framework/storage/Common.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-framework/storage/Table.h" +#include "bcos-ledger/libledger/utilities/Common.h" +#include +#define SLEEP_MILLI_SECONDS 10 + +using namespace bcos::storage; +using namespace bcos::ledger; +namespace bcos::test +{ +class MockStorage : public StorageInterface +{ +public: + MockStorage() + { + data[storage::SYS_TABLE] = std::map(); + m_threadPool = std::make_shared("mockStorage", 4); + } + virtual ~MockStorage() { m_threadPool->stop(); } + + std::vector getPrimaryKeys(const std::shared_ptr& _tableInfo, + const Condition::Ptr& _condition) const override + { + std::vector ret; + std::lock_guard lock(m_mutex); + if (data.count(_tableInfo->name)) + { + for (auto& kv : data.at(_tableInfo->name)) + { + if (!_condition || _condition->isValid(kv.first)) + { + ret.emplace_back(kv.first); + } + } + } + return ret; + } + std::shared_ptr getRow( + const std::shared_ptr& _tableInfo, const std::string_view& _key) override + { + std::shared_ptr ret = nullptr; + std::lock_guard lock(m_mutex); + if (data.find(_tableInfo->name) != data.end()) + { + if (data[_tableInfo->name].find(std::string(_key)) != data[_tableInfo->name].end()) + { + return data[_tableInfo->name][std::string(_key)]; + } + else + { + LEDGER_LOG(TRACE) << LOG_BADGE("getRow") << LOG_DESC("can't find key") + << LOG_KV("key", _key); + } + } + return ret; + } + std::map> getRows( + const std::shared_ptr& _tableInfo, + const std::vector& _keys) override + { + std::map> ret; + std::lock_guard lock(m_mutex); + if (data.count(_tableInfo->name)) + { + for (auto& key : _keys) + { + if (data[_tableInfo->name].count(std::string(key))) + { + ret[key] = data[_tableInfo->name][key]; + } + } + } + return ret; + } + std::pair commitBlock(protocol::BlockNumber _number, + const std::vector>& _tableInfos, + const std::vector>>& _tableDatas) override + { + size_t total = 0; + if (_tableInfos.size() != _tableDatas.size()) + { + auto error = std::make_shared(-1, ""); + return {0, error}; + } + std::shared_ptr stateTableFactory = nullptr; + if (_number != 0) + { + if (m_number2TableFactory.count(_number)) + { + stateTableFactory = m_number2TableFactory[_number]; + } + else + { + return {0, std::make_shared(StorageErrorCode::StateCacheNotFound, + std::to_string(_number) + "state cache not found")}; + } + auto stateData = stateTableFactory->exportData(_number); + stateData.first.insert(stateData.first.end(), _tableInfos.begin(), _tableInfos.end()); + stateData.second.insert(stateData.second.end(), _tableDatas.begin(), _tableDatas.end()); + std::lock_guard lock(m_mutex); + for (size_t i = 0; i < stateData.first.size(); ++i) + { + for (auto& item : *stateData.second[i]) + { + if (item.second->getStatus() == Entry::Status::NORMAL) + { + data[stateData.first[i]->name][item.first] = item.second; + ++total; + } + } + } + } + else + { + std::lock_guard lock(m_mutex); + for (size_t i = 0; i < _tableInfos.size(); ++i) + { + for (auto& item : *_tableDatas[i]) + { + if (item.second->getStatus() == Entry::Status::NORMAL) + { + data[_tableInfos[i]->name][item.first] = item.second; + ++total; + } + } + } + } + return {total, nullptr}; + } + + void asyncGetPrimaryKeys(const std::shared_ptr& _tableInfo, + const Condition::Ptr& _condition, + std::function&)> _callback) override + { + auto self = + std::weak_ptr(std::dynamic_pointer_cast(shared_from_this())); + m_threadPool->enqueue([_tableInfo, _condition, _callback, self]() { + auto storage = self.lock(); + if (storage) + { + time_t t = time(0); + auto keyList = storage->getPrimaryKeys(_tableInfo, _condition); + boost::this_thread::sleep_for(boost::chrono::milliseconds(SLEEP_MILLI_SECONDS)); + auto success = std::make_shared(0, ""); + LEDGER_LOG(TRACE) << LOG_BADGE("asyncGetPrimaryKeys") + << LOG_DESC("storage getKeys finish") + << LOG_KV("tableName", _tableInfo->name) + << LOG_KV("exec_time", time(0) - t); + t = time(0); + _callback(success, keyList); + LEDGER_LOG(TRACE) << LOG_BADGE("asyncGetPrimaryKeys") + << LOG_DESC("storage callback") + << LOG_KV("tableName", _tableInfo->name) + << LOG_KV("callback_time", time(0) - t); + } + else + { + _callback(std::make_shared(-1, ""), {}); + } + }); + } + + void asyncGetRow(const TableInfo::Ptr& _tableInfo, const std::string_view& _key, + std::function _callback) override + { + auto key = std::string(_key); + auto self = + std::weak_ptr(std::dynamic_pointer_cast(shared_from_this())); + m_threadPool->enqueue([_tableInfo, key, _callback, self]() { + auto storage = self.lock(); + if (storage) + { + time_t t = time(0); + auto entry = storage->getRow(_tableInfo, key); + boost::this_thread::sleep_for(boost::chrono::milliseconds(SLEEP_MILLI_SECONDS)); + LEDGER_LOG(TRACE) << LOG_BADGE("asyncGetRow") << LOG_DESC("storage getRow finish") + << LOG_KV("tableName", _tableInfo->name) + << LOG_KV("key", std::string(key)) + << LOG_KV("exec_time", time(0) - t); + t = time(0); + _callback(nullptr, entry); + LEDGER_LOG(TRACE) << LOG_BADGE("asyncGetRow") << LOG_DESC("storage callback") + << LOG_KV("tableName", _tableInfo->name) + << LOG_KV("key", std::string(key)) + << LOG_KV("callback_time", time(0) - t); + } + else + { + _callback(std::make_shared(-1, ""), nullptr); + } + }); + } + void asyncGetRows(const std::shared_ptr& _tableInfo, + const std::shared_ptr>& _keyList, + std::function&)> _callback) + override + { + auto self = + std::weak_ptr(std::dynamic_pointer_cast(shared_from_this())); + m_threadPool->enqueue([_tableInfo, _keyList, _callback, self]() { + auto storage = self.lock(); + if (storage) + { + time_t t = time(0); + auto rowMap = storage->getRows(_tableInfo, *_keyList); + boost::this_thread::sleep_for(boost::chrono::milliseconds(SLEEP_MILLI_SECONDS)); + LEDGER_LOG(TRACE) << LOG_BADGE("asyncGetRows") << LOG_DESC("storage getRows finish") + << LOG_KV("tableName", _tableInfo->name) + << LOG_KV("exec_time", time(0) - t); + t = time(0); + _callback(nullptr, rowMap); + LEDGER_LOG(TRACE) << LOG_BADGE("asyncGetRows") << LOG_DESC("storage callback") + << LOG_KV("tableName", _tableInfo->name) + << LOG_KV("callback_time", time(0) - t); + } + else + { + _callback(std::make_shared(-1, ""), {}); + } + }); + } + + void asyncCommitBlock(protocol::BlockNumber _number, + const std::shared_ptr>>& _tableInfo, + const std::shared_ptr>>>& + _tableMap, + std::function _callback) override + { + auto self = + std::weak_ptr(std::dynamic_pointer_cast(shared_from_this())); + m_threadPool->enqueue([_number, _tableInfo, _tableMap, _callback, self]() { + auto storage = self.lock(); + if (storage) + { + time_t t = time(0); + auto retPair = storage->commitBlock(_number, *_tableInfo, *_tableMap); + boost::this_thread::sleep_for(boost::chrono::milliseconds(SLEEP_MILLI_SECONDS)); + auto error = std::make_shared(0, ""); + LEDGER_LOG(TRACE) << LOG_BADGE("asyncCommitBlock") + << LOG_DESC("storage commit finish") << LOG_KV("number", _number) + << LOG_KV("exec_time", time(0) - t); + t = time(0); + _callback(error, retPair.first); + LEDGER_LOG(TRACE) << LOG_BADGE("asyncCommitBlock") << LOG_DESC("storage callback") + << LOG_KV("callback_time", time(0) - t); + } + else + { + _callback(std::make_shared(-1, ""), -1); + } + }); + } + + // cache TableFactory + void asyncAddStateCache(protocol::BlockNumber _number, + const std::shared_ptr& _table, + std::function _callback) override + { + auto self = + std::weak_ptr(std::dynamic_pointer_cast(shared_from_this())); + m_threadPool->enqueue([_number, _table, _callback, self]() { + auto storage = self.lock(); + if (storage) + { + boost::this_thread::sleep_for(boost::chrono::milliseconds(SLEEP_MILLI_SECONDS)); + storage->addStateCache(_number, _table); + _callback(nullptr); + } + else + { + _callback(std::make_shared(-1, "")); + } + }); + } + void asyncDropStateCache(protocol::BlockNumber, std::function) override + {} + void asyncGetStateCache(protocol::BlockNumber _blockNumber, + std::function&)> + _callback) override + { + auto self = + std::weak_ptr(std::dynamic_pointer_cast(shared_from_this())); + m_threadPool->enqueue([_blockNumber, _callback, self]() { + auto storage = self.lock(); + if (storage) + { + auto tableFactory = storage->getStateCache(_blockNumber); + boost::this_thread::sleep_for(boost::chrono::milliseconds(SLEEP_MILLI_SECONDS)); + auto error = std::make_shared(0, ""); + _callback(error, tableFactory); + } + else + { + _callback(std::make_shared(-1, ""), nullptr); + } + }); + } + + std::shared_ptr getStateCache( + protocol::BlockNumber _blockNumber) override + { + if (m_number2TableFactory.count(_blockNumber)) + { + return m_number2TableFactory[_blockNumber]; + } + return nullptr; + } + + void dropStateCache(protocol::BlockNumber) override {} + void addStateCache(protocol::BlockNumber _blockNumber, + const std::shared_ptr& _tableFactory) override + { + m_number2TableFactory[_blockNumber] = _tableFactory; + } + // KV store in split database, used to store data off-chain + Error::Ptr put( + const std::string_view&, const std::string_view&, const std::string_view&) override + { + return nullptr; + } + std::pair get( + const std::string_view&, const std::string_view&) override + { + return {"", nullptr}; + } + Error::Ptr remove(const std::string_view&, const std::string_view&) override { return nullptr; } + void asyncRemove(const std::string_view&, const std::string_view&, + std::function) override + {} + void asyncPut(const std::string_view&, const std::string_view&, const std::string_view&, + std::function) override + {} + void asyncGet(const std::string_view&, const std::string_view&, + std::function) override + {} + void asyncGetBatch(const std::string_view&, const std::shared_ptr>&, + std::function>&)>) + override + {} + +private: + bcos::ThreadPool::Ptr m_threadPool = nullptr; + std::map> data; + mutable std::mutex m_mutex; + std::map m_number2TableFactory; +}; + +class MockErrorStorage : public MockStorage +{ +public: + MockErrorStorage() : MockStorage() {} + std::vector getPrimaryKeys(const std::shared_ptr& _tableInfo, + const Condition::Ptr& _condition) const override + { + return MockStorage::getPrimaryKeys(_tableInfo, _condition); + } + std::shared_ptr getRow( + const std::shared_ptr& _tableInfo, const std::string_view& _key) override + { + return MockStorage::getRow(_tableInfo, _key); + } + std::map> getRows( + const std::shared_ptr& _tableInfo, + const std::vector& _keys) override + { + return MockStorage::getRows(_tableInfo, _keys); + } + std::pair commitBlock(protocol::BlockNumber number, + const std::vector>& _tableInfos, + const std::vector>>& _tableDatas) override + { + return MockStorage::commitBlock(number, _tableInfos, _tableDatas); + } + std::shared_ptr getStateCache( + protocol::BlockNumber _blockNumber) override + { + return MockStorage::getStateCache(_blockNumber); + } + void addStateCache(protocol::BlockNumber _blockNumber, + const std::shared_ptr& _tableFactory) override + { + MockStorage::addStateCache(_blockNumber, _tableFactory); + } + void asyncGetPrimaryKeys(const std::shared_ptr& _tableInfo, + const Condition::Ptr& _condition, + std::function&)> _callback) override + { + MockStorage::asyncGetPrimaryKeys(_tableInfo, _condition, _callback); + } + void asyncGetRow(const TableInfo::Ptr& _tableInfo, const std::string_view& _key, + std::function _callback) override + { + MockStorage::asyncGetRow(_tableInfo, _key, _callback); + } + void asyncGetRows(const std::shared_ptr& _tableInfo, + const std::shared_ptr>& _keyList, + std::function&)> _callback) + override + { + MockStorage::asyncGetRows(_tableInfo, _keyList, _callback); + } + void asyncCommitBlock(protocol::BlockNumber, + const std::shared_ptr>>&, + const std::shared_ptr>>>&, + std::function _callback) override + { + _callback(std::make_shared(-1, ""), 0); + } + void asyncAddStateCache(protocol::BlockNumber, const std::shared_ptr&, + std::function _callback) override + { + _callback(std::make_shared(-1, "")); + } + void asyncGetStateCache(protocol::BlockNumber _number, + std::function&)> + _callback) override + { + // random get success + auto rand = random(); + if (rand & 1) + { + if (rand & 3) + { + _callback(nullptr, nullptr); + } + else + { + _callback(std::make_shared(-1, ""), nullptr); + } + } + else + { + MockStorage::asyncGetStateCache(_number, _callback); + } + } +}; +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-ledger/test/mock/MockTable.h" "b/BFPL\345\243\271/bcos-ledger/test/mock/MockTable.h" new file mode 100644 index 00000000..dc63ea4d --- /dev/null +++ "b/BFPL\345\243\271/bcos-ledger/test/mock/MockTable.h" @@ -0,0 +1,101 @@ +/** + * Copyright (C) 2021 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 MockTable.h + * @author: kyonRay + * @date 2021-05-07 + */ +#pragma once + +#include "bcos-framework/storage/Common.h" +#include "bcos-framework/storage/Table.h" +#include "bcos-ledger/libledger/utilities/Common.h" +#include + +using namespace bcos::storage; +using namespace bcos::ledger; + +namespace bcos::test +{ +class MockTable : public Table +{ +public: + using Ptr = std::shared_ptr; + + explicit MockTable(std::string const& _tableName) + : Table(nullptr, nullptr, 0), m_tableName(_tableName) + {} + + std::shared_ptr getRow(const std::string& _key) override + { + auto entry = m_fakeStorage[_key]; + if (entry) + { + return entry; + } + return nullptr; + } + + bool setRow(const std::string& _key, const std::shared_ptr& _entry) override + { + m_fakeStorage[_key] = _entry; + return true; + } + + std::vector getPrimaryKeys(const Condition::Ptr&) const override + { + std::vector keys; + keys.reserve(m_fakeStorage.size()); + std::transform(m_fakeStorage.begin(), m_fakeStorage.end(), keys.begin(), + [](auto pair) { return pair.first; }); + return keys; + } + +private: + std::string m_tableName; + std::unordered_map m_fakeStorage; +}; + +class MockErrorTableFactory : public TableStorage +{ +public: + explicit MockErrorTableFactory(storage::StorageInterface::Ptr _db) + : TableStorage(_db, nullptr, -1) + { + m_sysTables.emplace_back(TableStorage::SYS_TABLES); + } + std::shared_ptr openTable(const std::string& _tableName) override + { + if (std::find(m_sysTables.begin(), m_sysTables.end(), _tableName) != m_sysTables.end()) + { + return TableFactory::openTable(_tableName); + } + else + { + return nullptr; + } + } + bool createTable(const std::string& _tableName, const std::string& _keyField, + const std::string& _valueFields) override + { + if (_tableName.at(0) == '/') + m_sysTables.emplace_back(_tableName); + return TableFactory::createTable(_tableName, _keyField, _valueFields); + } + std::pair commit() override { return {0, std::make_shared(-1, "")}; } + + std::vector m_sysTables; +}; +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-ledger/test/unittests/ledger/LedgerTest.cpp" "b/BFPL\345\243\271/bcos-ledger/test/unittests/ledger/LedgerTest.cpp" new file mode 100644 index 00000000..ec6dfe73 --- /dev/null +++ "b/BFPL\345\243\271/bcos-ledger/test/unittests/ledger/LedgerTest.cpp" @@ -0,0 +1,1164 @@ +/** + * Copyright (C) 2021 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 LedgerTest.cpp + * @author: kyonRay + * @date 2021-05-07 + * @file LedgerTest.cpp + * @author: ancelmo + * @date 2021-09-07 + */ + +#include "bcos-ledger/src/libledger/Ledger.h" +#include "../../mock/MockKeyFactor.h" +#include "bcos-crypto/interfaces/crypto/KeyPairInterface.h" +#include "bcos-framework/ledger/LedgerTypeDef.h" +#include "bcos-framework/protocol/Protocol.h" +#include "bcos-ledger/src/libledger/utilities/Common.h" +#include "bcos-tool/BfsFileFactory.h" +#include "bcos-tool/ConsensusNode.h" +#include "common/FakeBlock.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::ledger; +using namespace bcos::protocol; +using namespace bcos::storage; +using namespace bcos::crypto; +using namespace bcos::tool; + +namespace std +{ +inline ostream& operator<<(ostream& os, const std::optional& entry) +{ + os << entry.has_value(); + return os; +} + +inline ostream& operator<<(ostream& os, const std::optional
& table) +{ + os << table.has_value(); + return os; +} + +inline ostream& operator<<(ostream& os, const std::unique_ptr& error) +{ + os << error->what(); + return os; +} +} // namespace std + +namespace bcos::test +{ +class MockStorage : public virtual StateStorage +{ +public: + MockStorage(std::shared_ptr prev) + : storage::StateStorageInterface(prev), StateStorage(prev) + {} + bcos::Error::Ptr setRows(std::string_view table, std::vector keys, + std::vector values) override + { + for (size_t i = 0; i < keys.size(); ++i) + { + Entry e; + e.set(std::string(values[i])); + asyncSetRow(table, keys[i], e, [](Error::UniquePtr) {}); + } + return nullptr; + } +}; +class LedgerFixture : public TestPromptFixture +{ +public: + LedgerFixture() : TestPromptFixture() + { + m_blockFactory = createBlockFactory(createCryptoSuite()); + auto keyFactor = std::make_shared(); + m_blockFactory->cryptoSuite()->setKeyFactory(keyFactor); + + BOOST_CHECK(m_blockFactory != nullptr); + BOOST_CHECK(m_blockFactory->blockHeaderFactory() != nullptr); + BOOST_CHECK(m_blockFactory->transactionFactory() != nullptr); + initStorage(); + } + ~LedgerFixture() = default; + + inline void initStorage() + { + auto hashImpl = std::make_shared(); + auto memoryStorage = std::make_shared(nullptr); + memoryStorage->setEnableTraverse(true); + auto storage = std::make_shared(memoryStorage); + storage->setEnableTraverse(true); + m_storage = storage; + BOOST_TEST(m_storage != nullptr); + m_ledger = std::make_shared(m_blockFactory, m_storage); + BOOST_CHECK(m_ledger != nullptr); + } + + inline void initErrorStorage() + { + auto memoryStorage = std::make_shared(nullptr); + memoryStorage->setEnableTraverse(true); + auto storage = std::make_shared(memoryStorage); + storage->setEnableTraverse(true); + m_storage = storage; + BOOST_TEST(m_storage != nullptr); + m_ledger = std::make_shared(m_blockFactory, m_storage); + BOOST_CHECK(m_ledger != nullptr); + } + + inline void initFixture(std::string version = bcos::protocol::V3_1_VERSION_STR) + { + m_param = std::make_shared(); + m_param->setBlockNumber(0); + m_param->setHash(HashType("")); + m_param->setBlockTxCountLimit(1000); + + auto signImpl = std::make_shared(); + consensus::ConsensusNodeList consensusNodeList; + consensus::ConsensusNodeList observerNodeList; + for (int i = 0; i < 4; ++i) + { + auto node = std::make_shared( + signImpl->generateKeyPair()->publicKey(), 10 + i); + consensusNodeList.emplace_back(node); + } + auto observer_node = std::make_shared( + signImpl->generateKeyPair()->publicKey(), -1); + observerNodeList.emplace_back(observer_node); + + m_param->setConsensusNodeList(consensusNodeList); + m_param->setObserverNodeList(observerNodeList); + + LEDGER_LOG(TRACE) << "build genesis for first time"; + auto result = m_ledger->buildGenesisBlock(m_param, 3000000000, "", version); + BOOST_CHECK(result); + LEDGER_LOG(TRACE) << "build genesis for second time"; + auto result2 = m_ledger->buildGenesisBlock(m_param, 3000000000, "", version); + BOOST_CHECK(result2); + } + + inline void initEmptyFixture() + { + m_param = std::make_shared(); + m_param->setBlockNumber(0); + m_param->setHash(HashType("")); + m_param->setBlockTxCountLimit(0); + + auto result1 = + m_ledger->buildGenesisBlock(m_param, 3000000000, "", bcos::protocol::V3_1_VERSION_STR); + BOOST_CHECK(result1); + auto result2 = + m_ledger->buildGenesisBlock(m_param, 30, "", bcos::protocol::V3_1_VERSION_STR); + BOOST_CHECK(!result2); + auto result3 = + m_ledger->buildGenesisBlock(m_param, 3000000000, "", bcos::protocol::V3_1_VERSION_STR); + BOOST_CHECK(result3); + } + + inline void initBlocks(int _number) + { + std::promise fakeBlockPromise; + auto future = fakeBlockPromise.get_future(); + m_ledger->asyncGetBlockHashByNumber( + 0, [=, &fakeBlockPromise, this](Error::Ptr, HashType _hash) { + m_fakeBlocks = fakeBlocks( + m_blockFactory->cryptoSuite(), m_blockFactory, 1, 1, _number, _hash.hex()); + fakeBlockPromise.set_value(true); + }); + future.get(); + } + + inline void initEmptyBlocks(int _number) + { + std::promise fakeBlockPromise; + auto future = fakeBlockPromise.get_future(); + m_ledger->asyncGetBlockHashByNumber( + 0, [=, &fakeBlockPromise, this](Error::Ptr, HashType _hash) { + m_fakeBlocks = fakeEmptyBlocks( + m_blockFactory->cryptoSuite(), m_blockFactory, _number, _hash.hex()); + fakeBlockPromise.set_value(true); + }); + future.get(); + } + + inline void initChain(int _number) + { + initBlocks(_number); + for (int i = 0; i < _number; ++i) + { + auto txSize = m_fakeBlocks->at(i)->transactionsSize(); + auto txDataList = std::make_shared>(); + auto txHashList = std::make_shared(); + auto txList = std::make_shared>(); + for (size_t j = 0; j < txSize; ++j) + { + bcos::bytes txData; + m_fakeBlocks->at(i)->transaction(j)->encode(txData); + auto txPointer = std::make_shared(txData.begin(), txData.end()); + txDataList->push_back(txPointer); + txHashList->push_back(m_fakeBlocks->at(i)->transaction(j)->hash()); + txList->push_back(m_blockFactory->transactionFactory()->createTransaction(txData)); + } + + std::promise p1; + auto f1 = p1.get_future(); + m_ledger->asyncPreStoreBlockTxs( + txList, m_fakeBlocks->at(i), [=, &p1](Error::Ptr _error) { + BOOST_CHECK_EQUAL(_error, nullptr); + p1.set_value(true); + }); + // m_ledger->asyncStoreTransactions(txDataList, txHashList, [=, + // &p1](Error::Ptr _error) { + // BOOST_CHECK_EQUAL(_error, nullptr); + // p1.set_value(true); + // }); + BOOST_CHECK_EQUAL(f1.get(), true); + + auto& block = m_fakeBlocks->at(i); + + // write transactions + std::promise writeTransactions; + m_ledger->asyncStoreTransactions(txDataList, txHashList, [&](Error::Ptr error) { + BOOST_CHECK(!error); + writeTransactions.set_value(true); + }); + writeTransactions.get_future().get(); + + // write other meta data + std::promise prewritePromise; + m_ledger->asyncPrewriteBlock( + m_storage, nullptr, block, [&](Error::Ptr&&) { prewritePromise.set_value(true); }); + + prewritePromise.get_future().get(); + } + } + + inline void initEmptyChain(int _number) + { + initEmptyBlocks(_number); + for (int i = 0; i < _number; ++i) + { + // std::promise p2; + // auto f2 = p2.get_future(); + // m_ledger->asyncStoreReceipts(table, m_fakeBlocks->at(i), [=, &p2](Error::Ptr _error) + // { + // BOOST_CHECK_EQUAL(_error, nullptr); + // p2.set_value(true); + // }); + // BOOST_CHECK_EQUAL(f2.get(), true); + + // std::promise p3; + // auto f3 = p3.get_future(); + // m_ledger->asyncCommitBlock(m_fakeBlocks->at(i)->blockHeader(), + // [=, &p3](Error::Ptr _error, LedgerConfig::Ptr _config) { + // BOOST_CHECK_EQUAL(_error, nullptr); + // BOOST_CHECK_EQUAL(_config->blockNumber(), i + 1); + // BOOST_CHECK(!_config->consensusNodeList().empty()); + // BOOST_CHECK(!_config->observerNodeList().empty()); + // p3.set_value(true); + // }); + + // BOOST_CHECK_EQUAL(f3.get(), true); + + std::promise p3; + m_ledger->asyncPrewriteBlock( + m_storage, nullptr, m_fakeBlocks->at(i), [&](Error::Ptr&& error) { + BOOST_CHECK(!error); + p3.set_value(true); + }); + BOOST_CHECK_EQUAL(p3.get_future().get(), true); + } + } + + storage::StorageInterface::Ptr m_storage = nullptr; + BlockFactory::Ptr m_blockFactory = nullptr; + std::shared_ptr m_ledger = nullptr; + LedgerConfig::Ptr m_param; + BlocksPtr m_fakeBlocks; +}; + +BOOST_FIXTURE_TEST_SUITE(LedgerTest, LedgerFixture) + +BOOST_AUTO_TEST_CASE(testFixtureLedger) +{ + initFixture(); + std::promise p1; + auto f1 = p1.get_future(); + m_ledger->asyncGetBlockNumber([&](Error::Ptr _error, BlockNumber _number) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK_EQUAL(_number, 0); + p1.set_value(true); + }); + + std::promise p2; + auto f2 = p2.get_future(); + m_ledger->asyncGetBlockHashByNumber(0, [&](Error::Ptr _error, crypto::HashType _hash) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(_hash != HashType("")); + m_ledger->asyncGetBlockNumberByHash(_hash, [&](Error::Ptr _error, BlockNumber _number) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK_EQUAL(_number, 0); + p2.set_value(true); + }); + }); + + std::promise p3; + auto f3 = p3.get_future(); + m_ledger->asyncGetBlockDataByNumber(0, HEADER, [&](Error::Ptr _error, Block::Ptr _block) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(_block != nullptr); + BOOST_CHECK_EQUAL(_block->blockHeader()->number(), 0); + p3.set_value(true); + }); + + std::promise p4; + auto f4 = p4.get_future(); + m_ledger->asyncGetTotalTransactionCount( + [&](Error::Ptr _error, int64_t _totalTxCount, int64_t _failedTxCount, + protocol::BlockNumber _latestBlockNumber) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK_EQUAL(_totalTxCount, 0); + BOOST_CHECK_EQUAL(_failedTxCount, 0); + BOOST_CHECK_EQUAL(_latestBlockNumber, 0); + p4.set_value(true); + }); + + std::promise p5; + auto f5 = p5.get_future(); + m_ledger->asyncGetSystemConfigByKey( + SYSTEM_KEY_TX_COUNT_LIMIT, [&](Error::Ptr _error, std::string _value, BlockNumber _number) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK_EQUAL(_value, "1000"); + BOOST_CHECK_EQUAL(_number, 0); + p5.set_value(true); + }); + + std::promise p6; + auto f6 = p6.get_future(); + m_ledger->asyncGetNodeListByType( + CONSENSUS_OBSERVER, [&](Error::Ptr _error, consensus::ConsensusNodeListPtr _nodeList) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK_EQUAL(_nodeList->at(0)->nodeID()->hex(), + m_param->observerNodeList().at(0)->nodeID()->hex()); + p6.set_value(true); + }); + BOOST_CHECK_EQUAL(f1.get(), true); + BOOST_CHECK_EQUAL(f2.get(), true); + BOOST_CHECK_EQUAL(f3.get(), true); + BOOST_CHECK_EQUAL(f4.get(), true); + BOOST_CHECK_EQUAL(f5.get(), true); + BOOST_CHECK_EQUAL(f6.get(), true); +} + +BOOST_AUTO_TEST_CASE(test_3_0_FixtureLedger) +{ + initFixture(V3_0_VERSION_STR); + std::promise p1; + auto f1 = p1.get_future(); + m_ledger->asyncGetBlockNumber([&](Error::Ptr _error, BlockNumber _number) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK_EQUAL(_number, 0); + p1.set_value(true); + }); + + std::promise p2; + auto f2 = p2.get_future(); + m_ledger->asyncGetBlockHashByNumber(0, [&](Error::Ptr _error, crypto::HashType _hash) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(_hash != HashType("")); + m_ledger->asyncGetBlockNumberByHash(_hash, [&](Error::Ptr _error, BlockNumber _number) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK_EQUAL(_number, 0); + p2.set_value(true); + }); + }); + + std::promise p3; + auto f3 = p3.get_future(); + m_ledger->asyncGetBlockDataByNumber(0, HEADER, [&](Error::Ptr _error, Block::Ptr _block) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(_block != nullptr); + BOOST_CHECK_EQUAL(_block->blockHeader()->number(), 0); + p3.set_value(true); + }); + + std::promise p4; + auto f4 = p4.get_future(); + m_ledger->asyncGetTotalTransactionCount( + [&](Error::Ptr _error, int64_t _totalTxCount, int64_t _failedTxCount, + protocol::BlockNumber _latestBlockNumber) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK_EQUAL(_totalTxCount, 0); + BOOST_CHECK_EQUAL(_failedTxCount, 0); + BOOST_CHECK_EQUAL(_latestBlockNumber, 0); + p4.set_value(true); + }); + + std::promise p5; + auto f5 = p5.get_future(); + m_ledger->asyncGetSystemConfigByKey( + SYSTEM_KEY_TX_COUNT_LIMIT, [&](Error::Ptr _error, std::string _value, BlockNumber _number) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK_EQUAL(_value, "1000"); + BOOST_CHECK_EQUAL(_number, 0); + p5.set_value(true); + }); + + std::promise p6; + auto f6 = p6.get_future(); + m_ledger->asyncGetNodeListByType( + CONSENSUS_OBSERVER, [&](Error::Ptr _error, consensus::ConsensusNodeListPtr _nodeList) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK_EQUAL(_nodeList->at(0)->nodeID()->hex(), + m_param->observerNodeList().at(0)->nodeID()->hex()); + p6.set_value(true); + }); + + std::promise p7; + std::vector v = {"apps", "usr", "sys", "tables"}; + m_storage->asyncGetRow( + tool::FS_ROOT, tool::FS_KEY_SUB, [&](Error::UniquePtr, std::optional _entry) { + std::map bfsInfos; + auto&& out = asBytes(std::string(_entry->getField(0))); + codec::scale::decode(bfsInfos, gsl::make_span(out)); + for (const auto& item : v) + { + BOOST_CHECK(bfsInfos.find(item) != bfsInfos.end()); + std::promise p; + m_storage->asyncOpenTable( + "/" + item, [&](Error::UniquePtr _error, std::optional
_table) { + BOOST_CHECK(!_error); + BOOST_CHECK(_table.has_value()); + p.set_value(true); + }); + p.get_future().get(); + } + p7.set_value(true); + }); + p7.get_future().get(); + BOOST_CHECK_EQUAL(f1.get(), true); + BOOST_CHECK_EQUAL(f2.get(), true); + BOOST_CHECK_EQUAL(f3.get(), true); + BOOST_CHECK_EQUAL(f4.get(), true); + BOOST_CHECK_EQUAL(f5.get(), true); + BOOST_CHECK_EQUAL(f6.get(), true); +} + +BOOST_AUTO_TEST_CASE(getBlockNumber) +{ + std::promise p1; + auto f1 = p1.get_future(); + m_ledger->asyncGetBlockNumber([&](Error::Ptr _error, BlockNumber _number) { + BOOST_CHECK(_error != nullptr); + BOOST_CHECK_EQUAL(_error->errorCode(), LedgerError::GetStorageError); + BOOST_CHECK_EQUAL(_number, -1); + p1.set_value(true); + }); + BOOST_CHECK_EQUAL(f1.get(), true); +} + +BOOST_AUTO_TEST_CASE(getBlockHashByNumber) +{ + initFixture(); + std::promise p1; + auto f1 = p1.get_future(); + m_ledger->asyncGetBlockHashByNumber(-1, [=, &p1](Error::Ptr _error, HashType _hash) { + BOOST_CHECK_EQUAL(_error->errorCode(), LedgerError::ErrorArgument); + BOOST_CHECK_EQUAL(_hash, HashType()); + p1.set_value(true); + }); + + std::promise p2; + auto f2 = p2.get_future(); + m_ledger->asyncGetBlockHashByNumber(1000, [=, &p2](Error::Ptr _error, HashType _hash) { + BOOST_CHECK_EQUAL(_error->errorCode(), LedgerError::GetStorageError); + BOOST_CHECK_EQUAL(_hash, HashType()); + p2.set_value(true); + }); + + std::promise getRowPromise; + m_storage->asyncGetRow( + SYS_NUMBER_2_HASH, "0", [&getRowPromise](auto&& error, std::optional&& entry) { + BOOST_CHECK(!error); + getRowPromise.set_value(std::move(*entry)); + }); + + auto oldHashEntry = getRowPromise.get_future().get(); + + Entry hashEntry; + hashEntry.importFields({""}); + + // deal with version conflict + std::promise setRowPromise; + m_storage->asyncSetRow( + SYS_NUMBER_2_HASH, "0", std::move(std::move(hashEntry)), [&setRowPromise](auto&& error) { + BOOST_CHECK(!error); + + setRowPromise.set_value(std::move(error)); + }); + setRowPromise.get_future().get(); + + std::promise p3; + auto f3 = p3.get_future(); + m_ledger->asyncGetBlockHashByNumber(0, [=, &p3](Error::Ptr _error, HashType _hash) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK_EQUAL(_hash, HashType("")); + p3.set_value(true); + }); + BOOST_CHECK_EQUAL(f1.get(), true); + BOOST_CHECK_EQUAL(f2.get(), true); + BOOST_CHECK_EQUAL(f3.get(), true); +} + +BOOST_AUTO_TEST_CASE(getBlockNumberByHash) +{ + initFixture(); + + std::promise p1; + auto f1 = p1.get_future(); + // error hash + m_ledger->asyncGetBlockNumberByHash(HashType(), [&](Error::Ptr _error, BlockNumber _number) { + BOOST_CHECK_EQUAL(_error->errorCode(), LedgerError::GetStorageError); + BOOST_CHECK_EQUAL(_number, -1); + p1.set_value(true); + }); + + std::promise p2; + auto f2 = p2.get_future(); + m_ledger->asyncGetBlockNumberByHash( + HashType("123"), [&](Error::Ptr _error, BlockNumber _number) { + BOOST_CHECK_EQUAL(_error->errorCode(), LedgerError::GetStorageError); + BOOST_CHECK_EQUAL(_number, -1); + p2.set_value(true); + }); + + std::promise p3; + auto f3 = p3.get_future(); + + m_storage->asyncGetRow( + SYS_NUMBER_2_HASH, "0", [&](auto&& error, std::optional&& hashEntry) { + BOOST_CHECK(!error); + BOOST_CHECK(hashEntry); + auto hash = bcos::crypto::HashType( + std::string(hashEntry->getField(0)), bcos::crypto::HashType::FromBinary); + + Entry numberEntry; + m_storage->asyncSetRow(SYS_HASH_2_NUMBER, + std::string_view((const char*)hash.data(), hash.size()), std::move(numberEntry), + [&](auto&& error) { + BOOST_CHECK(!error); + + m_ledger->asyncGetBlockNumberByHash( + hash, [&](Error::Ptr error, BlockNumber number) { + BOOST_CHECK(!error); + BOOST_CHECK_EQUAL(number, -1); + + p3.set_value(true); + }); + }); + }); + + BOOST_CHECK_EQUAL(f1.get(), true); + BOOST_CHECK_EQUAL(f2.get(), true); + BOOST_CHECK_EQUAL(f3.get(), true); +} + +BOOST_AUTO_TEST_CASE(getTotalTransactionCount) +{ + std::promise p1; + auto f1 = p1.get_future(); + m_ledger->asyncGetTotalTransactionCount( + [&](Error::Ptr _error, int64_t totalCount, int64_t totalFailed, + bcos::protocol::BlockNumber _number) { + BOOST_CHECK(_error != nullptr); + BOOST_CHECK_EQUAL(totalCount, -1); + BOOST_CHECK_EQUAL(totalFailed, -1); + BOOST_CHECK_EQUAL(_number, -1); + p1.set_value(true); + }); + BOOST_CHECK_EQUAL(f1.get(), true); + + initFixture(); + initChain(5); + std::promise p2; + m_ledger->asyncGetTotalTransactionCount( + [&](Error::Ptr _error, int64_t totalCount, int64_t totalFailed, + bcos::protocol::BlockNumber _number) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(totalCount > 0); + BOOST_CHECK(totalFailed >= 0); + BOOST_CHECK_EQUAL(_number, 5); + p2.set_value(true); + }); + BOOST_CHECK_EQUAL(p2.get_future().get(), true); +} + +BOOST_AUTO_TEST_CASE(getNodeListByType) +{ + initEmptyFixture(); + + std::promise p1; + auto f1 = p1.get_future(); + // error type get empty node list + m_ledger->asyncGetNodeListByType( + "test", [&](Error::Ptr _error, consensus::ConsensusNodeListPtr _nodeList) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK_EQUAL(_nodeList->size(), 0); + p1.set_value(true); + }); + BOOST_CHECK_EQUAL(f1.get(), true); + + std::promise p2; + auto f2 = p2.get_future(); + m_ledger->asyncGetNodeListByType( + CONSENSUS_SEALER, [&](Error::Ptr _error, consensus::ConsensusNodeListPtr _nodeList) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(_nodeList != nullptr); + BOOST_CHECK(_nodeList->size() == 0); + p2.set_value(true); + }); + BOOST_CHECK_EQUAL(f2.get(), true); + + std::promise p3; + auto f3 = p3.get_future(); + m_ledger->asyncGetNodeListByType( + CONSENSUS_OBSERVER, [&](Error::Ptr _error, consensus::ConsensusNodeListPtr _nodeList) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(_nodeList != nullptr); + BOOST_CHECK(_nodeList->size() == 0); + p3.set_value(true); + }); + BOOST_CHECK_EQUAL(f3.get(), true); +} + +BOOST_AUTO_TEST_CASE(testNodeListByType) +{ + initFixture(); + std::promise p1; + auto f1 = p1.get_future(); + m_ledger->asyncGetNodeListByType( + CONSENSUS_SEALER, [&](Error::Ptr _error, consensus::ConsensusNodeListPtr _nodeList) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK_EQUAL(_nodeList->size(), 4); + p1.set_value(true); + }); + BOOST_CHECK_EQUAL(f1.get(), true); + + std::promise setSealer1; + m_storage->asyncGetRow( + SYS_CONSENSUS, "key", [&](Error::UniquePtr error, std::optional entry) { + BOOST_CHECK(!error); + BOOST_CHECK(entry); + + auto list = decodeConsensusList(entry->getField(0)); + list.emplace_back( + bcos::crypto::HashType("56789").hex(), 100, std::string{CONSENSUS_SEALER}, "5"); + + entry->setField(0, encodeConsensusList(list)); + m_storage->asyncSetRow( + SYS_CONSENSUS, "key", std::move(*entry), [&](Error::UniquePtr error) { + BOOST_CHECK(!error); + setSealer1.set_value(true); + }); + }); + setSealer1.get_future().get(); + + std::promise p2; + auto f2 = p2.get_future(); + m_ledger->asyncGetNodeListByType( + CONSENSUS_SEALER, [&](Error::Ptr _error, consensus::ConsensusNodeListPtr _nodeList) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK_EQUAL(_nodeList->size(), 4); + p2.set_value(true); + }); + BOOST_CHECK_EQUAL(f2.get(), true); + + // set block number to 5 + initChain(5); + + std::promise p3; + auto f3 = p3.get_future(); + m_ledger->asyncGetNodeListByType( + CONSENSUS_SEALER, [&](Error::Ptr _error, consensus::ConsensusNodeListPtr _nodeList) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK_EQUAL(_nodeList->size(), 5); + p3.set_value(true); + }); + BOOST_CHECK_EQUAL(f3.get(), true); +} + +BOOST_AUTO_TEST_CASE(getBlockDataByNumber) +{ + initFixture(); + // test cache + initChain(20); + + std::promise p1; + auto f1 = p1.get_future(); + // error number + m_ledger->asyncGetBlockDataByNumber( + 1000, FULL_BLOCK, [=, &p1](Error::Ptr _error, Block::Ptr _block) { + BOOST_CHECK(_error != nullptr); + BOOST_CHECK_EQUAL(_block, nullptr); + p1.set_value(true); + }); + + std::promise pp1; + auto ff1 = pp1.get_future(); + m_ledger->asyncGetBlockDataByNumber( + -1, FULL_BLOCK, [=, &pp1](Error::Ptr _error, Block::Ptr _block) { + BOOST_CHECK(_error != nullptr); + BOOST_CHECK_EQUAL(_block, nullptr); + pp1.set_value(true); + }); + + std::promise p2; + auto f2 = p2.get_future(); + // cache hit + m_ledger->asyncGetBlockDataByNumber( + 15, FULL_BLOCK, [=, &p2](Error::Ptr _error, Block::Ptr _block) { + BOOST_CHECK_EQUAL(_error, nullptr); + BOOST_CHECK(_block->blockHeader() != nullptr); + BOOST_CHECK(_block->transactionsSize() != 0); + BOOST_CHECK(_block->receiptsSize() != 0); + p2.set_value(true); + }); + + std::promise p3; + auto f3 = p3.get_future(); + // cache not hit + m_ledger->asyncGetBlockDataByNumber( + 3, FULL_BLOCK, [=, &p3](Error::Ptr _error, Block::Ptr _block) { + BOOST_CHECK_EQUAL(_error, nullptr); + BOOST_CHECK(_block->blockHeader() != nullptr); + BOOST_CHECK(_block->transactionsSize() != 0); + BOOST_CHECK(_block->receiptsSize() != 0); + p3.set_value(true); + }); + + std::promise p4; + auto f4 = p4.get_future(); + m_ledger->asyncGetBlockDataByNumber( + 5, TRANSACTIONS, [=, &p4](Error::Ptr _error, Block::Ptr _block) { + BOOST_CHECK_EQUAL(_error, nullptr); + BOOST_CHECK(_block->transactionsSize() != 0); + p4.set_value(true); + }); + + std::promise p5; + auto f5 = p5.get_future(); + m_ledger->asyncGetBlockDataByNumber( + 5, RECEIPTS, [=, &p5](Error::Ptr _error, Block::Ptr _block) { + BOOST_CHECK_EQUAL(_error, nullptr); + BOOST_CHECK(_block->receiptsSize() != 0); + p5.set_value(true); + }); + + std::promise p6; + auto f6 = p6.get_future(); + m_ledger->asyncGetBlockDataByNumber( + 0, TRANSACTIONS, [=, &p6](Error::Ptr _error, Block::Ptr _block) { + BOOST_CHECK_EQUAL(_error, nullptr); + BOOST_CHECK_EQUAL(_block->transactionsSize(), 0); + p6.set_value(true); + }); + + std::promise p7; + auto f7 = p7.get_future(); + m_ledger->asyncGetBlockDataByNumber( + 0, RECEIPTS, [=, &p7](Error::Ptr _error, Block::Ptr _block) { + BOOST_CHECK_EQUAL(_error, nullptr); + BOOST_CHECK_EQUAL(_block->receiptsSize(), 0); + p7.set_value(true); + }); + BOOST_CHECK_EQUAL(f1.get(), true); + BOOST_CHECK_EQUAL(ff1.get(), true); + BOOST_CHECK_EQUAL(f2.get(), true); + BOOST_CHECK_EQUAL(f3.get(), true); + BOOST_CHECK_EQUAL(f4.get(), true); + BOOST_CHECK_EQUAL(f5.get(), true); + BOOST_CHECK_EQUAL(f6.get(), true); + BOOST_CHECK_EQUAL(f7.get(), true); +} + +BOOST_AUTO_TEST_CASE(getTransactionByHash) +{ + initFixture(); + initChain(5); + auto hashList = std::make_shared(); + auto errorHashList = std::make_shared(); + auto fullHashList = std::make_shared(); + hashList->emplace_back(m_fakeBlocks->at(3)->transactionHash(0)); + hashList->emplace_back(m_fakeBlocks->at(3)->transactionHash(1)); + hashList->emplace_back(m_fakeBlocks->at(4)->transactionHash(0)); + errorHashList->emplace_back(HashType("123")); + errorHashList->emplace_back(HashType("456")); + fullHashList->emplace_back(m_fakeBlocks->at(3)->transactionHash(0)); + fullHashList->emplace_back(m_fakeBlocks->at(3)->transactionHash(1)); + fullHashList->emplace_back(m_fakeBlocks->at(3)->transactionHash(2)); + fullHashList->emplace_back(m_fakeBlocks->at(3)->transactionHash(3)); + + std::promise p1; + auto f1 = p1.get_future(); + m_ledger->asyncGetBatchTxsByHashList(hashList, true, + [=, &p1, this](Error::Ptr _error, protocol::TransactionsPtr _txList, + std::shared_ptr> _proof) { + BOOST_CHECK_EQUAL(_error, nullptr); + BOOST_CHECK(_txList != nullptr); + + BOOST_CHECK(_proof->at(m_fakeBlocks->at(3)->transaction(0)->hash().hex()) != nullptr); + p1.set_value(true); + }); + BOOST_CHECK_EQUAL(f1.get(), true); + + std::promise p2; + auto f2 = p2.get_future(); + m_ledger->asyncGetBatchTxsByHashList(fullHashList, true, + [=, &p2, this](Error::Ptr _error, protocol::TransactionsPtr _txList, + std::shared_ptr> _proof) { + BOOST_CHECK_EQUAL(_error, nullptr); + BOOST_CHECK(_txList != nullptr); + + BOOST_CHECK(_proof->at(m_fakeBlocks->at(3)->transaction(0)->hash().hex()) != nullptr); + p2.set_value(true); + }); + BOOST_CHECK_EQUAL(f2.get(), true); + + std::promise p3; + auto f3 = p3.get_future(); + // error hash list + m_ledger->asyncGetBatchTxsByHashList(errorHashList, true, + [=, &p3](Error::Ptr _error, protocol::TransactionsPtr _txList, + std::shared_ptr> _proof) { + BOOST_CHECK(_error != nullptr); + BOOST_CHECK(_txList == nullptr); + + BOOST_CHECK(_proof == nullptr); + p3.set_value(true); + }); + BOOST_CHECK_EQUAL(f3.get(), true); + + std::promise p4; + auto f4 = p4.get_future(); + // without proof + m_ledger->asyncGetBatchTxsByHashList(hashList, false, + [=, &p4](Error::Ptr _error, protocol::TransactionsPtr _txList, + std::shared_ptr> _proof) { + BOOST_CHECK_EQUAL(_error, nullptr); + BOOST_CHECK(_txList != nullptr); + + BOOST_CHECK(_proof == nullptr); + p4.set_value(true); + }); + BOOST_CHECK_EQUAL(f4.get(), true); + + std::promise p5; + auto f5 = p5.get_future(); + // null hash list + m_ledger->asyncGetBatchTxsByHashList(nullptr, false, + [=, &p5](Error::Ptr _error, protocol::TransactionsPtr _txList, + std::shared_ptr> _proof) { + BOOST_CHECK_EQUAL(_error->errorCode(), LedgerError::ErrorArgument); + BOOST_CHECK(_txList == nullptr); + BOOST_CHECK(_proof == nullptr); + p5.set_value(true); + }); + BOOST_CHECK_EQUAL(f5.get(), true); +} + +BOOST_AUTO_TEST_CASE(getTransactionReceiptByHash) +{ + initFixture(); + initChain(5); + + std::promise p1; + auto f1 = p1.get_future(); + m_ledger->asyncGetTransactionReceiptByHash(m_fakeBlocks->at(3)->transactionHash(0), true, + [&](Error::Ptr _error, TransactionReceipt::ConstPtr _receipt, MerkleProofPtr _proof) { + BOOST_CHECK_EQUAL(_error, nullptr); + BOOST_CHECK_EQUAL( + _receipt->hash().hex(), m_fakeBlocks->at(3)->receipt(0)->hash().hex()); + + BOOST_CHECK(_proof != nullptr); + p1.set_value(true); + }); + + std::promise p2; + auto f2 = p2.get_future(); + // without proof + m_ledger->asyncGetTransactionReceiptByHash(m_fakeBlocks->at(3)->transactionHash(0), false, + [&](Error::Ptr _error, TransactionReceipt::ConstPtr _receipt, MerkleProofPtr _proof) { + BOOST_CHECK_EQUAL(_error, nullptr); + BOOST_CHECK_EQUAL( + _receipt->hash().hex(), m_fakeBlocks->at(3)->receipt(0)->hash().hex()); + BOOST_CHECK(_proof == nullptr); + p2.set_value(true); + }); + + std::promise p3; + auto f3 = p3.get_future(); + // error hash + m_ledger->asyncGetTransactionReceiptByHash(HashType(), false, + [&](Error::Ptr _error, TransactionReceipt::ConstPtr _receipt, MerkleProofPtr _proof) { + BOOST_CHECK_EQUAL(_error->errorCode(), LedgerError::GetStorageError); + BOOST_CHECK_EQUAL(_receipt, nullptr); + BOOST_CHECK(_proof == nullptr); + p3.set_value(true); + }); + + std::promise p4; + auto f4 = p4.get_future(); + m_ledger->asyncGetTransactionReceiptByHash(HashType("123"), true, + [&](Error::Ptr _error, TransactionReceipt::ConstPtr _receipt, MerkleProofPtr _proof) { + BOOST_CHECK_EQUAL(_error->errorCode(), LedgerError::GetStorageError); + BOOST_CHECK_EQUAL(_receipt, nullptr); + BOOST_CHECK(_proof == nullptr); + p4.set_value(true); + }); + BOOST_CHECK_EQUAL(f1.get(), true); + BOOST_CHECK_EQUAL(f2.get(), true); + BOOST_CHECK_EQUAL(f3.get(), true); + BOOST_CHECK_EQUAL(f4.get(), true); +} + +BOOST_AUTO_TEST_CASE(getNonceList) +{ + initFixture(); + initChain(5); + + std::promise p1; + auto f1 = p1.get_future(); + m_ledger->asyncGetNonceList(3, 6, + [&](Error::Ptr _error, + std::shared_ptr> _nonceMap) { + BOOST_CHECK_EQUAL(_error, nullptr); + BOOST_CHECK(_nonceMap != nullptr); + BOOST_CHECK_EQUAL(_nonceMap->size(), 3); + p1.set_value(true); + }); + + std::promise p3; + auto f3 = p3.get_future(); + m_ledger->asyncGetNonceList(4, 2, + [&](Error::Ptr _error, + std::shared_ptr> _nonceMap) { + BOOST_CHECK_EQUAL(_error, nullptr); + BOOST_CHECK(_nonceMap != nullptr); + BOOST_CHECK_EQUAL(_nonceMap->size(), 2); + p3.set_value(true); + }); + + std::promise p2; + auto f2 = p2.get_future(); + // error param + m_ledger->asyncGetNonceList(-1, -5, + [&](Error::Ptr _error, + std::shared_ptr> _nonceMap) { + BOOST_CHECK(_error != nullptr); + BOOST_CHECK(_nonceMap == nullptr); + p2.set_value(true); + }); + BOOST_CHECK_EQUAL(f1.get(), true); + BOOST_CHECK_EQUAL(f2.get(), true); + BOOST_CHECK_EQUAL(f3.get(), true); +} + +BOOST_AUTO_TEST_CASE(preStoreTransaction) +{ + initFixture(); + initBlocks(5); + auto txBytesList = std::make_shared>(); + auto hashList = std::make_shared(); + for (size_t i = 0; i < m_fakeBlocks->at(3)->transactionsSize(); ++i) + { + bcos::bytes txData; + m_fakeBlocks->at(3)->transaction(i)->encode(txData); + auto txPointer = std::make_shared(txData); + auto hash = m_fakeBlocks->at(3)->transaction(i)->hash(); + txBytesList->emplace_back(txPointer); + hashList->emplace_back(hash); + } + + std::promise p1; + auto f1 = p1.get_future(); + m_ledger->asyncStoreTransactions(txBytesList, hashList, [&](Error::Ptr _error) { + BOOST_CHECK_EQUAL(_error, nullptr); + p1.set_value(true); + }); + + std::promise p2; + auto f2 = p2.get_future(); + // null pointer + m_ledger->asyncStoreTransactions(txBytesList, nullptr, [&](Error::Ptr _error) { + BOOST_CHECK_EQUAL(_error->errorCode(), LedgerError::ErrorArgument); + p2.set_value(true); + }); + + std::promise p3; + auto f3 = p3.get_future(); + m_ledger->asyncStoreTransactions(nullptr, hashList, [&](Error::Ptr _error) { + BOOST_CHECK_EQUAL(_error->errorCode(), LedgerError::ErrorArgument); + p3.set_value(true); + }); + BOOST_CHECK_EQUAL(f1.get(), true); + BOOST_CHECK_EQUAL(f2.get(), true); + BOOST_CHECK_EQUAL(f3.get(), true); +} + +BOOST_AUTO_TEST_CASE(preStoreReceipt) +{ + // initFixture(); + // initBlocks(5); + + // std::promise p1; + // auto f1 = p1.get_future(); + // m_ledger->asyncStoreReceipts(nullptr, m_fakeBlocks->at(1), [&](Error::Ptr _error) { + // BOOST_CHECK_EQUAL(_error->errorCode(), LedgerError::ErrorArgument); + // p1.set_value(true); + // }); + // BOOST_CHECK_EQUAL(f1.get(), true); +} + +BOOST_AUTO_TEST_CASE(getSystemConfig) +{ + initFixture(); + + std::promise p1; + auto f1 = p1.get_future(); + m_ledger->asyncGetSystemConfigByKey( + SYSTEM_KEY_TX_COUNT_LIMIT, [&](Error::Ptr _error, std::string _value, BlockNumber _number) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK_EQUAL(_value, "1000"); + BOOST_CHECK_EQUAL(_number, 0); + p1.set_value(true); + }); + BOOST_CHECK_EQUAL(f1.get(), true); + + initChain(5); + + std::promise
tablePromise; + m_storage->asyncOpenTable(SYS_CONFIG, [&](auto&& error, std::optional
&& table) { + BOOST_CHECK(!error); + + tablePromise.set_value(std::move(*table)); + }); + + auto table = tablePromise.get_future().get(); + + auto oldEntry = table.getRow(SYSTEM_KEY_TX_COUNT_LIMIT); + auto [txCountLimit, enableNum] = oldEntry->getObject(); + BOOST_CHECK_EQUAL(txCountLimit, "1000"); + BOOST_CHECK_EQUAL(enableNum, 0); + + Entry newEntry = table.newEntry(); + newEntry.setObject(SystemConfigEntry{"2000", 5}); + + table.setRow(SYSTEM_KEY_TX_COUNT_LIMIT, newEntry); + + std::promise pp2; + m_ledger->asyncGetSystemConfigByKey( + SYSTEM_KEY_TX_COUNT_LIMIT, [&](Error::Ptr _error, std::string _value, BlockNumber _number) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK_EQUAL(_value, "2000"); + BOOST_CHECK_EQUAL(_number, 5); + pp2.set_value(true); + }); + BOOST_CHECK_EQUAL(pp2.get_future().get(), true); + + std::promise p3; + auto f3 = p3.get_future(); + // get error key + m_ledger->asyncGetSystemConfigByKey( + "test", [&](Error::Ptr _error, std::string _value, BlockNumber _number) { + BOOST_CHECK(_error->errorCode() == LedgerError::GetStorageError); + BOOST_CHECK_EQUAL(_value, ""); + BOOST_CHECK_EQUAL(_number, -1); + p3.set_value(true); + }); + BOOST_CHECK_EQUAL(f3.get(), true); +} + +BOOST_AUTO_TEST_CASE(testEmptyBlock) +{ + initFixture(); + initEmptyChain(20); + + std::promise p1; + auto f1 = p1.get_future(); + m_ledger->asyncGetBlockDataByNumber( + 4, FULL_BLOCK, [&](Error::Ptr _error, bcos::protocol::Block::Ptr _block) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(_block != nullptr); + p1.set_value(true); + }); + BOOST_CHECK(f1.get()); +} + +BOOST_AUTO_TEST_CASE(testSyncBlock) +{ + auto block = m_blockFactory->createBlock(); + auto blockHeader = m_blockFactory->blockHeaderFactory()->createBlockHeader(); + blockHeader->setNumber(100); + + block->setBlockHeader(blockHeader); + + std::string inputStr = "hello world!"; + bytes input(inputStr.begin(), inputStr.end()); + + bcos::crypto::KeyPairInterface::Ptr keyPair = + m_blockFactory->cryptoSuite()->signatureImpl()->generateKeyPair(); + auto tx = m_blockFactory->transactionFactory()->createTransaction( + 0, "to", input, 200, 300, "chainid", "groupid", 800, keyPair); + + block->appendTransaction(tx); + + auto txs = std::make_shared>(); + auto hashList = std::make_shared(); + bcos::bytes encoded; + tx->encode(encoded); + txs->emplace_back(std::make_shared(encoded.begin(), encoded.end())); + hashList->emplace_back(tx->hash()); + + initFixture(); + + m_ledger->asyncStoreTransactions(txs, hashList, [](Error::Ptr error) { BOOST_CHECK(!error); }); + m_ledger->asyncPrewriteBlock( + m_storage, nullptr, block, [](Error::Ptr&& error) { BOOST_CHECK(!error); }); + + m_ledger->asyncGetBlockDataByNumber( + 100, TRANSACTIONS, [tx](Error::Ptr error, bcos::protocol::Block::Ptr block) { + BOOST_CHECK(!error); + BOOST_CHECK_EQUAL(block->transactionsSize(), 1); + BOOST_CHECK_EQUAL(block->transaction(0)->hash().hex(), tx->hash().hex()); + }); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-ledger/test/unittests/ledger/common/FakeBlock.h" "b/BFPL\345\243\271/bcos-ledger/test/unittests/ledger/common/FakeBlock.h" new file mode 100644 index 00000000..83bedd52 --- /dev/null +++ "b/BFPL\345\243\271/bcos-ledger/test/unittests/ledger/common/FakeBlock.h" @@ -0,0 +1,151 @@ +/** + * Copyright (C) 2021 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 FakeBlock.h + * @author: kyonRay + * @date 2021-04-14 + */ + +#pragma once +#include "FakeBlockHeader.h" +#include "FakeReceipt.h" +#include "FakeTransaction.h" +#include "bcos-framework/protocol/TransactionMetaData.h" +#include "bcos-tars-protocol/protocol/BlockHeaderFactoryImpl.h" +#include "bcos-tars-protocol/protocol/TransactionFactoryImpl.h" +#include "bcos-tars-protocol/protocol/TransactionReceiptFactoryImpl.h" +#include +#include +#include +#include +using namespace bcos; +using namespace bcos::protocol; +using namespace bcos::crypto; + +namespace bcos +{ +namespace test +{ +inline CryptoSuite::Ptr createCryptoSuite() +{ + auto hashImpl = std::make_shared(); + auto signImpl = std::make_shared(); + return std::make_shared(hashImpl, signImpl, nullptr); +} + +inline BlockFactory::Ptr createBlockFactory(CryptoSuite::Ptr _cryptoSuite) +{ + auto blockHeaderFactory = + std::make_shared(_cryptoSuite); + auto transactionFactory = + std::make_shared(_cryptoSuite); + auto receiptFactory = + std::make_shared(_cryptoSuite); + return std::make_shared( + _cryptoSuite, blockHeaderFactory, transactionFactory, receiptFactory); +} + +inline Block::Ptr fakeBlock(CryptoSuite::Ptr _cryptoSuite, BlockFactory::Ptr _blockFactory, + size_t _txsNum, size_t _receiptsNum, BlockNumber _blockNumber) +{ + auto block = _blockFactory->createBlock(); + + auto blockHeader = testPBBlockHeader(_cryptoSuite, _blockNumber); + block->setBlockHeader(blockHeader); + block->setBlockType(CompleteBlock); + // fake transactions + for (size_t i = 0; i < _txsNum; i++) + { + auto tx = fakeTransaction(_cryptoSuite); + block->appendTransaction(tx); + } + // fake receipts + for (size_t i = 0; i < _receiptsNum; i++) + { + auto receipt = testPBTransactionReceipt(_cryptoSuite, _blockNumber); + block->appendReceipt(receipt); + } + // fake txsHash + for (size_t i = 0; i < _txsNum; i++) + { + auto transactionMetaData = + _blockFactory->createTransactionMetaData(block->transaction(i)->hash(), "/abc"); + block->appendTransactionMetaData(std::move(transactionMetaData)); + } + NonceList nonceList; + for (size_t i = 0; i < _txsNum; i++) + { + nonceList.emplace_back(u256(123)); + } + block->setNonceList(nonceList); + return block; +} + +inline Block::Ptr fakeEmptyBlock( + CryptoSuite::Ptr _cryptoSuite, BlockFactory::Ptr _blockFactory, BlockNumber _blockNumber) +{ + auto block = _blockFactory->createBlock(); + + auto blockHeader = testPBBlockHeader(_cryptoSuite, _blockNumber); + block->setBlockHeader(blockHeader); + return block; +} + +inline BlocksPtr fakeBlocks(CryptoSuite::Ptr _cryptoSuite, BlockFactory::Ptr _blockFactory, + size_t _txsNumBegin, size_t _receiptsNumBegin, size_t _blockNumber, std::string hash = "") +{ + BlocksPtr blocks = std::make_shared(); + ParentInfo parentInfo; + parentInfo.blockNumber = 0; + parentInfo.blockHash = HashType(hash); + for (size_t i = 0; i < _blockNumber; ++i) + { + ParentInfoList parentInfos; + auto block = + fakeBlock(_cryptoSuite, _blockFactory, _txsNumBegin + i, _receiptsNumBegin + i, i + 1); + parentInfos.push_back(parentInfo); + block->blockHeader()->setNumber(1 + i); + block->blockHeader()->setParentInfo(parentInfos); + parentInfo.blockNumber = block->blockHeader()->number(); + parentInfo.blockHash = block->blockHeader()->hash(); + blocks->emplace_back(block); + } + return blocks; +} + +inline BlocksPtr fakeEmptyBlocks(CryptoSuite::Ptr _cryptoSuite, BlockFactory::Ptr _blockFactory, + size_t _blockNumber, std::string hash = "") +{ + BlocksPtr blocks = std::make_shared(); + ParentInfo parentInfo; + parentInfo.blockNumber = 0; + parentInfo.blockHash = HashType(hash); + for (size_t i = 0; i < _blockNumber; ++i) + { + ParentInfoList parentInfos; + auto block = fakeEmptyBlock(_cryptoSuite, _blockFactory, i + 1); + parentInfos.push_back(parentInfo); + block->blockHeader()->setNumber(1 + i); + block->blockHeader()->setParentInfo(parentInfos); + parentInfo.blockNumber = block->blockHeader()->number(); + parentInfo.blockHash = block->blockHeader()->hash(); + blocks->emplace_back(block); + } + return blocks; +} + + +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-ledger/test/unittests/ledger/common/FakeBlockHeader.h" "b/BFPL\345\243\271/bcos-ledger/test/unittests/ledger/common/FakeBlockHeader.h" new file mode 100644 index 00000000..0eb0b0fd --- /dev/null +++ "b/BFPL\345\243\271/bcos-ledger/test/unittests/ledger/common/FakeBlockHeader.h" @@ -0,0 +1,132 @@ +/** + * Copyright (C) 2021 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 FakeBlockHeader.h + * @author: kyonRay + * @date 2021-04-14 + */ + +#pragma once +#include "FakeBlockHeader.h" +#include "bcos-protocol/Common.h" +#include "bcos-tars-protocol/protocol/BlockHeaderFactoryImpl.h" +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::protocol; +using namespace bcos::crypto; +namespace bcos +{ +namespace test +{ +inline BlockHeader::Ptr fakeAndTestBlockHeader(CryptoSuite::Ptr _cryptoSuite, int32_t _version, + const ParentInfoList& _parentInfo, h256 const& _txsRoot, h256 const& _receiptsRoot, + h256 const& _stateRoot, int64_t _number, u256 const& _gasUsed, int64_t _timestamp, + int64_t _sealer, const std::vector& _sealerList, bytes const& _extraData, + SignatureList _signatureList) +{ + BlockHeaderFactory::Ptr blockHeaderFactory = + std::make_shared(_cryptoSuite); + BlockHeader::Ptr blockHeader = blockHeaderFactory->createBlockHeader(); + blockHeader->setVersion(_version); + blockHeader->setParentInfo(_parentInfo); + blockHeader->setTxsRoot(_txsRoot); + blockHeader->setReceiptsRoot(_receiptsRoot); + blockHeader->setStateRoot(_stateRoot); + blockHeader->setNumber(_number); + blockHeader->setGasUsed(_gasUsed); + blockHeader->setTimestamp(_timestamp); + blockHeader->setSealer(_sealer); + blockHeader->setSealerList(gsl::span(_sealerList)); + blockHeader->setExtraData(_extraData); + blockHeader->setSignatureList(_signatureList); + WeightList weights; + weights.push_back(0); + blockHeader->setConsensusWeights(weights); + return blockHeader; +} + +inline ParentInfoList fakeParentInfo(Hash::Ptr _hashImpl, size_t _size) +{ + ParentInfoList parentInfos; + for (size_t i = 0; i < _size; i++) + { + ParentInfo parentInfo; + parentInfo.blockNumber = i; + parentInfo.blockHash = _hashImpl->hash(std::to_string(i)); + parentInfos.emplace_back(parentInfo); + } + return parentInfos; +} + +inline std::vector fakeSealerList( + std::vector& _keyPairVec, SignatureCrypto::Ptr _signImpl, size_t size) +{ + std::vector sealerList; + for (size_t i = 0; i < size; i++) + { + bcos::crypto::KeyPairInterface::Ptr keyPair = _signImpl->generateKeyPair(); + _keyPairVec.emplace_back(keyPair); + sealerList.emplace_back(*(keyPair->publicKey()->encode())); + } + return sealerList; +} + +inline SignatureList fakeSignatureList(SignatureCrypto::Ptr _signImpl, + std::vector& _keyPairVec, h256 const& _hash) +{ + auto sealerIndex = 0; + SignatureList signatureList; + for (auto keyPair : _keyPairVec) + { + auto signature = _signImpl->sign(*keyPair, _hash); + Signature sig; + sig.index = sealerIndex++; + sig.signature = *signature; + signatureList.push_back(sig); + } + return signatureList; +} + +inline BlockHeader::Ptr testPBBlockHeader(CryptoSuite::Ptr _cryptoSuite, BlockNumber _blockNumber) +{ + auto hashImpl = _cryptoSuite->hashImpl(); + auto signImpl = _cryptoSuite->signatureImpl(); + auto cryptoSuite = std::make_shared(hashImpl, signImpl, nullptr); + int version = 10; + auto parentInfo = fakeParentInfo(hashImpl, 1); + auto txsRoot = hashImpl->hash((std::string) "txsRoot"); + auto receiptsRoot = hashImpl->hash((std::string) "receiptsRoot"); + auto stateRoot = hashImpl->hash((std::string) "stateRoot"); + int64_t number = _blockNumber; + u256 gasUsed = 3453456346534; + int64_t timestamp = 9234234234; + int64_t sealer = 100; + std::vector keyPairVec; + auto sealerList = fakeSealerList(keyPairVec, signImpl, 4); + bytes extraData = stateRoot.asBytes(); + auto signatureList = fakeSignatureList(signImpl, keyPairVec, receiptsRoot); + + auto blockHeader = + fakeAndTestBlockHeader(cryptoSuite, version, parentInfo, txsRoot, receiptsRoot, stateRoot, + number, gasUsed, timestamp, sealer, sealerList, extraData, signatureList); + return blockHeader; +} +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-ledger/test/unittests/ledger/common/FakeReceipt.h" "b/BFPL\345\243\271/bcos-ledger/test/unittests/ledger/common/FakeReceipt.h" new file mode 100644 index 00000000..b7cc6c06 --- /dev/null +++ "b/BFPL\345\243\271/bcos-ledger/test/unittests/ledger/common/FakeReceipt.h" @@ -0,0 +1,90 @@ +/** + * Copyright (C) 2021 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 FakeReceipt.h + * @author: kyonRay + * @date 2021-05-06 + */ + +#pragma once +#include "bcos-protocol/TransactionStatus.h" +#include "bcos-tars-protocol/protocol/TransactionReceiptFactoryImpl.h" +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::protocol; +using namespace bcos::crypto; + +namespace bcos +{ +namespace test +{ +inline LogEntriesPtr fakeLogEntries(Hash::Ptr _hashImpl, size_t _size) +{ + LogEntriesPtr logEntries = std::make_shared(); + for (size_t i = 0; i < _size; i++) + { + auto topic = _hashImpl->hash(std::to_string(i)); + h256s topics; + topics.push_back(topic); + auto address = right160(topic).asBytes(); + bytes output = topic.asBytes(); + LogEntry logEntry(address, topics, output); + logEntries->push_back(logEntry); + } + return logEntries; +} + +inline TransactionReceipt::Ptr testPBTransactionReceipt( + CryptoSuite::Ptr _cryptoSuite, BlockNumber _blockNumber) +{ + auto hashImpl = _cryptoSuite->hashImpl(); + u256 gasUsed = 12343242342 + random(); + auto contractAddress = "5fe3c4c3e2079879a0dba1937aca95ac16e68f0f"; + auto logEntries = fakeLogEntries(hashImpl, 2); + TransactionStatus status = TransactionStatus::BadJumpDestination; + auto contractAddressBytes = toAddress(contractAddress); + bytes output = contractAddressBytes.asBytes(); + for (int i = 0; i < (random() % 9); i++) + { + output += contractAddressBytes.asBytes(); + } + auto factory = + std::make_shared(_cryptoSuite); + auto receipt = factory->createReceipt( + gasUsed, contractAddress, logEntries, (int32_t)status, output, _blockNumber); + return receipt; +} + +inline ReceiptsPtr fakeReceipts(int _size) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + + ReceiptsPtr receipts = std::make_shared(); + for (int i = 0; i < _size; ++i) + { + receipts->emplace_back(testPBTransactionReceipt(cryptoSuite, i + 1)); + } + return receipts; +} +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-ledger/test/unittests/ledger/common/FakeTransaction.h" "b/BFPL\345\243\271/bcos-ledger/test/unittests/ledger/common/FakeTransaction.h" new file mode 100644 index 00000000..3cb15394 --- /dev/null +++ "b/BFPL\345\243\271/bcos-ledger/test/unittests/ledger/common/FakeTransaction.h" @@ -0,0 +1,104 @@ +/** + * Copyright (C) 2021 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 FakeTransaction.h + * @author: kyonRay + * @date 2021-05-06 + */ + +#pragma once +#include "bcos-crypto/interfaces/crypto/KeyPairInterface.h" +#include "bcos-tars-protocol/protocol/TransactionImpl.h" +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::crypto; +using namespace bcos::protocol; + +namespace bcos +{ +namespace test +{ +inline auto fakeTransaction(CryptoSuite::Ptr _cryptoSuite, KeyPairInterface::Ptr _keyPair, + std::string const& _to, bytes const& _input, u256 const& _nonce, int64_t const& _blockLimit, + std::string const& _chainId, std::string const& _groupId) +{ + bcostars::Transaction transaction; + transaction.data.to = _to; + transaction.data.input.assign(_input.begin(), _input.end()); + transaction.data.nonce = boost::lexical_cast(_nonce); + transaction.data.blockLimit = _blockLimit; + transaction.data.chainID = _chainId; + transaction.data.groupID = _groupId; + auto pbTransaction = std::make_shared(_cryptoSuite, + [m_transaction = std::move(transaction)]() mutable { return &m_transaction; }); + // set signature + auto signData = _cryptoSuite->signatureImpl()->sign(*_keyPair, pbTransaction->hash(), true); + pbTransaction->setSignatureData(*signData); + pbTransaction->forceSender(_keyPair->address(_cryptoSuite->hashImpl()).asBytes()); + return pbTransaction; +} + +inline Transaction::Ptr testTransaction(CryptoSuite::Ptr _cryptoSuite, + KeyPairInterface::Ptr _keyPair, std::string const& _to, bytes const& _input, u256 const& _nonce, + int64_t const& _blockLimit, std::string const& _chainId, std::string const& _groupId) +{ + auto factory = std::make_shared(_cryptoSuite); + auto pbTransaction = fakeTransaction( + _cryptoSuite, _keyPair, _to, _input, _nonce, _blockLimit, _chainId, _groupId); + return pbTransaction; +} + +inline Transaction::Ptr fakeTransaction(CryptoSuite::Ptr _cryptoSuite) +{ + bcos::crypto::KeyPairInterface::Ptr keyPair = _cryptoSuite->signatureImpl()->generateKeyPair(); + auto to = *toHexString(keyPair->address(_cryptoSuite->hashImpl()).asBytes()); + std::string inputStr = "testTransaction"; + bytes input = asBytes(inputStr); + u256 nonce = 120012323; + int64_t blockLimit = 1000023; + std::string chainId = "chainId"; + std::string groupId = "groupId"; + return testTransaction(_cryptoSuite, keyPair, to, input, nonce, blockLimit, chainId, groupId); +} +inline TransactionsPtr fakeTransactions(int _size) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + bcos::crypto::KeyPairInterface::Ptr keyPair = cryptoSuite->signatureImpl()->generateKeyPair(); + auto to = *toHexString(cryptoSuite->calculateAddress(keyPair->publicKey()).asBytes()); + + TransactionsPtr txs = std::make_shared(); + for (int i = 0; i < _size; ++i) + { + std::string inputStr = "testTransaction" + std::to_string(i); + bytes input = asBytes(inputStr); + u256 nonce = 120012323 + i; + int64_t blockLimit = 1000 + i; + std::string chainId = "chainId"; + std::string groupId = "groupId"; + txs->emplace_back( + testTransaction(cryptoSuite, keyPair, to, input, nonce, blockLimit, chainId, groupId)); + } + return txs; +} +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-pbft/CMakeLists.txt" "b/BFPL\345\243\271/bcos-pbft/CMakeLists.txt" new file mode 100644 index 00000000..3597c1c9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/CMakeLists.txt" @@ -0,0 +1,62 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for bcos-pbft +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 bcos-pbft +# 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. +#------------------------------------------------------------------------------ + +cmake_minimum_required(VERSION 3.10) +set(CMAKE_OSX_DEPLOYMENT_TARGET "11.3" CACHE STRING "Minimum OS X deployment version") + +include(Version) +project(bcos-pbft VERSION ${VERSION}) + +# proto generation +set(PROTO_INPUT_PATH ${CMAKE_SOURCE_DIR}/bcos-pbft) +set(PROTO_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/) + +set(MESSAGES_PROTOS bcos-pbft/core/proto/Consensus.proto bcos-pbft/pbft/protocol/proto/PBFT.proto) +foreach(proto_file ${MESSAGES_PROTOS}) + get_filename_component(bcos_proto_abs "${PROTO_INPUT_PATH}" ABSOLUTE) + set(proto_file_abs ${bcos_proto_abs}/${proto_file}) + get_filename_component(rel_dir ${proto_file} DIRECTORY) + get_filename_component(basename ${proto_file} NAME_WE) + set(generated_files ${PROTO_OUTPUT_PATH}/${rel_dir}/${basename}.pb.cc) + + list(APPEND MESSAGES_SRCS ${generated_files}) + + message("Command: protoc --cpp_out ${PROTO_OUTPUT_PATH} -I ${PROTO_INPUT_PATH} ${proto_file}") + add_custom_command( + OUTPUT ${generated_files} + COMMAND protobuf::protoc --cpp_out ${PROTO_OUTPUT_PATH} -I ${PROTO_INPUT_PATH} ${proto_file} + COMMENT "Generating ${generated_files} from ${proto_file_abs}" + VERBATIM + ) +endforeach() + +include_directories(${PROTO_OUTPUT_PATH}) + +find_package(Protobuf CONFIG REQUIRED) +find_package(jsoncpp CONFIG REQUIRED) + +file(GLOB_RECURSE SRCS bcos-pbft/*.cpp) +add_library(${PBFT_TARGET} ${SRCS} ${MESSAGES_SRCS}) +target_link_libraries(${PBFT_TARGET} PUBLIC ${TXPOOL_TARGET} ${UTILITIES_TARGET} ${TOOL_TARGET} ${PROTOCOL_TARGET} bcos-framework jsoncpp_static) + +if (TESTS) + # fetch bcos-test + enable_testing() + set(CTEST_OUTPUT_ON_FAILURE TRUE) + add_subdirectory(test) +endif() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/Common.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/Common.h" new file mode 100644 index 00000000..8e77833e --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/Common.h" @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2021 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: yujiechen + * @date 2021-04-12 + */ +#pragma once +#include +#include +#include + +#define CONSENSUS_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("CONSENSUS") << LOG_BADGE("Core") +namespace bcos +{ +namespace consensus +{ +const IndexType NON_CONSENSUS_NODE = (IndexType)(-1); +DERIVE_BCOS_EXCEPTION(InitConsensusException); +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/ConsensusConfig.cpp" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/ConsensusConfig.cpp" new file mode 100644 index 00000000..f9e78f72 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/ConsensusConfig.cpp" @@ -0,0 +1,158 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Base implementation of consensus Config + * @file ConsensusConfig.cpp + * @author: yujiechen + * @date 2021-04-09 + */ +#include "ConsensusConfig.h" + +using namespace bcos; +using namespace bcos::crypto; +using namespace bcos::consensus; +// the consensus node list +// Note: copy here to ensure thread safety +// And the cost of copying the pointer is more efficient +ConsensusNodeList ConsensusConfig::consensusNodeList() const +{ + ReadGuard l(x_consensusNodeList); + return *m_consensusNodeList; +} + +NodeIDs ConsensusConfig::consensusNodeIDList(bool _excludeSelf) const +{ + ReadGuard l(x_consensusNodeList); + std::vector nodeIDList; + for (auto node : *m_consensusNodeList) + { + if (_excludeSelf && node->nodeID()->data() == nodeID()->data()) + { + continue; + } + nodeIDList.push_back(node->nodeID()); + } + return nodeIDList; +} + +bool ConsensusConfig::compareConsensusNode( + ConsensusNodeList const& _left, ConsensusNodeList const& _right) +{ + if (_left.size() != _right.size()) + { + return false; + } + size_t i = 0; + for (auto const& node : _left) + { + auto compareNode = _right[i]; + if (node->nodeID()->data() != compareNode->nodeID()->data() || + node->weight() != compareNode->weight()) + { + return false; + } + i++; + } + return true; +} + +void ConsensusConfig::setObserverNodeList(ConsensusNodeList& _observerNodeList) +{ + std::sort(_observerNodeList.begin(), _observerNodeList.end(), ConsensusNodeComparator()); + // update the observer list + { + UpgradableGuard l(x_observerNodeList); + // consensus node list have not been changed + if (compareConsensusNode(_observerNodeList, *m_observerNodeList)) + { + m_observerNodeListUpdated = false; + return; + } + UpgradeGuard ul(l); + // consensus node list have been changed + *m_observerNodeList = _observerNodeList; + m_observerNodeListUpdated = true; + } +} + +void ConsensusConfig::setConsensusNodeList(ConsensusNodeList& _consensusNodeList) +{ + if (_consensusNodeList.size() == 0) + { + BOOST_THROW_EXCEPTION(InitConsensusException() + << errinfo_comment("Must contain at least one consensus node")); + } + + std::sort(_consensusNodeList.begin(), _consensusNodeList.end(), ConsensusNodeComparator()); + // update the consensus list + { + UpgradableGuard l(x_consensusNodeList); + // consensus node list have not been changed + if (compareConsensusNode(_consensusNodeList, *m_consensusNodeList)) + { + m_consensusNodeListUpdated = false; + return; + } + UpgradeGuard ul(l); + // consensus node list have been changed + *m_consensusNodeList = _consensusNodeList; + m_consensusNodeListUpdated = true; + } + { + // update the consensusNodeNum + ReadGuard l(x_consensusNodeList); + m_consensusNodeNum.store(m_consensusNodeList->size()); + } + // update the nodeIndex + auto nodeIndex = getNodeIndexByNodeID(m_keyPair->publicKey()); + if (nodeIndex != m_nodeIndex) + { + m_nodeIndex.store(nodeIndex); + } + // update quorum + updateQuorum(); + CONSENSUS_LOG(INFO) << METRIC << LOG_DESC("updateConsensusNodeList") + << LOG_KV("nodeNum", m_consensusNodeNum) << LOG_KV("nodeIndex", nodeIndex) + << LOG_KV("committedIndex", + (committedProposal() ? committedProposal()->index() : 0)) + << decsConsensusNodeList(_consensusNodeList); +} + +IndexType ConsensusConfig::getNodeIndexByNodeID(bcos::crypto::PublicPtr _nodeID) +{ + ReadGuard l(x_consensusNodeList); + IndexType nodeIndex = NON_CONSENSUS_NODE; + IndexType i = 0; + for (auto _consensusNode : *m_consensusNodeList) + { + if (_consensusNode->nodeID()->data() == _nodeID->data()) + { + nodeIndex = i; + break; + } + i++; + } + return nodeIndex; +} + +ConsensusNodeInterface::Ptr ConsensusConfig::getConsensusNodeByIndex(IndexType _nodeIndex) +{ + ReadGuard l(x_consensusNodeList); + if (_nodeIndex < m_consensusNodeList->size()) + { + return (*m_consensusNodeList)[_nodeIndex]; + } + return nullptr; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/ConsensusConfig.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/ConsensusConfig.h" new file mode 100644 index 00000000..5eb62722 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/ConsensusConfig.h" @@ -0,0 +1,169 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation of Consensus Config + * @file ConsensusConfig.h + * @author: yujiechen + * @date 2021-04-09 + */ +#pragma once +#include "../framework/ConsensusConfigInterface.h" +#include "Common.h" +#include "bcos-framework/protocol/Protocol.h" +#include +#include + +namespace bcos +{ +namespace consensus +{ +class ConsensusConfig : public ConsensusConfigInterface +{ +public: + using Ptr = std::shared_ptr; + explicit ConsensusConfig(bcos::crypto::KeyPairInterface::Ptr _keyPair) + : m_keyPair(_keyPair), + m_consensusNodeList(std::make_shared()), + m_observerNodeList(std::make_shared()) + {} + virtual ~ConsensusConfig() {} + + // the NodeID of the consensus node + bcos::crypto::PublicPtr nodeID() const override { return m_keyPair->publicKey(); } + + // the nodeIndex of this node + IndexType nodeIndex() const override { return m_nodeIndex; } + + bool isConsensusNode() const override + { + return (m_nodeIndex != NON_CONSENSUS_NODE) && m_asMasterNode.load(); + } + // the consensus node list + ConsensusNodeList consensusNodeList() const override; + bcos::crypto::NodeIDs consensusNodeIDList(bool _excludeSelf = true) const override; + + uint64_t consensusTimeout() const override { return m_consensusTimeout; } + + void setConsensusNodeList(ConsensusNodeList& _consensusNodeList) override; + + void setConsensusTimeout(uint64_t _consensusTimeout) override + { + m_consensusTimeout.store(_consensusTimeout); + } + + // Note: After the block sync, + // need to set the committedProposal of the consensus in the ordering phase + void setCommittedProposal(ProposalInterface::Ptr _committedProposal) override + { + WriteGuard l(x_committedProposal); + m_committedProposal = _committedProposal; + auto index = m_committedProposal->index() + 1; + if (m_progressedIndex < index) + { + m_progressedIndex = index; + } + } + + ProposalInterface::ConstPtr committedProposal() override + { + ReadGuard l(x_committedProposal); + if (!m_committedProposal) + { + return nullptr; + } + return std::const_pointer_cast(m_committedProposal); + } + + virtual bcos::protocol::BlockNumber progressedIndex() { return m_progressedIndex; } + virtual void setProgressedIndex(bcos::protocol::BlockNumber _progressedIndex) + { + m_progressedIndex = _progressedIndex; + CONSENSUS_LOG(DEBUG) << LOG_DESC("PBFTConfig: setProgressedIndex") + << LOG_KV("progressedIndex", m_progressedIndex); + } + + virtual void updateQuorum() = 0; + IndexType getNodeIndexByNodeID(bcos::crypto::PublicPtr _nodeID); + ConsensusNodeInterface::Ptr getConsensusNodeByIndex(IndexType _nodeIndex); + bcos::crypto::KeyPairInterface::Ptr keyPair() { return m_keyPair; } + + virtual void setBlockTxCountLimit(uint64_t _blockTxCountLimit) + { + m_blockTxCountLimit = _blockTxCountLimit; + } + virtual uint64_t blockTxCountLimit() const { return m_blockTxCountLimit.load(); } + bcos::protocol::BlockNumber syncingHighestNumber() const { return m_syncingHighestNumber; } + void setSyncingHighestNumber(bcos::protocol::BlockNumber _number) + { + m_syncingHighestNumber = _number; + } + + IndexType consensusNodesNum() const { return m_consensusNodeNum.load(); } + + void setObserverNodeList(ConsensusNodeList& _observerNodeList); + + bool asMasterNode() const { return m_asMasterNode.load(); } + virtual void enableAsMasterNode(bool _isMasterNode) + { + m_asMasterNode.store(_isMasterNode); + if (m_versionNotification) + { + m_versionNotification(m_compatibilityVersion); + } + } + + virtual void registerVersionInfoNotification( + std::function _versionNotification) + { + m_versionNotification = _versionNotification; + } + + uint32_t compatibilityVersion() const { return m_compatibilityVersion; } + +private: + bool compareConsensusNode(ConsensusNodeList const& _left, ConsensusNodeList const& _right); + +protected: + bcos::crypto::KeyPairInterface::Ptr m_keyPair; + std::atomic m_nodeIndex = {0}; + std::atomic m_consensusNodeNum = {0}; + + ConsensusNodeListPtr m_consensusNodeList; + mutable bcos::SharedMutex x_consensusNodeList; + std::atomic_bool m_consensusNodeListUpdated = {false}; + + ConsensusNodeListPtr m_observerNodeList; + mutable bcos::SharedMutex x_observerNodeList; + std::atomic_bool m_observerNodeListUpdated = {false}; + + // default timeout is 3000ms + std::atomic m_consensusTimeout = {3000}; + // default blockTxCountLimit is 1000 + std::atomic m_blockTxCountLimit = {1000}; + + ProposalInterface::Ptr m_committedProposal; + mutable bcos::SharedMutex x_committedProposal; + + std::atomic m_progressedIndex = {0}; + std::atomic_bool m_syncingState = {false}; + bcos::protocol::BlockNumber m_syncingHighestNumber = {0}; + + std::atomic_bool m_asMasterNode = {false}; + + std::function m_versionNotification; + uint32_t m_compatibilityVersion = (uint32_t)(bcos::protocol::DEFAULT_VERSION); +}; +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/ConsensusEngine.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/ConsensusEngine.h" new file mode 100644 index 00000000..742cdc70 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/ConsensusEngine.h" @@ -0,0 +1,87 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Base implementation for ConsensusEngine + * @file ConsensusEngine.cpp + * @author: yujiechen + * @date 2021-04-22 + */ +#pragma once +#include "../framework/ConsensusEngineInterface.h" +#include "Common.h" +#include + +namespace bcos +{ +namespace consensus +{ +class ConsensusEngine : public virtual ConsensusEngineInterface, public Worker +{ +public: + ConsensusEngine(std::string _name, unsigned _idleWaitMs) : Worker(_name, _idleWaitMs) {} + + ~ConsensusEngine() override { stop(); } + void start() override + { + if (m_started) + { + CONSENSUS_LOG(WARNING) << LOG_DESC("The consensusEngine has already been started"); + return; + } + CONSENSUS_LOG(INFO) << LOG_DESC("Start the consensusEngine"); + // start a thread to execute task + startWorking(); + m_started = true; + } + + void stop() override + { + if (m_started == false) + { + return; + } + CONSENSUS_LOG(INFO) << LOG_DESC("Stop consensusEngine"); + m_started = false; + finishWorker(); + if (isWorking()) + { + // stop the worker thread + stopWorking(); + terminate(); + } + CONSENSUS_LOG(INFO) << LOG_DESC("ConsensusEngine stopped"); + } + + void workerProcessLoop() override + { + while (isWorking()) + { + try + { + executeWorker(); + } + catch (std::exception const& _e) + { + CONSENSUS_LOG(ERROR) << LOG_DESC("Process consensus task exception") + << LOG_KV("error", boost::diagnostic_information(_e)); + } + } + } + +protected: + std::atomic_bool m_started = {false}; +}; +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/Proposal.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/Proposal.h" new file mode 100644 index 00000000..caf2fdf8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/Proposal.h" @@ -0,0 +1,156 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation of Proposal + * @file Proposal.h + * @author: yujiechen + * @date 2021-04-09 + */ +#pragma once +#include "bcos-pbft/core/proto/Consensus.pb.h" +#include "bcos-pbft/framework/ProposalInterface.h" +#include +#include + +namespace bcos +{ +namespace consensus +{ +const bcos::protocol::BlockNumber InvalidBlockNumber = -1; +class Proposal : virtual public ProposalInterface +{ +public: + using Ptr = std::shared_ptr; + Proposal() : m_rawProposal(std::make_shared()) {} + explicit Proposal(bytesConstRef _data) : Proposal() { decode(_data); } + explicit Proposal(std::shared_ptr _rawProposal) : m_rawProposal(_rawProposal) + { + deserializeObject(); + } + ~Proposal() override {} + + // the index of the proposal + bcos::protocol::BlockNumber index() const override { return m_rawProposal->index(); } + void setIndex(bcos::protocol::BlockNumber _index) override { m_rawProposal->set_index(_index); } + + // the hash of the proposal + bcos::crypto::HashType const& hash() const override { return m_hash; } + void setHash(bcos::crypto::HashType const& _hash) override + { + m_hash = _hash; + m_rawProposal->set_hash(_hash.data(), bcos::crypto::HashType::SIZE); + } + // the payload of the proposal + bcos::bytesConstRef data() const override + { + auto const& data = m_rawProposal->data(); + return bcos::bytesConstRef((byte const*)data.c_str(), data.size()); + } + void setData(bytes const& _data) override + { + m_rawProposal->set_data(_data.data(), _data.size()); + } + + void setData(bytes&& _data) override + { + auto size = _data.size(); + m_rawProposal->set_data(std::move(_data).data(), size); + } + + void setData(bcos::bytesConstRef _data) override + { + m_rawProposal->set_data(_data.data(), _data.size()); + } + + bytesConstRef extraData() const override + { + auto const& extraData = m_rawProposal->extradata(); + return bytesConstRef((byte const*)extraData.data(), extraData.size()); + } + void setExtraData(bytes const& _data) override + { + m_rawProposal->set_extradata(_data.data(), _data.size()); + } + + void setExtraData(bytes&& _data) override + { + auto dataSize = _data.size(); + m_rawProposal->set_extradata(std::move(_data).data(), dataSize); + } + void setExtraData(bcos::bytesConstRef _data) override + { + m_rawProposal->set_extradata(_data.data(), _data.size()); + } + + bytesConstRef signature() const override + { + auto const& signature = m_rawProposal->signature(); + return bcos::bytesConstRef((byte const*)signature.c_str(), signature.size()); + } + + void setSignature(bytes const& _data) override + { + m_rawProposal->set_signature(_data.data(), _data.size()); + } + + bool operator==(Proposal const _proposal) const + { + return _proposal.index() == index() && _proposal.hash() == hash() && + _proposal.data().toBytes() == data().toBytes(); + } + bool operator!=(Proposal const _proposal) const { return !(operator==(_proposal)); } + + std::shared_ptr rawProposal() { return m_rawProposal; } + + bytesPointer encode() const override { return bcos::protocol::encodePBObject(m_rawProposal); } + void decode(bytesConstRef _data) override + { + bcos::protocol::decodePBObject(m_rawProposal, _data); + deserializeObject(); + } + + void setSealerId(int64_t _sealerId) override { m_rawProposal->set_sealerid(_sealerId); } + + int64_t sealerId() override { return m_rawProposal->sealerid(); } + + bool systemProposal() const override { return m_rawProposal->systemproposal(); } + void setSystemProposal(bool _systemProposal) override + { + m_rawProposal->set_systemproposal(_systemProposal); + } + +protected: + void setRawProposal(std::shared_ptr _rawProposal) + { + m_rawProposal = _rawProposal; + deserializeObject(); + } + virtual void deserializeObject() + { + auto const& hashData = m_rawProposal->hash(); + if (hashData.size() < bcos::crypto::HashType::SIZE) + { + return; + } + m_hash = + bcos::crypto::HashType((byte const*)hashData.c_str(), bcos::crypto::HashType::SIZE); + } + +protected: + std::shared_ptr m_rawProposal; + bcos::crypto::HashType m_hash; +}; +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/StateMachine.cpp" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/StateMachine.cpp" new file mode 100644 index 00000000..870e630e --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/StateMachine.cpp" @@ -0,0 +1,188 @@ +/** + * Copyright (C) 2021 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. + * + * @brief state machine to execute the transactions + * @file StateMachine.cpp + * @author: yujiechen + * @date 2021-05-18 + */ +#include "StateMachine.h" +#include "Common.h" + +using namespace bcos; +using namespace bcos::consensus; +using namespace bcos::protocol; +using namespace bcos::crypto; + +void StateMachine::asyncApply(ssize_t _timeout, ProposalInterface::ConstPtr _lastAppliedProposal, + ProposalInterface::Ptr _proposal, ProposalInterface::Ptr _executedProposal, + std::function _onExecuteFinished) +{ + auto self = weak_from_this(); + // Note: async here to increase performance + m_worker->enqueue( + [self, _timeout, _lastAppliedProposal, _proposal, _executedProposal, _onExecuteFinished]() { + auto stateMachine = self.lock(); + if (!stateMachine) + { + return; + } + stateMachine->apply( + _timeout, _lastAppliedProposal, _proposal, _executedProposal, _onExecuteFinished); + }); +} + +void StateMachine::asyncPreApply( + ProposalInterface::Ptr _proposal, std::function _onPreApplyFinished) +{ + auto self = weak_from_this(); + // Note: async here to increase performance, trigger preExecuteBlock + m_schedulerWorker->enqueue([self, _proposal, _onPreApplyFinished]() { + auto stateMachine = self.lock(); + if (!stateMachine) + { + return; + } + stateMachine->preApply(_proposal, _onPreApplyFinished); + }); +} + +void StateMachine::apply(ssize_t, ProposalInterface::ConstPtr _lastAppliedProposal, + ProposalInterface::Ptr _proposal, ProposalInterface::Ptr _executedProposal, + std::function _onExecuteFinished) +{ + if (_proposal->index() <= _lastAppliedProposal->index()) + { + CONSENSUS_LOG(WARNING) << LOG_DESC("asyncApply: the proposal has already been applied") + << LOG_KV("proposalIndex", _proposal->index()) + << LOG_KV("lastAppliedProposal", _lastAppliedProposal->index()); + if (_onExecuteFinished) + { + _onExecuteFinished(-1); + } + return; + } + auto block = m_blockFactory->createBlock(_proposal->data()); + // invalid block + auto blockHeader = block->blockHeader(); + if (!blockHeader) + { + if (_onExecuteFinished) + { + _onExecuteFinished(-1); + } + return; + } + // set the parentHash information + if (_proposal->index() == _lastAppliedProposal->index() + 1) + { + ParentInfoList parentInfoList; + ParentInfo parentInfo{_lastAppliedProposal->index(), _lastAppliedProposal->hash()}; + parentInfoList.push_back(parentInfo); + blockHeader->setParentInfo(std::move(parentInfoList)); + CONSENSUS_LOG(DEBUG) << LOG_DESC("setParentInfo for the proposal") + << LOG_KV("proposalIndex", _proposal->index()) + << LOG_KV("lastAppliedProposal", _lastAppliedProposal->index()) + << LOG_KV("parentHash", _lastAppliedProposal->hash().abridged()); + } + else + { + CONSENSUS_LOG(FATAL) << LOG_DESC("invalid lastAppliedProposal") + << LOG_KV("lastAppliedIndex", _lastAppliedProposal->index()) + << LOG_KV("proposal", _proposal->index()); + } + // calls dispatcher to execute the block + auto startT = utcTime(); + m_scheduler->executeBlock(block, false, + [startT, block, _onExecuteFinished, _proposal, _executedProposal]( + Error::Ptr&& _error, BlockHeader::Ptr&& _blockHeader, bool _sysBlock) { + if (!_onExecuteFinished) + { + return; + } + auto blockHeader = block->blockHeader(); + if (_error != nullptr) + { + CONSENSUS_LOG(WARNING) << LOG_DESC("asyncExecuteBlock failed") + << LOG_KV("number", blockHeader->number()) + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorInfo", _error->errorMessage()); + _onExecuteFinished(_error->errorCode()); + return; + } + auto execT = (double)(utcTime() - startT) / (double)(block->transactionsHashSize()); + CONSENSUS_LOG(INFO) << METRIC << LOG_DESC("asyncExecuteBlock success") + << LOG_KV("sysBlock", _sysBlock) + << LOG_KV("number", _blockHeader->number()) + << LOG_KV("result", _blockHeader->hash().abridged()) + << LOG_KV("txsSize", block->transactionsHashSize()) + << LOG_KV("txsRoot", _blockHeader->txsRoot().abridged()) + << LOG_KV("receiptsRoot", _blockHeader->receiptsRoot().abridged()) + << LOG_KV("stateRoot", _blockHeader->stateRoot().abridged()) + << LOG_KV("timeCost", (utcTime() - startT)) + << LOG_KV("execPerTx", execT); + if (_blockHeader->number() != blockHeader->number()) + { + CONSENSUS_LOG(WARNING) << LOG_DESC("asyncExecuteBlock exception") + << LOG_KV("expectedNumber", blockHeader->number()) + << LOG_KV("number", _blockHeader->number()) + << LOG_KV("timeCost", (utcTime() - startT)); + return; + } + _executedProposal->setIndex(_blockHeader->number()); + _executedProposal->setHash(_blockHeader->hash()); + + bcos::bytes blockHeaderBuffer; + _blockHeader->encode(blockHeaderBuffer); + _executedProposal->setData(std::move(blockHeaderBuffer)); + // the transactions hash list + _executedProposal->setExtraData(_proposal->data()); + // The _onExecuteFinished callback itself does the asynchronous logic, so there is no + // need to use m_worker to re-synchronize it here. + _onExecuteFinished(0); + }); +} + +void StateMachine::preApply( + ProposalInterface::Ptr _proposal, std::function _onPreApplyFinished) +{ + auto block = m_blockFactory->createBlock(_proposal->data()); + + auto startT = utcTime(); + m_scheduler->preExecuteBlock(block, false, + [block, startT, _onPreApplyFinished = std::move(_onPreApplyFinished)](Error::Ptr&& error) { + if (!error) + { + CONSENSUS_LOG(DEBUG) + << LOG_BADGE("prepareBlockExecutive") << LOG_DESC("preApply") + << LOG_KV("blockNumber", block->blockHeaderConst()->number()) + << LOG_KV("blockHeader.timestamps", block->blockHeaderConst()->timestamp()) + << LOG_KV("timeCost", (utcTime() - startT)); + _onPreApplyFinished(true); + } + else + { + CONSENSUS_LOG(ERROR) + << LOG_BADGE("prepareBlockExecutive") << LOG_DESC("preApply failed!") + << LOG_KV("errorCode", error->errorCode()) + << LOG_KV("errorMessage", error->errorMessage()) + << LOG_KV("message", error->errorMessage()) + << LOG_KV("blockNumber", block->blockHeaderConst()->number()) + << LOG_KV("blockHeader.timestamps", block->blockHeaderConst()->timestamp()) + << LOG_KV("timeCost", (utcTime() - startT)); + _onPreApplyFinished(false); + } + }); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/StateMachine.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/StateMachine.h" new file mode 100644 index 00000000..712ae153 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/StateMachine.h" @@ -0,0 +1,80 @@ +/** + * Copyright (C) 2021 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. + * + * @brief state machine to execute the transactions + * @file StateMachine.h + * @author: yujiechen + * @date 2021-05-18 + */ +#pragma once +#include "../framework/StateMachineInterface.h" +#include +#include +#include + +#include +namespace bcos +{ +namespace consensus +{ +class StateMachine : public StateMachineInterface, public std::enable_shared_from_this +{ +public: + StateMachine(bcos::scheduler::SchedulerInterface::Ptr _scheduler, + bcos::protocol::BlockFactory::Ptr _blockFactory) + : m_scheduler(std::move(_scheduler)), m_blockFactory(_blockFactory) + { + // since execute block is serial, only use one thread to decrease the timecost + m_worker = std::make_shared("stateMachine", 1); + m_schedulerWorker = + std::make_shared("preExec", (std::thread::hardware_concurrency() * 2)); + } + + ~StateMachine() override + { + if (m_worker) + { + m_worker->stop(); + } + if (m_schedulerWorker) + { + m_schedulerWorker->stop(); + } + } + + void asyncApply(ssize_t _execTimeout, ProposalInterface::ConstPtr _lastAppliedProposal, + ProposalInterface::Ptr _proposal, ProposalInterface::Ptr _executedProposal, + std::function _onExecuteFinished) override; + + void asyncPreApply( + ProposalInterface::Ptr _proposal, std::function _onPreApplyFinished) override; + +private: + void apply(ssize_t _execTimeout, ProposalInterface::ConstPtr _lastAppliedProposal, + ProposalInterface::Ptr _proposal, ProposalInterface::Ptr _executedProposal, + std::function _onExecuteFinished); + + void preApply(ProposalInterface::Ptr _proposal, std::function _onPreApplyFinished); + +protected: + bcos::scheduler::SchedulerInterface::Ptr m_scheduler; + bcos::protocol::BlockFactory::Ptr m_blockFactory; + bcos::ThreadPool::Ptr m_worker; + // threadPool used for scheduler preExecuteBlock, since preExecuteBlock may fetch transactions + // from the txpool, it need to use multiple thread to improve the txs-fetching-speed + bcos::ThreadPool::Ptr m_schedulerWorker; +}; +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/proto/Consensus.proto" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/proto/Consensus.proto" new file mode 100644 index 00000000..2bf634da --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/core/proto/Consensus.proto" @@ -0,0 +1,16 @@ +syntax = "proto3"; +package bcos.consensus; +message RawProposal +{ + // the index of the proposal + int64 index = 1; + // the hash of the proposal + bytes hash = 2; + // the proposal data, optional + bytes data = 3; + // the proposal signature, optional + bytes signature = 4; + bytes extraData = 5; + int64 sealerId = 6; + bool systemProposal = 7; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/framework/ConsensusConfigInterface.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/framework/ConsensusConfigInterface.h" new file mode 100644 index 00000000..cfbb836d --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/framework/ConsensusConfigInterface.h" @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for Consensus Config + * @file ConsensusConfigInterface.h + * @author: yujiechen + * @date 2021-04-09 + */ +#pragma once +#include "ProposalInterface.h" +#include +#include +namespace bcos::consensus +{ +class ConsensusConfigInterface +{ +public: + using Ptr = std::shared_ptr; + ConsensusConfigInterface() = default; + virtual ~ConsensusConfigInterface() = default; + + // the NodeID of the consensus node + virtual bcos::crypto::PublicPtr nodeID() const = 0; + // the nodeIndex of this node + virtual IndexType nodeIndex() const = 0; + + // the sealer list + virtual ConsensusNodeList consensusNodeList() const = 0; + virtual bcos::crypto::NodeIDs consensusNodeIDList(bool _excludeSelf = true) const = 0; + virtual bool isConsensusNode() const = 0; + + // the consensus timeout + virtual uint64_t consensusTimeout() const = 0; + + // the min valid quorum before agree on a round of consensus + virtual uint64_t minRequiredQuorum() const = 0; + + virtual void setConsensusNodeList(ConsensusNodeList& _sealerList) = 0; + virtual void setConsensusTimeout(uint64_t _consensusTimeout) = 0; + + virtual void setCommittedProposal(ProposalInterface::Ptr _committedProposal) = 0; + virtual ProposalInterface::ConstPtr committedProposal() = 0; +}; +} // namespace bcos::consensus \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/framework/ConsensusEngineInterface.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/framework/ConsensusEngineInterface.h" new file mode 100644 index 00000000..8c65de76 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/framework/ConsensusEngineInterface.h" @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for Consensus Engine + * @file ConsensusEngineInterface.h + * @author: yujiechen + * @date 2021-04-09 + */ +#pragma once +#include "ConsensusConfigInterface.h" +namespace bcos +{ +namespace consensus +{ +class ConsensusEngineInterface +{ +public: + using Ptr = std::shared_ptr; + ConsensusEngineInterface() = default; + virtual ~ConsensusEngineInterface() {} + + // start the consensus engine + virtual void start() = 0; + // stop the consensus engine + virtual void stop() = 0; +}; +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/framework/ProposalInterface.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/framework/ProposalInterface.h" new file mode 100644 index 00000000..b2d6f3a9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/framework/ProposalInterface.h" @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2021 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. + * + * @brief the proposal information + * @file ProposalInterface.h + * @author: yujiechen + * @date 2021-04-09 + */ +#pragma once +#include +namespace bcos +{ +namespace consensus +{ +class ProposalInterface +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + ProposalInterface() = default; + virtual ~ProposalInterface() = default; + + virtual bytesPointer encode() const = 0; + virtual void decode(bytesConstRef _data) = 0; + + // the index of the proposal + virtual bcos::protocol::BlockNumber index() const = 0; + virtual void setIndex(bcos::protocol::BlockNumber _index) = 0; + + // the hash of the proposal + virtual bcos::crypto::HashType const& hash() const = 0; + virtual void setHash(bcos::crypto::HashType const& _hash) = 0; + // the data of the proposal + virtual bcos::bytesConstRef data() const = 0; + virtual void setData(bytes const& _data) = 0; + virtual void setData(bytes&& _data) = 0; + virtual void setData(bcos::bytesConstRef _data) = 0; + + // the extra data of the proposal + virtual bcos::bytesConstRef extraData() const = 0; + virtual void setExtraData(bytes const& _data) = 0; + virtual void setExtraData(bytes&& _data) = 0; + virtual void setExtraData(bcos::bytesConstRef _data) = 0; + + // the sealerId + virtual void setSealerId(int64_t _sealerId) = 0; + virtual int64_t sealerId() = 0; + + // the signature to the proposal(optional) + virtual bytesConstRef signature() const = 0; + virtual void setSignature(bytes const& _signature) = 0; + + // the proposal type + virtual bool systemProposal() const = 0; + virtual void setSystemProposal(bool _systemProposal) = 0; +}; +using ProposalList = std::vector; +using ProposalListPtr = std::shared_ptr; +} // namespace consensus +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/framework/StateMachineInterface.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/framework/StateMachineInterface.h" new file mode 100644 index 00000000..d7940cdc --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/framework/StateMachineInterface.h" @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for the state machine to execute the transactions + * @file StateMachineInterface.h + * @author: yujiechen + * @date 2021-05-18 + */ +#pragma once +#include "ProposalInterface.h" +#include +namespace bcos::consensus +{ +class StateMachineInterface +{ +public: + using Ptr = std::shared_ptr; + StateMachineInterface() = default; + virtual ~StateMachineInterface() = default; + + virtual void asyncApply(ssize_t _execTimeout, ProposalInterface::ConstPtr _lastAppliedProposal, + ProposalInterface::Ptr _proposal, ProposalInterface::Ptr _executedProposal, + std::function _onExecuteFinished) = 0; + + // (Not required): Just for performance, call this before "asyncApply" in the other thread. + virtual void asyncPreApply( + ProposalInterface::Ptr _proposal, std::function _onPreApplyFinished) = 0; +}; +} // namespace bcos::consensus diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/PBFTFactory.cpp" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/PBFTFactory.cpp" new file mode 100644 index 00000000..38bcc480 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/PBFTFactory.cpp" @@ -0,0 +1,82 @@ +/** + * Copyright (C) 2021 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. + * + * @brief factory to create the PBFTEngine + * @file PBFTFactory.cpp + * @author: yujiechen + * @date 2021-05-19 + */ +#include "PBFTFactory.h" +#include "bcos-pbft/core/StateMachine.h" +#include "engine/Validator.h" +#include "protocol/PB/PBFTCodec.h" +#include "protocol/PB/PBFTMessageFactoryImpl.h" +#include "storage/LedgerStorage.h" +#include "utilities/Common.h" +#include +#include + +using namespace bcos; +using namespace bcos::consensus; +using namespace bcos::protocol; + +PBFTFactory::PBFTFactory(bcos::crypto::CryptoSuite::Ptr _cryptoSuite, + bcos::crypto::KeyPairInterface::Ptr _keyPair, + std::shared_ptr _frontService, + std::shared_ptr _storage, + std::shared_ptr _ledger, + bcos::scheduler::SchedulerInterface::Ptr _scheduler, bcos::txpool::TxPoolInterface::Ptr _txpool, + bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::protocol::TransactionSubmitResultFactory::Ptr _txResultFactory) + : m_cryptoSuite(std::move(_cryptoSuite)), + m_keyPair(std::move(_keyPair)), + m_frontService(std::move(_frontService)), + m_storage(std::move(_storage)), + m_ledger(std::move(_ledger)), + m_scheduler(std::move(_scheduler)), + m_txpool(std::move(_txpool)), + m_blockFactory(std::move(_blockFactory)), + m_txResultFactory(std::move(_txResultFactory)) +{} + +PBFTImpl::Ptr PBFTFactory::createPBFT() +{ + auto pbftMessageFactory = std::make_shared(); + PBFT_LOG(INFO) << LOG_DESC("create PBFTCodec"); + auto pbftCodec = std::make_shared(m_keyPair, m_cryptoSuite, pbftMessageFactory); + + PBFT_LOG(INFO) << LOG_DESC("create PBFT validator"); + auto validator = std::make_shared(m_txpool, m_blockFactory, m_txResultFactory); + + PBFT_LOG(DEBUG) << LOG_DESC("create StateMachine"); + auto stateMachine = std::make_shared(m_scheduler, m_blockFactory); + + PBFT_LOG(INFO) << LOG_DESC("create pbftStorage"); + auto pbftStorage = + std::make_shared(m_scheduler, m_storage, m_blockFactory, pbftMessageFactory); + + PBFT_LOG(INFO) << LOG_DESC("create pbftConfig"); + auto pbftConfig = std::make_shared(m_cryptoSuite, m_keyPair, pbftMessageFactory, + pbftCodec, validator, m_frontService, stateMachine, pbftStorage); + + PBFT_LOG(INFO) << LOG_DESC("create PBFTEngine"); + auto pbftEngine = std::make_shared(pbftConfig); + + PBFT_LOG(INFO) << LOG_DESC("create PBFT"); + auto ledgerFetcher = std::make_shared(m_ledger); + auto pbft = std::make_shared(pbftEngine); + pbft->setLedgerFetcher(ledgerFetcher); + return pbft; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/PBFTFactory.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/PBFTFactory.h" new file mode 100644 index 00000000..5c5b1992 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/PBFTFactory.h" @@ -0,0 +1,58 @@ +/** + * Copyright (C) 2021 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. + * + * @brief factory to create the PBFTEngine + * @file PBFTFactory.h + * @author: yujiechen + * @date 2021-05-19 + */ +#pragma once +#include "PBFTImpl.h" +#include "config/PBFTConfig.h" +#include +#include +#include +#include + +namespace bcos::consensus +{ +class PBFTFactory : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + PBFTFactory(bcos::crypto::CryptoSuite::Ptr _cryptoSuite, + bcos::crypto::KeyPairInterface::Ptr _keyPair, + std::shared_ptr _frontService, + std::shared_ptr _storage, + std::shared_ptr _ledger, + bcos::scheduler::SchedulerInterface::Ptr _scheduler, + bcos::txpool::TxPoolInterface::Ptr _txpool, bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::protocol::TransactionSubmitResultFactory::Ptr _txResultFactory); + + virtual ~PBFTFactory() = default; + virtual PBFTImpl::Ptr createPBFT(); + +protected: + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + bcos::crypto::KeyPairInterface::Ptr m_keyPair; + std::shared_ptr m_frontService; + std::shared_ptr m_storage; + std::shared_ptr m_ledger; + bcos::scheduler::SchedulerInterface::Ptr m_scheduler; + bcos::txpool::TxPoolInterface::Ptr m_txpool; + bcos::protocol::BlockFactory::Ptr m_blockFactory; + bcos::protocol::TransactionSubmitResultFactory::Ptr m_txResultFactory; +}; +} // namespace bcos::consensus \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/PBFTImpl.cpp" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/PBFTImpl.cpp" new file mode 100644 index 00000000..f01edac4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/PBFTImpl.cpp" @@ -0,0 +1,190 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for ConsensusInterface + * @file PBFTImpl.cpp + * @author: yujiechen + * @date 2021-05-17 + */ +#include "PBFTImpl.h" +#include +using namespace bcos; +using namespace bcos::consensus; + +void PBFTImpl::start() +{ + if (m_running) + { + PBFT_LOG(INFO) << LOG_DESC("The PBFT module has already been started!"); + return; + } + m_running = true; + m_pbftEngine->start(); + PBFT_LOG(INFO) << LOG_DESC("Start the PBFT module."); +} + +void PBFTImpl::stop() +{ + if (!m_running) + { + PBFT_LOG(INFO) << LOG_DESC("The PBFT module has already been stopped!"); + return; + } + m_blockValidator->stop(); + m_pbftEngine->stop(); + m_running = false; + PBFT_LOG(INFO) << LOG_DESC("Stop the PBFT module."); +} + +void PBFTImpl::asyncSubmitProposal(bool _containSysTxs, bytesConstRef _proposalData, + bcos::protocol::BlockNumber _proposalIndex, bcos::crypto::HashType const& _proposalHash, + std::function _onProposalSubmitted) +{ + return m_pbftEngine->asyncSubmitProposal( + _containSysTxs, _proposalData, _proposalIndex, _proposalHash, _onProposalSubmitted); +} + +void PBFTImpl::asyncGetPBFTView(std::function _onGetView) +{ + auto view = m_pbftEngine->pbftConfig()->view(); + if (!_onGetView) + { + return; + } + _onGetView(nullptr, view); +} + +void PBFTImpl::asyncNotifyConsensusMessage(bcos::Error::Ptr _error, std::string const& _id, + bcos::crypto::NodeIDPtr _nodeID, bytesConstRef _data, + std::function _onRecv) +{ + m_pbftEngine->onReceivePBFTMessage(_error, _id, _nodeID, _data); + if (!_onRecv) + { + return; + } + _onRecv(nullptr); +} + +// the sync module calls this interface to check block +void PBFTImpl::asyncCheckBlock( + bcos::protocol::Block::Ptr _block, std::function _onVerifyFinish) +{ + m_blockValidator->asyncCheckBlock(_block, _onVerifyFinish); +} + +// the sync module calls this interface to notify new block +void PBFTImpl::asyncNotifyNewBlock( + bcos::ledger::LedgerConfig::Ptr _ledgerConfig, std::function _onRecv) +{ + m_pbftEngine->asyncNotifyNewBlock(_ledgerConfig, _onRecv); +} + +void PBFTImpl::notifyHighestSyncingNumber(bcos::protocol::BlockNumber _blockNumber) +{ + m_pbftEngine->pbftConfig()->setSyncingHighestNumber(_blockNumber); +} + +void PBFTImpl::asyncNoteUnSealedTxsSize( + uint64_t _unsealedTxsSize, std::function _onRecvResponse) +{ + m_pbftEngine->pbftConfig()->setUnSealedTxsSize(_unsealedTxsSize); + if (_onRecvResponse) + { + _onRecvResponse(nullptr); + } +} + +void PBFTImpl::init() +{ + auto config = m_pbftEngine->pbftConfig(); + config->validator()->init(); + m_pbftEngine->fetchAndUpdateLedgerConfig(); + PBFT_LOG(INFO) << LOG_DESC("init PBFT success"); +} + +void PBFTImpl::asyncGetConsensusStatus( + std::function _onGetConsensusStatus) +{ + auto config = m_pbftEngine->pbftConfig(); + Json::Value consensusStatus; + consensusStatus["nodeID"] = *toHexString(config->nodeID()->data()); + consensusStatus["index"] = (Json::UInt64)config->nodeIndex(); + consensusStatus["leaderIndex"] = (Json::UInt64)config->getLeader(); + consensusStatus["consensusNodesNum"] = (Json::UInt64)config->consensusNodesNum(); + consensusStatus["maxFaultyQuorum"] = (Json::UInt64)config->maxFaultyQuorum(); + consensusStatus["minRequiredQuorum"] = (Json::UInt64)config->minRequiredQuorum(); + consensusStatus["isConsensusNode"] = config->isConsensusNode(); + consensusStatus["blockNumber"] = (Json::UInt64)config->committedProposal()->index(); + consensusStatus["hash"] = *toHexString(config->committedProposal()->hash()); + if (config->isConsensusNode()) + { + consensusStatus["timeout"] = config->timeout(); + } + else + { + consensusStatus["timeout"] = false; + } + consensusStatus["changeCycle"] = (Json::UInt64)config->timer()->changeCycle(); + consensusStatus["view"] = (Json::UInt64)config->view(); + consensusStatus["connectedNodeList"] = (Json::UInt64)((config->connectedNodeList()).size()); + + // print the nodeIndex of all other nodes + auto nodeList = config->consensusNodeList(); + Json::Value consensusNodeInfo(Json::arrayValue); + size_t i = 0; + for (auto const& node : nodeList) + { + Json::Value info; + info["nodeID"] = *toHexString(node->nodeID()->data()); + info["weight"] = (Json::UInt64)node->weight(); + info["index"] = (Json::Int64)(i); + consensusNodeInfo.append(info); + i++; + } + consensusStatus["consensusNodeList"] = consensusNodeInfo; + Json::FastWriter fastWriter; + std::string statusStr = fastWriter.write(consensusStatus); + _onGetConsensusStatus(nullptr, statusStr); +} + +void PBFTImpl::enableAsMasterNode(bool _isMasterNode) +{ + if (m_masterNode == _isMasterNode) + { + PBFT_LOG(INFO) << LOG_DESC("enableAsMasterNode: The masterNodeState is not changed") + << LOG_KV("master", _isMasterNode); + return; + } + if (!m_masterNode) + { + PBFT_LOG(INFO) << LOG_DESC( + "enableAsMasterNode: clearAllCache for the node switch into backup node"); + m_pbftEngine->clearAllCache(); + } + PBFT_LOG(INFO) << LOG_DESC("enableAsMasterNode: ") << _isMasterNode; + m_pbftEngine->pbftConfig()->enableAsMasterNode(_isMasterNode); + if (!_isMasterNode) + { + m_masterNode.store(_isMasterNode); + return; + } + PBFT_LOG(INFO) << LOG_DESC("enableAsMasterNode: init and start the consensus module"); + init(); + m_pbftEngine->recoverState(); + m_pbftEngine->restart(); + // only reset m_masterNode to true when init success + m_masterNode.store(_isMasterNode); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/PBFTImpl.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/PBFTImpl.h" new file mode 100644 index 00000000..66d77abf --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/PBFTImpl.h" @@ -0,0 +1,156 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for ConsensusInterface + * @file PBFTImpl.h + * @author: yujiechen + * @date 2021-05-17 + */ +#pragma once +#include "engine/BlockValidator.h" +#include "engine/PBFTEngine.h" +#include + +#include +namespace bcos::consensus +{ +class PBFTImpl : public ConsensusInterface +{ +public: + using Ptr = std::shared_ptr; + explicit PBFTImpl(PBFTEngine::Ptr _pbftEngine) : m_pbftEngine(std::move(_pbftEngine)) + { + m_blockValidator = std::make_shared(m_pbftEngine->pbftConfig()); + } + ~PBFTImpl() override { stop(); } + + void start() override; + void stop() override; + + void asyncSubmitProposal(bool _containSysTxs, bytesConstRef _proposalData, + bcos::protocol::BlockNumber _proposalIndex, bcos::crypto::HashType const& _proposalHash, + std::function _onProposalSubmitted) override; + + void asyncGetPBFTView(std::function _onGetView) override; + + void asyncNotifyConsensusMessage(bcos::Error::Ptr _error, std::string const& _id, + bcos::crypto::NodeIDPtr _nodeID, bytesConstRef _data, + std::function _onRecv) override; + + // the sync module calls this interface to check block + void asyncCheckBlock(bcos::protocol::Block::Ptr _block, + std::function _onVerifyFinish) override; + + // the sync module calls this interface to notify new block + void asyncNotifyNewBlock(bcos::ledger::LedgerConfig::Ptr _ledgerConfig, + std::function _onRecv) override; + + void notifyHighestSyncingNumber(bcos::protocol::BlockNumber _blockNumber) override; + void asyncNoteUnSealedTxsSize( + uint64_t _unsealedTxsSize, std::function _onRecvResponse) override; + void setLedgerFetcher(bcos::tool::LedgerConfigFetcher::Ptr _ledgerFetcher) + { + m_pbftEngine->setLedgerFetcher(_ledgerFetcher); + } + PBFTEngine::Ptr pbftEngine() { return m_pbftEngine; } + + virtual void init(); + + // notify the sealer seal Proposal + void registerSealProposalNotifier( + std::function)> + _sealProposalNotifier) + { + m_pbftEngine->pbftConfig()->registerSealProposalNotifier(_sealProposalNotifier); + } + + // notify the sealer the latest blockNumber + void registerStateNotifier(std::function _stateNotifier) + { + m_pbftEngine->pbftConfig()->registerStateNotifier(_stateNotifier); + } + // the sync module notify the consensus module the new block + void registerNewBlockNotifier( + std::function)> + _newBlockNotifier) + { + m_pbftEngine->pbftConfig()->registerNewBlockNotifier(_newBlockNotifier); + } + + void registerFaultyDiscriminator( + std::function _faultyDiscriminator) + { + m_pbftEngine->pbftConfig()->registerFaultyDiscriminator(_faultyDiscriminator); + } + + // handler to notify the consensusing proposal index to the sync module + void registerCommittedProposalNotifier( + std::function)> + _committedProposalNotifier) + { + m_pbftEngine->registerCommittedProposalNotifier(_committedProposalNotifier); + } + + // handler to notify the sealer reset the sealing proposals + void registerSealerResetNotifier( + std::function)> _sealerResetNotifier) + { + m_pbftEngine->pbftConfig()->registerSealerResetNotifier(_sealerResetNotifier); + } + + ConsensusNodeList consensusNodeList() const override + { + return m_pbftEngine->pbftConfig()->consensusNodeList(); + } + uint64_t nodeIndex() const override { return m_pbftEngine->pbftConfig()->nodeIndex(); } + void asyncGetConsensusStatus( + std::function _onGetConsensusStatus) override; + + void notifyConnectedNodes(bcos::crypto::NodeIDSet const& _connectedNodes, + std::function _onResponse) override + { + m_pbftEngine->pbftConfig()->setConnectedNodeList(_connectedNodes); + if (_onResponse) + { + _onResponse(nullptr); + } + } + virtual void enableAsMasterNode(bool _isMasterNode); + + virtual bool masterNode() const { return m_masterNode.load(); } + + virtual void registerVersionInfoNotification( + std::function _versionNotification) + { + m_pbftEngine->pbftConfig()->registerVersionInfoNotification(_versionNotification); + } + + uint32_t compatibilityVersion() const override + { + return m_pbftEngine->pbftConfig()->compatibilityVersion(); + } + + void clearExceptionProposalState(bcos::protocol::BlockNumber _number) override + { + m_pbftEngine->clearExceptionProposalState(_number); + } + +protected: + PBFTEngine::Ptr m_pbftEngine; + BlockValidator::Ptr m_blockValidator; + std::atomic_bool m_running = {false}; + std::atomic_bool m_masterNode = {false}; +}; +} // namespace bcos::consensus \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCache.cpp" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCache.cpp" new file mode 100644 index 00000000..cf1f62c2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCache.cpp" @@ -0,0 +1,391 @@ +/** + * Copyright (C) 2021 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. + * + * @brief cache for the consensus state of the proposal + * @file PBFTCache.cpp + * @author: yujiechen + * @date 2021-04-23 + */ +#include "PBFTCache.h" + +using namespace bcos; +using namespace bcos::consensus; +using namespace bcos::protocol; +using namespace bcos::crypto; + +PBFTCache::PBFTCache(PBFTConfig::Ptr _config, BlockNumber _index) + : m_config(std::move(_config)), m_index(_index) +{} + +void PBFTCache::onCheckPointTimeout() +{ + // Note: this logic is unreachable + if (!m_checkpointProposal) + { + return; + } + if (m_committedIndexNotifier && !m_config->timer()->running()) + { + m_committedIndexNotifier(m_config->committedProposal()->index()); + } + PBFT_LOG(WARNING) << LOG_DESC("onCheckPointTimeout: resend the checkpoint message package") + << LOG_KV("index", m_checkpointProposal->index()) + << LOG_KV("hash", m_checkpointProposal->hash().abridged()) + << m_config->printCurrentState(); + auto checkPointMsg = m_config->pbftMessageFactory()->populateFrom(PacketType::CheckPoint, + m_config->pbftMsgDefaultVersion(), m_config->view(), utcTime(), m_config->nodeIndex(), + m_checkpointProposal, m_config->cryptoSuite(), m_config->keyPair(), true); + auto encodedData = m_config->codec()->encode(checkPointMsg); + // only broadcast message to consensus node + m_config->frontService()->asyncSendBroadcastMessage( + bcos::protocol::NodeType::CONSENSUS_NODE, ModuleID::PBFT, ref(*encodedData)); +} + +bool PBFTCache::existPrePrepare(PBFTMessageInterface::Ptr _prePrepareMsg) +{ + if (!m_prePrepare) + { + return false; + } + return (_prePrepareMsg->hash() == m_prePrepare->hash()) && + (m_prePrepare->view() >= _prePrepareMsg->view()); +} + +void PBFTCache::addCache(CollectionCacheType& _cachedReq, QuorumRecoderType& _weightInfo, + PBFTMessageInterface::Ptr _pbftCache) +{ + if (_pbftCache->index() != m_index) + { + return; + } + auto const& proposalHash = _pbftCache->hash(); + auto generatedFrom = _pbftCache->generatedFrom(); + if (_cachedReq.count(proposalHash) && _cachedReq[proposalHash].count(generatedFrom)) + { + return; + } + auto nodeInfo = m_config->getConsensusNodeByIndex(generatedFrom); + if (!nodeInfo) + { + return; + } + if (!_weightInfo.count(proposalHash)) + { + _weightInfo[proposalHash] = 0; + } + _weightInfo[proposalHash] += nodeInfo->weight(); + _cachedReq[proposalHash][generatedFrom] = _pbftCache; +} + +bool PBFTCache::conflictWithProcessedReq(PBFTMessageInterface::Ptr _msg) +{ + if (m_submitted || m_stableCommitted) + { + return true; + } + if (_msg->view() < m_config->view()) + { + return true; + } + if (!m_prePrepare) + { + return false; + } + // expired msg + if (_msg->view() < m_prePrepare->view()) + { + return true; + } + // conflict msg + if (_msg->view() == m_prePrepare->view()) + { + return (_msg->hash() != m_prePrepare->hash()); + } + return false; +} + +bool PBFTCache::checkPrePrepareProposalStatus() +{ + if (m_prePrepare == nullptr) + { + return false; + } + if (m_prePrepare->view() != m_config->view()) + { + return false; + } + return true; +} + +bool PBFTCache::collectEnoughQuorum( + bcos::crypto::HashType const& _hash, QuorumRecoderType& _weightInfo) +{ + if (!_weightInfo.count(_hash)) + { + return false; + } + return (_weightInfo[_hash] >= m_config->minRequiredQuorum()); +} + +bool PBFTCache::collectEnoughPrepareReq() +{ + if (!checkPrePrepareProposalStatus()) + { + return false; + } + return collectEnoughQuorum(m_prePrepare->hash(), m_prepareReqWeight); +} + +bool PBFTCache::collectEnoughCommitReq() +{ + if (!checkPrePrepareProposalStatus()) + { + return false; + } + return collectEnoughQuorum(m_prePrepare->hash(), m_commitReqWeight); +} + +void PBFTCache::intoPrecommit() +{ + m_precommit = m_prePrepare; + m_precommit->setGeneratedFrom(m_config->nodeIndex()); + setSignatureList(m_precommit->consensusProposal(), m_prepareCacheList); + + m_precommitWithoutData = m_precommit->populateWithoutProposal(); + auto precommitProposalWithoutData = + m_config->pbftMessageFactory()->populateFrom(m_precommit->consensusProposal(), false); + m_precommitWithoutData->setConsensusProposal(precommitProposalWithoutData); + PBFT_LOG(INFO) << LOG_DESC("intoPrecommit") << printPBFTMsgInfo(m_precommit) + << m_config->printCurrentState(); +} + +void PBFTCache::setSignatureList(PBFTProposalInterface::Ptr _proposal, CollectionCacheType& _cache) +{ + assert(_cache.count(_proposal->hash())); + _proposal->clearSignatureProof(); + for (auto const& it : _cache[_proposal->hash()]) + { + _proposal->appendSignatureProof(it.first, it.second->consensusProposal()->signature()); + } + PBFT_LOG(INFO) << LOG_DESC("setSignatureList") + << LOG_KV("signatureSize", _proposal->signatureProofSize()) + << printPBFTProposal(_proposal); +} + +bool PBFTCache::conflictWithPrecommitReq(PBFTMessageInterface::Ptr _prePrepareMsg) +{ + if (!m_precommit) + { + return false; + } + if (m_precommit->index() < m_config->progressedIndex()) + { + return false; + } + if (_prePrepareMsg->index() == m_precommit->index() && + _prePrepareMsg->hash() != m_precommit->hash()) + { + PBFT_LOG(INFO) << LOG_DESC( + "the received pre-prepare msg is conflict with the preparedCache") + << printPBFTMsgInfo(_prePrepareMsg); + return true; + } + return false; +} + +bool PBFTCache::checkAndPreCommit() +{ + // already precommitted + if (m_precommitted) + { + return false; + } + if (!m_prePrepare) + { + return false; + } + // avoid to intoPrecommit when in timeout state + if (m_config->timeout()) + { + return false; + } + if (m_precommit && m_precommit->view() >= m_prePrepare->view()) + { + return false; + } + if (m_prePrepare && m_prePrepare->view() != m_config->view()) + { + return false; + } + if (!collectEnoughPrepareReq()) + { + return false; + } + // update and backup the proposal into precommit-status + intoPrecommit(); + // generate the commitReq + auto commitReq = m_config->pbftMessageFactory()->populateFrom(PacketType::CommitPacket, + m_config->pbftMsgDefaultVersion(), m_config->view(), utcTime(), m_config->nodeIndex(), + m_precommitWithoutData->consensusProposal(), m_config->cryptoSuite(), m_config->keyPair()); + // add the commitReq to local cache + addCommitCache(commitReq); + // broadcast the commitReq + PBFT_LOG(INFO) << LOG_DESC("checkAndPreCommit: broadcast commitMsg") + << LOG_KV("Idx", m_config->nodeIndex()) + << LOG_KV("hash", commitReq->hash().abridged()) + << LOG_KV("index", commitReq->index()); + auto encodedData = m_config->codec()->encode(commitReq, m_config->pbftMsgDefaultVersion()); + // only broadcast message to consensus nodes + m_config->frontService()->asyncSendBroadcastMessage( + bcos::protocol::NodeType::CONSENSUS_NODE, ModuleID::PBFT, ref(*encodedData)); + m_precommitted = true; + // collect the commitReq and try to commit + return checkAndCommit(); +} + +bool PBFTCache::checkAndCommit() +{ + // avoid to intoPrecommit when in timeout state + if (m_config->timeout()) + { + return false; + } + if (m_submitted) + { + return false; + } + // collect enough commit message before intoPrecommit + // can only into commit status when precommitted + if (!m_precommit) + { + return false; + } + if (m_precommit->view() != m_config->view()) + { + return false; + } + if (!collectEnoughCommitReq()) + { + return false; + } + PBFT_LOG(INFO) << LOG_DESC("checkAndCommit") + << printPBFTProposal(m_precommit->consensusProposal()) + << m_config->printCurrentState(); + m_submitted.store(true); + return true; +} + +bool PBFTCache::shouldStopTimer() +{ + if (m_index <= m_config->committedProposal()->index()) + { + return true; + } + return m_submitted; +} + +void PBFTCache::resetCache(ViewType _curView) +{ + m_submitted = false; + m_precommitted = false; + if (!m_precommit && m_prePrepare && m_prePrepare->consensusProposal() && + m_prePrepare->view() < _curView) + { + PBFT_LOG(INFO) << LOG_DESC("resetCache : asyncResetTxsFlag") + << printPBFTProposal(m_prePrepare->consensusProposal()); + // reset the sealingManager, in case of the same block has been sealed twice + m_config->notifyResetSealing(m_prePrepare->consensusProposal()->index()); + // reset the exceptioned txs to unsealed + m_config->validator()->asyncResetTxsFlag(m_prePrepare->consensusProposal()->data(), false); + m_prePrepare = nullptr; + } + // clear the expired prepare cache + resetCacheAfterViewChange(m_prepareCacheList, _curView); + // clear the expired commit cache + resetCacheAfterViewChange(m_commitCacheList, _curView); + + // recalculate m_prepareReqWeight + recalculateQuorum(m_prepareReqWeight, m_prepareCacheList); + // recalculate m_commitReqWeight + recalculateQuorum(m_commitReqWeight, m_commitCacheList); +} + +void PBFTCache::setCheckPointProposal(PBFTProposalInterface::Ptr _proposal) +{ + // expired checkpoint proposal + if (_proposal->index() <= m_config->committedProposal()->index()) + { + PBFT_LOG(WARNING) << LOG_DESC("setCheckPointProposal failed for expired checkpoint index") + << m_config->printCurrentState() << printPBFTProposal(_proposal); + return; + } + if (_proposal->index() != index()) + { + return; + } + m_checkpointProposal = _proposal; + PBFT_LOG(INFO) << LOG_DESC("setCheckPointProposal") << printPBFTProposal(m_checkpointProposal) + << m_config->printCurrentState(); +} + +bool PBFTCache::collectEnoughCheckpoint() +{ + if (!m_checkpointProposal) + { + return false; + } + return collectEnoughQuorum(m_checkpointProposal->hash(), m_checkpointCacheWeight); +} + +bool PBFTCache::checkAndCommitStableCheckPoint() +{ + if (m_stableCommitted) + { + return false; + } + // Before this proposal reach checkPoint consensus, + // it must be ensured that the dependent system transactions + // (such as transactions including dynamically addSealer/removeNode, setConsensusWeight, etc.) + // have been committed + auto committedIndex = m_config->committedProposal()->index(); + auto dependsProposal = std::min((m_index - 1), m_config->waitSealUntil()); + // wait for the sys-proposal committed to trigger checkAndCommitStableCheckPoint + if (committedIndex < dependsProposal) + { + return false; + } + if (committedIndex == dependsProposal) + { + recalculateQuorum(m_checkpointCacheWeight, m_checkpointCacheList); + } + if (!collectEnoughCheckpoint()) + { + return false; + } + setSignatureList(m_checkpointProposal, m_checkpointCacheList); + m_stableCommitted = true; + PBFT_LOG(INFO) << LOG_DESC("checkAndCommitStableCheckPoint") + << LOG_KV("index", m_checkpointProposal->index()) + << LOG_KV("hash", m_checkpointProposal->hash().abridged()) + << m_config->printCurrentState(); + if (m_config->committedProposal()->index() >= m_checkpointProposal->index()) + { + PBFT_LOG(WARNING) << LOG_DESC("checkAndCommitStableCheckPoint: expired checkpointProposal") + << LOG_KV("checkPointIndex", m_checkpointProposal->index()) + << m_config->printCurrentState(); + return false; + } + return true; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCache.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCache.h" new file mode 100644 index 00000000..3fa9e19d --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCache.h" @@ -0,0 +1,222 @@ +/** + * Copyright (C) 2021 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. + * + * @brief cache for the consensus state of the proposal + * @file PBFTCache.h + * @author: yujiechen + * @date 2021-04-23 + */ +#pragma once +#include "../config/PBFTConfig.h" +#include "../interfaces/PBFTMessageInterface.h" + +namespace bcos::consensus +{ +class PBFTCache : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + PBFTCache(PBFTConfig::Ptr _config, bcos::protocol::BlockNumber _index); + virtual ~PBFTCache() = default; + bool existPrePrepare(PBFTMessageInterface::Ptr _prePrepareMsg); + bool conflictWithProcessedReq(PBFTMessageInterface::Ptr _msg); + bool conflictWithPrecommitReq(PBFTMessageInterface::Ptr _prePrepareMsg); + + virtual void addPrepareCache(PBFTMessageInterface::Ptr _prepareProposal) + { + addCache(m_prepareCacheList, m_prepareReqWeight, _prepareProposal); + PBFT_LOG(INFO) << LOG_DESC("addPrepareCache") << printPBFTMsgInfo(_prepareProposal) + << m_config->printCurrentState() + << LOG_KV("weight", m_prepareReqWeight[_prepareProposal->hash()]); + } + + virtual void addCommitCache(PBFTMessageInterface::Ptr _commitProposal) + { + addCache(m_commitCacheList, m_commitReqWeight, _commitProposal); + PBFT_LOG(INFO) << LOG_DESC("addCommitCache") << printPBFTMsgInfo(_commitProposal) + << m_config->printCurrentState() + << LOG_KV("weight", m_commitReqWeight[_commitProposal->hash()]); + } + + virtual void addPrePrepareCache(PBFTMessageInterface::Ptr _prePrepareMsg) + { + if (m_stableCommitted) + { + return; + } + if (m_checkpointProposal && m_prePrepare && + _prePrepareMsg->consensusProposal()->hash() != + m_prePrepare->consensusProposal()->hash()) + { + return; + } + m_prePrepare = _prePrepareMsg; + PBFT_LOG(INFO) << LOG_DESC("addPrePrepareCache") << printPBFTMsgInfo(_prePrepareMsg) + << LOG_KV("sys", _prePrepareMsg->consensusProposal()->systemProposal()) + << m_config->printCurrentState(); + } + + bcos::protocol::BlockNumber index() const { return m_index; } + + virtual PBFTMessageInterface::Ptr preCommitCache() { return m_precommit; } + // Note: only called when receive checkPoint-triggered-proposal response + virtual void setPrecommitCache(PBFTMessageInterface::Ptr _precommit) + { + PBFT_LOG(INFO) << LOG_DESC("setPrecommitCache") << printPBFTMsgInfo(_precommit); + m_precommit = _precommit; + m_precommitWithoutData = _precommit; + } + virtual PBFTMessageInterface::Ptr preCommitWithoutData() { return m_precommitWithoutData; } + virtual bool checkAndPreCommit(); + virtual bool checkAndCommit(); + virtual bool shouldStopTimer(); + // reset the cache after viewchange + virtual void resetCache(ViewType _curView); + + virtual void setCheckPointProposal(PBFTProposalInterface::Ptr _proposal); + PBFTProposalInterface::Ptr checkPointProposal() { return m_checkpointProposal; } + + virtual void addCheckPointMsg(PBFTMessageInterface::Ptr _checkPointMsg) + { + addCache(m_checkpointCacheList, m_checkpointCacheWeight, _checkPointMsg); + PBFT_LOG(INFO) << LOG_DESC("addCheckPointMsg") << printPBFTMsgInfo(_checkPointMsg) + << LOG_KV("Idx", m_config->nodeIndex()) + << LOG_KV("weight", m_checkpointCacheWeight[_checkPointMsg->hash()]) + << LOG_KV("minRequiredWeight", m_config->minRequiredQuorum()); + } + + virtual bool checkAndCommitStableCheckPoint(); + virtual void onCheckPointTimeout(); + bool stableCommitted() const { return m_stableCommitted; } + bool precommitted() const { return m_precommitted; } + + void registerCommittedIndexNotify( + std::function _committedIndexNotifier) + { + m_committedIndexNotifier = std::move(_committedIndexNotifier); + } + + uint64_t getCollectedCheckPointWeight(bcos::crypto::HashType const& _hash) + { + if (m_checkpointCacheWeight.count(_hash)) + { + return m_checkpointCacheWeight[_hash]; + } + return 0; + } + + void resetState() + { + m_stableCommitted.store(false); + m_submitted.store(false); + m_precommitted.store(false); + m_checkpointProposal = nullptr; + } + +protected: + bool checkPrePrepareProposalStatus(); + using CollectionCacheType = + std::map>; + using QuorumRecoderType = std::map; + void addCache(CollectionCacheType& _cachedReq, QuorumRecoderType& _weightInfo, + PBFTMessageInterface::Ptr _proposal); + bool collectEnoughQuorum(bcos::crypto::HashType const& _hash, QuorumRecoderType& _weightInfo); + + bool collectEnoughPrepareReq(); + bool collectEnoughCommitReq(); + bool collectEnoughCheckpoint(); + virtual void intoPrecommit(); + virtual void setSignatureList( + PBFTProposalInterface::Ptr _proposal, CollectionCacheType& _cache); + + template + void resetCacheAfterViewChange(T& _caches, ViewType _curView) + { + for (auto it = _caches.begin(); it != _caches.end();) + { + // Note: must use reference here, in case of erase nothing + auto& cache = it->second; + for (auto pcache = cache.begin(); pcache != cache.end();) + { + auto pbftMsg = pcache->second; + if (pbftMsg->view() < _curView) + { + pcache = cache.erase(pcache); + continue; + } + pcache++; + } + if (cache.size() == 0) + { + it = _caches.erase(it); + continue; + } + it++; + } + } + + template + void recalculateQuorum(QuorumRecoderType& _quorum, T const& _caches) + { + _quorum.clear(); + for (auto const& it : _caches) + { + auto hash = it.first; + if (!_quorum.count(hash)) + { + _quorum[hash] = 0; + } + auto const& cache = it.second; + for (auto pcache : cache) + { + auto generatedFrom = pcache.second->generatedFrom(); + auto nodeInfo = m_config->getConsensusNodeByIndex(generatedFrom); + if (!nodeInfo) + { + continue; + } + _quorum[hash] += nodeInfo->weight(); + } + } + } + +protected: + PBFTConfig::Ptr m_config; + // avoid submitting the same committed proposal multiple times + std::atomic_bool m_submitted = {false}; + // avoid submitting the same stable checkpoint multiple times + std::atomic_bool m_stableCommitted = {false}; + std::atomic_bool m_precommitted = {false}; + std::atomic m_index; + // prepareCacheList + CollectionCacheType m_prepareCacheList; + QuorumRecoderType m_prepareReqWeight; + + // commitCache + CollectionCacheType m_commitCacheList; + QuorumRecoderType m_commitReqWeight; + + PBFTMessageInterface::Ptr m_prePrepare = nullptr; + PBFTMessageInterface::Ptr m_precommit = nullptr; + PBFTMessageInterface::Ptr m_precommitWithoutData = nullptr; + + PBFTProposalInterface::Ptr m_checkpointProposal = nullptr; + + CollectionCacheType m_checkpointCacheList; + QuorumRecoderType m_checkpointCacheWeight; + + std::function m_committedIndexNotifier; +}; +} // namespace bcos::consensus diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCacheFactory.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCacheFactory.h" new file mode 100644 index 00000000..425fda60 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCacheFactory.h" @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2021 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. + * + * @brief factory for PBFTCache + * @file PBFTCacheFactory.h + * @author: yujiechen + * @date 2021-06-01 + */ +#pragma once +#include "PBFTCache.h" +namespace bcos::consensus +{ +class PBFTCacheFactory +{ +public: + using Ptr = std::shared_ptr; + PBFTCacheFactory() = default; + virtual ~PBFTCacheFactory() {} + + virtual PBFTCache::Ptr createPBFTCache(PBFTConfig::Ptr _config, + bcos::protocol::BlockNumber _index, + std::function _committedIndexNotifier) + { + auto cache = std::make_shared(_config, _index); + cache->registerCommittedIndexNotify(_committedIndexNotifier); + return cache; + } +}; +} // namespace bcos::consensus \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCacheProcessor.cpp" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCacheProcessor.cpp" new file mode 100644 index 00000000..21dede66 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCacheProcessor.cpp" @@ -0,0 +1,1191 @@ +/** + * Copyright (C) 2021 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. + * + * @brief cache processor for the PBFTReq + * @file PBFTCacheProcessor.cpp + * @author: yujiechen + * @date 2021-04-21 + */ +#include "PBFTCacheProcessor.h" +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::consensus; +using namespace bcos::protocol; +using namespace bcos::crypto; + +void PBFTCacheProcessor::tryToResendCheckPoint() +{ + for (auto const& cache : m_caches) + { + cache.second->onCheckPointTimeout(); + } +} + +void PBFTCacheProcessor::initState(PBFTProposalList const& _proposals, NodeIDPtr _fromNode) +{ + for (const auto& proposal : _proposals) + { + // the proposal has already been committed + if (proposal->index() <= m_config->committedProposal()->index()) + { + PBFT_LOG(DEBUG) << LOG_DESC("initState: skip committedProposal") + << LOG_KV("index", proposal->index()) + << LOG_KV("hash", proposal->hash().abridged()); + continue; + } + PBFT_LOG(DEBUG) << LOG_DESC("initState: apply committedProposal") + << LOG_KV("index", proposal->index()) + << LOG_KV("hash", proposal->hash().abridged()); + // set the txs status to be sealed + m_config->validator()->asyncResetTxsFlag(proposal->data(), true); + // try to verify and load the proposal + loadAndVerifyProposal(_fromNode, proposal); + } +} + +void PBFTCacheProcessor::loadAndVerifyProposal( + NodeIDPtr _fromNode, PBFTProposalInterface::Ptr _proposal, size_t _retryTime) +{ + if (_retryTime > 3) + { + return; + } + // Note: to fetch the remote proposal(the from node hits all transactions) + auto self = weak_from_this(); + m_config->validator()->verifyProposal(_fromNode, _proposal, + [self, _fromNode, _proposal, _retryTime](Error::Ptr _error, bool _verifyResult) { + try + { + auto cache = self.lock(); + if (!cache) + { + return; + } + if (_error && _error->errorCode() == bcos::protocol::CommonError::TIMEOUT) + { + PBFT_LOG(INFO) + << LOG_DESC("loadAndVerifyProposal failed for timeout, retry again") + << LOG_KV("msg", _error->errorMessage()); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + cache->loadAndVerifyProposal(_fromNode, _proposal, (_retryTime + 1)); + return; + } + auto config = cache->m_config; + if (_error || !_verifyResult) + { + auto waterMark = std::min(config->lowWaterMark(), _proposal->index() - 1); + waterMark = std::max(waterMark, config->progressedIndex()); + config->setLowWaterMark(waterMark); + PBFT_LOG(INFO) + << LOG_DESC("loadAndVerifyProposal failed") << printPBFTProposal(_proposal) + << LOG_KV("from", _fromNode->shortHex()) + << LOG_KV("code", _error ? _error->errorCode() : 0) + << LOG_KV("msg", _error ? _error->errorMessage() : "requestSent") + << LOG_KV("verifyRet", _verifyResult) + << LOG_KV("lowWaterMark", config->lowWaterMark()); + } + else + { + PBFT_LOG(INFO) + << LOG_DESC("loadAndVerifyProposal success") + << LOG_KV("from", _fromNode->shortHex()) << printPBFTProposal(_proposal); + } + cache->m_onLoadAndVerifyProposalFinish(_verifyResult, _error, _proposal); + } + catch (std::exception const& e) + { + PBFT_LOG(WARNING) << LOG_DESC("loadAndVerifyProposal exception") + << printPBFTProposal(_proposal) + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} +// Note: please ensure the passed in _prePrepareMsg not be modified after addPrePrepareCache +void PBFTCacheProcessor::addPrePrepareCache(PBFTMessageInterface::Ptr _prePrepareMsg) +{ + addCache(m_caches, _prePrepareMsg, + [](PBFTCache::Ptr _pbftCache, PBFTMessageInterface::Ptr proposal) { + _pbftCache->addPrePrepareCache(std::move(proposal)); + }); + // notify the consensusing proposal index to the sync module + notifyMaxProposalIndex(_prePrepareMsg->index()); +} + +void PBFTCacheProcessor::notifyMaxProposalIndex(bcos::protocol::BlockNumber _proposalIndex) +{ + // notify the consensusing proposal index to the sync module + if (m_maxNotifyIndex < _proposalIndex) + { + m_maxNotifyIndex = _proposalIndex; + notifyCommittedProposalIndex(m_maxNotifyIndex); + } +} + +bool PBFTCacheProcessor::existPrePrepare(PBFTMessageInterface::Ptr _prePrepareMsg) +{ + if (!m_caches.contains(_prePrepareMsg->index())) + { + return false; + } + auto pbftCache = m_caches[_prePrepareMsg->index()]; + return pbftCache->existPrePrepare(_prePrepareMsg); +} + +bool PBFTCacheProcessor::tryToFillProposal(PBFTMessageInterface::Ptr _prePrepareMsg) +{ + if (!m_caches.contains(_prePrepareMsg->index())) + { + return false; + } + auto pbftCache = m_caches[_prePrepareMsg->index()]; + auto precommit = pbftCache->preCommitCache(); + if (!precommit) + { + return false; + } + auto hit = (precommit->hash() == _prePrepareMsg->hash()) && + (precommit->index() == _prePrepareMsg->index()); + if (!hit) + { + return false; + } + auto proposalData = precommit->consensusProposal()->data(); + _prePrepareMsg->consensusProposal()->setData(proposalData); + return true; +} + +bool PBFTCacheProcessor::conflictWithProcessedReq(PBFTMessageInterface::Ptr _msg) +{ + if (!m_caches.contains(_msg->index())) + { + return false; + } + auto pbftCache = m_caches[_msg->index()]; + return pbftCache->conflictWithProcessedReq(_msg); +} + +bool PBFTCacheProcessor::conflictWithPrecommitReq(PBFTMessageInterface::Ptr _prePrepareMsg) +{ + if (!m_caches.contains(_prePrepareMsg->index())) + { + return false; + } + auto pbftCache = m_caches[_prePrepareMsg->index()]; + return pbftCache->conflictWithPrecommitReq(_prePrepareMsg); +} + +void PBFTCacheProcessor::addCache( + PBFTCachesType& _pbftCache, PBFTMessageInterface::Ptr _pbftReq, UpdateCacheHandler _handler) + +{ + auto index = _pbftReq->index(); + if (!_pbftCache.contains(index)) + { + _pbftCache[index] = m_cacheFactory->createPBFTCache(m_config, index, + boost::bind( + &PBFTCacheProcessor::notifyCommittedProposalIndex, this, boost::placeholders::_1)); + } + _handler(_pbftCache[index], _pbftReq); +} + +void PBFTCacheProcessor::checkAndPreCommit() +{ + for (auto const& cache : m_caches) + { + auto ret = cache.second->checkAndPreCommit(); + if (!ret) + { + continue; + } + updateCommitQueue(cache.second->preCommitCache()->consensusProposal()); + // refresh the timer when commit success + m_config->timer()->restart(); + m_config->resetToView(); + } + resetTimer(); +} + +void PBFTCacheProcessor::checkAndCommit() +{ + for (auto const& cache : m_caches) + { + auto ret = cache.second->checkAndCommit(); + if (!ret) + { + continue; + } + updateCommitQueue(cache.second->preCommitCache()->consensusProposal()); + // refresh the timer when commit success + m_config->timer()->restart(); + m_config->resetToView(); + } + resetTimer(); +} + +void PBFTCacheProcessor::resetTimer() +{ + for (auto const& cache : m_caches) + { + if (!cache.second->shouldStopTimer()) + { + // start the timer when there has proposals in consensus + if (!m_config->timer()->running()) + { + m_config->timer()->start(); + } + return; + } + } + // reset the timer when has no proposals in consensus + m_config->freshTimer(); + m_config->tryTriggerFastViewChange(m_config->getLeader()); +} + +void PBFTCacheProcessor::updateCommitQueue(PBFTProposalInterface::Ptr _committedProposal) +{ + assert(_committedProposal); + if (m_executingProposals.contains(_committedProposal->hash())) + { + return; + } + auto proposalIndex = _committedProposal->index(); + notifyMaxProposalIndex(proposalIndex); + m_committedQueue.push(_committedProposal); + m_committedProposalList.insert(proposalIndex); + m_proposalsToStableConsensus.insert(proposalIndex); + PBFT_LOG(INFO) << LOG_DESC("######## CommitProposal") << printPBFTProposal(_committedProposal) + << LOG_KV("sys", _committedProposal->systemProposal()) + << m_config->printCurrentState(); + if (_committedProposal->systemProposal()) + { + m_config->setWaitSealUntil(proposalIndex); + PBFT_LOG(INFO) << LOG_DESC( + "Receive valid system prePrepare proposal, stop to notify sealing") + << LOG_KV("waitSealUntil", proposalIndex); + } + // Note: should notify to seal nextBlock after waitSealUntil setted, in case of the system + // proposals are generated and committed not by serial + notifyToSealNextBlock(); + tryToPreApplyProposal(_committedProposal); // will query scheduler to encode message and fill + // txbytes in blocks + tryToApplyCommitQueue(); +} + +void PBFTCacheProcessor::notifyCommittedProposalIndex(bcos::protocol::BlockNumber _index) +{ + if (!m_committedProposalNotifier) + { + return; + } + m_committedProposalNotifier(_index, [_index](Error::Ptr _error) { + if (!_error) + { + PBFT_LOG(INFO) << LOG_DESC( + "notify the committed proposal index to the sync module success") + << LOG_KV("index", _index); + return; + } + PBFT_LOG(WARNING) << LOG_DESC( + "notify the committed proposal index to the sync module failed") + << LOG_KV("index", _index); + }); +} + +ProposalInterface::ConstPtr PBFTCacheProcessor::getAppliedCheckPointProposal( + bcos::protocol::BlockNumber _index) +{ + if (_index == m_config->committedProposal()->index()) + { + return m_config->committedProposal(); + } + + if (!m_caches.contains(_index)) + { + return nullptr; + } + return (m_caches[_index])->checkPointProposal(); +} + +bool PBFTCacheProcessor::tryToPreApplyProposal(ProposalInterface::Ptr _proposal) +{ + m_config->stateMachine()->asyncPreApply( + std::move(_proposal), [](bool success) { (void)success; }); + + return true; +} + +bool PBFTCacheProcessor::tryToApplyCommitQueue() +{ + notifyToSealNextBlock(); + while (!m_committedQueue.empty() && + m_committedQueue.top()->index() < m_config->expectedCheckPoint()) + { + PBFT_LOG(INFO) << LOG_DESC("updateCommitQueue: remove invalid proposal") + << LOG_KV("index", m_committedQueue.top()->index()) + << LOG_KV("expectedIndex", m_config->expectedCheckPoint()) + << m_config->printCurrentState(); + m_committedQueue.pop(); + } + // try to execute the proposal + if (!m_committedQueue.empty() && + m_committedQueue.top()->index() == m_config->expectedCheckPoint()) + { + auto committedIndex = m_config->committedProposal()->index(); + // must wait for the sys-proposal committed to execute new proposal + auto dependsProposal = + std::min((m_config->expectedCheckPoint() - 1), m_config->waitSealUntil()); + // enforce to serial execute if the system-proposal not committed + if (committedIndex < dependsProposal) + { + return false; + } + auto proposal = m_committedQueue.top(); + auto lastAppliedProposal = getAppliedCheckPointProposal(m_config->expectedCheckPoint() - 1); + if (!lastAppliedProposal) + { + PBFT_LOG(WARNING) << LOG_DESC("The last proposal has not been applied") + << m_config->printCurrentState(); + return false; + } + if (m_executingProposals.contains(proposal->hash())) + { + m_config->timer()->restart(); + PBFT_LOG(INFO) << LOG_DESC("the proposal is executing, not executed again") + << LOG_KV("index", proposal->index()) + << LOG_KV("hash", proposal->hash().abridged()) + << m_config->printCurrentState(); + return false; + } + // commit the proposal + m_committedQueue.pop(); + // in case of the same block execute more than once + m_executingProposals[proposal->hash()] = proposal->index(); + applyStateMachine(lastAppliedProposal, proposal); + return true; + } + return false; +} + +void PBFTCacheProcessor::notifyToSealNextBlock() +{ + // find the non-consecutive minimum proposal index and notify the corresponding leader to pack + // the block + auto committedIndex = m_config->committedProposal()->index(); + bcos::protocol::BlockNumber lastIndex = committedIndex; + for (auto const& proposalIndex : m_proposalsToStableConsensus) + { + if (lastIndex + 1 < proposalIndex) + { + break; + } + lastIndex = proposalIndex; + } + auto nextProposalIndex = lastIndex + 1; + m_config->notifySealer(nextProposalIndex); + PBFT_LOG(INFO) << LOG_DESC("notify to seal next proposal") + << LOG_KV("nextProposalIndex", nextProposalIndex); +} + +// execute the proposal and broadcast checkpoint message +void PBFTCacheProcessor::applyStateMachine( + ProposalInterface::ConstPtr _lastAppliedProposal, PBFTProposalInterface::Ptr _proposal) +{ + PBFT_LOG(INFO) << LOG_DESC("applyStateMachine") << LOG_KV("index", _proposal->index()) + << LOG_KV("hash", _proposal->hash().abridged()) << m_config->printCurrentState() + << LOG_KV("unAppliedProposals", m_committedQueue.size()); + auto executedProposal = m_config->pbftMessageFactory()->createPBFTProposal(); + auto self = weak_from_this(); + auto startT = utcTime(); + m_config->stateMachine()->asyncApply(m_config->timer()->timeout(), + std::move(_lastAppliedProposal), _proposal, executedProposal, + [self, startT, _proposal, executedProposal](int64_t _ret) { + try + { + auto cache = self.lock(); + if (!cache) + { + return; + } + auto config = cache->m_config; + if (config->committedProposal()->index() >= _proposal->index()) + { + PBFT_LOG(WARNING) + << LOG_DESC("applyStateMachine: give up the proposal for expired") + << config->printCurrentState() + << LOG_KV("beforeExec", _proposal->hash().abridged()) + << LOG_KV("afterExec", executedProposal->hash().abridged()) + << LOG_KV("timecost", utcTime() - startT); + return; + } + if (cache->m_proposalAppliedHandler) + { + cache->m_proposalAppliedHandler(_ret, _proposal, executedProposal); + } + PBFT_LOG(INFO) << LOG_DESC("applyStateMachine finished") + << LOG_KV("index", _proposal->index()) + << LOG_KV("beforeExec", _proposal->hash().abridged()) + << LOG_KV("afterExec", executedProposal->hash().abridged()) + << config->printCurrentState() + << LOG_KV("timecost", utcTime() - startT); + } + catch (std::exception const& e) + { + PBFT_LOG(WARNING) << LOG_DESC("applyStateMachine failed") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +void PBFTCacheProcessor::setCheckPointProposal(PBFTProposalInterface::Ptr _proposal) +{ + auto index = _proposal->index(); + if (!m_caches.contains(index)) + { + // Note: since cache is created and freed frequently, it should be safer to use weak_ptr in + // the callback + auto self = weak_from_this(); + m_caches[index] = m_cacheFactory->createPBFTCache( + m_config, index, [self](bcos::protocol::BlockNumber _proposalIndex) { + try + { + auto cache = self.lock(); + if (!cache) + { + return; + } + cache->notifyCommittedProposalIndex(_proposalIndex); + } + catch (std::exception const& e) + { + PBFT_LOG(WARNING) << LOG_DESC("notifyCommittedProposalIndex error") + << LOG_KV("index", _proposalIndex) + << LOG_KV("errorInfo", boost::diagnostic_information(e)); + } + }); + } + (m_caches[index])->setCheckPointProposal(_proposal); +} + +void PBFTCacheProcessor::addCheckPointMsg(PBFTMessageInterface::Ptr _checkPointMsg) +{ + addCache(m_caches, std::move(_checkPointMsg), + [](PBFTCache::Ptr _pbftCache, PBFTMessageInterface::Ptr _checkPointMsg) { + _pbftCache->addCheckPointMsg(std::move(_checkPointMsg)); + }); +} + +void PBFTCacheProcessor::addViewChangeReq(ViewChangeMsgInterface::Ptr _viewChange) +{ + auto reqView = _viewChange->view(); + auto fromIdx = _viewChange->generatedFrom(); + if (m_viewChangeCache.contains(reqView) && m_viewChangeCache[reqView].contains(fromIdx)) + { + return; + } + + auto nodeInfo = m_config->getConsensusNodeByIndex(fromIdx); + if (!nodeInfo) + { + return; + } + m_viewChangeCache[reqView][fromIdx] = _viewChange; + if (!m_viewChangeWeight.contains(reqView)) + { + m_viewChangeWeight[reqView] = 0; + } + m_viewChangeWeight[reqView] += nodeInfo->weight(); + auto committedIndex = _viewChange->committedProposal()->index(); + if (!m_maxCommittedIndex.contains(reqView) || m_maxCommittedIndex[reqView] < committedIndex) + { + m_maxCommittedIndex[reqView] = committedIndex; + } + // get the max precommitIndex + for (const auto& precommit : _viewChange->preparedProposals()) + { + auto precommitIndex = precommit->index(); + if (!m_maxPrecommitIndex.contains(reqView) || m_maxPrecommitIndex[reqView] < precommitIndex) + { + m_maxPrecommitIndex[reqView] = precommitIndex; + } + } + // print the prepared proposal info + std::stringstream preparedProposalInfo; + preparedProposalInfo << "preparedProposalInfo: "; + for (const auto& proposal : _viewChange->preparedProposals()) + { + preparedProposalInfo << LOG_KV("propIndex", proposal->index()) + << LOG_KV("propHash", proposal->hash().abridged()) + << LOG_KV("dataSize", proposal->consensusProposal()->data().size()); + } + PBFT_LOG(INFO) << LOG_DESC("addViewChangeReq") << printPBFTMsgInfo(_viewChange) + << LOG_KV("weight", m_viewChangeWeight[reqView]) + << LOG_KV("maxCommittedIndex", m_maxCommittedIndex[reqView]) + << LOG_KV("maxPrecommitIndex", m_maxPrecommitIndex[reqView]) + << LOG_DESC(preparedProposalInfo.str()) << m_config->printCurrentState(); +} + +PBFTMessageList PBFTCacheProcessor::generatePrePrepareMsg( + std::map _viewChangeCache) +{ + auto toView = m_config->toView(); + auto committedIndex = m_config->committedProposal()->index(); + auto maxCommittedIndex = committedIndex; + if (m_maxCommittedIndex.contains(toView)) + { + maxCommittedIndex = m_maxCommittedIndex[toView]; + } + auto maxPrecommitIndex = committedIndex; + if (m_maxPrecommitIndex.contains(toView)) + { + maxPrecommitIndex = m_maxPrecommitIndex[toView]; + } + // should not handle the proposal future than the system proposal + if (m_config->waitSealUntil() > committedIndex) + { + maxPrecommitIndex = std::min(m_config->waitSealUntil(), maxPrecommitIndex); + } + std::map preparedProposals; + for (const auto& it : _viewChangeCache) + { + auto viewChangeReq = it.second; + for (const auto& proposal : viewChangeReq->preparedProposals()) + { + // invalid precommit proposal + if (proposal->index() < maxCommittedIndex) + { + continue; + } + // repeated precommit proposal + if (preparedProposals.contains(proposal->index())) + { + auto precommitProposal = preparedProposals[proposal->index()]; + if (precommitProposal->index() != proposal->index() || + precommitProposal->hash() != proposal->hash()) + { + // fatal case: two proposals in the same view with different hash + if (precommitProposal->view() == proposal->view()) + { + PBFT_LOG(FATAL) + << LOG_DESC( + "generatePrePrepareMsg error: found conflict precommit " + "proposals") + << LOG_DESC("proposal already exist:") + << printPBFTProposal(precommitProposal) + << LOG_DESC("conflicted proposal:") << printPBFTProposal(proposal); + } + // newer precommit proposal + if (precommitProposal->view() < proposal->view()) + { + preparedProposals[proposal->index()] = proposal; + } + } + continue; + } + // new precommit proposal + preparedProposals[proposal->index()] = proposal; + proposal->setGeneratedFrom(viewChangeReq->generatedFrom()); + } + } + // generate prepareMsg from maxCommittedIndex to maxPrecommitIndex + PBFTMessageList prePrepareMsgList; + for (auto i = (maxCommittedIndex + 1); i <= maxPrecommitIndex; i++) + { + PBFTProposalInterface::Ptr prePrepareProposal = nullptr; + auto generatedFrom = m_config->nodeIndex(); + bool empty = false; + if (preparedProposals.contains(i)) + { + prePrepareProposal = preparedProposals[i]->consensusProposal(); + generatedFrom = preparedProposals[i]->generatedFrom(); + } + else + { + // empty prePrepare + prePrepareProposal = + m_config->validator()->generateEmptyProposal(m_config->compatibilityVersion(), + m_config->pbftMessageFactory(), i, m_config->nodeIndex()); + empty = true; + } + auto prePrepareMsg = m_config->pbftMessageFactory()->populateFrom( + PacketType::PrePreparePacket, prePrepareProposal, m_config->pbftMsgDefaultVersion(), + m_config->toView(), utcTime(), generatedFrom); + prePrepareMsgList.push_back(prePrepareMsg); + PBFT_LOG(INFO) << LOG_DESC("generatePrePrepareMsg") << printPBFTMsgInfo(prePrepareMsg) + << LOG_KV("dataSize", prePrepareMsg->consensusProposal()->data().size()) + << LOG_KV("emptyProposal", empty); + } + return prePrepareMsgList; +} + +NewViewMsgInterface::Ptr PBFTCacheProcessor::checkAndTryIntoNewView() +{ + // in syncing mode, no need to try into the newView period + if (m_config->committedProposal()->index() < m_config->syncingHighestNumber()) + { + return nullptr; + } + if (m_newViewGenerated || !m_config->leaderAfterViewChange()) + { + return nullptr; + } + auto toView = m_config->toView(); + if (!m_viewChangeWeight.count(toView)) + { + return nullptr; + } + if (m_viewChangeWeight[toView] < m_config->minRequiredQuorum()) + { + return nullptr; + } + // the next leader collect enough viewChange requests + // set the viewchanges(without prePreparedProposals) + auto viewChangeCache = m_viewChangeCache[toView]; + ViewChangeMsgList viewChangeList; + for (auto const& it : viewChangeCache) + { + viewChangeList.push_back(it.second); + } + // create newView message + auto newViewMsg = m_config->pbftMessageFactory()->createNewViewMsg(); + newViewMsg->setHash(m_config->committedProposal()->hash()); + newViewMsg->setIndex(m_config->committedProposal()->index()); + newViewMsg->setPacketType(PacketType::NewViewPacket); + newViewMsg->setVersion(m_config->pbftMsgDefaultVersion()); + newViewMsg->setView(toView); + newViewMsg->setTimestamp(utcTime()); + newViewMsg->setGeneratedFrom(m_config->nodeIndex()); + // set viewchangeList + newViewMsg->setViewChangeMsgList(viewChangeList); + // set generated pre-prepare list + auto generatedPrePrepareList = generatePrePrepareMsg(viewChangeCache); + newViewMsg->setPrePrepareList(generatedPrePrepareList); + // encode and broadcast the viewchangeReq + auto encodedData = m_config->codec()->encode(newViewMsg); + // only broadcast message to the consensus nodes + m_config->frontService()->asyncSendBroadcastMessage( + bcos::protocol::NodeType::CONSENSUS_NODE, ModuleID::PBFT, ref(*encodedData)); + m_newViewGenerated = true; + PBFT_LOG(INFO) << LOG_DESC("The next leader broadcast NewView request") + << printPBFTMsgInfo(newViewMsg) << LOG_KV("Idx", m_config->nodeIndex()); + return newViewMsg; +} + +ViewType PBFTCacheProcessor::tryToTriggerFastViewChange() +{ + uint64_t greaterViewWeight = 0; + ViewType viewToReach = 0; + bool findViewToReach = false; + for (auto const& it : m_viewChangeCache) + { + auto view = it.first; + if (view <= m_config->toView()) + { + continue; + } + if (viewToReach > view || (viewToReach == 0)) + { + // check the quorum + auto viewChangeCache = it.second; + greaterViewWeight = 0; + for (auto const& cache : viewChangeCache) + { + auto fromIdx = cache.first; + auto nodeInfo = m_config->getConsensusNodeByIndex(fromIdx); + if (!nodeInfo) + { + continue; + } + greaterViewWeight += nodeInfo->weight(); + } + // must ensure at least (f+1) nodes at the same view can trigger fast-viewchange + if (greaterViewWeight >= (m_config->maxFaultyQuorum() + 1)) + { + findViewToReach = true; + viewToReach = view; + } + } + } + if (!findViewToReach) + { + return 0; + } + if (m_config->toView() >= viewToReach) + { + return 0; + } + if (viewToReach > 0) + { + // set toView to (viewToReach - 1) and then trigger timeout to increase toView to + // viewToReach + m_config->setToView(viewToReach - 1); + } + PBFT_LOG(INFO) << LOG_DESC("tryToTriggerFastViewChange") << LOG_KV("viewToReach", viewToReach) + << m_config->printCurrentState(); + return viewToReach; +} + +bool PBFTCacheProcessor::checkPrecommitMsg(PBFTMessageInterface::Ptr _precommitMsg) +{ + // check the view(the first started node no need to check the view) + if (m_config->startRecovered() && (_precommitMsg->view() > m_config->toView())) + { + return false; + } + if (!_precommitMsg->consensusProposal()) + { + return false; + } + auto ret = checkPrecommitWeight(_precommitMsg); + if (ret) + { + return ret; + } + // avoid the failure to verify proposalWeight due to the modification of consensus node list and + // consensus weight + if (!m_caches.count(_precommitMsg->index())) + { + return ret; + } + auto precommit = (m_caches.at(_precommitMsg->index()))->preCommitCache(); + if (!precommit) + { + return ret; + } + // erase the cache + if (precommit->hash() == _precommitMsg->hash() && !checkPrecommitWeight(precommit)) + { + m_caches.erase(precommit->index()); + } + return ret; +} + +bool PBFTCacheProcessor::checkPrecommitWeight(PBFTMessageInterface::Ptr _precommitMsg) +{ + auto precommitProposal = _precommitMsg->consensusProposal(); + // check the signature + uint64_t weight = 0; + auto proofSize = precommitProposal->signatureProofSize(); + for (size_t i = 0; i < proofSize; i++) + { + auto proof = precommitProposal->signatureProof(i); + // check the proof + auto nodeInfo = m_config->getConsensusNodeByIndex(proof.first); + if (!nodeInfo) + { + return false; + } + // verify the signature + auto ret = m_config->cryptoSuite()->signatureImpl()->verify( + nodeInfo->nodeID(), precommitProposal->hash(), proof.second); + if (!ret) + { + return false; + } + weight += nodeInfo->weight(); + } + // check the quorum + return (weight >= m_config->minRequiredQuorum()); +} + +ViewChangeMsgInterface::Ptr PBFTCacheProcessor::fetchPrecommitData( + BlockNumber _index, bcos::crypto::HashType const& _hash) +{ + if (!m_caches.count(_index)) + { + return nullptr; + } + auto cache = m_caches[_index]; + if (cache->preCommitCache() == nullptr || cache->preCommitCache()->hash() != _hash) + { + return nullptr; + } + + PBFTMessageList precommitMessage; + precommitMessage.push_back(cache->preCommitCache()); + + auto pbftMessage = m_config->pbftMessageFactory()->createViewChangeMsg(); + pbftMessage->setPreparedProposals(precommitMessage); + return pbftMessage; +} + +void PBFTCacheProcessor::removeConsensusedCache(ViewType _view, BlockNumber _consensusedNumber) +{ + m_proposalsToStableConsensus.erase(_consensusedNumber); + for (auto pcache = m_caches.begin(); pcache != m_caches.end();) + { + // Note: can't remove stabledCommitted cache here for need to fetch + // lastAppliedProposalCheckPoint when apply the next proposal + if (pcache->first <= _consensusedNumber) + { + pcache = m_caches.erase(pcache); + continue; + } + pcache++; + } + removeInvalidViewChange(_view, _consensusedNumber); + m_newViewGenerated = false; +} + + +void PBFTCacheProcessor::resetCacheAfterViewChange( + ViewType _view, BlockNumber _latestCommittedProposal) +{ + for (auto const& it : m_caches) + { + it.second->resetCache(_view); + } + m_maxPrecommitIndex.clear(); + m_maxCommittedIndex.clear(); + m_newViewGenerated = false; + removeInvalidViewChange(_view, _latestCommittedProposal); + removeInvalidRecoverCache(_view); +} + +void PBFTCacheProcessor::removeInvalidRecoverCache(ViewType _view) +{ + for (auto it = m_recoverReqCache.begin(); it != m_recoverReqCache.end();) + { + auto view = it->first; + if (view <= _view) + { + it = m_recoverReqCache.erase(it); + m_recoverCacheWeight.erase(view); + continue; + } + it++; + } +} + +void PBFTCacheProcessor::removeInvalidViewChange( + ViewType _view, BlockNumber _latestCommittedProposal) +{ + for (auto it = m_viewChangeCache.begin(); it != m_viewChangeCache.end();) + { + auto view = it->first; + if (view <= _view) + { + it = m_viewChangeCache.erase(it); + m_viewChangeWeight.erase(view); + continue; + } + // Note: must use reference here, in case of erase nothing + auto& viewChangeCache = it->second; + for (auto pcache = viewChangeCache.begin(); pcache != viewChangeCache.end();) + { + auto index = pcache->second->index(); + if (index < _latestCommittedProposal) + { + pcache = viewChangeCache.erase(pcache); + continue; + } + pcache++; + } + it++; + } + reCalculateViewChangeWeight(); +} + +void PBFTCacheProcessor::reCalculateViewChangeWeight() +{ + m_maxPrecommitIndex.clear(); + m_maxCommittedIndex.clear(); + for (auto const& it : m_viewChangeCache) + { + auto view = it.first; + m_viewChangeWeight[view] = 0; + auto const& viewChangeCache = it.second; + for (auto const& cache : viewChangeCache) + { + auto generatedFrom = cache.second->generatedFrom(); + auto nodeInfo = m_config->getConsensusNodeByIndex(generatedFrom); + if (!nodeInfo) + { + continue; + } + m_viewChangeWeight[view] += nodeInfo->weight(); + auto viewChangeReq = cache.second; + auto committedIndex = viewChangeReq->committedProposal()->index(); + if (!m_maxCommittedIndex.count(view) || m_maxCommittedIndex[view] < committedIndex) + { + m_maxCommittedIndex[view] = committedIndex; + } + // get the max precommitIndex + for (const auto& precommit : viewChangeReq->preparedProposals()) + { + auto precommitIndex = precommit->index(); + if (!m_maxPrecommitIndex.count(view) || m_maxPrecommitIndex[view] < precommitIndex) + { + m_maxPrecommitIndex[view] = precommitIndex; + } + } + } + } +} + +void PBFTCacheProcessor::checkAndCommitStableCheckPoint() +{ + std::vector stabledCacheList; + for (auto const& it : m_caches) + { + auto ret = it.second->checkAndCommitStableCheckPoint(); + if (!ret) + { + continue; + } + stabledCacheList.emplace_back(it.second); + } + // Note: since updateStableCheckPointQueue may update m_caches after commitBlock + // must call it after iterator m_caches + for (const auto& cache : stabledCacheList) + { + updateStableCheckPointQueue(cache->checkPointProposal()); + } +} + +void PBFTCacheProcessor::updateStableCheckPointQueue(PBFTProposalInterface::Ptr _stableCheckPoint) +{ + assert(_stableCheckPoint); + m_stableCheckPointQueue.push(_stableCheckPoint); + PBFT_LOG(INFO) << LOG_DESC("updateStableCheckPointQueue: insert new checkpoint proposal") + << LOG_KV("index", _stableCheckPoint->index()) + << LOG_KV("hash", _stableCheckPoint->hash().abridged()) + << m_config->printCurrentState(); + tryToCommitStableCheckPoint(); +} + +void PBFTCacheProcessor::tryToCommitStableCheckPoint() +{ + // remove the invalid checkpoint + while (!m_stableCheckPointQueue.empty() && + m_stableCheckPointQueue.top()->index() <= m_config->committedProposal()->index()) + { + PBFT_LOG(INFO) << LOG_DESC("updateStableCheckPointQueue: remove invalid checkpoint") + << LOG_KV("index", m_stableCheckPointQueue.top()->index()) + << LOG_KV("committedIndex", m_config->committedProposal()->index()); + m_committedProposalList.erase(m_stableCheckPointQueue.top()->index()); + m_stableCheckPointQueue.pop(); + } + // submit stable-checkpoint to ledger in ordeer + if (!m_stableCheckPointQueue.empty() && + m_stableCheckPointQueue.top()->index() == m_config->committedProposal()->index() + 1) + { + PBFT_LOG(INFO) << LOG_DESC("updateStableCheckPointQueue: commit stable checkpoint") + << LOG_KV("index", m_stableCheckPointQueue.top()->index()) + << LOG_KV("committedIndex", m_config->committedProposal()->index()); + auto stableCheckPoint = m_stableCheckPointQueue.top(); + m_committedProposalList.erase(stableCheckPoint->index()); + m_stableCheckPointQueue.pop(); + m_config->storage()->asyncCommitStableCheckPoint(stableCheckPoint); + } +} + +bool PBFTCacheProcessor::shouldRequestCheckPoint(PBFTMessageInterface::Ptr _checkPointMsg) +{ + auto checkPointIndex = _checkPointMsg->index(); + auto committedIndex = m_config->committedProposal()->index(); + // expired checkpoint + if (checkPointIndex <= committedIndex || checkPointIndex <= m_config->syncingHighestNumber()) + { + return false; + } + if (checkPointIndex > (committedIndex + m_config->waterMarkLimit())) + { + return false; + } + // hit in the local committedProposalList or already been requested + if (m_committedProposalList.count(checkPointIndex)) + { + return false; + } + // the local cache already has the checkPointProposal + if (m_caches.count(checkPointIndex) && m_caches[checkPointIndex]->checkPointProposal()) + { + return false; + } + // request the checkpoint proposal when timeout + if (m_config->timeout()) + { + return true; + } + // no-timeout + // has not received any checkPoint message before, wait for generating local checkPoint + if (!m_caches.count(checkPointIndex)) + { + return false; + } + auto cache = m_caches[checkPointIndex]; + // precommitted in the local cache, wait for generating local checkPoint + if (cache->precommitted()) + { + return false; + } + // receive at least (f+1) checkPoint proposal, wait for generating local checkPoint + auto checkPointWeight = cache->getCollectedCheckPointWeight(_checkPointMsg->hash()); + auto minRequiredCheckPointWeight = m_config->maxFaultyQuorum() + 1; + if (checkPointWeight < minRequiredCheckPointWeight) + { + return false; + } + // collect more than (f+1) checkpoint message with the same hash + // in case of request again + m_committedProposalList.insert(checkPointIndex); + return true; +} + +void PBFTCacheProcessor::eraseCommittedProposalList(bcos::protocol::BlockNumber _index) +{ + if (!m_committedProposalList.count(_index)) + { + return; + } + m_committedProposalList.erase(_index); +} + +void PBFTCacheProcessor::clearExpiredExecutingProposal() +{ + auto committedIndex = m_config->committedProposal()->index(); + for (auto it = m_executingProposals.begin(); it != m_executingProposals.end();) + { + if (it->second > committedIndex) + { + it++; + continue; + } + it = m_executingProposals.erase(it); + } +} + +void PBFTCacheProcessor::addRecoverReqCache(PBFTMessageInterface::Ptr _recoverResponse) +{ + auto fromIdx = _recoverResponse->generatedFrom(); + auto view = _recoverResponse->view(); + if (m_recoverReqCache.count(view) && m_recoverReqCache[view].count(fromIdx)) + { + return; + } + m_recoverReqCache[view][fromIdx] = _recoverResponse; + // update the weight + auto nodeInfo = m_config->getConsensusNodeByIndex(fromIdx); + if (!nodeInfo) + { + return; + } + if (!m_recoverCacheWeight.count(view)) + { + m_recoverCacheWeight[view] = 0; + } + m_recoverCacheWeight[view] += nodeInfo->weight(); + PBFT_LOG(INFO) << LOG_DESC("addRecoverReqCache") << LOG_KV("weight", m_recoverCacheWeight[view]) + << printPBFTMsgInfo(_recoverResponse) << m_config->printCurrentState(); +} + +bool PBFTCacheProcessor::checkAndTryToRecover() +{ + ViewType recoveredView = 0; + for (auto const& it : m_recoverCacheWeight) + { + auto view = it.first; + // collect enough recover response with the same view + if (it.second >= m_config->minRequiredQuorum() && recoveredView < view) + { + recoveredView = view; + } + } + if (recoveredView == 0) + { + return false; + } + m_config->resetNewViewState(recoveredView); + resetCacheAfterViewChange(recoveredView, m_config->committedProposal()->index()); + // clear the recoverReqCache + m_recoverReqCache.clear(); + m_recoverCacheWeight.clear(); + // try to preCommit/commit after no-timeout + checkAndPreCommit(); + checkAndCommit(); + PBFT_LOG(INFO) << LOG_DESC("checkAndTryToRecoverView: reachNewView") + << LOG_KV("recoveredView", recoveredView) << m_config->printCurrentState(); + return true; +} + +PBFTProposalInterface::Ptr PBFTCacheProcessor::fetchPrecommitProposal( + bcos::protocol::BlockNumber _index) +{ + if (!m_caches.contains(_index)) + { + return nullptr; + } + auto cache = m_caches[_index]; + if (cache->preCommitCache() == nullptr || + cache->preCommitCache()->consensusProposal() == nullptr) + { + return nullptr; + } + return cache->preCommitCache()->consensusProposal(); +} + +void PBFTCacheProcessor::updatePrecommit(PBFTProposalInterface::Ptr _proposal) +{ + auto pbftMessage = m_config->pbftMessageFactory()->createPBFTMsg(); + pbftMessage->setConsensusProposal(_proposal); + pbftMessage->setIndex(_proposal->index()); + pbftMessage->setHash(_proposal->hash()); + addCache( + m_caches, pbftMessage, [](PBFTCache::Ptr _pbftCache, PBFTMessageInterface::Ptr _precommit) { + _pbftCache->setPrecommitCache(std::move(_precommit)); + }); +} + +PBFTMessageList PBFTCacheProcessor::preCommitCachesWithoutData() +{ + PBFTMessageList precommitCacheList; + auto waitSealUntil = m_config->waitSealUntil(); + auto committedIndex = m_config->committedProposal()->index(); + for (auto it = m_caches.begin(); it != m_caches.end();) + { + auto precommitCache = it->second->preCommitWithoutData(); + if (precommitCache != nullptr) + { + // should not handle the proposal future than the system proposal + if (waitSealUntil > committedIndex && precommitCache->index() > waitSealUntil) + { + it = m_caches.erase(it); + continue; + } + precommitCacheList.push_back(precommitCache); + } + it++; + } + return precommitCacheList; +} + +void PBFTCacheProcessor::resetUnCommittedCacheState(bcos::protocol::BlockNumber _number) +{ + PBFT_LOG(INFO) << LOG_DESC("resetUnCommittedCacheState") << LOG_KV("number", _number) + << m_config->printCurrentState(); + for (auto const& it : m_caches) + { + if (it.second->index() >= _number) + { + it.second->resetState(); + } + } + m_committedProposalList.clear(); + m_executingProposals.clear(); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCacheProcessor.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCacheProcessor.h" new file mode 100644 index 00000000..4fce4519 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/cache/PBFTCacheProcessor.h" @@ -0,0 +1,282 @@ +/** + * Copyright (C) 2021 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. + * + * @brief cache processor for the PBFTReq + * @file PBFTCacheProcessor.h + * @author: yujiechen + * @date 2021-04-21 + */ +#pragma once +#include "../cache/PBFTCache.h" +#include "../config/PBFTConfig.h" +#include "../interfaces/PBFTMessageFactory.h" +#include "../interfaces/PBFTMessageInterface.h" +#include "../interfaces/ViewChangeMsgInterface.h" +#include "PBFTCacheFactory.h" +#include +#include +namespace bcos::consensus +{ +struct PBFTProposalCmp +{ + bool operator()( + const PBFTProposalInterface::Ptr& _first, const PBFTProposalInterface::Ptr& _second) + { + // increase order + return _first->index() > _second->index(); + } +}; + +class PBFTCacheProcessor : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + PBFTCacheProcessor(PBFTCacheFactory::Ptr _cacheFactory, PBFTConfig::Ptr _config) + : m_cacheFactory(std::move(_cacheFactory)), m_config(std::move(_config)) + {} + + virtual void tryToResendCheckPoint(); + + virtual ~PBFTCacheProcessor() = default; + virtual void initState( + PBFTProposalList const& _committedProposals, bcos::crypto::NodeIDPtr _fromNode); + + virtual void addPrePrepareCache(PBFTMessageInterface::Ptr _prePrepareMsg); + virtual bool existPrePrepare(PBFTMessageInterface::Ptr _prePrepareMsg); + + virtual bool tryToFillProposal(PBFTMessageInterface::Ptr _prePrepareMsg); + + virtual bool conflictWithProcessedReq(PBFTMessageInterface::Ptr _msg); + virtual bool conflictWithPrecommitReq(PBFTMessageInterface::Ptr _prePrepareMsg); + virtual void addPrepareCache(PBFTMessageInterface::Ptr _prepareReq) + { + addCache(m_caches, std::move(_prepareReq), + [](PBFTCache::Ptr _pbftCache, PBFTMessageInterface::Ptr _prepareReq) { + _pbftCache->addPrepareCache(std::move(_prepareReq)); + }); + } + + virtual void addCommitReq(PBFTMessageInterface::Ptr _commitReq) + { + addCache(m_caches, std::move(_commitReq), + [](PBFTCache::Ptr _pbftCache, PBFTMessageInterface::Ptr _commitReq) { + _pbftCache->addCommitCache(std::move(_commitReq)); + }); + } + + PBFTMessageList preCommitCachesWithData() + { + PBFTMessageList precommitCacheList; + for (auto const& it : m_caches) + { + auto precommitCache = it.second->preCommitCache(); + if (precommitCache != nullptr) + { + precommitCacheList.push_back(precommitCache); + } + } + return precommitCacheList; + } + + PBFTMessageList preCommitCachesWithoutData(); + virtual void checkAndPreCommit(); + virtual void checkAndCommit(); + + virtual void addViewChangeReq(ViewChangeMsgInterface::Ptr _viewChange); + virtual NewViewMsgInterface::Ptr checkAndTryIntoNewView(); + virtual ViewType tryToTriggerFastViewChange(); + + virtual ViewChangeMsgInterface::Ptr fetchPrecommitData( + bcos::protocol::BlockNumber _index, bcos::crypto::HashType const& _hash); + + virtual PBFTProposalInterface::Ptr fetchPrecommitProposal(bcos::protocol::BlockNumber _index); + virtual void updatePrecommit(PBFTProposalInterface::Ptr _proposal); + + virtual bool checkPrecommitMsg(PBFTMessageInterface::Ptr _precommitMsg); + + virtual void removeConsensusedCache( + ViewType _view, bcos::protocol::BlockNumber _consensusedNumber); + virtual void resetCacheAfterViewChange( + ViewType _view, bcos::protocol::BlockNumber _latestCommittedProposal); + virtual void removeInvalidViewChange( + ViewType _view, bcos::protocol::BlockNumber _latestCommittedProposal); + + virtual void setCheckPointProposal(PBFTProposalInterface::Ptr _proposal); + virtual void addCheckPointMsg(PBFTMessageInterface::Ptr _checkPointMsg); + virtual void checkAndCommitStableCheckPoint(); + virtual void tryToCommitStableCheckPoint(); + + virtual void resetTimer(); + + virtual bool shouldRequestCheckPoint(PBFTMessageInterface::Ptr _checkPointMsg); + + virtual void registerProposalAppliedHandler( + std::function + _callback) + { + m_proposalAppliedHandler = std::move(_callback); + } + + void registerCommittedProposalNotifier( + std::function)> + _committedProposalNotifier) + { + m_committedProposalNotifier = std::move(_committedProposalNotifier); + } + + bool tryToPreApplyProposal(ProposalInterface::Ptr _proposal); + bool tryToApplyCommitQueue(); + + // notify the consensusing proposal index to the sync module + void notifyCommittedProposalIndex(bcos::protocol::BlockNumber _index); + + virtual void updateCommitQueue(PBFTProposalInterface::Ptr _committedProposal); + + // TODO: ensure thread-safe here + virtual void eraseCommittedProposalList(bcos::protocol::BlockNumber _index); + + virtual void eraseExecutedProposal(bcos::crypto::HashType const& _hash) + { + if (!m_executingProposals.contains(_hash)) + { + return; + } + m_executingProposals.erase(_hash); + } + + virtual size_t executingProposalSize() { return m_executingProposals.size(); } + virtual void clearExpiredExecutingProposal(); + virtual void registerOnLoadAndVerifyProposalFinish( + std::function + _onLoadAndVerifyProposalFinish) + { + m_onLoadAndVerifyProposalFinish = std::move(_onLoadAndVerifyProposalFinish); + } + + virtual void addRecoverReqCache(PBFTMessageInterface::Ptr _recoverResponse); + virtual bool checkAndTryToRecover(); + + std::map const& executingProposals() + { + return m_executingProposals; + } + + bool proposalCommitted(bcos::protocol::BlockNumber _index) + { + return m_committedProposalList.contains(_index); + } + + void clearCacheAfterRecoverStateFailed() + { + // since request checkPoint will insert requested-proposal into m_committedProposalList, + // must clear the cache when loadAndVerifyBlock failed + m_committedProposalList.clear(); + } + + virtual uint64_t getViewChangeWeight(ViewType _view) { return m_viewChangeWeight.at(_view); } + + virtual void clearAllCache() + { + m_caches.clear(); + m_viewChangeCache.clear(); + std::priority_queue, + PBFTProposalCmp> + emptyQueue; + m_committedQueue.swap(emptyQueue); + m_executingProposals.clear(); + m_committedProposalList.clear(); + m_proposalsToStableConsensus.clear(); + + std::priority_queue, + PBFTProposalCmp> + emptyStableCheckPointQueue; + m_stableCheckPointQueue.swap(emptyStableCheckPointQueue); + m_recoverReqCache.clear(); + } + + void resetUnCommittedCacheState(bcos::protocol::BlockNumber _number); + virtual void updateStableCheckPointQueue(PBFTProposalInterface::Ptr _stableCheckPoint); + +protected: + virtual void loadAndVerifyProposal(bcos::crypto::NodeIDPtr _fromNode, + PBFTProposalInterface::Ptr _proposal, size_t _retryTime = 0); + + virtual bool checkPrecommitWeight(PBFTMessageInterface::Ptr _precommitMsg); + virtual void applyStateMachine( + ProposalInterface::ConstPtr _lastAppliedProposal, PBFTProposalInterface::Ptr _proposal); + + virtual ProposalInterface::ConstPtr getAppliedCheckPointProposal( + bcos::protocol::BlockNumber _index); + + virtual void notifyToSealNextBlock(); + +protected: + using PBFTCachesType = std::map; + using UpdateCacheHandler = + std::function; + void addCache(PBFTCachesType& _pbftCache, PBFTMessageInterface::Ptr _pbftReq, + UpdateCacheHandler _handler); + + PBFTMessageList generatePrePrepareMsg( + std::map _viewChangeCache); + void reCalculateViewChangeWeight(); + void removeInvalidRecoverCache(ViewType _view); + void notifyMaxProposalIndex(bcos::protocol::BlockNumber _proposalIndex); + +protected: + PBFTCacheFactory::Ptr m_cacheFactory; + PBFTConfig::Ptr m_config; + /// map: number => PBFTCache + PBFTCachesType m_caches; + + // viewchange caches + using ViewChangeCacheType = + std::map>; + ViewChangeCacheType m_viewChangeCache; + std::map m_viewChangeWeight; + // only needed for viewchange + std::map m_maxCommittedIndex; + std::map m_maxPrecommitIndex; + + std::atomic_bool m_newViewGenerated = {false}; + + std::priority_queue, + PBFTProposalCmp> + m_committedQueue; + std::map m_executingProposals; + + std::set> m_committedProposalList; + + // ordered by the index + std::set> m_proposalsToStableConsensus; + + std::priority_queue, + PBFTProposalCmp> + m_stableCheckPointQueue; + + std::function + m_proposalAppliedHandler; + std::function)> + m_committedProposalNotifier; + std::function + m_onLoadAndVerifyProposalFinish; + + // the recover message cache + std::map> m_recoverReqCache; + std::map m_recoverCacheWeight; + + bcos::protocol::BlockNumber m_maxNotifyIndex = 0; +}; +} // namespace bcos::consensus \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/config/PBFTConfig.cpp" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/config/PBFTConfig.cpp" new file mode 100644 index 00000000..0943f843 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/config/PBFTConfig.cpp" @@ -0,0 +1,477 @@ +/** + * Copyright (C) 2021 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. + * + * @brief config for PBFT + * @file PBFTConfig.cpp + * @author: yujiechen + * @date 2021-04-12 + */ +#include "PBFTConfig.h" +#include "bcos-txpool/sync/protocol/PB/TxsSyncMsgFactoryImpl.h" +#include "bcos-txpool/sync/interfaces/TxsSyncMsgFactory.h" +#include "bcos-txpool/sync/utilities/Common.h" + +using namespace bcos; +using namespace bcos::consensus; +using namespace bcos::protocol; +using namespace bcos::ledger; + +void PBFTConfig::resetConfig(LedgerConfig::Ptr _ledgerConfig, bool _syncedBlock) +{ + bcos::protocol::BlockNumber committedIndex = 0; + if (m_committedProposal) + { + committedIndex = m_committedProposal->index(); + } + if (_ledgerConfig->blockNumber() <= committedIndex && committedIndex > 0) + { + return; + } + PBFT_LOG(INFO) << METRIC << LOG_DESC("resetConfig") + << LOG_KV("committedIndex", _ledgerConfig->blockNumber()) + << LOG_KV("propHash", _ledgerConfig->hash().abridged()) + << LOG_KV("blockCountLimit", _ledgerConfig->blockTxCountLimit()) + << LOG_KV("leaderPeriod", _ledgerConfig->leaderSwitchPeriod()) + << LOG_KV( + "consensusNodesSize", _ledgerConfig->mutableConsensusNodeList().size()); + // set committed proposal + auto committedProposal = m_pbftMessageFactory->createPBFTProposal(); + committedProposal->setIndex(_ledgerConfig->blockNumber()); + committedProposal->setHash(_ledgerConfig->hash()); + setCommittedProposal(committedProposal); + // set blockTxCountLimit + setBlockTxCountLimit(_ledgerConfig->blockTxCountLimit()); + // set ConsensusNodeList + auto& consensusList = _ledgerConfig->mutableConsensusNodeList(); + setConsensusNodeList(consensusList); + auto observerList = _ledgerConfig->mutableObserverList(); + setObserverNodeList(*observerList); + // set leader_period + setLeaderSwitchPeriod(_ledgerConfig->leaderSwitchPeriod()); + // reset the timer + freshTimer(); + + if (_ledgerConfig->sealerId() == -1) + { + PBFT_LOG(INFO) << METRIC << LOG_DESC("^^^^^^^^Report") << printCurrentState(); + } + else + { + PBFT_LOG(INFO) << METRIC << LOG_DESC("^^^^^^^^Report") + << LOG_KV("sealer", _ledgerConfig->sealerId()) + << LOG_KV("txs", _ledgerConfig->txsSize()) << printCurrentState(); + } + if (m_compatibilityVersion != _ledgerConfig->compatibilityVersion()) + { + PBFT_LOG(INFO) << LOG_DESC("compatibilityVersion updated") + << LOG_KV("version", (bcos::protocol::BlockVersion)m_compatibilityVersion) + << LOG_KV("updatedVersion", (bcos::protocol::BlockVersion)( + _ledgerConfig->compatibilityVersion())); + m_compatibilityVersion = _ledgerConfig->compatibilityVersion(); + if (m_versionNotification && m_asMasterNode) + { + m_versionNotification(m_compatibilityVersion); + } + } + // notify the txpool validator to update the consensusNodeList and the observerNodeList + if (m_consensusNodeListUpdated || m_observerNodeListUpdated) + { + m_validator->updateValidatorConfig(consensusList, *observerList); + PBFT_LOG(INFO) << LOG_DESC("updateValidatorConfig") + << LOG_KV("consensusNodeListUpdated", m_consensusNodeListUpdated) + << LOG_KV("observerNodeListUpdated", m_observerNodeListUpdated) + << LOG_KV("consensusNodeSize", consensusList.size()) + << LOG_KV("observerNodeSize", observerList->size()); + } + + // notify the latest block number to the sealer + if (m_stateNotifier) + { + m_stateNotifier(_ledgerConfig->blockNumber()); + } + // notify the latest block to the sync module + if (m_newBlockNotifier && !_syncedBlock) + { + m_newBlockNotifier(_ledgerConfig, [_ledgerConfig](Error::Ptr _error) { + if (_error) + { + PBFT_LOG(WARNING) << LOG_DESC("asyncNotifyNewBlock to sync module failed") + << LOG_KV("number", _ledgerConfig->blockNumber()) + << LOG_KV("hash", _ledgerConfig->hash().abridged()) + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + } + }); + } + + // the node is syncing, reset the timeout state to false for view recovery + if (m_syncingHighestNumber > _ledgerConfig->blockNumber()) + { + m_syncingState = true; + // notify resetSealing(the syncing node should not seal block) + notifyResetSealing(); + return; + } + // after syncing finished, try to reach a new view after syncing completed + if (m_syncingState) + { + m_syncingState = false; + m_timer->start(); + } + // try to notify the sealer module to seal proposals + if (!m_timeoutState) + { + if (m_waitResealUntil == _ledgerConfig->blockNumber()) + { + auto notifyBeginIndex = std::max(sealStartIndex(), m_waitResealUntil + 1); + PBFT_LOG(INFO) << LOG_DESC("Reach reseal index") + << LOG_KV("notifyBeginIndex", notifyBeginIndex) << printCurrentState(); + reNotifySealer(notifyBeginIndex); + } + notifySealer(sealStartIndex()); + } +} + +void PBFTConfig::notifyResetSealing(std::function _callback) +{ + if (!m_sealerResetNotifier) + { + return; + } + // only notify the non-leader to reset sealing + PBFT_LOG(INFO) << LOG_DESC("notifyResetSealing") << printCurrentState(); + auto committedIndex = m_committedProposal->index(); + m_sealerResetNotifier([this, _callback, committedIndex](Error::Ptr _error) { + if (_error) + { + PBFT_LOG(INFO) << LOG_DESC("notifyResetSealing failed") + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()) << printCurrentState(); + return; + } + if (_callback && m_waitResealUntil <= committedIndex) + { + _callback(); + } + PBFT_LOG(INFO) << LOG_DESC("notifyResetSealing success") << printCurrentState(); + }); + // reset the sealEndIndex and the sealStartIndex + m_sealEndIndex = (sealStartIndex() - 1); + m_sealStartIndex = (sealStartIndex() - 1); +} + +void PBFTConfig::reNotifySealer(bcos::protocol::BlockNumber _index) +{ + if (_index >= highWaterMark() || _index < m_committedProposal->index()) + { + PBFT_LOG(INFO) << LOG_DESC("reNotifySealer return for invalid expectedStart") + << LOG_KV("expectedStart", _index) + << LOG_KV("highWaterMark", highWaterMark()) << printCurrentState(); + return; + } + m_sealStartIndex = (_index - 1); + m_sealEndIndex = (_index - 1); + notifySealer(_index); + PBFT_LOG(INFO) << LOG_DESC("reNotifySealer") << LOG_KV("expectedStart", _index) + << LOG_KV("highWaterMark", highWaterMark()) + << LOG_KV("leader", leaderIndex(_index)) << printCurrentState(); +} + +bool PBFTConfig::canHandleNewProposal() +{ + ReadGuard lock(x_committedProposal); + bcos::protocol::BlockNumber committedIndex = 0; + if (m_committedProposal) + { + committedIndex = m_committedProposal->index(); + } + if (m_waitSealUntil > committedIndex) + { + return false; + } + if (m_waitResealUntil > committedIndex) + { + return false; + } + return true; +} + +bool PBFTConfig::canHandleNewProposal(PBFTBaseMessageInterface::Ptr _msg) +{ + if (canHandleNewProposal()) + { + return true; + } + ReadGuard lock(x_committedProposal); + auto committedIndex = m_committedProposal->index(); + return _msg->index() <= committedIndex || _msg->index() <= m_waitSealUntil || + _msg->index() <= m_waitResealUntil; +} + +bool PBFTConfig::tryTriggerFastViewChange(IndexType _leaderIndex) +{ + if (!m_fastViewChangeHandler) + { + return false; + } + auto nodeList = connectedNodeList(); + // empty connection + if (nodeList.empty()) + { + return false; + } + // the non-consensus node should not trigger fast viewchange + if (!isConsensusNode()) + { + return false; + } + // the leader is the current node + if (_leaderIndex == nodeIndex()) + { + return false; + } + auto leaderNodeInfo = getConsensusNodeByIndex(_leaderIndex); + if (!leaderNodeInfo) + { + return false; + } + // Note: must register m_faultyDiscriminator before start the PBFTEngine + if (nodeList.contains(leaderNodeInfo->nodeID()) && + !m_faultyDiscriminator(leaderNodeInfo->nodeID())) + { + return false; + } + PBFT_LOG(INFO) << LOG_DESC("tryTriggerFastViewChange for the leader disconnect") + << LOG_KV("leaderIndex", _leaderIndex) + << LOG_KV("leader", leaderNodeInfo->nodeID()->shortHex()) << printCurrentState(); + m_fastViewChangeHandler(); + // check the newLeader connection + auto newLeader = leaderIndexInNewViewPeriod(m_toView); + return tryTriggerFastViewChange(newLeader); +} + +void PBFTConfig::notifySealer(BlockNumber _progressedIndex, bool _enforce) +{ + RecursiveGuard lock(m_mutex); + auto currentLeader = leaderIndex(_progressedIndex); + if (currentLeader != nodeIndex()) + { + return; + } + if (!canHandleNewProposal()) + { + PBFT_LOG(INFO) << LOG_DESC( + "Not notify the sealer to sealing for not reach waitResealUntil/waitToSeal limit"); + return; + } + + if (_enforce) + { + asyncNotifySealProposal(_progressedIndex, _progressedIndex, blockTxCountLimit()); + m_sealEndIndex = std::max(_progressedIndex, m_sealEndIndex.load()); + m_sealStartIndex = std::min(_progressedIndex, m_sealStartIndex.load()); + PBFT_LOG(INFO) << LOG_DESC("notifySealer: enforce notify the leader to seal block") + << LOG_KV("idx", nodeIndex()) << LOG_KV("startIndex", _progressedIndex) + << LOG_KV("endIndex", _progressedIndex) + << LOG_KV("maxTxsToSeal", blockTxCountLimit()) << printCurrentState(); + return; + } + int64_t endProposalIndex = + (_progressedIndex / m_leaderSwitchPeriod + 1) * m_leaderSwitchPeriod - 1; + // Note: the valid proposal index range should be [max(lowWaterMark, committedIndex+1, + // expectedCheckpoint), highWaterMark) + endProposalIndex = std::min(endProposalIndex, (highWaterMark() - 1)); + if (m_sealEndIndex.load() >= endProposalIndex) + { + PBFT_LOG(INFO) << LOG_DESC("notifySealer return for invalid seal range") + << LOG_KV("currentEndIndex", m_sealEndIndex) + << LOG_KV("expectedEndIndex", endProposalIndex) << printCurrentState(); + return; + } + auto startSealIndex = std::max(m_sealEndIndex.load() + 1, _progressedIndex); + if (startSealIndex > endProposalIndex) + { + PBFT_LOG(INFO) << LOG_DESC("notifySealer return for invalid seal range") + << LOG_KV("expectedStartIndex", startSealIndex) + << LOG_KV("expectedEndIndex", endProposalIndex) << printCurrentState(); + return; + } + // already notified + if (m_sealEndIndex >= endProposalIndex && m_sealStartIndex <= startSealIndex) + { + return; + } + auto committedIndex = m_committedProposal->index(); + if (m_validator->resettingProposalSize() > 0 && (startSealIndex > (committedIndex + 1))) + { + PBFT_LOG(INFO) << LOG_DESC( + "Not notify the sealer to sealing for txs of some proposals have not " + "been resetted success") + << LOG_KV("resettingProposalSize", m_validator->resettingProposalSize()) + << LOG_KV("startSealIndex", startSealIndex) << printCurrentState(); + // notify the leader to seal when all txs of all proposals have been resetted + auto self = weak_from_this(); + m_validator->setVerifyCompletedHook([self, _progressedIndex, _enforce]() { + auto config = self.lock(); + if (!config) + { + return; + } + config->notifySealer(_progressedIndex, _enforce); + }); + return; + } + asyncNotifySealProposal(startSealIndex, endProposalIndex, blockTxCountLimit()); + + m_sealStartIndex = startSealIndex; + m_sealEndIndex = endProposalIndex; + PBFT_LOG(INFO) << LOG_DESC("notifySealer: notify the new leader to seal block") + << LOG_KV("idx", nodeIndex()) << LOG_KV("startIndex", startSealIndex) + << LOG_KV("endIndex", endProposalIndex) + << LOG_KV("notifyBeginIndex", _progressedIndex) + << LOG_KV("waitSealUntil", m_waitSealUntil) + << LOG_KV("waitResealUntil", m_waitResealUntil) + << LOG_KV("maxTxsToSeal", blockTxCountLimit()) << printCurrentState(); +} + +void PBFTConfig::asyncNotifySealProposal( + size_t _proposalIndex, size_t _proposalEndIndex, size_t _maxTxsToSeal, size_t _retryTime) +{ + if (!m_sealProposalNotifier) + { + return; + } + if (_retryTime > 3) + { + return; + } + auto self = weak_from_this(); + m_sealProposalNotifier(_proposalIndex, _proposalEndIndex, _maxTxsToSeal, + [_proposalIndex, _proposalEndIndex, _maxTxsToSeal, self, _retryTime](Error::Ptr _error) { + if (_error == nullptr) + { + PBFT_LOG(INFO) << LOG_DESC("asyncNotifySealProposal success") + << LOG_KV("startIndex", _proposalIndex) + << LOG_KV("endIndex", _proposalEndIndex) + << LOG_KV("maxTxsToSeal", _maxTxsToSeal); + return; + } + try + { + auto pbftConfig = self.lock(); + if (!pbftConfig) + { + return; + } + // retry after 1 seconds + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + // retry when send failed + pbftConfig->asyncNotifySealProposal( + _proposalIndex, _proposalEndIndex, _maxTxsToSeal, _retryTime + 1); + } + catch (std::exception const& e) + { + PBFT_LOG(WARNING) << LOG_DESC("asyncNotifySealProposal exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +uint64_t PBFTConfig::minRequiredQuorum() const +{ + return m_minRequiredQuorum; +} + +void PBFTConfig::updateQuorum() +{ + m_totalQuorum.store(0); + ReadGuard lock(x_consensusNodeList); + for (const auto& consensusNode : *m_consensusNodeList) + { + m_totalQuorum += consensusNode->weight(); + } + // get m_maxFaultyQuorum + m_maxFaultyQuorum = (m_totalQuorum - 1) / 3; + m_minRequiredQuorum = m_totalQuorum - m_maxFaultyQuorum; +} + +IndexType PBFTConfig::leaderIndex(BlockNumber _proposalIndex) +{ + return (_proposalIndex / m_leaderSwitchPeriod + m_view) % m_consensusNodeNum; +} + +bool PBFTConfig::leaderAfterViewChange() +{ + auto expectedLeader = leaderIndexInNewViewPeriod(m_toView); + return (m_nodeIndex == expectedLeader); +} + +IndexType PBFTConfig::leaderIndexInNewViewPeriod(ViewType _view) +{ + return leaderIndexInNewViewPeriod(m_progressedIndex, _view); +} + +IndexType PBFTConfig::leaderIndexInNewViewPeriod( + bcos::protocol::BlockNumber _proposalIndex, ViewType _view) +{ + return (_proposalIndex / m_leaderSwitchPeriod + _view) % m_consensusNodeNum; +} + +PBFTProposalInterface::Ptr PBFTConfig::populateCommittedProposal() +{ + ReadGuard lock(x_committedProposal); + if (!m_committedProposal) + { + return nullptr; + } + return m_pbftMessageFactory->populateFrom( + std::dynamic_pointer_cast(m_committedProposal)); +} + +std::string PBFTConfig::printCurrentState() +{ + std::ostringstream stringstream; + if (!committedProposal()) + { + stringstream << LOG_DESC("The storage has not been init."); + return stringstream.str(); + } + stringstream << LOG_KV("committedIndex", committedProposal()->index()) + << LOG_KV("consNum", progressedIndex()) + << LOG_KV("committedHash", committedProposal()->hash().abridged()) + << LOG_KV("view", view()) << LOG_KV("toView", toView()) + << LOG_KV("changeCycle", m_timer->changeCycle()) + << LOG_KV("expectedCheckPoint", m_expectedCheckPoint) << LOG_KV("Idx", nodeIndex()) + << LOG_KV("unsealedTxs", m_unsealedTxsSize.load()) + << LOG_KV("sealUntil", m_waitSealUntil) + << LOG_KV("waitResealUntil", m_waitResealUntil) + << LOG_KV("nodeId", nodeID()->shortHex()); + return stringstream.str(); +} + +void PBFTConfig::broadCastEmptyTxsReq() +{ + if (m_unsealedTxsSize > 0 || m_timer->running()) + { + return; + } + std::unique_ptr syncMsgFactory = + std::make_unique(); + auto emptyStat = syncMsgFactory->createTxsSyncMsg( + sync::TxsSyncPacketType::TxsStatusPacket, bcos::crypto::HashList({})); + auto reqData = emptyStat->encode(); + m_frontService->asyncSendBroadcastMessage( + bcos::protocol::NodeType::CONSENSUS_NODE, protocol::ModuleID::TxsSync, ref(*reqData)); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/config/PBFTConfig.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/config/PBFTConfig.h" new file mode 100644 index 00000000..d8a12ab6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/config/PBFTConfig.h" @@ -0,0 +1,441 @@ +/** + * Copyright (C) 2021 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. + * + * @brief config for PBFT + * @file PBFTConfig.h + * @author: yujiechen + * @date 2021-04-12 + */ +#pragma once +#include "bcos-pbft/core/ConsensusConfig.h" +#include "bcos-pbft/framework/StateMachineInterface.h" +#include "bcos-pbft/pbft/engine/PBFTTimer.h" +#include "bcos-pbft/pbft/engine/Validator.h" +#include "bcos-pbft/pbft/interfaces/PBFTCodecInterface.h" +#include "bcos-pbft/pbft/interfaces/PBFTMessageFactory.h" +#include "bcos-pbft/pbft/interfaces/PBFTStorage.h" +#include "bcos-pbft/pbft/utilities/Common.h" +#include +#include +#include + +namespace bcos::consensus +{ +class PBFTConfig : public ConsensusConfig, public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + PBFTConfig(bcos::crypto::CryptoSuite::Ptr _cryptoSuite, + bcos::crypto::KeyPairInterface::Ptr _keyPair, + std::shared_ptr _pbftMessageFactory, + std::shared_ptr _codec, std::shared_ptr _validator, + std::shared_ptr _frontService, + StateMachineInterface::Ptr _stateMachine, PBFTStorage::Ptr _storage) + : ConsensusConfig(std::move(_keyPair)), + m_cryptoSuite(std::move(_cryptoSuite)), + m_pbftMessageFactory(std::move(_pbftMessageFactory)), + m_codec(std::move(_codec)), + m_validator(std::move(_validator)), + m_frontService(std::move(_frontService)), + m_stateMachine(std::move(_stateMachine)), + m_storage(std::move(_storage)), + m_connectedNodeList(std::make_shared()) + { + m_timer = std::make_shared(consensusTimeout(), "pbftTimer"); + m_pullTxsTimer = std::make_shared(consensusTimeout(), "pullTxsTimer"); + m_pullTxsTimer->registerTimeoutHandler(std::bind(&PBFTConfig::broadCastEmptyTxsReq, this)); + } + + ~PBFTConfig() override = default; + + virtual void stop() + { + // stop the validator + if (m_validator) + { + m_validator->stop(); + } + // destroy the timer + if (m_timer) + { + m_timer->destroy(); + } + if (m_pullTxsTimer) + { + m_pullTxsTimer->destroy(); + } + } + virtual void resetConfig( + bcos::ledger::LedgerConfig::Ptr _ledgerConfig, bool _syncedBlock = false); + + uint64_t minRequiredQuorum() const override; + + virtual ViewType view() const { return m_view; } + virtual void setView(ViewType _view) { m_view.store(_view); } + + virtual ViewType toView() const { return m_toView; } + virtual void setToView(ViewType _toView) { m_toView.store(_toView); } + virtual void incToView(ViewType _increasedValue) { m_toView += _increasedValue; } + + virtual IndexType leaderIndex(bcos::protocol::BlockNumber _proposalIndex); + virtual bool leaderAfterViewChange(); + IndexType leaderIndexInNewViewPeriod(ViewType _view); + IndexType leaderIndexInNewViewPeriod( + bcos::protocol::BlockNumber _proposalIndex, ViewType _view); + virtual uint64_t leaderSwitchPeriod() const { return m_leaderSwitchPeriod; } + virtual void setLeaderSwitchPeriod(uint64_t _leaderSwitchPeriod) + { + if (_leaderSwitchPeriod == m_leaderSwitchPeriod) + { + return; + } + m_leaderSwitchPeriod.store(_leaderSwitchPeriod); + // notify the sealer module to reset sealing + notifyResetSealing(sealStartIndex()); + PBFT_LOG(INFO) << METRIC + << LOG_DESC( + "updateLeaderSwitchPeriod and re-notify the sealer to seal block") + << LOG_KV("leader_period", m_leaderSwitchPeriod) + << LOG_KV("committedIndex", committedProposal()->index()); + } + + bcos::crypto::CryptoSuite::Ptr cryptoSuite() { return m_cryptoSuite; } + std::shared_ptr pbftMessageFactory() { return m_pbftMessageFactory; } + std::shared_ptr frontService() { return m_frontService; } + std::shared_ptr codec() { return m_codec; } + + PBFTProposalInterface::Ptr populateCommittedProposal(); + unsigned pbftMsgDefaultVersion() const { return c_pbftMsgDefaultVersion; } + unsigned networkTimeoutInterval() const { return c_networkTimeoutInterval; } + std::shared_ptr validator() { return m_validator; } + PBFTStorage::Ptr storage() { return m_storage; } + + std::string printCurrentState(); + int64_t highWaterMark() { return m_progressedIndex + m_waterMarkLimit; } + int64_t lowWaterMark() { return m_lowWaterMark; } + void setLowWaterMark(bcos::protocol::BlockNumber _index) { m_lowWaterMark = _index; } + + PBFTTimer::Ptr timer() { return m_timer; } + + void setConsensusTimeout(uint64_t _consensusTimeout) override + { + ConsensusConfig::setConsensusTimeout(_consensusTimeout); + m_timer->reset(_consensusTimeout); + } + + void setCommittedProposal(ProposalInterface::Ptr _committedProposal) override + { + ConsensusConfig::setCommittedProposal(_committedProposal); + auto progressedIndex = _committedProposal->index() + 1; + if (progressedIndex > m_expectedCheckPoint) + { + PBFT_LOG(INFO) << LOG_DESC("PBFTConfig: resetExpectedCheckPoint") << printCurrentState() + << LOG_KV("expectedCheckPoint", m_expectedCheckPoint); + m_expectedCheckPoint = _committedProposal->index() + 1; + } + if (progressedIndex > m_lowWaterMark) + { + setLowWaterMark(progressedIndex); + } + } + + int64_t expectedCheckPoint() { return m_expectedCheckPoint; } + void setExpectedCheckPoint(bcos::protocol::BlockNumber _expectedCheckPoint) + { + m_expectedCheckPoint = std::max(committedProposal()->index() + 1, _expectedCheckPoint); + PBFT_LOG(INFO) << LOG_DESC("PBFTConfig: setExpectedCheckPoint") << printCurrentState() + << LOG_KV("expectedCheckPoint", m_expectedCheckPoint); + } + + StateMachineInterface::Ptr stateMachine() { return m_stateMachine; } + + int64_t waterMarkLimit() const { return m_waterMarkLimit; } + void setWaterMarkLimit(int64_t _waterMarkLimit) { m_waterMarkLimit = _waterMarkLimit; } + + int64_t checkPointTimeoutInterval() const { return m_checkPointTimeoutInterval; } + void setCheckPointTimeoutInterval(int64_t _timeoutInterval) + { + m_checkPointTimeoutInterval = _timeoutInterval; + } + + void resetToView() + { + m_toView.store(m_view); + m_timer->resetChangeCycle(); + m_timeoutState.store(false); + } + + uint64_t maxFaultyQuorum() const { return m_maxFaultyQuorum; } + + virtual void notifySealer(bcos::protocol::BlockNumber _progressedIndex, bool _enforce = false); + virtual void reNotifySealer(bcos::protocol::BlockNumber _index); + virtual bool shouldResetConfig(bcos::protocol::BlockNumber _index) + { + ReadGuard l(x_committedProposal); + if (!m_committedProposal) + { + return false; + } + return m_committedProposal->index() < _index; + } + + virtual void setTimeoutState(bool _timeoutState) { m_timeoutState = _timeoutState; } + virtual bool timeout() { return m_timeoutState; } + + virtual void resetTimeoutState(bool _incTimeout = true) + { + m_timeoutState.store(true); + // update toView + incToView(1); + if (_incTimeout) + { + // increase the changeCycle + timer()->incChangeCycle(1); + } + // start the timer again(the timer here must be restarted) + timer()->restart(); + } + + virtual void resetNewViewState(ViewType _view) + { + if (m_view > _view) + { + return; + } + if (!m_startRecovered.load()) + { + m_startRecovered.store(true); + } + // reset the timer when reach a new-view + m_timeoutState.store(false); + freshTimer(); + // update the changeCycle + timer()->resetChangeCycle(); + setView(_view); + setToView(_view); + m_timeoutState.store(false); + } + virtual void setUnSealedTxsSize(size_t _unsealedTxsSize) + { + m_unsealedTxsSize = _unsealedTxsSize; + if (m_unsealedTxsSize > 0 && !m_timer->running()) + { + m_timer->start(); + } + } + + virtual void freshTimer() + { + if (m_unsealedTxsSize > 0) + { + m_timer->restart(); + m_pullTxsTimer->stop(); + } + else + { + m_timer->stop(); + m_pullTxsTimer->restart(); + } + } + + void registerSealProposalNotifier( + std::function)> + _sealProposalNotifier) + { + m_sealProposalNotifier = std::move(_sealProposalNotifier); + } + + void registerStateNotifier(std::function _stateNotifier) + { + m_stateNotifier = std::move(_stateNotifier); + } + + void registerNewBlockNotifier( + std::function)> + _newBlockNotifier) + { + m_newBlockNotifier = std::move(_newBlockNotifier); + } + + void registerSealerResetNotifier( + std::function)> _sealerResetNotifier) + { + m_sealerResetNotifier = std::move(_sealerResetNotifier); + } + + void registerFaultyDiscriminator( + std::function _faultyDiscriminator) + { + m_faultyDiscriminator = std::move(_faultyDiscriminator); + } + + virtual void notifyResetSealing(bcos::protocol::BlockNumber _consIndex) + { + auto self = weak_from_this(); + notifyResetSealing([self, _consIndex]() { + auto config = self.lock(); + if (!config) + { + return; + } + // notify the sealer to reseal + config->reNotifySealer(_consIndex); + }); + } + + virtual void notifyResetSealing(std::function _callback = nullptr); + + virtual void setWaitResealUntil(bcos::protocol::BlockNumber _waitResealUntil) + { + m_waitResealUntil = _waitResealUntil; + } + + virtual void setWaitSealUntil(bcos::protocol::BlockNumber _waitSealUntil) + { + m_waitSealUntil = std::max(m_waitSealUntil.load(), _waitSealUntil); + } + + void setConsensusNodeList(ConsensusNodeList& _consensusNodeList) override + { + ConsensusConfig::setConsensusNodeList(_consensusNodeList); + if (!m_consensusNodeListUpdated) + { + return; + } + if (committedProposal()) + { + notifyResetSealing(sealStartIndex()); + } + } + + bcos::protocol::BlockNumber sealStartIndex() + { + auto sealStartIndex = expectedCheckPoint(); + if (committedProposal()) + { + sealStartIndex = std::max(sealStartIndex, committedProposal()->index() + 1); + } + return sealStartIndex; + } + + bool canHandleNewProposal(); + bool canHandleNewProposal(PBFTBaseMessageInterface::Ptr _msg); + + void registerFastViewChangeHandler(std::function _fastViewChangeHandler) + { + m_fastViewChangeHandler = std::move(_fastViewChangeHandler); + } + + virtual void setConnectedNodeList(bcos::crypto::NodeIDSet&& _connectedNodeList) + { + WriteGuard l(x_connectedNodeList); + *m_connectedNodeList = std::move(_connectedNodeList); + } + virtual void setConnectedNodeList(bcos::crypto::NodeIDSet const& _connectedNodeList) + { + WriteGuard l(x_connectedNodeList); + *m_connectedNodeList = _connectedNodeList; + } + + virtual bcos::crypto::NodeIDSet connectedNodeList() + { + ReadGuard l(x_connectedNodeList); + return *m_connectedNodeList; + } + + IndexType getLeader() { return leaderIndex(sealStartIndex()); } + + virtual bool tryTriggerFastViewChange(IndexType _leaderIndex); + + virtual bool startRecovered() const { return m_startRecovered; } + + bcos::protocol::BlockNumber waitSealUntil() { return m_waitSealUntil; } + +protected: + void updateQuorum() override; + virtual void asyncNotifySealProposal(size_t _proposalIndex, size_t _proposalEndIndex, + size_t _maxTxsToSeal, size_t _retryTime = 0); + + void broadCastEmptyTxsReq(); + + +protected: + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + // Factory for creating PBFT message package + std::shared_ptr m_pbftMessageFactory; + // Codec for serialization/deserialization of PBFT message packets + std::shared_ptr m_codec; + // Proposal validator + std::shared_ptr m_validator; + // FrontService, used to send/receive P2P message packages + std::shared_ptr m_frontService; + StateMachineInterface::Ptr m_stateMachine; + PBFTStorage::Ptr m_storage; + // Timer, for pbft consensus + PBFTTimer::Ptr m_timer; + // only for pull txs + // trigger start: when m_timer.stop() && unsealTxs.size()==0 + // trigger stop: m_timer.start() + PBFTTimer::Ptr m_pullTxsTimer; + // notify the sealer seal Proposal + std::function)> + m_sealProposalNotifier; + // notify the sealer to reset sealing + std::function)> m_sealerResetNotifier; + + // notify the sealer the latest blockNumber + std::function m_stateNotifier; + // the sync module notify the consensus module the new block + std::function)> + m_newBlockNotifier; + std::atomic m_maxFaultyQuorum = {0}; + std::atomic m_totalQuorum = {0}; + std::atomic m_minRequiredQuorum = {0}; + std::atomic m_view = {0}; + std::atomic m_toView = {0}; + + std::atomic m_expectedCheckPoint = {0}; + std::atomic m_lowWaterMark = {0}; + + std::atomic m_sealStartIndex = {0}; + std::atomic m_sealEndIndex = {0}; + + int64_t m_waterMarkLimit = 50; + std::atomic m_checkPointTimeoutInterval = {3000}; + + std::atomic m_leaderSwitchPeriod = {1}; + const unsigned c_pbftMsgDefaultVersion = 0; + const unsigned c_networkTimeoutInterval = 1000; + // state variable that identifies whether has timed out + std::atomic_bool m_timeoutState = {false}; + + std::atomic m_unsealedTxsSize = {0}; + // notify the sealer to reseal new block until m_waitResealUntil stable committed + std::atomic m_waitResealUntil = {0}; + // notify the ealer to seal new block until m_waitSealUntil committed + std::atomic m_waitSealUntil = {0}; + + bcos::crypto::NodeIDSetPtr m_connectedNodeList; + SharedMutex x_connectedNodeList; + + std::function m_fastViewChangeHandler; + + std::atomic_bool m_startRecovered = {false}; + + std::function m_faultyDiscriminator; + + mutable RecursiveMutex m_mutex; +}; +} // namespace bcos::consensus diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/BlockValidator.cpp" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/BlockValidator.cpp" new file mode 100644 index 00000000..06a3786e --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/BlockValidator.cpp" @@ -0,0 +1,182 @@ +/** + * Copyright (C) 2021 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. + * + * @brief validator for the block from the sync module + * @file BlockValidator.cpp + * @author: yujiechen + * @date 2021-05-25 + */ +#include "BlockValidator.h" +#include "../utilities/Common.h" +using namespace bcos; +using namespace bcos::consensus; +using namespace bcos::protocol; + +// check the block +void BlockValidator::asyncCheckBlock( + Block::Ptr _block, std::function _onVerifyFinish) +{ + auto self = weak_from_this(); + m_taskPool->enqueue([self, _block, _onVerifyFinish]() { + auto blockHeader = _block->blockHeader(); + // ignore the genesis block + if (blockHeader->number() == 0) + { + _onVerifyFinish(nullptr, true); + return; + } + try + { + auto validator = self.lock(); + if (!validator) + { + _onVerifyFinish(nullptr, false); + return; + } + auto config = validator->m_config; + if (blockHeader->number() <= config->committedProposal()->index()) + { + _onVerifyFinish(nullptr, false); + return; + } + if (_block->transactionsSize() == 0) + { + _onVerifyFinish(nullptr, true); + return; + } + if (!validator->checkSealerListAndWeightList(_block)) + { + _onVerifyFinish(nullptr, false); + return; + } + if (!validator->checkSignatureList(_block)) + { + _onVerifyFinish(nullptr, false); + return; + } + _onVerifyFinish(nullptr, true); + } + catch (std::exception const& e) + { + PBFT_LOG(WARNING) << LOG_DESC("asyncCheckBlock exception") + << LOG_KV("error", boost::diagnostic_information(e)); + _onVerifyFinish(nullptr, false); + } + }); +} + +bool BlockValidator::checkSealerListAndWeightList(Block::Ptr _block) +{ + // Note: for tars service, blockHeader must be here to ensure the sealer list not been released + auto blockHeader = _block->blockHeader(); + // check sealer(sealer must be a consensus node) + if (!m_config->getConsensusNodeByIndex(blockHeader->sealer())) + { + PBFT_LOG(ERROR) + << LOG_DESC("checkBlock for sync module: invalid sealer for not a consensus node") + << LOG_KV("sealer", blockHeader->sealer()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("number", blockHeader->number()); + return false; + } + // check the sealer list + auto blockSealerList = blockHeader->sealerList(); + auto blockWeightList = blockHeader->consensusWeights(); + auto consensusNodeList = m_config->consensusNodeList(); + if ((size_t)blockSealerList.size() != (size_t)consensusNodeList.size() || + (size_t)blockWeightList.size() != (size_t)consensusNodeList.size()) + { + PBFT_LOG(ERROR) << LOG_DESC("checkBlock for sync module: wrong sealerList") + << LOG_KV("Nsealer", consensusNodeList.size()) + << LOG_KV("NBlockSealer", blockSealerList.size()) + << LOG_KV("NBlockWeight", blockWeightList.size()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("number", blockHeader->number()) << m_config->printCurrentState(); + return false; + } + size_t i = 0; + for (auto const& blockSealer : blockSealerList) + { + auto consNodePtr = consensusNodeList[i]; + if (consNodePtr->nodeID()->data() != blockSealer) + { + PBFT_LOG(ERROR) << LOG_DESC("checkBlock for sync module: inconsistent sealerList") + << LOG_KV("blkSealer", consNodePtr->nodeID()->shortHex()) + << LOG_KV("chainSealer", *toHexString(blockSealer)) + << LOG_KV("number", blockHeader->number()) + << LOG_KV("weight", consNodePtr->weight()) + << m_config->printCurrentState(); + return false; + } + // check weight + auto blockWeight = blockWeightList[i]; + if (consNodePtr->weight() != blockWeight) + { + PBFT_LOG(ERROR) << LOG_DESC("checkBlock for sync module: inconsistent weight") + << LOG_KV("blkWeight", blockWeight) + << LOG_KV("chainWeight", consNodePtr->weight()) + << LOG_KV("number", blockHeader->number()) + << LOG_KV("blkSealer", consNodePtr->nodeID()->shortHex()) + << LOG_KV("chainSealer", *toHexString(blockSealer)) + << m_config->printCurrentState(); + return false; + } + i++; + } + return true; +} + +bool BlockValidator::checkSignatureList(Block::Ptr _block) +{ + // check sign num + // Note: for tars service, blockHeader must be here to ensure the signatureList + auto blockHeader = _block->blockHeader(); + auto signatureList = blockHeader->signatureList(); + // check sign and weight + size_t signatureWeight = 0; + for (auto const& sign : signatureList) + { + auto nodeIndex = sign.index; + auto nodeInfo = m_config->getConsensusNodeByIndex(nodeIndex); + auto signatureData = ref(sign.signature); + if (!signatureData.data()) + { + PBFT_LOG(FATAL) << LOG_DESC("BlockValidator checkSignatureList: invalid signature") + << LOG_KV("signatureSize", signatureList.size()) + << LOG_KV("nodeIndex", nodeIndex) + << LOG_KV("number", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()); + } + if (!nodeInfo || !m_config->cryptoSuite()->signatureImpl()->verify( + nodeInfo->nodeID(), blockHeader->hash(), signatureData)) + { + PBFT_LOG(ERROR) << LOG_DESC("checkBlock for sync module: checkSign failed") + << LOG_KV("sealerIdx", nodeIndex) + << LOG_KV("blockHash", blockHeader->hash().abridged()) + << LOG_KV("number", blockHeader->number()); + return false; + } + signatureWeight += nodeInfo->weight(); + } + if (signatureWeight < (size_t)m_config->minRequiredQuorum()) + { + PBFT_LOG(ERROR) << LOG_DESC("checkBlock for sync module: insufficient signatures") + << LOG_KV("signNum", signatureList.size()) + << LOG_KV("sigWeight", signatureWeight) + << LOG_KV("minRequiredQuorum", m_config->minRequiredQuorum()); + return false; + } + return true; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/BlockValidator.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/BlockValidator.h" new file mode 100644 index 00000000..ec462178 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/BlockValidator.h" @@ -0,0 +1,58 @@ +/** + * Copyright (C) 2021 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. + * + * @brief validator for the block from the sync module + * @file BlockValidator.h + * @author: yujiechen + * @date 2021-05-25 + */ +#pragma once +#include "../config/PBFTConfig.h" +#include +#include +namespace bcos +{ +namespace consensus +{ +class BlockValidator : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + explicit BlockValidator(PBFTConfig::Ptr _config) + : m_config(_config), m_taskPool(std::make_shared("blockValidator", 1)) + {} + virtual ~BlockValidator() {} + + virtual void asyncCheckBlock( + bcos::protocol::Block::Ptr _block, std::function _onVerifyFinish); + + virtual void stop() + { + if (m_taskPool) + { + m_taskPool->stop(); + } + } + +protected: + virtual bool checkSealerListAndWeightList(bcos::protocol::Block::Ptr _block); + virtual bool checkSignatureList(bcos::protocol::Block::Ptr _block); + +private: + PBFTConfig::Ptr m_config; + ThreadPool::Ptr m_taskPool; +}; +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTEngine.cpp" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTEngine.cpp" new file mode 100644 index 00000000..9248db68 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTEngine.cpp" @@ -0,0 +1,1655 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for PBFTEngine + * @file PBFTEngine.cpp + * @author: yujiechen + * @date 2021-04-20 + */ +#include "PBFTEngine.h" +#include "../cache/PBFTCacheFactory.h" +#include "../cache/PBFTCacheProcessor.h" +#include +#include +#include +#include +#include +#include +using namespace bcos; +using namespace bcos::consensus; +using namespace bcos::ledger; +using namespace bcos::front; +using namespace bcos::crypto; +using namespace bcos::protocol; + +PBFTEngine::PBFTEngine(PBFTConfig::Ptr _config) + : ConsensusEngine("pbft", 0), + m_config(_config), + m_worker(std::make_shared("pbftWorker", 1)), + m_msgQueue(std::make_shared()) +{ + auto cacheFactory = std::make_shared(); + m_cacheProcessor = std::make_shared(cacheFactory, _config); + m_logSync = std::make_shared(m_config, m_cacheProcessor); + // register the timeout function + m_config->timer()->registerTimeoutHandler(boost::bind(&PBFTEngine::onTimeout, this)); + m_config->storage()->registerFinalizeHandler(boost::bind( + &PBFTEngine::finalizeConsensus, this, boost::placeholders::_1, boost::placeholders::_2)); + + m_config->storage()->registerOnStableCheckPointCommitFailed( + [this](Error::Ptr&& _error, PBFTProposalInterface::Ptr _stableProposal) { + onStableCheckPointCommitFailed(std::move(_error), std::move(_stableProposal)); + }); + + m_config->registerFastViewChangeHandler([this]() { triggerTimeout(false); }); + m_cacheProcessor->registerProposalAppliedHandler(boost::bind(&PBFTEngine::onProposalApplied, + this, boost::placeholders::_1, boost::placeholders::_2, boost::placeholders::_3)); + + m_cacheProcessor->registerOnLoadAndVerifyProposalFinish( + boost::bind(&PBFTEngine::onLoadAndVerifyProposalFinish, this, boost::placeholders::_1, + boost::placeholders::_2, boost::placeholders::_3)); + initSendResponseHandler(); + // when the node first setup, set timeout to be true for view recovery + // set timeout to be true to in case of notify-seal before the PBFTEngine + // started + m_config->setTimeoutState(true); + + // Timer is used to manage checkpoint timeout + m_timer = + std::make_shared(m_config->checkPointTimeoutInterval(), "checkPointResendTimer"); +} + +void PBFTEngine::initSendResponseHandler() +{ + // set the sendResponse callback + std::weak_ptr weakFrontService = m_config->frontService(); + m_sendResponseHandler = [weakFrontService](std::string const& _id, int _moduleID, + NodeIDPtr _dstNode, bytesConstRef _data) { + try + { + auto frontService = weakFrontService.lock(); + if (!frontService) + { + return; + } + frontService->asyncSendResponse( + _id, _moduleID, _dstNode, _data, [_id, _moduleID, _dstNode](Error::Ptr _error) { + if (_error) + { + PBFT_LOG(WARNING) << LOG_DESC("sendResponse failed") << LOG_KV("uuid", _id) + << LOG_KV("module", std::to_string(_moduleID)) + << LOG_KV("dst", _dstNode->shortHex()) + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + } + }); + } + catch (std::exception const& e) + { + PBFT_LOG(WARNING) << LOG_DESC("sendResponse exception") + << LOG_KV("error", boost::diagnostic_information(e)) + << LOG_KV("uuid", _id) << LOG_KV("moduleID", _moduleID) + << LOG_KV("peer", _dstNode->shortHex()); + } + }; +} + +void PBFTEngine::start() +{ + ConsensusEngine::start(); + // when the node setup, start the timer for view recovery + m_config->timer()->start(); + // register timeout handler + auto self = weak_from_this(); + m_timer->registerTimeoutHandler([self]() { + try + { + auto engine = self.lock(); + if (!engine) + { + return; + } + engine->tryToResendCheckPoint(); + } + catch (std::exception const& e) + { + PBFT_LOG(WARNING) << LOG_DESC("tryToResendCheckPoint error") + << LOG_KV("errorInfo", boost::diagnostic_information(e)); + } + }); + m_timer->start(); + // trigger fast viewchange to reachNewView + if (!m_config->startRecovered()) + { + triggerTimeout(false); + } +} + +void PBFTEngine::tryToResendCheckPoint() +{ + { + RecursiveGuard l(m_mutex); + m_cacheProcessor->tryToResendCheckPoint(); + } + m_timer->restart(); +} + +void PBFTEngine::restart() +{ + PBFT_LOG(INFO) << LOG_DESC("restart the consensus module"); + m_config->enableAsMasterNode(true); + triggerTimeout(false); +} + +void PBFTEngine::stop() +{ + if (m_stopped.load()) + { + PBFT_LOG(WARNING) << LOG_DESC("The PBFTEngine already been stopped!"); + return; + } + m_stopped.store(true); + ConsensusEngine::stop(); + if (m_worker) + { + m_worker->stop(); + } + if (m_logSync) + { + m_logSync->stop(); + } + if (m_config) + { + m_config->stop(); + } + if (m_timer) + { + m_timer->stop(); + } + PBFT_LOG(INFO) << LOG_DESC("stop the PBFTEngine"); +} + +void PBFTEngine::onLoadAndVerifyProposalFinish( + bool _verifyResult, Error::Ptr _error, PBFTProposalInterface::Ptr _proposal) +{ + // loadAndVerify proposal failed + if (_error || !_verifyResult) + { + RecursiveGuard l(m_mutex); + m_cacheProcessor->clearCacheAfterRecoverStateFailed(); + return; + } + // must add lock here to ensure thread-safe + RecursiveGuard l(m_mutex); + m_cacheProcessor->updateCommitQueue(_proposal); + // Note: The node that obtains the consensus proposal by request + // proposal from othe nodes will also broadcast the checkPoint message packet, + // Therefore, the node may also be requested by other nodes. + // Therefore, it must be ensured that the obtained proposal is updated to the + // preCommitCache. + m_cacheProcessor->updatePrecommit(_proposal); + m_config->timer()->restart(); +} + +void PBFTEngine::onProposalApplyFailed(int64_t _errorCode, PBFTProposalInterface::Ptr _proposal) +{ + if (!m_config->asMasterNode()) + { + PBFT_LOG(WARNING) << LOG_DESC( + "onProposalApplyFailed: proposal execute failed, but do nothing " + "for the node-self is not " + "the master node") + << printPBFTProposal(_proposal) << m_config->printCurrentState(); + return; + } + PBFT_LOG(WARNING) << LOG_DESC("proposal execute failed") << printPBFTProposal(_proposal) + << m_config->printCurrentState(); + // Note: must add lock here to ensure thread-safe + RecursiveGuard l(m_mutex); + if (_errorCode == bcos::scheduler::SchedulerError::InvalidBlocks) + { + PBFT_LOG(INFO) << LOG_DESC("fetchAndUpdateLedgerConfig for InvalidBlocks"); + fetchAndUpdateLedgerConfig(); + } + // re-push the proposal into the queue + if (_proposal->index() >= m_config->committedProposal()->index() || + _proposal->index() >= m_config->syncingHighestNumber()) + { + m_config->timer()->restart(); + PBFT_LOG(INFO) << LOG_DESC( + "proposal execute failed and re-push the proposal " + "into the cache") + << printPBFTProposal(_proposal); + // Note: must erase the proposal firstly for updateCommitQueue will not + // receive the duplicated executing proposal + m_cacheProcessor->eraseExecutedProposal(_proposal->hash()); + // retry after 20ms + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + m_cacheProcessor->updateCommitQueue(_proposal); + return; + } + m_config->setExpectedCheckPoint(m_config->committedProposal()->index() + 1); + m_cacheProcessor->eraseExecutedProposal(_proposal->hash()); +} + +void PBFTEngine::onProposalApplySuccess( + PBFTProposalInterface::Ptr _proposal, PBFTProposalInterface::Ptr _executedProposal) +{ + // commit the proposal when execute success + m_config->storage()->asyncCommitProposal(_proposal); + + // broadcast checkpoint message + auto checkPointMsg = m_config->pbftMessageFactory()->populateFrom(PacketType::CheckPoint, + m_config->pbftMsgDefaultVersion(), m_config->view(), utcTime(), m_config->nodeIndex(), + _executedProposal, m_config->cryptoSuite(), m_config->keyPair(), true); + + auto encodedData = m_config->codec()->encode(checkPointMsg); + // only broadcast message to the consensus nodes + m_config->frontService()->asyncSendBroadcastMessage( + bcos::protocol::NodeType::CONSENSUS_NODE, ModuleID::PBFT, ref(*encodedData)); + auto startT = utcTime(); + auto recordT = utcTime(); + // Note: must lock here to ensure thread safe + RecursiveGuard l(m_mutex); + auto lockT = (utcTime() - startT); + // restart the timer when proposal execute finished to in case of timeout + if (m_config->timer()->running()) + { + m_config->timer()->restart(); + } + m_cacheProcessor->addCheckPointMsg(checkPointMsg); + m_cacheProcessor->setCheckPointProposal(_executedProposal); + auto currentExpectedCheckPoint = m_config->expectedCheckPoint(); + if (currentExpectedCheckPoint < _executedProposal->index()) + { + PBFT_LOG(WARNING) << LOG_DESC( + "onProposalApplySuccess: maybe the consensus state has been " + "changed, not reset the expectedCheckPoint") + << LOG_KV("expectedCheckPoint", currentExpectedCheckPoint) + << LOG_KV("index", checkPointMsg->index()) + << LOG_KV("hash", checkPointMsg->hash().abridged()); + } + else + { + m_config->setExpectedCheckPoint(_executedProposal->index() + 1); + } + m_cacheProcessor->checkAndCommitStableCheckPoint(); + m_cacheProcessor->tryToApplyCommitQueue(); + m_cacheProcessor->eraseExecutedProposal(_proposal->hash()); + PBFT_LOG(INFO) << LOG_DESC("onProposalApplySuccess") << LOG_KV("index", checkPointMsg->index()) + << LOG_KV("hash", checkPointMsg->hash().abridged()) << LOG_KV("lockT", lockT) + << LOG_KV("timecost", (utcTime() - recordT)); +} + +// called after proposal executed successfully +void PBFTEngine::onProposalApplied(int64_t _errorCode, PBFTProposalInterface::Ptr _proposal, + PBFTProposalInterface::Ptr _executedProposal) +{ + auto self = weak_from_this(); + m_worker->enqueue([self, _errorCode, _proposal, _executedProposal]() { + try + { + auto engine = self.lock(); + if (!engine) + { + return; + } + if (_errorCode != 0) + { + engine->onProposalApplyFailed(_errorCode, _proposal); + return; + } + engine->onProposalApplySuccess(_proposal, _executedProposal); + } + catch (std::exception const& e) + { + PBFT_LOG(WARNING) << LOG_DESC("onProposalApplied exception") + << printPBFTProposal(_executedProposal) + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +void PBFTEngine::asyncSubmitProposal(bool _containSysTxs, bytesConstRef _proposalData, + BlockNumber _proposalIndex, HashType const& _proposalHash, + std::function _onProposalSubmitted) +{ + if (_onProposalSubmitted) + { + _onProposalSubmitted(nullptr); + } + onRecvProposal(_containSysTxs, _proposalData, _proposalIndex, _proposalHash); +} + +void PBFTEngine::onRecvProposal(bool _containSysTxs, bytesConstRef _proposalData, + BlockNumber _proposalIndex, HashType const& _proposalHash) +{ + if (_proposalData.size() == 0) + { + return; + } + if (!m_config->isConsensusNode()) + { + return; + } + // expired proposal + auto consProposalIndex = m_config->committedProposal()->index() + 1; + if (_proposalIndex <= m_config->syncingHighestNumber()) + { + PBFT_LOG(WARNING) << LOG_DESC("asyncSubmitProposal failed for expired index") + << LOG_KV("index", _proposalIndex) + << LOG_KV("hash", _proposalHash.abridged()) + << m_config->printCurrentState() + << LOG_KV("syncingHighestNumber", m_config->syncingHighestNumber()); + m_config->notifyResetSealing(consProposalIndex); + m_config->validator()->asyncResetTxsFlag(_proposalData, false, true); + return; + } + if (_proposalIndex <= m_config->committedProposal()->index() || + _proposalIndex < m_config->expectedCheckPoint() || + _proposalIndex < m_config->lowWaterMark()) + { + PBFT_LOG(WARNING) << LOG_DESC("asyncSubmitProposal failed for invalid index") + << LOG_KV("index", _proposalIndex) + << LOG_KV("hash", _proposalHash.abridged()) + << m_config->printCurrentState() + << LOG_KV("lowWaterMark", m_config->lowWaterMark()); + return; + } + auto leaderIndex = m_config->leaderIndex(_proposalIndex); + if (leaderIndex != m_config->nodeIndex()) + { + PBFT_LOG(WARNING) << LOG_DESC( + "asyncSubmitProposal failed for the node-self is not the leader") + << LOG_KV("expectedLeader", leaderIndex) + << LOG_KV("index", _proposalIndex) + << LOG_KV("hash", _proposalHash.abridged()) + << m_config->printCurrentState(); + m_config->notifyResetSealing(consProposalIndex); + m_config->validator()->asyncResetTxsFlag(_proposalData, false, true); + return; + } + PBFT_LOG(INFO) << LOG_DESC("asyncSubmitProposal") << LOG_KV("index", _proposalIndex) + << LOG_KV("hash", _proposalHash.abridged()) << m_config->printCurrentState(); + // generate the pre-prepare packet + auto pbftProposal = m_config->pbftMessageFactory()->createPBFTProposal(); + pbftProposal->setData(_proposalData); + pbftProposal->setIndex(_proposalIndex); + pbftProposal->setHash(_proposalHash); + pbftProposal->setSealerId(m_config->nodeIndex()); + pbftProposal->setSystemProposal(_containSysTxs); + + auto pbftMessage = + m_config->pbftMessageFactory()->populateFrom(PacketType::PrePreparePacket, pbftProposal, + m_config->pbftMsgDefaultVersion(), m_config->view(), utcTime(), m_config->nodeIndex()); + PBFT_LOG(INFO) << LOG_DESC("++++++++++++++++ Generating seal on") + << LOG_KV("index", pbftMessage->index()) << LOG_KV("Idx", m_config->nodeIndex()) + << LOG_KV("hash", pbftMessage->hash().abridged()) + << LOG_KV("sysProposal", pbftProposal->systemProposal()); + + // handle the pre-prepare packet + RecursiveGuard l(m_mutex); + auto ret = handlePrePrepareMsg(pbftMessage, false, false, false); + // only broadcast the prePrepareMsg when local handlePrePrepareMsg success + if (ret) + { + // broadcast the pre-prepare packet + auto encodedData = m_config->codec()->encode(pbftMessage); + // only broadcast pbft message to the consensus nodes + m_config->frontService()->asyncSendBroadcastMessage( + bcos::protocol::NodeType::CONSENSUS_NODE, ModuleID::PBFT, ref(*encodedData)); + } + else + { + resetSealedTxs(pbftMessage); + } +} + +void PBFTEngine::resetSealedTxs(std::shared_ptr _prePrepareMsg) +{ + if (_prePrepareMsg->generatedFrom() != m_config->nodeIndex()) + { + return; + } + m_config->notifyResetSealing(); + m_config->validator()->asyncResetTxsFlag(_prePrepareMsg->consensusProposal()->data(), false); +} + +// receive the new block notification from the sync module +void PBFTEngine::asyncNotifyNewBlock( + LedgerConfig::Ptr _ledgerConfig, std::function _onRecv) +{ + if (_onRecv) + { + _onRecv(nullptr); + } + if (m_config->shouldResetConfig(_ledgerConfig->blockNumber())) + { + PBFT_LOG(INFO) << LOG_DESC("The sync module notify the latestBlock") + << LOG_KV("index", _ledgerConfig->blockNumber()) + << LOG_KV("hash", _ledgerConfig->hash().abridged()); + finalizeConsensus(_ledgerConfig, true); + } +} + +void PBFTEngine::onReceivePBFTMessage( + bcos::Error::Ptr _error, std::string const& _id, NodeIDPtr _nodeID, bytesConstRef _data) +{ + auto self = weak_from_this(); + onReceivePBFTMessage( + std::move(_error), _nodeID, _data, [_id, _nodeID, self](bytesConstRef _respData) { + try + { + auto engine = self.lock(); + if (!engine) + { + return; + } + engine->m_sendResponseHandler(_id, ModuleID::PBFT, _nodeID, _respData); + } + catch (std::exception const& e) + { + PBFT_LOG(WARNING) << LOG_DESC("onReceivePBFTMessage exception") + << LOG_KV("fromNode", _nodeID->hex()) << LOG_KV("uuid", _id) + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +void PBFTEngine::onReceivePBFTMessage(Error::Ptr _error, NodeIDPtr _fromNode, bytesConstRef _data, + SendResponseCallback _sendResponseCallback) +{ + try + { + if (_error != nullptr) + { + return; + } + // the node is not the consensusNode + if (!m_config->isConsensusNode()) + { + PBFT_LOG(TRACE) << LOG_DESC( + "onReceivePBFTMessage: reject the message " + "for the node is not the consensus " + "node"); + return; + } + // decode the message and push the message into the queue + auto pbftMsg = m_config->codec()->decode(_data); + pbftMsg->setFrom(_fromNode); + // the committed proposal request message + if (pbftMsg->packetType() == PacketType::CommittedProposalRequest) + { + auto self = weak_from_this(); + m_worker->enqueue([self, pbftMsg, _sendResponseCallback]() { + try + { + auto pbftEngine = self.lock(); + if (!pbftEngine) + { + return; + } + pbftEngine->onReceiveCommittedProposalRequest(pbftMsg, _sendResponseCallback); + } + catch (std::exception const& e) + { + PBFT_LOG(WARNING) << LOG_DESC("onReceiveCommittedProposalRequest exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); + return; + } + // the precommitted proposals request message + if (pbftMsg->packetType() == PacketType::PreparedProposalRequest) + { + auto self = weak_from_this(); + m_worker->enqueue([self, pbftMsg, _sendResponseCallback]() { + try + { + auto pbftEngine = self.lock(); + if (!pbftEngine) + { + return; + } + pbftEngine->onReceivePrecommitRequest(pbftMsg, _sendResponseCallback); + } + catch (std::exception const& e) + { + PBFT_LOG(WARNING) << LOG_DESC("onReceivePrecommitRequest exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); + return; + } + m_msgQueue->push(pbftMsg); + m_signalled.notify_all(); + } + catch (std::exception const& _e) + { + PBFT_LOG(WARNING) << LOG_DESC("onReceivePBFTMessage exception") + << LOG_KV("fromNode", _fromNode->hex()) + << LOG_KV("Idx", m_config->nodeIndex()) + << LOG_KV("nodeId", m_config->nodeID()->hex()) + << LOG_KV("error", boost::diagnostic_information(_e)); + } +} + +void PBFTEngine::clearAllCache() +{ + RecursiveGuard l(m_mutex); + m_cacheProcessor->clearAllCache(); +} + +void PBFTEngine::executeWorker() +{ + // the node is not the consensusNode + if (!m_config->isConsensusNode()) + { + waitSignal(); + return; + } + // when the node is syncing, not handle the PBFT message + if (isSyncingHigher()) + { + waitSignal(); + return; + } + // handle the PBFT message(here will wait when the msgQueue is empty) + auto messageResult = m_msgQueue->tryPop(c_PopWaitSeconds); + auto empty = m_msgQueue->empty(); + if (messageResult.first) + { + auto pbftMsg = messageResult.second; + auto packetType = pbftMsg->packetType(); + // can't handle the future consensus messages when handling the system + // proposal + if ((c_consensusPacket.count(packetType)) && !m_config->canHandleNewProposal(pbftMsg)) + { +#if 0 + PBFT_LOG(TRACE) << LOG_DESC( + "receive consensus packet, re-push it to the msgQueue for " + "canHandleNewProposal") + << LOG_KV("index", pbftMsg->index()) << LOG_KV("type", packetType) + << m_config->printCurrentState(); +#endif + m_msgQueue->push(pbftMsg); + if (empty) + { + waitSignal(); + } + return; + } + handleMsg(pbftMsg); + } + // wait for PBFTMsg + else + { + waitSignal(); + } +} + +void PBFTEngine::handleMsg(std::shared_ptr _msg) +{ + // check the view + if (_msg->view() > MaxView) + { + PBFT_LOG(WARNING) << LOG_DESC("handleMsg: reject msg with invalid view") + << printPBFTMsgInfo(_msg); + return; + } + RecursiveGuard l(m_mutex); + switch (_msg->packetType()) + { + case PacketType::PrePreparePacket: + { + auto prePrepareMsg = std::dynamic_pointer_cast(_msg); + handlePrePrepareMsg(prePrepareMsg, true); + break; + } + case PacketType::PreparePacket: + { + auto prepareMsg = std::dynamic_pointer_cast(_msg); + handlePrepareMsg(prepareMsg); + break; + } + case PacketType::CommitPacket: + { + auto commitMsg = std::dynamic_pointer_cast(_msg); + handleCommitMsg(commitMsg); + break; + } + case PacketType::ViewChangePacket: + { + auto viewChangeMsg = std::dynamic_pointer_cast(_msg); + handleViewChangeMsg(viewChangeMsg); + break; + } + case PacketType::NewViewPacket: + { + auto newViewMsg = std::dynamic_pointer_cast(_msg); + handleNewViewMsg(newViewMsg); + break; + } + case PacketType::CheckPoint: + { + auto checkPointMsg = std::dynamic_pointer_cast(_msg); + handleCheckPointMsg(checkPointMsg); + break; + } + case PacketType::RecoverRequest: + { + auto request = std::dynamic_pointer_cast(_msg); + handleRecoverRequest(request); + break; + } + case PacketType::RecoverResponse: + { + auto recoverResponse = std::dynamic_pointer_cast(_msg); + handleRecoverResponse(recoverResponse); + break; + } + default: + { + PBFT_LOG(WARNING) << LOG_DESC("handleMsg: unknown PBFT message") + << LOG_KV("type", std::to_string(_msg->packetType())) + << LOG_KV("genIdx", _msg->generatedFrom()) + << LOG_KV("nodeSelf", m_config->nodeID()->hex()); + return; + } + } +} + +CheckResult PBFTEngine::checkPBFTMsgState(PBFTMessageInterface::Ptr _pbftReq) const +{ + if (!_pbftReq->consensusProposal()) + { + return CheckResult::INVALID; + } + bool proposalCommitted = m_cacheProcessor->proposalCommitted(_pbftReq->index()); + if (_pbftReq->index() < m_config->lowWaterMark() || + _pbftReq->index() < m_config->expectedCheckPoint() || + _pbftReq->index() <= m_config->syncingHighestNumber() || proposalCommitted) + { + PBFT_LOG(DEBUG) << LOG_DESC("checkPBFTMsgState: invalid pbftMsg for invalid index") + << LOG_KV("highWaterMark", m_config->highWaterMark()) + << LOG_KV("lowWaterMark", m_config->lowWaterMark()) + << printPBFTMsgInfo(_pbftReq) << m_config->printCurrentState() + << LOG_KV("syncingNumber", m_config->syncingHighestNumber()) + << LOG_KV("proposalCommitted", proposalCommitted); + return CheckResult::INVALID; + } + // case index equal + if (_pbftReq->view() < m_config->view()) + { + PBFT_LOG(DEBUG) << LOG_DESC("checkPBFTMsgState: invalid pbftMsg for invalid view") + << printPBFTMsgInfo(_pbftReq) << m_config->printCurrentState(); + return CheckResult::INVALID; + } + return CheckResult::VALID; +} + +CheckResult PBFTEngine::checkPrePrepareMsg(std::shared_ptr _prePrepareMsg) +{ + if (checkPBFTMsgState(_prePrepareMsg) == CheckResult::INVALID) + { + return CheckResult::INVALID; + } + // check the existence of the msg + if (m_cacheProcessor->existPrePrepare(_prePrepareMsg)) + { + PBFT_LOG(DEBUG) << LOG_DESC("handlePrePrepareMsg: duplicated") + << LOG_KV("committedIndex", m_config->committedProposal()->index()) + << LOG_KV("recvIndex", _prePrepareMsg->index()) + << LOG_KV("hash", _prePrepareMsg->hash().abridged()) + << m_config->printCurrentState(); + return CheckResult::INVALID; + } + // already has prePrepare msg + if (m_cacheProcessor->conflictWithProcessedReq(_prePrepareMsg)) + { + return CheckResult::INVALID; + } + // check conflict + if (m_cacheProcessor->conflictWithPrecommitReq(_prePrepareMsg)) + { + return CheckResult::INVALID; + } + return CheckResult::VALID; +} + +CheckResult PBFTEngine::checkSignature(PBFTBaseMessageInterface::Ptr _req) +{ + // check the signature + auto nodeInfo = m_config->getConsensusNodeByIndex(_req->generatedFrom()); + if (!nodeInfo) + { + PBFT_LOG(WARNING) << LOG_DESC("checkSignature failed for the node is not a consensus node") + << printPBFTMsgInfo(_req); + return CheckResult::INVALID; + } + auto publicKey = nodeInfo->nodeID(); + if (!_req->verifySignature(m_config->cryptoSuite(), publicKey)) + { + PBFT_LOG(WARNING) << LOG_DESC("checkSignature failed for invalid signature") + << printPBFTMsgInfo(_req); + return CheckResult::INVALID; + } + return CheckResult::VALID; +} + +bool PBFTEngine::checkProposalSignature( + IndexType _generatedFrom, PBFTProposalInterface::Ptr _proposal) +{ + if (!_proposal || _proposal->signature().size() == 0) + { + return false; + } + auto nodeInfo = m_config->getConsensusNodeByIndex(_generatedFrom); + if (!nodeInfo) + { + PBFT_LOG(WARNING) << LOG_DESC( + "checkProposalSignature failed for the node " + "is not a consensus node") + << printPBFTProposal(_proposal); + return false; + } + + return m_config->cryptoSuite()->signatureImpl()->verify( + nodeInfo->nodeID(), _proposal->hash(), _proposal->signature()); +} + +bool PBFTEngine::isSyncingHigher() +{ + auto committedIndex = m_config->committedProposal()->index(); + auto syncNumber = m_config->syncingHighestNumber(); + if (syncNumber < (committedIndex + m_config->waterMarkLimit())) + { + return false; + } + return true; +} + +bool PBFTEngine::handlePrePrepareMsg(PBFTMessageInterface::Ptr _prePrepareMsg, + bool _needVerifyProposal, bool _generatedFromNewView, bool _needCheckSignature) +{ + if (isSyncingHigher()) + { + PBFT_LOG(INFO) << LOG_DESC( + "handlePrePrepareMsg: reject the prePrepareMsg " + "for the node is syncing") + << LOG_KV("committedIndex", m_config->committedProposal()->index()) + << LOG_KV("recvIndex", _prePrepareMsg->index()) + << LOG_KV("hash", _prePrepareMsg->hash().abridged()) + << LOG_KV("syncingNum", m_config->syncingHighestNumber()) + << m_config->printCurrentState(); + return false; + } + if (m_cacheProcessor->executingProposals().count(_prePrepareMsg->hash())) + { + PBFT_LOG(DEBUG) << LOG_DESC( + "handlePrePrepareMsg: reject the prePrepareMsg " + "for the proposal is executing") + << LOG_KV("committedIndex", m_config->committedProposal()->index()) + << LOG_KV("recvIndex", _prePrepareMsg->index()) + << LOG_KV("hash", _prePrepareMsg->hash().abridged()) + << m_config->printCurrentState(); + return false; + } + PBFT_LOG(INFO) << LOG_DESC("handlePrePrepareMsg") << printPBFTMsgInfo(_prePrepareMsg) + << m_config->printCurrentState(); + + auto result = checkPrePrepareMsg(_prePrepareMsg); + if (result == CheckResult::INVALID) + { + return false; + } + if (!_generatedFromNewView) + { + // packet can be processed in this round of consensus + // check the proposal is generated from the leader + auto expectedLeader = m_config->leaderIndex(_prePrepareMsg->index()); + if (expectedLeader != _prePrepareMsg->generatedFrom()) + { + PBFT_LOG(TRACE) << LOG_DESC( + "handlePrePrepareMsg: invalid packet for not from the leader") + << printPBFTMsgInfo(_prePrepareMsg) << m_config->printCurrentState() + << LOG_KV("expectedLeader", expectedLeader); + return false; + } + if (_needCheckSignature) + { + // check the signature + result = checkSignature(_prePrepareMsg); + if (result == CheckResult::INVALID) + { + m_config->notifySealer(_prePrepareMsg->index(), true); + return false; + } + } + } + // add the prePrepareReq to the cache + if (!_needVerifyProposal) + { + // Note: must reset the txs to be sealed no matter verify success or failed + // because some nodes may verify failed for timeout, while other nodes may + // verify success + m_config->validator()->asyncResetTxsFlag(_prePrepareMsg->consensusProposal()->data(), true); + // add the pre-prepare packet into the cache + m_cacheProcessor->addPrePrepareCache(_prePrepareMsg); + m_config->timer()->restart(); + // broadcast PrepareMsg the packet + broadcastPrepareMsg(_prePrepareMsg); + PBFT_LOG(INFO) << LOG_DESC("handlePrePrepareMsg and broadcast prepare packet") + << printPBFTMsgInfo(_prePrepareMsg) << m_config->printCurrentState(); + m_cacheProcessor->checkAndPreCommit(); + return true; + } + // verify the proposal + auto self = weak_from_this(); + auto leaderNodeInfo = m_config->getConsensusNodeByIndex(_prePrepareMsg->generatedFrom()); + if (!leaderNodeInfo) + { + return false; + } + m_config->validator()->verifyProposal(leaderNodeInfo->nodeID(), + _prePrepareMsg->consensusProposal(), + [self, _prePrepareMsg, _generatedFromNewView](Error::Ptr _error, bool _verifyResult) { + try + { + auto pbftEngine = self.lock(); + if (!pbftEngine) + { + return; + } + auto committedIndex = pbftEngine->m_config->committedProposal()->index(); + if (committedIndex >= _prePrepareMsg->index()) + { + return; + } + // Note: must reset the txs to be sealed no matter verify success or + // failed because some nodes may verify failed for timeout, while + // other nodes may verify success + pbftEngine->m_config->validator()->asyncResetTxsFlag( + _prePrepareMsg->consensusProposal()->data(), true); + + // verify exceptioned + if (_error != nullptr) + { + PBFT_LOG(WARNING) << LOG_DESC("verify proposal exceptioned") + << printPBFTMsgInfo(_prePrepareMsg) + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMsg", _error->errorMessage()); + pbftEngine->m_config->notifySealer(_prePrepareMsg->index(), true); + return; + } + // verify failed + if (!_verifyResult) + { + PBFT_LOG(WARNING) + << LOG_DESC("verify proposal failed") << printPBFTMsgInfo(_prePrepareMsg); + pbftEngine->m_config->notifySealer(_prePrepareMsg->index(), true); + return; + } + // verify success + RecursiveGuard l(pbftEngine->m_mutex); + pbftEngine->handlePrePrepareMsg( + _prePrepareMsg, false, _generatedFromNewView, false); + } + catch (std::exception const& _e) + { + PBFT_LOG(WARNING) << LOG_DESC("exception when calls onVerifyFinishedHandler") + << printPBFTMsgInfo(_prePrepareMsg) + << LOG_KV("error", boost::diagnostic_information(_e)); + } + }); + return true; +} + +void PBFTEngine::broadcastPrepareMsg(PBFTMessageInterface::Ptr _prePrepareMsg) +{ + auto prepareMsg = m_config->pbftMessageFactory()->populateFrom(PacketType::PreparePacket, + m_config->pbftMsgDefaultVersion(), m_config->view(), utcTime(), m_config->nodeIndex(), + _prePrepareMsg->consensusProposal(), m_config->cryptoSuite(), m_config->keyPair()); + prepareMsg->setIndex(_prePrepareMsg->index()); + // add the message to local cache + m_cacheProcessor->addPrepareCache(prepareMsg); + + auto encodedData = m_config->codec()->encode(prepareMsg, m_config->pbftMsgDefaultVersion()); + // only broadcast to the consensus nodes + m_config->frontService()->asyncSendBroadcastMessage( + bcos::protocol::NodeType::CONSENSUS_NODE, ModuleID::PBFT, ref(*encodedData)); + // try to precommit the message + m_cacheProcessor->checkAndPreCommit(); +} + +CheckResult PBFTEngine::checkPBFTMsg(std::shared_ptr _prepareMsg) +{ + auto result = checkPBFTMsgState(_prepareMsg); + if (result == CheckResult::INVALID) + { + return result; + } + if (_prepareMsg->generatedFrom() == m_config->nodeIndex()) + { + PBFT_LOG(TRACE) << LOG_DESC("checkPrepareMsg: Recv own req") + << printPBFTMsgInfo(_prepareMsg); + return CheckResult::INVALID; + } + // check the existence of Pre-Prepare request + if (m_cacheProcessor->existPrePrepare(_prepareMsg)) + { + // compare with the local pre-prepare cache + if (m_cacheProcessor->conflictWithProcessedReq(_prepareMsg)) + { + return CheckResult::INVALID; + } + } + return checkSignature(_prepareMsg); +} + +bool PBFTEngine::handlePrepareMsg(PBFTMessageInterface::Ptr _prepareMsg) +{ + PBFT_LOG(TRACE) << LOG_DESC("handlePrepareMsg") << printPBFTMsgInfo(_prepareMsg) + << m_config->printCurrentState(); + auto result = checkPBFTMsg(_prepareMsg); + if (result == CheckResult::INVALID) + { + return false; + } + if (!checkProposalSignature(_prepareMsg->generatedFrom(), _prepareMsg->consensusProposal())) + { + return false; + } + m_cacheProcessor->addPrepareCache(_prepareMsg); + m_cacheProcessor->checkAndPreCommit(); + return true; +} + +bool PBFTEngine::handleCommitMsg(PBFTMessageInterface::Ptr _commitMsg) +{ + PBFT_LOG(TRACE) << LOG_DESC("handleCommitMsg") << printPBFTMsgInfo(_commitMsg) + << m_config->printCurrentState(); + auto result = checkPBFTMsg(_commitMsg); + if (result == CheckResult::INVALID) + { + return false; + } + m_cacheProcessor->addCommitReq(_commitMsg); + m_cacheProcessor->checkAndCommit(); + return true; +} + +void PBFTEngine::onTimeout() +{ + // only restart the timer if the node is not exist in the group + if (!m_config->isConsensusNode()) + { + m_config->timer()->restart(); + return; + } + auto startT = utcTime(); + auto recordT = utcTime(); + RecursiveGuard l(m_mutex); + auto lockT = utcTime() - startT; + if (m_cacheProcessor->tryToApplyCommitQueue()) + { + PBFT_LOG(INFO) << LOG_DESC("onTimeout: apply proposal to state-machine, restart the timer") + << m_config->printCurrentState(); + m_config->timer()->restart(); + return; + } + m_cacheProcessor->clearExpiredExecutingProposal(); + // when some proposals are executing, not trigger timeout + auto executingProposalSize = m_cacheProcessor->executingProposalSize(); + if (executingProposalSize > 0) + { + PBFT_LOG(INFO) << LOG_DESC("onTimeout: Proposal is executing, restart the timer") + << LOG_KV("executingProposalSize", executingProposalSize) + << m_config->printCurrentState(); + m_config->timer()->restart(); + return; + } + triggerTimeout(true); + PBFT_LOG(WARNING) << LOG_DESC("After onTimeout") << m_config->printCurrentState() + << LOG_KV("lockT", lockT) << LOG_KV("timecost", (utcTime() - recordT)); +} + +void PBFTEngine::triggerTimeout(bool _incTimeout) +{ + m_config->resetTimeoutState(_incTimeout); + // clear the viewchange cache + m_cacheProcessor->removeInvalidViewChange( + m_config->view(), m_config->committedProposal()->index()); + // notify the latest proposal index to the sync module when timeout to enable + // syncing + m_cacheProcessor->notifyCommittedProposalIndex(m_config->committedProposal()->index()); + // broadcast viewchange and try to the new-view phase + broadcastViewChangeReq(); +} + +ViewChangeMsgInterface::Ptr PBFTEngine::generateViewChange() +{ + // broadcast the viewChangeReq + auto committedProposal = m_config->populateCommittedProposal(); + if (committedProposal == nullptr) + { + PBFT_LOG(WARNING) << LOG_DESC( + "broadcastViewChangeReq failed for the " + "latest storage state has not been loaded."); + } + auto viewChangeReq = m_config->pbftMessageFactory()->createViewChangeMsg(); + viewChangeReq->setHash(m_config->committedProposal()->hash()); + viewChangeReq->setIndex(m_config->committedProposal()->index()); + viewChangeReq->setPacketType(PacketType::ViewChangePacket); + viewChangeReq->setVersion(m_config->pbftMsgDefaultVersion()); + viewChangeReq->setView(m_config->toView()); + viewChangeReq->setTimestamp(utcTime()); + viewChangeReq->setGeneratedFrom(m_config->nodeIndex()); + // set the committed proposal + viewChangeReq->setCommittedProposal(committedProposal); + // set prepared proposals + viewChangeReq->setPreparedProposals(m_cacheProcessor->preCommitCachesWithoutData()); + return viewChangeReq; +} + +void PBFTEngine::sendViewChange(bcos::crypto::NodeIDPtr _dstNode) +{ + auto viewChangeReq = generateViewChange(); + // encode and broadcast the viewchangeReq + auto encodedData = m_config->codec()->encode(viewChangeReq); + // only broadcast to the consensus nodes + m_config->frontService()->asyncSendMessageByNodeID( + ModuleID::PBFT, std::move(_dstNode), ref(*encodedData), 0, nullptr); + // collect the viewchangeReq + m_cacheProcessor->addViewChangeReq(viewChangeReq); + auto newViewMsg = m_cacheProcessor->checkAndTryIntoNewView(); + if (newViewMsg) + { + reHandlePrePrepareProposals(newViewMsg); + } +} + +void PBFTEngine::sendRecoverResponse(bcos::crypto::NodeIDPtr _dstNode) +{ + auto response = m_config->pbftMessageFactory()->createPBFTMsg(); + response->setPacketType(PacketType::RecoverResponse); + response->setGeneratedFrom(m_config->nodeIndex()); + response->setView(m_config->view()); + response->setTimestamp(utcTime()); + response->setIndex(m_config->committedProposal()->index()); + auto encodedData = m_config->codec()->encode(response); + m_config->frontService()->asyncSendMessageByNodeID( + ModuleID::PBFT, _dstNode, ref(*encodedData), 0, nullptr); + PBFT_LOG(DEBUG) << LOG_DESC("sendRecoverResponse") << LOG_KV("peer", _dstNode->shortHex()) + << m_config->printCurrentState(); +} + +void PBFTEngine::broadcastViewChangeReq() +{ + auto viewChangeReq = generateViewChange(); + // encode and broadcast the viewchangeReq + auto encodedData = m_config->codec()->encode(viewChangeReq); + // only broadcast to the consensus nodes + m_config->frontService()->asyncSendBroadcastMessage( + bcos::protocol::NodeType::CONSENSUS_NODE, ModuleID::PBFT, ref(*encodedData)); + PBFT_LOG(INFO) << LOG_DESC("broadcastViewChangeReq") << printPBFTMsgInfo(viewChangeReq); + // collect the viewchangeReq + m_cacheProcessor->addViewChangeReq(viewChangeReq); + auto newViewMsg = m_cacheProcessor->checkAndTryIntoNewView(); + if (newViewMsg) + { + reHandlePrePrepareProposals(newViewMsg); + } +} + +bool PBFTEngine::isValidViewChangeMsg(bcos::crypto::NodeIDPtr _fromNode, + std::shared_ptr _viewChangeMsg, bool _shouldCheckSig) +{ + // check the committed-proposal index + if (_viewChangeMsg->committedProposal()->index() < m_config->committedProposal()->index()) + { + PBFT_LOG(INFO) << LOG_DESC("InvalidViewChangeReq: invalid index") + << printPBFTMsgInfo(_viewChangeMsg) << m_config->printCurrentState(); + return false; + } + if (_fromNode && !isTimeout()) + { + PBFT_LOG(INFO) << LOG_DESC("sendRecoverResponse to the node try to trigger viewchange") + << LOG_KV("dst", _fromNode->shortHex()) << printPBFTMsgInfo(_viewChangeMsg) + << m_config->printCurrentState(); + sendRecoverResponse(_fromNode); + } + // check the view + if ((_viewChangeMsg->view() < m_config->view()) || + (_viewChangeMsg->view() + 1 < m_config->toView())) + { + if (_fromNode && isTimeout()) + { + PBFT_LOG(INFO) << LOG_DESC("send viewchange to the node whose view falling behind") + << LOG_KV("dst", _fromNode->shortHex()) + << printPBFTMsgInfo(_viewChangeMsg) << m_config->printCurrentState(); + sendViewChange(_fromNode); + } + } + if (_viewChangeMsg->view() < m_config->view()) + { + return false; + } + // check the committed proposal hash + if (_viewChangeMsg->committedProposal()->index() == m_config->committedProposal()->index() && + _viewChangeMsg->committedProposal()->hash() != m_config->committedProposal()->hash()) + { + PBFT_LOG(WARNING) << LOG_DESC("InvalidViewChangeReq: conflict with local committedProposal") + << LOG_DESC(", received proposal: ") + << printPBFTProposal(_viewChangeMsg->committedProposal()) + << LOG_DESC(", local committedProposal:") + << printPBFTProposal(m_config->committedProposal()); + return false; + } + // check the precommitted proposals + for (const auto& precommitMsg : _viewChangeMsg->preparedProposals()) + { + if (precommitMsg->view() > _viewChangeMsg->view()) + { + PBFT_LOG(INFO) << LOG_DESC("InvalidViewChangeReq for invalid view") + << printPBFTMsgInfo(precommitMsg) << printPBFTMsgInfo(_viewChangeMsg) + << m_config->printCurrentState(); + return false; + } + if (!m_cacheProcessor->checkPrecommitMsg(precommitMsg)) + { + PBFT_LOG(INFO) << LOG_DESC("InvalidViewChangeReq for invalid proposal") + << LOG_KV("viewChangeFrom", _viewChangeMsg->generatedFrom()) + << printPBFTMsgInfo(precommitMsg) << m_config->printCurrentState(); + return false; + } + } + if (!_shouldCheckSig) + { + return true; + } + auto ret = checkSignature(_viewChangeMsg); + if (ret == CheckResult::INVALID) + { + PBFT_LOG(INFO) << LOG_DESC("InvalidViewChangeReq: invalid signature") + << printPBFTMsgInfo(_viewChangeMsg) << m_config->printCurrentState(); + return false; + } + return true; +} + +bool PBFTEngine::handleViewChangeMsg(ViewChangeMsgInterface::Ptr _viewChangeMsg) +{ + if (!isValidViewChangeMsg(_viewChangeMsg->from(), _viewChangeMsg)) + { + return false; + } + if (!m_config->timeout() && _viewChangeMsg->from()) + { + sendRecoverResponse(_viewChangeMsg->from()); + } + m_cacheProcessor->addViewChangeReq(_viewChangeMsg); + // try to trigger fast view change if receive more than (f+1) valid view + // change messages whose view is greater than the current view: sends a + // view-change message for the smallest view in the set, even if its timer has + // not expired + auto leaderIndex = + m_config->leaderIndexInNewViewPeriod(_viewChangeMsg->index() + 1, _viewChangeMsg->index()); + if (_viewChangeMsg->generatedFrom() == leaderIndex || + (m_cacheProcessor->getViewChangeWeight(_viewChangeMsg->view()) > + m_config->maxFaultyQuorum())) + { + auto view = m_cacheProcessor->tryToTriggerFastViewChange(); + if (view > 0) + { + // trigger timeout to reach fast view change + triggerTimeout(false); + } + } + auto newViewMsg = m_cacheProcessor->checkAndTryIntoNewView(); + if (newViewMsg) + { + reHandlePrePrepareProposals(newViewMsg); + return true; + } + return true; +} + +bool PBFTEngine::isValidNewViewMsg(std::shared_ptr _newViewMsg) +{ + if (_newViewMsg->view() <= m_config->view()) + { + PBFT_LOG(INFO) << LOG_DESC("InvalidNewViewMsg for invalid view") + << printPBFTMsgInfo(_newViewMsg) << m_config->printCurrentState(); + return false; + } + // check the viewchange + uint64_t weight = 0; + auto viewChangeList = _newViewMsg->viewChangeMsgList(); + for (const auto& viewChangeReq : viewChangeList) + { + if (!isValidViewChangeMsg(_newViewMsg->from(), viewChangeReq)) + { + PBFT_LOG(WARNING) << LOG_DESC("InvalidNewViewMsg for viewChange check failed") + << printPBFTMsgInfo(viewChangeReq); + return false; + } + auto nodeInfo = m_config->getConsensusNodeByIndex(viewChangeReq->generatedFrom()); + if (!nodeInfo) + { + continue; + } + weight += nodeInfo->weight(); + } + // TODO: need to ensure the accuracy of local weight parameters + if (weight < m_config->minRequiredQuorum()) + { + PBFT_LOG(WARNING) << LOG_DESC("InvalidNewViewMsg for unenough weight") + << LOG_KV("weight", weight) + << LOG_KV("minRequiredQuorum", m_config->minRequiredQuorum()); + return false; + } + // TODO: check the prePrepared message + auto ret = checkSignature(_newViewMsg); + if (ret == CheckResult::INVALID) + { + return false; + } + return true; +} + +bool PBFTEngine::handleNewViewMsg(NewViewMsgInterface::Ptr _newViewMsg) +{ + PBFT_LOG(INFO) << LOG_DESC("handleNewViewMsg: receive newViewChangeMsg") + << printPBFTMsgInfo(_newViewMsg) << m_config->printCurrentState(); + if (!isValidNewViewMsg(_newViewMsg)) + { + return false; + } + PBFT_LOG(INFO) << LOG_DESC("handleNewViewMsg success") << printPBFTMsgInfo(_newViewMsg) + << m_config->printCurrentState(); + reHandlePrePrepareProposals(_newViewMsg); + return true; +} + +void PBFTEngine::reachNewView(ViewType _view) +{ + m_config->resetNewViewState(_view); + m_cacheProcessor->resetCacheAfterViewChange( + m_config->view(), m_config->committedProposal()->index()); + // try to preCommit/commit after no-timeout + m_cacheProcessor->checkAndPreCommit(); + m_cacheProcessor->checkAndCommit(); + PBFT_LOG(INFO) << LOG_DESC("reachNewView") << m_config->printCurrentState(); + m_cacheProcessor->tryToApplyCommitQueue(); + m_cacheProcessor->tryToCommitStableCheckPoint(); +} + +void PBFTEngine::reHandlePrePrepareProposals(NewViewMsgInterface::Ptr _newViewReq) +{ + reachNewView(_newViewReq->view()); + // note the sealer to reset after viewchange + m_config->notifyResetSealing(); + auto const& prePrepareList = _newViewReq->prePrepareList(); + auto maxProposalIndex = m_config->committedProposal()->index(); + auto self = weak_from_this(); + for (const auto& prePrepare : prePrepareList) + { + // empty block proposal + if (prePrepare->consensusProposal()->data().size() > 0) + { + PBFT_LOG(INFO) << LOG_DESC("reHandlePrePrepareProposals: hit the proposal") + << printPBFTMsgInfo(prePrepare) << m_config->printCurrentState(); + handlePrePrepareMsg(prePrepare, true, true, false); + continue; + } + if (prePrepare->index() > maxProposalIndex) + { + maxProposalIndex = prePrepare->index(); + } + // hit the cache + if (m_cacheProcessor->tryToFillProposal(prePrepare)) + { + PBFT_LOG(INFO) << LOG_DESC( + "reHandlePrePrepareProposals: hit the cache, " + "into prepare phase directly") + << printPBFTMsgInfo(prePrepare) << m_config->printCurrentState(); + handlePrePrepareMsg(prePrepare, true, true, false); + continue; + } + // miss the cache, request to from node + auto from = m_config->getConsensusNodeByIndex(prePrepare->generatedFrom()); + m_logSync->requestPrecommitData( + from->nodeID(), prePrepare, [self](PBFTMessageInterface::Ptr _prePrepare) { + auto engine = self.lock(); + if (!engine) + { + return; + } + PBFT_LOG(INFO) << LOG_DESC( + "reHandlePrePrepareProposals: get the " + "missed proposal and handle now") + << printPBFTMsgInfo(_prePrepare) + << engine->m_config->printCurrentState(); + RecursiveGuard l(engine->m_mutex); + engine->handlePrePrepareMsg(_prePrepare, true, true, false); + }); + } + if (!prePrepareList.empty()) + { + // Note: in case of the reHandled proposals have system transactions, must + // wait to reseal until all reHandled proposal committed + m_config->setWaitResealUntil(maxProposalIndex); + PBFT_LOG(INFO) << LOG_DESC("reHandlePrePrepareProposals and wait to reseal new proposal") + << LOG_KV("waitResealUntil", maxProposalIndex) + << m_config->printCurrentState(); + } + else + { + m_config->reNotifySealer(maxProposalIndex + 1); + } +} + +void PBFTEngine::finalizeConsensus(LedgerConfig::Ptr _ledgerConfig, bool _syncedBlock) +{ + RecursiveGuard l(m_mutex); + // resetConfig after submit the block to ledger + m_config->resetConfig(_ledgerConfig, _syncedBlock); + m_cacheProcessor->checkAndCommitStableCheckPoint(); + m_cacheProcessor->tryToApplyCommitQueue(); + // tried to commit the stable checkpoint + m_cacheProcessor->removeConsensusedCache(m_config->view(), _ledgerConfig->blockNumber()); + m_cacheProcessor->tryToCommitStableCheckPoint(); + // Note: only the consensus-triggered finalize should resetTimer + // resetTimer will try to trigger-fast-viewchange when the leader disconnected or + // falling-far-behind + if (!_syncedBlock) + { + m_cacheProcessor->resetTimer(); + } +} + +bool PBFTEngine::handleCheckPointMsg(std::shared_ptr _checkPointMsg) +{ + // check index + if (_checkPointMsg->index() <= m_config->committedProposal()->index()) + { + PBFT_LOG(TRACE) << LOG_DESC("handleCheckPointMsg: Invalid expired checkpoint msg") + << LOG_KV("committedIndex", m_config->committedProposal()->index()) + << LOG_KV("recvIndex", _checkPointMsg->index()) + << LOG_KV("hash", _checkPointMsg->hash().abridged()) + << m_config->printCurrentState(); + return false; + } + if (isSyncingHigher()) + { + PBFT_LOG(INFO) << LOG_DESC( + "handleCheckPointMsg: reject the checkPoint for the node is " + "syncing higher block") + << LOG_KV("committedIndex", m_config->committedProposal()->index()) + << LOG_KV("recvIndex", _checkPointMsg->index()) + << LOG_KV("hash", _checkPointMsg->hash().abridged()) + << LOG_KV("syncingNum", m_config->syncingHighestNumber()) + << m_config->printCurrentState(); + return false; + } + // check signature + auto result = checkSignature(_checkPointMsg); + if (result == CheckResult::INVALID) + { + PBFT_LOG(WARNING) << LOG_DESC("handleCheckPointMsg: invalid signature") + << printPBFTMsgInfo(_checkPointMsg); + return false; + } + // check the proposal signature + if (!checkProposalSignature( + _checkPointMsg->generatedFrom(), _checkPointMsg->consensusProposal())) + { + PBFT_LOG(WARNING) << LOG_DESC("handleCheckPointMsg: invalid proposal signature") + << printPBFTMsgInfo(_checkPointMsg); + return false; + } + PBFT_LOG(INFO) << LOG_DESC( + "handleCheckPointMsg: try to add the checkpoint " + "message into the cache") + << printPBFTMsgInfo(_checkPointMsg) << m_config->printCurrentState(); + m_cacheProcessor->addCheckPointMsg(_checkPointMsg); + m_cacheProcessor->tryToApplyCommitQueue(); + m_cacheProcessor->checkAndCommitStableCheckPoint(); + if (m_cacheProcessor->shouldRequestCheckPoint(_checkPointMsg)) + { + PBFT_LOG(INFO) << LOG_DESC("request checkPoint proposal") + << LOG_KV("checkPointIndex", _checkPointMsg->index()) + << LOG_KV("checkPointHash", _checkPointMsg->hash().abridged()) + << m_config->printCurrentState(); + m_logSync->requestCommittedProposals(_checkPointMsg->from(), _checkPointMsg->index(), 1); + } + return true; +} + +void PBFTEngine::handleRecoverResponse(PBFTMessageInterface::Ptr _recoverResponse) +{ + if (checkSignature(_recoverResponse) == CheckResult::INVALID) + { + return; + } + m_cacheProcessor->addRecoverReqCache(_recoverResponse); + m_cacheProcessor->checkAndTryToRecover(); +} + +void PBFTEngine::handleRecoverRequest(PBFTMessageInterface::Ptr _request) +{ + if (checkSignature(_request) == CheckResult::INVALID) + { + return; + } + sendRecoverResponse(_request->from()); + PBFT_LOG(INFO) << LOG_DESC("handleRecoverRequest and response current state") + << LOG_KV("peer", _request->from()->shortHex()) << m_config->printCurrentState(); +} + +void PBFTEngine::sendCommittedProposalResponse( + PBFTProposalList const& _proposalList, SendResponseCallback _sendResponse) +{ + auto pbftMessage = m_config->pbftMessageFactory()->createPBFTMsg(); + pbftMessage->setPacketType(PacketType::CommittedProposalResponse); + pbftMessage->setProposals(_proposalList); + auto encodedData = m_config->codec()->encode(pbftMessage); + _sendResponse(ref(*encodedData)); +} + +void PBFTEngine::onReceiveCommittedProposalRequest( + PBFTBaseMessageInterface::Ptr _pbftMsg, SendResponseCallback _sendResponse) +{ + RecursiveGuard l(m_mutex); + auto pbftRequest = std::dynamic_pointer_cast(_pbftMsg); + PBFT_LOG(INFO) << LOG_DESC("Receive CommittedProposalRequest") + << LOG_KV("fromIndex", pbftRequest->index()) + << LOG_KV("size", pbftRequest->size()); + // hit the local cache + auto proposal = m_cacheProcessor->fetchPrecommitProposal(pbftRequest->index()); + if (pbftRequest->size() == 1 && proposal) + { + PBFTProposalList proposalList; + proposalList.emplace_back(proposal); + sendCommittedProposalResponse(proposalList, _sendResponse); + return; + } + auto self = weak_from_this(); + m_config->storage()->asyncGetCommittedProposals(pbftRequest->index(), pbftRequest->size(), + [self, pbftRequest, _sendResponse](PBFTProposalListPtr _proposalList) { + auto engine = self.lock(); + if (!engine) + { + return; + } + // empty case + if (!_proposalList || _proposalList->size() == 0) + { + PBFT_LOG(DEBUG) << LOG_DESC( + "onReceiveCommittedProposalRequest: miss " + "the expected proposal") + << LOG_KV("fromIndex", pbftRequest->index()) + << LOG_KV("size", pbftRequest->size()); + _sendResponse(bytesConstRef()); + return; + } + // hit case + engine->sendCommittedProposalResponse(*_proposalList, _sendResponse); + }); +} + +void PBFTEngine::onReceivePrecommitRequest( + std::shared_ptr _pbftMessage, SendResponseCallback _sendResponse) +{ + RecursiveGuard l(m_mutex); + // receive the precommitted proposals request message + auto pbftRequest = std::dynamic_pointer_cast(_pbftMessage); + // get the local precommitData + auto precommitMsg = + m_cacheProcessor->fetchPrecommitData(pbftRequest->index(), pbftRequest->hash()); + if (!precommitMsg) + { + PBFT_LOG(INFO) << LOG_DESC("onReceivePrecommitRequest: miss the requested precommit") + << LOG_KV("hash", pbftRequest->hash().abridged()) + << LOG_KV("index", pbftRequest->index()); + return; + } + precommitMsg->setPacketType(PacketType::PreparedProposalResponse); + auto encodedData = m_config->codec()->encode(precommitMsg); + // response the precommitData + _sendResponse(ref(*encodedData)); + PBFT_LOG(INFO) << LOG_DESC("Receive precommitRequest and send response") + << LOG_KV("hash", pbftRequest->hash().abridged()) + << LOG_KV("index", pbftRequest->index()); +} + +void PBFTEngine::onStableCheckPointCommitFailed( + Error::Ptr&& _error, PBFTProposalInterface::Ptr _stableProposal) +{ + if (!m_config->asMasterNode()) + { + PBFT_LOG(WARNING) << LOG_DESC( + "onStableCheckPointCommitFailed: but do nothing for the node-self " + "is not a master node") + << printPBFTProposal(_stableProposal) << m_config->printCurrentState(); + return; + } + + if (_stableProposal->index() <= m_config->committedProposal()->index()) + { + PBFT_LOG(INFO) << LOG_DESC( + "onStableCheckPointCommitFailed: drop the expired stable proposal") + << m_config->printCurrentState() << printPBFTProposal(_stableProposal); + return; + } + if (_error->errorCode() == bcos::scheduler::SchedulerError::BlockIsCommitting) + { + PBFT_LOG(WARNING) << LOG_DESC( + "onStableCheckPointCommitFailed for BlockIsCommitting: " + "retry to commit again") + << m_config->printCurrentState() << printPBFTProposal(_stableProposal); + // retry after 20ms + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + RecursiveGuard l(m_mutex); + m_cacheProcessor->updateStableCheckPointQueue(_stableProposal); + return; + } + if (_error->errorCode() == bcos::scheduler::SchedulerError::InvalidBlocks) + { + PBFT_LOG(WARNING) << LOG_DESC( + "onStableCheckPointCommitFailed for InvalidBlocks: " + "fetchAndUpdateLedgerConfig") + << m_config->printCurrentState() << printPBFTProposal(_stableProposal); + fetchAndUpdateLedgerConfig(); + return; + } + clearExceptionProposalState(_stableProposal->index()); +} + +void PBFTEngine::recoverState() +{ + // Note: only replay the PBFT state when all-modules ready + PBFT_LOG(INFO) << LOG_DESC("fetch PBFT state"); + auto stateProposals = m_config->storage()->loadState(m_config->committedProposal()->index()); + if (stateProposals && stateProposals->size() > 0) + { + initState(*stateProposals, m_config->keyPair()->publicKey()); + auto lowWaterMarkIndex = stateProposals->size() - 1; + auto lowWaterMark = ((*stateProposals)[lowWaterMarkIndex])->index(); + m_config->setLowWaterMark(lowWaterMark + 1); + PBFT_LOG(INFO) << LOG_DESC("recoverState") + << LOG_KV("stateProposals", stateProposals->size()) + << LOG_KV("lowWaterMark", lowWaterMark) + << LOG_KV("highWaterMark", m_config->highWaterMark()); + } + m_config->timer()->start(); +} + +void PBFTEngine::clearExceptionProposalState(bcos::protocol::BlockNumber _number) +{ + try + { + if (!m_config->asMasterNode()) + { + PBFT_LOG(INFO) << LOG_DESC( + "clearExceptionProposalState return directly for the node is the backup node"); + return; + } + RecursiveGuard l(m_mutex); + if (!m_config->committedProposal()) + { + PBFT_LOG(WARNING) << LOG_DESC( + "clearExceptionProposalState return directly for the pbft module has not been " + "inited"); + return; + } + // update the ledgerConfig when switch + fetchAndUpdateLedgerConfig(); + m_config->timer()->restart(); + m_cacheProcessor->resetUnCommittedCacheState(_number); + m_config->setExpectedCheckPoint(_number); + m_cacheProcessor->checkAndPreCommit(); + m_cacheProcessor->checkAndCommit(); + m_cacheProcessor->tryToApplyCommitQueue(); + recoverState(); + } + catch (std::exception const& e) + { + PBFT_LOG(WARNING) << LOG_DESC("clearExceptionProposalState exception") + << LOG_KV("number", _number) + << LOG_KV("msg", boost::diagnostic_information(e)); + } +} + +void PBFTEngine::fetchAndUpdateLedgerConfig() +{ + PBFT_LOG(INFO) << LOG_DESC("fetchAndUpdateLedgerConfig"); + m_ledgerFetcher->fetchBlockNumberAndHash(); + m_ledgerFetcher->fetchConsensusNodeList(); + // Note: must fetchObserverNode here to notify the latest sealerList and observerList to + // txpool + m_ledgerFetcher->fetchObserverNodeList(); + m_ledgerFetcher->fetchBlockTxCountLimit(); + m_ledgerFetcher->fetchConsensusLeaderPeriod(); + m_ledgerFetcher->fetchCompatibilityVersion(); + auto ledgerConfig = m_ledgerFetcher->ledgerConfig(); + PBFT_LOG(INFO) << LOG_DESC("fetchAndUpdateLedgerConfig success") + << LOG_KV("blockNumber", ledgerConfig->blockNumber()) + << LOG_KV("hash", ledgerConfig->hash().abridged()) + << LOG_KV("maxTxsPerBlock", ledgerConfig->blockTxCountLimit()) + << LOG_KV("consensusNodeList", ledgerConfig->consensusNodeList().size()); + m_config->resetConfig(ledgerConfig); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTEngine.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTEngine.h" new file mode 100644 index 00000000..7639c22a --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTEngine.h" @@ -0,0 +1,245 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for PBFTEngine + * @file PBFTEngine.h + * @author: yujiechen + * @date 2021-04-20 + */ +#pragma once +#include "PBFTLogSync.h" +#include "bcos-pbft/core/ConsensusEngine.h" +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace ledger +{ +class LedgerConfig; +} +namespace consensus +{ +class PBFTBaseMessageInterface; +class PBFTMessageInterface; +class ViewChangeMsgInterface; +class NewViewMsgInterface; +class PBFTConfig; +class PBFTCacheProcessor; +class PBFTProposalInterface; + +using PBFTMsgQueue = ConcurrentQueue>; +using PBFTMsgQueuePtr = std::shared_ptr; + +enum CheckResult +{ + VALID = 0, + INVALID = 1, +}; + +class PBFTEngine : public ConsensusEngine, public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + using SendResponseCallback = std::function; + explicit PBFTEngine(std::shared_ptr _config); + ~PBFTEngine() override { stop(); } + + void start() override; + void stop() override; + + virtual void asyncSubmitProposal(bool _containSysTxs, bytesConstRef _proposalData, + bcos::protocol::BlockNumber _proposalIndex, bcos::crypto::HashType const& _proposalHash, + std::function _onProposalSubmitted); + + std::shared_ptr pbftConfig() { return m_config; } + + // Receive PBFT message package from frontService + virtual void onReceivePBFTMessage(bcos::Error::Ptr _error, std::string const& _id, + bcos::crypto::NodeIDPtr _nodeID, bytesConstRef _data); + + virtual void initState(PBFTProposalList const& _proposals, bcos::crypto::NodeIDPtr _fromNode) + { + m_cacheProcessor->initState(_proposals, std::move(_fromNode)); + } + + virtual void asyncNotifyNewBlock( + bcos::ledger::LedgerConfig::Ptr _ledgerConfig, std::function _onRecv); + + virtual std::shared_ptr cacheProcessor() { return m_cacheProcessor; } + virtual bool isTimeout() { return m_config->timeout(); } + void registerCommittedProposalNotifier( + std::function)> + _committedProposalNotifier) + { + m_cacheProcessor->registerCommittedProposalNotifier(_committedProposalNotifier); + } + + virtual void restart(); + + virtual void clearExceptionProposalState(bcos::protocol::BlockNumber _number); + + void clearAllCache(); + void recoverState(); + + void fetchAndUpdateLedgerConfig(); + void setLedgerFetcher(bcos::tool::LedgerConfigFetcher::Ptr _ledgerFetcher) + { + m_ledgerFetcher = std::move(_ledgerFetcher); + } + +protected: + virtual void initSendResponseHandler(); + virtual void tryToResendCheckPoint(); + virtual void onReceivePBFTMessage(bcos::Error::Ptr _error, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, SendResponseCallback _sendResponse); + + virtual void onRecvProposal(bool _containSysTxs, bytesConstRef _proposalData, + bcos::protocol::BlockNumber _proposalIndex, bcos::crypto::HashType const& _proposalHash); + + // PBFT main processing function + void executeWorker() override; + + // General entry for message processing + virtual void handleMsg(std::shared_ptr _msg); + + // Process Pre-prepare type message packets + virtual bool handlePrePrepareMsg(std::shared_ptr _prePrepareMsg, + bool _needVerifyProposal, bool _generatedFromNewView = false, + bool _needCheckSignature = true); + // When handlePrePrepareMsg return false, then reset sealed txs + virtual void resetSealedTxs(std::shared_ptr _prePrepareMsg); + + // To check pre-prepare msg valid + virtual CheckResult checkPrePrepareMsg(std::shared_ptr _prePrepareMsg); + // To check pbft msg sign valid + virtual CheckResult checkSignature(std::shared_ptr _req); + virtual bool checkProposalSignature( + IndexType _generatedFrom, PBFTProposalInterface::Ptr _proposal); + + virtual CheckResult checkPBFTMsgState(std::shared_ptr _pbftReq) const; + + // When pre-prepare proposal seems ok, then broadcast prepare msg + virtual void broadcastPrepareMsg(std::shared_ptr _prePrepareMsg); + + // Process the Prepare type message packet + virtual bool handlePrepareMsg(std::shared_ptr _prepareMsg); + // To check 'Prepare' or 'Commit' type proposal + virtual CheckResult checkPBFTMsg(std::shared_ptr _prepareMsg); + + virtual bool handleCommitMsg(std::shared_ptr _commitMsg); + + virtual void onTimeout(); + virtual ViewChangeMsgInterface::Ptr generateViewChange(); + virtual void broadcastViewChangeReq(); + virtual void sendViewChange(bcos::crypto::NodeIDPtr _dstNode); + + virtual bool handleViewChangeMsg(std::shared_ptr _viewChangeMsg); + virtual bool isValidViewChangeMsg(bcos::crypto::NodeIDPtr _fromNode, + std::shared_ptr _viewChangeMsg, bool _shouldCheckSig = true); + + virtual bool handleNewViewMsg(std::shared_ptr _newViewMsg); + virtual void reHandlePrePrepareProposals(std::shared_ptr _newViewMsg); + virtual bool isValidNewViewMsg(std::shared_ptr _newViewMsg); + virtual void reachNewView(ViewType _view); + + // handle the checkpoint message + virtual bool handleCheckPointMsg(std::shared_ptr _checkPointMsg); + + // function called after reaching a consensus + virtual void finalizeConsensus( + std::shared_ptr _ledgerConfig, bool _syncedBlock = false); + + + virtual void onProposalApplied(int64_t _errorCode, PBFTProposalInterface::Ptr _proposal, + PBFTProposalInterface::Ptr _executedProposal); + virtual void onProposalApplySuccess( + PBFTProposalInterface::Ptr _proposal, PBFTProposalInterface::Ptr _executedProposal); + virtual void onProposalApplyFailed(int64_t _errorCode, PBFTProposalInterface::Ptr _proposal); + virtual void onLoadAndVerifyProposalFinish( + bool _verifyResult, Error::Ptr _error, PBFTProposalInterface::Ptr _proposal); + virtual void triggerTimeout(bool _incTimeout = true); + + void handleRecoverResponse(PBFTMessageInterface::Ptr _recoverResponse); + void handleRecoverRequest(PBFTMessageInterface::Ptr _request); + void sendRecoverResponse(bcos::crypto::NodeIDPtr _dstNode); + bool isSyncingHigher(); + /** + * @brief Receive proposal requests from other nodes and reply to corresponding proposals + * + * @param _pbftMsg the proposal request + * @param _sendResponse callback used to send the requested-proposals back to the node + */ + virtual void onReceiveCommittedProposalRequest( + PBFTBaseMessageInterface::Ptr _pbftMsg, SendResponseCallback _sendResponse); + + /** + * @brief Receive precommit requests from other nodes and reply to the corresponding precommit + * data + * + * @param _pbftMessage the precommit request + * @param _sendResponse callback used to send the requested-proposals back to the node + */ + virtual void onReceivePrecommitRequest( + std::shared_ptr _pbftMessage, SendResponseCallback _sendResponse); + void sendCommittedProposalResponse( + PBFTProposalList const& _proposalList, SendResponseCallback _sendResponse); + + virtual void onStableCheckPointCommitFailed( + Error::Ptr&& _error, PBFTProposalInterface::Ptr _stableProposal); + +private: + // utility functions + void waitSignal() + { + boost::unique_lock l(x_signalled); + m_signalled.wait_for(l, boost::chrono::milliseconds(5)); + } + +protected: + // PBFT configuration class + // mainly maintains the node information, consensus configuration information + // such as consensus node list, consensus weight, etc. + std::shared_ptr m_config; + ThreadPool::Ptr m_worker; + + // PBFT message cache queue + PBFTMsgQueuePtr m_msgQueue; + std::shared_ptr m_cacheProcessor; + // for log syncing + PBFTLogSync::Ptr m_logSync; + + std::function + m_sendResponseHandler; + + boost::condition_variable m_signalled; + boost::mutex x_signalled; + mutable RecursiveMutex m_mutex; + + const unsigned c_PopWaitSeconds = 5; + const std::set c_consensusPacket = {PrePreparePacket, PreparePacket, CommitPacket}; + + std::atomic_bool m_stopped = {false}; + bcos::tool::LedgerConfigFetcher::Ptr m_ledgerFetcher; + + // the timer used to resend checkPointProposal + std::shared_ptr m_timer; +}; +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTLogSync.cpp" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTLogSync.cpp" new file mode 100644 index 00000000..9f79f302 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTLogSync.cpp" @@ -0,0 +1,189 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for PBFT log syncing + * @file PBFTLogSync.cpp + * @author: yujiechen + * @date 2021-04-28 + */ +#include "PBFTLogSync.h" +#include +#include + +using namespace bcos; +using namespace bcos::front; +using namespace bcos::protocol; +using namespace bcos::crypto; +using namespace bcos::consensus; + +PBFTLogSync::PBFTLogSync(PBFTConfig::Ptr _config, PBFTCacheProcessor::Ptr _pbftCache) + : m_config(std::move(_config)), + m_pbftCache(std::move(_pbftCache)), + m_requestThread(std::make_shared("pbftLogSync", 1)) +{} + +void PBFTLogSync::requestCommittedProposals( + PublicPtr _from, BlockNumber _startIndex, size_t _offset) +{ + auto pbftRequest = m_config->pbftMessageFactory()->populateFrom( + PacketType::CommittedProposalRequest, _startIndex, _offset); + auto self = weak_from_this(); + requestPBFTData(std::move(_from), pbftRequest, + [self, _startIndex, _offset](Error::Ptr _error, NodeIDPtr _nodeID, bytesConstRef _data, + std::string const&, SendResponseCallback _sendResponse) { + auto logSync = self.lock(); + if (!logSync) + { + return; + } + return logSync->onRecvCommittedProposalsResponse(std::move(_error), std::move(_nodeID), + _data, _startIndex, _offset, std::move(_sendResponse)); + }); +} + +void PBFTLogSync::requestPrecommitData(bcos::crypto::PublicPtr _from, + PBFTMessageInterface::Ptr _prePrepareMsg, HandlePrePrepareCallback _prePrepareCallback) +{ + auto pbftRequest = m_config->pbftMessageFactory()->populateFrom( + PacketType::PreparedProposalRequest, _prePrepareMsg->index(), _prePrepareMsg->hash()); + PBFT_LOG(INFO) << LOG_DESC("request the missed precommit proposal") + << LOG_KV("index", _prePrepareMsg->index()) + << LOG_KV("hash", _prePrepareMsg->hash().abridged()); + auto self = weak_from_this(); + requestPBFTData(std::move(_from), pbftRequest, + [self, _prePrepareMsg = std::move(_prePrepareMsg), + _prePrepareCallback = std::move(_prePrepareCallback)](Error::Ptr _error, + NodeIDPtr _nodeID, bytesConstRef _data, std::string const&, + SendResponseCallback _sendResponse) { + auto logSync = self.lock(); + if (!logSync) + { + return; + } + return logSync->onRecvPrecommitResponse(std::move(_error), std::move(_nodeID), _data, + _prePrepareMsg, _prePrepareCallback, std::move(_sendResponse)); + }); +} + +void PBFTLogSync::requestPBFTData( + PublicPtr _from, PBFTRequestInterface::Ptr _pbftRequest, CallbackFunc _callback) +{ + auto self = weak_from_this(); + m_requestThread->enqueue([self, _from, _pbftRequest, _callback]() { + try + { + auto pbftLogSync = self.lock(); + if (!pbftLogSync) + { + return; + } + auto config = pbftLogSync->m_config; + // encode + auto encodedData = + config->codec()->encode(_pbftRequest, config->pbftMsgDefaultVersion()); + // send the request + config->frontService()->asyncSendMessageByNodeID(ModuleID::PBFT, _from, + ref(*encodedData), config->networkTimeoutInterval(), _callback); + PBFT_LOG(INFO) << LOG_DESC("request the missed precommit proposal") + << LOG_KV("peer", _from->shortHex()) + << LOG_KV("index", _pbftRequest->index()) + << LOG_KV("hash", _pbftRequest->hash().abridged()); + } + catch (std::exception const& e) + { + PBFT_LOG(WARNING) << LOG_DESC("requestCommittedProposals exception") + << LOG_KV("to", _from->shortHex()) + << LOG_KV("startIndex", _pbftRequest->index()) + << LOG_KV("offset", _pbftRequest->size()) + << LOG_KV("hash", _pbftRequest->hash().abridged()) + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +void PBFTLogSync::onRecvCommittedProposalsResponse(Error::Ptr _error, NodeIDPtr _nodeID, + bytesConstRef _data, bcos::protocol::BlockNumber _startIndex, size_t _offset, + SendResponseCallback) +{ + if (_error) + { + PBFT_LOG(WARNING) << LOG_DESC("onRecvCommittedProposalResponse error") + << LOG_KV("from", _nodeID->shortHex()) + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMsg", _error->errorMessage()); + for (size_t i = 0; i < _offset; i++) + { + m_pbftCache->eraseCommittedProposalList(_startIndex + i); + } + return; + } + if (_data.size() == 0) + { + return; + } + auto response = m_config->codec()->decode(_data); + if (response->packetType() != PacketType::CommittedProposalResponse) + { + return; + } + auto proposalResponse = std::dynamic_pointer_cast(response); + // TODO: check the proposal to ensure security + // load the fetched checkpoint proposal into the cache + auto proposals = proposalResponse->proposals(); + m_pbftCache->initState(proposals, _nodeID); + PBFT_LOG(INFO) << LOG_DESC("onRecvCommittedProposalsResponse") + << LOG_KV("from", _nodeID->shortHex()) + << LOG_KV("proposalSize", proposals.size()); +} + +void PBFTLogSync::onRecvPrecommitResponse(Error::Ptr _error, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, PBFTMessageInterface::Ptr _prePrepareMsg, + HandlePrePrepareCallback _prePrepareCallback, SendResponseCallback) +{ + if (_error != nullptr) + { + PBFT_LOG(WARNING) << LOG_DESC("onRecvPrecommitResponse error") + << LOG_KV("from", _nodeID->shortHex()) + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMsg", _error->errorMessage()); + } + auto response = m_config->codec()->decode(_data); + if (response->packetType() != PacketType::PreparedProposalResponse) + { + return; + } + PBFT_LOG(INFO) << LOG_DESC("onRecvPrecommitResponse") << printPBFTMsgInfo(response); + auto pbftMessage = std::dynamic_pointer_cast(response); + assert(pbftMessage->preparedProposals().size() == 1); + auto precommitMsg = (pbftMessage->preparedProposals())[0]; + if (!precommitMsg->consensusProposal()) + { + return; + } + if (precommitMsg->consensusProposal()->index() != + _prePrepareMsg->consensusProposal()->index() || + precommitMsg->consensusProposal()->hash() != _prePrepareMsg->consensusProposal()->hash()) + { + return; + } + if (!m_pbftCache->checkPrecommitMsg(precommitMsg)) + { + PBFT_LOG(WARNING) << LOG_DESC("Recv invalid precommit response") + << printPBFTMsgInfo(precommitMsg); + return; + } + _prePrepareMsg->consensusProposal()->setData(precommitMsg->consensusProposal()->data()); + _prePrepareCallback(_prePrepareMsg); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTLogSync.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTLogSync.h" new file mode 100644 index 00000000..2bbacd7b --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTLogSync.h" @@ -0,0 +1,76 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for PBFT log syncing + * @file PBFTLogSync.h + * @author: yujiechen + * @date 2021-04-28 + */ +#pragma once +#include "../cache/PBFTCacheProcessor.h" +#include "../config/PBFTConfig.h" +#include +#include +namespace bcos::consensus +{ +class PBFTLogSync : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + PBFTLogSync(PBFTConfig::Ptr _config, PBFTCacheProcessor::Ptr _pbftCache); + virtual ~PBFTLogSync() = default; + using SendResponseCallback = std::function; + using HandlePrePrepareCallback = std::function; + /** + * @brief batch request committed proposals to the given node + * + * @param _from the node that maintains the requested proposals + * @param _startIndex the start index of the request + * @param _offset the requested proposal size + */ + virtual void requestCommittedProposals( + bcos::crypto::PublicPtr _from, bcos::protocol::BlockNumber _startIndex, size_t _offset); + + /** + * @brief request precommit data from the given node + * + * @param _from the node that maintain the precommit data + * @param _index the index of the requested precommit data + * @param _hash the hash of the requested precommit data + */ + virtual void requestPrecommitData(bcos::crypto::PublicPtr _from, + PBFTMessageInterface::Ptr _prePrepareMsg, HandlePrePrepareCallback _prePrepareCallback); + + virtual void stop() { m_requestThread->stop(); } + +protected: + virtual void onRecvCommittedProposalsResponse(bcos::Error::Ptr _error, + bcos::crypto::NodeIDPtr _nodeID, bytesConstRef _data, + bcos::protocol::BlockNumber _startIndex, size_t _offset, + SendResponseCallback _sendResponse); + + virtual void onRecvPrecommitResponse(bcos::Error::Ptr _error, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, PBFTMessageInterface::Ptr _prePrepareMsg, + HandlePrePrepareCallback _prePrepareCallback, SendResponseCallback _sendResponse); + + void requestPBFTData(bcos::crypto::PublicPtr _from, PBFTRequestInterface::Ptr _pbftRequest, + bcos::front::CallbackFunc _callback); + +private: + PBFTConfig::Ptr m_config; + PBFTCacheProcessor::Ptr m_pbftCache; + std::shared_ptr m_requestThread; +}; +} // namespace bcos::consensus \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTTimer.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTTimer.h" new file mode 100644 index 00000000..60be2216 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/PBFTTimer.h" @@ -0,0 +1,80 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for PBFTTimer + * @file PBFTTimer.h + * @author: yujiechen + * @date 2021-04-26 + */ +#pragma once +#include +namespace bcos::consensus +{ +class PBFTTimer : public Timer +{ +public: + using Ptr = std::shared_ptr; + explicit PBFTTimer(uint64_t _timeout, std::string const& _name = "pbftTimer") + : Timer(_timeout, _name) + { + updateAdjustedTimeout(); + } + + ~PBFTTimer() override = default; + + void updateChangeCycle(uint64_t _changeCycle) + { + m_changeCycle.store(std::min(_changeCycle, c_maxChangeCycle)); + updateAdjustedTimeout(); + } + void incChangeCycle(uint64_t _increasedValue) + { + updateChangeCycle(m_changeCycle.load() + _increasedValue); + } + void resetChangeCycle() { updateChangeCycle(0); } + uint64_t changeCycle() const { return m_changeCycle; } + + void reset(uint64_t _timeout) override + { + m_timeout = _timeout; + updateAdjustedTimeout(); + } + +protected: + // ensure that this period of time increases exponentially until some requested operation + // executes + void updateAdjustedTimeout() + { + auto changeCycle = std::min(m_changeCycle.load(), c_maxChangeCycle); + uint64_t timeout = m_timeout.load() * std::pow(m_base, changeCycle); + if (timeout == m_adjustedTimeout) + { + return; + } + m_adjustedTimeout.store(timeout); + if (running()) + { + restart(); + } + } + uint64_t adjustTimeout() override { return m_adjustedTimeout; } + +private: + std::atomic m_adjustedTimeout = {0}; + std::atomic m_changeCycle = {0}; + double const m_base = 1.5; + uint64_t c_maxChangeCycle = 10; +}; +} // namespace bcos::consensus \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/Validator.cpp" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/Validator.cpp" new file mode 100644 index 00000000..a5d92d26 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/Validator.cpp" @@ -0,0 +1,132 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Validator for the consensus module + * @file Validator.cpp + * @author: yujiechen + * @date 2021-04-21 + */ +#include "Validator.h" +using namespace bcos; +using namespace bcos::consensus; +using namespace bcos::crypto; +using namespace bcos::protocol; + +void TxsValidator::verifyProposal(bcos::crypto::PublicPtr _fromNode, + PBFTProposalInterface::Ptr _proposal, + std::function _verifyFinishedHandler) +{ + // TODO: check the sealerList here + auto block = m_blockFactory->createBlock(_proposal->data()); + auto blockHeader = block->blockHeader(); + if (blockHeader->number() != _proposal->index()) + { + if (_verifyFinishedHandler) + { + auto error = std::make_shared(-1, "Invalid proposal"); + _verifyFinishedHandler(error, false); + } + return; + } + m_txPool->asyncVerifyBlock(_fromNode, _proposal->data(), _verifyFinishedHandler); +} + +void TxsValidator::asyncResetTxsFlag(bytesConstRef _data, bool _flag, bool _emptyTxBatchHash) +{ + auto block = m_blockFactory->createBlock(_data); + auto blockHeader = block->blockHeader(); + if (_flag) + { + // already has the reset request + if (!insertResettingProposal(blockHeader->hash())) + { + return; + } + } + auto self = std::weak_ptr(shared_from_this()); + m_worker->enqueue([self, blockHeader, block, _flag, _emptyTxBatchHash]() { + try + { + auto validator = self.lock(); + if (!validator) + { + return; + } + + auto txsHash = std::make_shared(); + for (size_t i = 0; i < block->transactionsHashSize(); i++) + { + txsHash->emplace_back(block->transactionHash(i)); + } + if (txsHash->empty()) + { + return; + } + PBFT_LOG(INFO) << LOG_DESC("asyncResetTxsFlag") + << LOG_KV("index", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("flag", _flag); + validator->asyncResetTxsFlag(block, txsHash, _flag, _emptyTxBatchHash); + } + catch (std::exception const& e) + { + PBFT_LOG(WARNING) << LOG_DESC("asyncResetTxsFlag exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +void TxsValidator::asyncResetTxsFlag( + bcos::protocol::Block::Ptr _block, HashListPtr _txsHashList, bool _flag, bool _emptyTxBatchHash) +{ + auto blockHeader = _block->blockHeader(); + auto proposalNumber = blockHeader->number(); + auto proposalHash = blockHeader->hash(); + if (_emptyTxBatchHash) + { + proposalNumber = -1; + proposalHash = bcos::crypto::HashType(); + } + auto self = std::weak_ptr(shared_from_this()); + m_txPool->asyncMarkTxs(_txsHashList, _flag, proposalNumber, proposalHash, + [self, _block, blockHeader, _txsHashList, _flag, _emptyTxBatchHash](Error::Ptr _error) { + auto validator = self.lock(); + if (!validator) + { + return; + } + // must ensure asyncResetTxsFlag success before seal new next blocks + if (_flag) + { + validator->eraseResettingProposal(blockHeader->hash()); + } + if (_error == nullptr) + { + PBFT_LOG(INFO) << LOG_DESC("asyncMarkTxs success") + << LOG_KV("index", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("flag", _flag) + << LOG_KV("emptyTxBatchHash", _emptyTxBatchHash); + return; + } + PBFT_LOG(WARNING) << LOG_DESC("asyncMarkTxs failed") + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + if (_flag) + { + validator->insertResettingProposal(blockHeader->hash()); + } + }); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/Validator.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/Validator.h" new file mode 100644 index 00000000..2678d521 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/engine/Validator.h" @@ -0,0 +1,245 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Validator for the consensus module + * @file Validator.h + * @author: yujiechen + * @date 2021-04-21 + */ +#pragma once +#include "../interfaces/PBFTMessageFactory.h" +#include "../interfaces/PBFTProposalInterface.h" +#include "bcos-framework/txpool/TxPoolInterface.h" +#include +#include +#include + +#include + +namespace bcos::consensus +{ +class ValidatorInterface +{ +public: + using Ptr = std::shared_ptr; + ValidatorInterface() = default; + virtual ~ValidatorInterface() = default; + virtual void verifyProposal(bcos::crypto::PublicPtr _fromNode, + PBFTProposalInterface::Ptr _proposal, + std::function _verifyFinishedHandler) = 0; + + virtual void asyncResetTxsFlag( + bytesConstRef _data, bool _flag, bool _emptyTxBatchHash = false) = 0; + virtual PBFTProposalInterface::Ptr generateEmptyProposal(uint32_t _proposalVersion, + PBFTMessageFactory::Ptr _factory, int64_t _index, int64_t _sealerId) = 0; + + virtual void notifyTransactionsResult( + bcos::protocol::Block::Ptr _block, bcos::protocol::BlockHeader::Ptr _header) = 0; + virtual void updateValidatorConfig(bcos::consensus::ConsensusNodeList const& _consensusNodeList, + bcos::consensus::ConsensusNodeList const& _observerNodeList) = 0; + + virtual void stop() = 0; + virtual void init() = 0; + virtual void asyncResetTxPool() = 0; + virtual ssize_t resettingProposalSize() const = 0; + virtual void setVerifyCompletedHook(std::function) = 0; +}; + +class TxsValidator : public ValidatorInterface, public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + explicit TxsValidator(bcos::txpool::TxPoolInterface::Ptr _txPool, + bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::protocol::TransactionSubmitResultFactory::Ptr _txResultFactory) + : m_txPool(std::move(_txPool)), + m_blockFactory(std::move(_blockFactory)), + m_txResultFactory(std::move(_txResultFactory)), + m_worker(std::make_shared("validator", 2)) + {} + + ~TxsValidator() override = default; + + void stop() override { m_worker->stop(); } + + void init() override + { + PBFT_LOG(INFO) << LOG_DESC("asyncResetTxPool when startup"); + asyncResetTxPool(); + } + + void asyncResetTxPool() override + { + m_txPool->asyncResetTxPool([](Error::Ptr _error) { + if (_error) + { + PBFT_LOG(WARNING) << LOG_DESC("asyncResetTxPool failed") + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + } + }); + } + void verifyProposal(bcos::crypto::PublicPtr _fromNode, PBFTProposalInterface::Ptr _proposal, + std::function _verifyFinishedHandler) override; + + void asyncResetTxsFlag( + bytesConstRef _data, bool _flag, bool _emptyTxBatchHash = false) override; + ssize_t resettingProposalSize() const override + { + ReadGuard l(x_resettingProposals); + return m_resettingProposals.size(); + } + + PBFTProposalInterface::Ptr generateEmptyProposal(uint32_t _proposalVersion, + PBFTMessageFactory::Ptr _factory, int64_t _index, int64_t _sealerId) override + { + auto proposal = _factory->createPBFTProposal(); + proposal->setIndex(_index); + auto block = m_blockFactory->createBlock(); + auto blockHeader = m_blockFactory->blockHeaderFactory()->createBlockHeader(); + blockHeader->populateEmptyBlock(_index, _sealerId); + blockHeader->setVersion(_proposalVersion); + block->setBlockHeader(blockHeader); + auto encodedData = std::make_shared(); + block->encode(*encodedData); + proposal->setHash(blockHeader->hash()); + proposal->setData(std::move(*encodedData)); + return proposal; + } + + void notifyTransactionsResult( + bcos::protocol::Block::Ptr _block, bcos::protocol::BlockHeader::Ptr _header) override + { + auto results = std::make_shared(); + for (size_t i = 0; i < _block->transactionsHashSize(); i++) + { + auto txHash = _block->transactionHash(i); + auto txResult = m_txResultFactory->createTxSubmitResult(); + txResult->setBlockHash(_header->hash()); + txResult->setTxHash(txHash); + results->emplace_back(std::move(txResult)); + } + m_txPool->asyncNotifyBlockResult( + _header->number(), results, [_block, _header](Error::Ptr _error) { + if (_error == nullptr) + { + PBFT_LOG(INFO) << LOG_DESC("notify block result success") + << LOG_KV("number", _header->number()) + << LOG_KV("hash", _header->hash().abridged()) + << LOG_KV("txsSize", _block->transactionsHashSize()); + return; + } + PBFT_LOG(INFO) << LOG_DESC("notify block result failed") + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + }); + } + + void updateValidatorConfig(bcos::consensus::ConsensusNodeList const& _consensusNodeList, + bcos::consensus::ConsensusNodeList const& _observerNodeList) override + { + m_txPool->notifyConsensusNodeList(_consensusNodeList, [](Error::Ptr _error) { + if (_error == nullptr) + { + PBFT_LOG(DEBUG) << LOG_DESC("notify to update consensusNodeList config success"); + return; + } + PBFT_LOG(WARNING) << LOG_DESC("notify to update consensusNodeList config failed") + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + }); + + m_txPool->notifyObserverNodeList(_observerNodeList, [](Error::Ptr _error) { + if (_error == nullptr) + { + PBFT_LOG(DEBUG) << LOG_DESC("notify to update observerNodeList config success"); + return; + } + PBFT_LOG(WARNING) << LOG_DESC("notify to update observerNodeList config failed") + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + }); + } + + void setVerifyCompletedHook(std::function _hook) override + { + RecursiveGuard l(x_verifyCompletedHook); + m_verifyCompletedHook = _hook; + } + +protected: + virtual void eraseResettingProposal(bcos::crypto::HashType const& _hash) + { + { + WriteGuard l(x_resettingProposals); + m_resettingProposals.erase(_hash); + if (!m_resettingProposals.empty()) + { + return; + } + } + // When all consensusing proposals are notified, call verifyCompletedHook + triggerVerifyCompletedHook(); + } + + void triggerVerifyCompletedHook() + { + RecursiveGuard l(x_verifyCompletedHook); + if (!m_verifyCompletedHook) + { + return; + } + auto callback = m_verifyCompletedHook; + m_verifyCompletedHook = nullptr; + auto self = weak_from_this(); + m_worker->enqueue([self, callback]() { + auto validator = self.lock(); + if (!validator) + { + return; + } + if (!callback) + { + return; + } + callback(); + }); + } + virtual bool insertResettingProposal(bcos::crypto::HashType const& _hash) + { + UpgradableGuard l(x_resettingProposals); + if (m_resettingProposals.count(_hash)) + { + return false; + } + UpgradeGuard ul(l); + m_resettingProposals.insert(_hash); + return true; + } + + virtual void asyncResetTxsFlag(bcos::protocol::Block::Ptr _block, + bcos::crypto::HashListPtr _txsHashList, bool _flag, bool _emptyTxBatchHash); + + bcos::txpool::TxPoolInterface::Ptr m_txPool; + bcos::protocol::BlockFactory::Ptr m_blockFactory; + bcos::protocol::TransactionSubmitResultFactory::Ptr m_txResultFactory; + ThreadPool::Ptr m_worker; + std::set m_resettingProposals; + mutable SharedMutex x_resettingProposals; + + std::function m_verifyCompletedHook = nullptr; + mutable RecursiveMutex x_verifyCompletedHook; +}; +} // namespace bcos::consensus \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/NewViewMsgInterface.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/NewViewMsgInterface.h" new file mode 100644 index 00000000..968f6dc8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/NewViewMsgInterface.h" @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for NewViewMsg + * @file NewViewMsgInterface.h + * @author: yujiechen + * @date 2021-04-15 + */ +#pragma once +#include "PBFTMessageInterface.h" +#include "ViewChangeMsgInterface.h" +namespace bcos +{ +namespace consensus +{ +class NewViewMsgInterface : virtual public PBFTBaseMessageInterface +{ +public: + using Ptr = std::shared_ptr; + NewViewMsgInterface() = default; + virtual ~NewViewMsgInterface() {} + + virtual ViewChangeMsgList const& viewChangeMsgList() const = 0; + virtual void setViewChangeMsgList(ViewChangeMsgList const& _viewChangeMsgs) = 0; + + virtual PBFTMessageList const& prePrepareList() = 0; + virtual void setPrePrepareList(PBFTMessageList const& _prePrepareList) = 0; +}; +} // namespace consensus +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTBaseMessageInterface.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTBaseMessageInterface.h" new file mode 100644 index 00000000..141a6b81 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTBaseMessageInterface.h" @@ -0,0 +1,80 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for PBFTBaseMessage + * @file PBFTBaseMessageInterface.h + * @author: yujiechen + * @date 2021-04-13 + */ +#pragma once +#include "../utilities/Common.h" +#include +#include +#include +#include +#include +#include +namespace bcos::consensus +{ +class PBFTBaseMessageInterface +{ +public: + using Ptr = std::shared_ptr; + PBFTBaseMessageInterface() = default; + virtual ~PBFTBaseMessageInterface() = default; + + virtual int64_t timestamp() const = 0; + virtual int32_t version() const = 0; + virtual ViewType view() const = 0; + virtual IndexType generatedFrom() const = 0; + virtual int64_t index() const = 0; + virtual void setIndex(int64_t _proposalStartIndex) = 0; + + virtual bcos::crypto::HashType const& hash() const = 0; + virtual PacketType packetType() const = 0; + + virtual void setTimestamp(int64_t _timestamp) = 0; + virtual void setVersion(int32_t _version) = 0; + virtual void setView(ViewType _view) = 0; + virtual void setGeneratedFrom(IndexType _generatedFrom) = 0; + virtual void setHash(bcos::crypto::HashType const& _hash) = 0; + virtual void setPacketType(PacketType _packetType) = 0; + + virtual bytesPointer encode(bcos::crypto::CryptoSuite::Ptr _cryptoSuite, + bcos::crypto::KeyPairInterface::Ptr _keyPair) const = 0; + virtual void decode(bytesConstRef _data) = 0; + + virtual bytesConstRef signatureData() = 0; + virtual bcos::crypto::HashType const& signatureDataHash() = 0; + + virtual void setSignatureData(bytes&& _signatureData) = 0; + + virtual void setSignatureData(bytes const& _signatureData) = 0; + virtual void setSignatureDataHash(bcos::crypto::HashType const& _hash) = 0; + virtual bool verifySignature( + bcos::crypto::CryptoSuite::Ptr _cryptoSuite, bcos::crypto::PublicPtr _pubKey) = 0; + + virtual void setFrom(bcos::crypto::PublicPtr _from) = 0; + virtual bcos::crypto::PublicPtr from() const = 0; +}; +inline std::string printPBFTMsgInfo(PBFTBaseMessageInterface::Ptr _pbftMsg) +{ + std::ostringstream stringstream; + stringstream << LOG_KV("reqHash", _pbftMsg->hash().abridged()) + << LOG_KV("reqIndex", _pbftMsg->index()) << LOG_KV("reqV", _pbftMsg->view()) + << LOG_KV("fromIdx", _pbftMsg->generatedFrom()); + return stringstream.str(); +} +} // namespace bcos::consensus \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTCodecInterface.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTCodecInterface.h" new file mode 100644 index 00000000..c9df01a0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTCodecInterface.h" @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for PBFTCodec + * @file PBFTCodecInterface.h + * @author: yujiechen + * @date 2021-04-13 + */ +#pragma once +#include "PBFTBaseMessageInterface.h" +#include +#include +namespace bcos +{ +namespace consensus +{ +class PBFTCodecInterface +{ +public: + using Ptr = std::shared_ptr; + PBFTCodecInterface() = default; + virtual ~PBFTCodecInterface() {} + + virtual bytesPointer encode( + PBFTBaseMessageInterface::Ptr _pbftMessage, int32_t _version = 0) const = 0; + // Taking into account the situation of future blocks, verify the signature if and only when + // processing the message packet + virtual PBFTBaseMessageInterface::Ptr decode(bytesConstRef _data) const = 0; +}; +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTMessageFactory.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTMessageFactory.h" new file mode 100644 index 00000000..f6c72fe8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTMessageFactory.h" @@ -0,0 +1,144 @@ +/** + * Copyright (C) 2021 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. + * + * @brief factory for PBFTMessage + * @file PBFTMessageFactory.h + * @author: yujiechen + * @date 2021-04-20 + */ +#pragma once +#include "NewViewMsgInterface.h" +#include "PBFTMessageInterface.h" +#include "PBFTProposalInterface.h" +#include "PBFTRequestInterface.h" +#include "ViewChangeMsgInterface.h" +#include +#include +namespace bcos +{ +namespace consensus +{ +class PBFTMessageFactory +{ +public: + using Ptr = std::shared_ptr; + PBFTMessageFactory() = default; + virtual ~PBFTMessageFactory() {} + + virtual PBFTMessageInterface::Ptr createPBFTMsg() = 0; + virtual PBFTMessageInterface::Ptr createPBFTMsg( + bcos::crypto::CryptoSuite::Ptr _cryptoSuite, bytesConstRef _data) = 0; + + virtual ViewChangeMsgInterface::Ptr createViewChangeMsg() = 0; + virtual ViewChangeMsgInterface::Ptr createViewChangeMsg(bytesConstRef _data) = 0; + + virtual NewViewMsgInterface::Ptr createNewViewMsg() = 0; + virtual NewViewMsgInterface::Ptr createNewViewMsg(bytesConstRef _data) = 0; + virtual PBFTProposalInterface::Ptr createPBFTProposal() = 0; + virtual PBFTProposalInterface::Ptr createPBFTProposal(bytesConstRef _data) = 0; + + virtual PBFTRequestInterface::Ptr createPBFTRequest() = 0; + virtual PBFTRequestInterface::Ptr createPBFTRequest(bytesConstRef _data) = 0; + + virtual PBFTRequestInterface::Ptr populateFrom( + PacketType _packetType, bcos::protocol::BlockNumber _startIndex, int64_t _offset) + { + auto pbftRequest = createPBFTRequest(); + pbftRequest->setPacketType(_packetType); + pbftRequest->setIndex(_startIndex); + pbftRequest->setSize(_offset); + return pbftRequest; + } + + virtual PBFTRequestInterface::Ptr populateFrom(PacketType _packetType, + bcos::protocol::BlockNumber _index, bcos::crypto::HashType const& _hash) + { + auto pbftRequest = createPBFTRequest(); + pbftRequest->setPacketType(_packetType); + pbftRequest->setIndex(_index); + pbftRequest->setHash(_hash); + return pbftRequest; + } + + virtual PBFTMessageInterface::Ptr populateFrom(PacketType _packetType, int32_t _version, + ViewType _view, int64_t _timestamp, IndexType _generatedFrom, + PBFTProposalInterface::Ptr _proposal, bcos::crypto::CryptoSuite::Ptr _cryptoSuite, + bcos::crypto::KeyPairInterface::Ptr _keyPair, bool _needSign = true) + { + auto pbftMessage = createPBFTMsg(); + pbftMessage->setPacketType(_packetType); + pbftMessage->setVersion(_version); + pbftMessage->setView(_view); + pbftMessage->setTimestamp(_timestamp); + pbftMessage->setGeneratedFrom(_generatedFrom); + pbftMessage->setHash(_proposal->hash()); + pbftMessage->setIndex(_proposal->index()); + PBFTProposalList populatedProposalList; + // create the proposal + auto signedProposal = createPBFTProposal(); + signedProposal->setIndex(_proposal->index()); + signedProposal->setHash(_proposal->hash()); + signedProposal->setSealerId(_proposal->sealerId()); + if (_needSign) + { + auto signatureData = _cryptoSuite->signatureImpl()->sign(*_keyPair, _proposal->hash()); + signedProposal->setSignature(*signatureData); + } + pbftMessage->setConsensusProposal(signedProposal); + return pbftMessage; + } + + virtual PBFTMessageInterface::Ptr populateFrom(PacketType _packetType, + PBFTProposalInterface::Ptr _proposal, int32_t _version, ViewType _view, int64_t _timestamp, + IndexType _generatedFrom) + { + auto pbftMessage = createPBFTMsg(); + pbftMessage->setPacketType(_packetType); + pbftMessage->setVersion(_version); + pbftMessage->setView(_view); + pbftMessage->setTimestamp(_timestamp); + pbftMessage->setGeneratedFrom(_generatedFrom); + pbftMessage->setHash(_proposal->hash()); + pbftMessage->setIndex(_proposal->index()); + pbftMessage->setConsensusProposal(_proposal); + return pbftMessage; + } + + virtual PBFTProposalInterface::Ptr populateFrom( + PBFTProposalInterface::Ptr _proposal, bool _withData = true, bool _withProof = true) + { + auto proposal = createPBFTProposal(); + proposal->setIndex(_proposal->index()); + proposal->setHash(_proposal->hash()); + proposal->setSealerId(_proposal->sealerId()); + if (_withData) + { + proposal->setData(_proposal->data()); + } + // set the signature proof + if (_withProof) + { + auto signatureSize = _proposal->signatureProofSize(); + for (size_t i = 0; i < signatureSize; i++) + { + auto proof = _proposal->signatureProof(i); + proposal->appendSignatureProof(proof.first, proof.second); + } + } + return proposal; + } +}; +} // namespace consensus +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTMessageInterface.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTMessageInterface.h" new file mode 100644 index 00000000..8714f8de --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTMessageInterface.h" @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for PBFT Message + * @file PBFTMessageInterface.h + * @author: yujiechen + * @date 2021-04-13 + */ +#pragma once +#include "PBFTBaseMessageInterface.h" +#include "PBFTProposalInterface.h" +namespace bcos +{ +namespace consensus +{ +class PBFTMessageInterface : virtual public PBFTBaseMessageInterface +{ +public: + using Ptr = std::shared_ptr; + PBFTMessageInterface() = default; + virtual ~PBFTMessageInterface() {} + + virtual void setConsensusProposal(PBFTProposalInterface::Ptr _consensusProposals) = 0; + virtual PBFTProposalInterface::Ptr consensusProposal() = 0; + + virtual void setProposals(PBFTProposalList const& _proposals) = 0; + virtual PBFTProposalList const& proposals() const = 0; + + virtual PBFTMessageInterface::Ptr populateWithoutProposal() = 0; +}; +using PBFTMessageList = std::vector; +using PBFTMessageListPtr = std::shared_ptr; +} // namespace consensus +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTProposalInterface.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTProposalInterface.h" new file mode 100644 index 00000000..0d0b77c1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTProposalInterface.h" @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for PBFTProposal + * @file PBFTProposalInterfaces.h + * @author: yujiechen + * @date 2021-04-15 + */ +#pragma once +#include "../../framework/ProposalInterface.h" +#include "../utilities/Common.h" + +namespace bcos +{ +namespace consensus +{ +class PBFTProposalInterface : virtual public ProposalInterface +{ +public: + using Ptr = std::shared_ptr; + PBFTProposalInterface() = default; + ~PBFTProposalInterface() override {} + + virtual size_t signatureProofSize() const = 0; + virtual std::pair signatureProof(size_t _index) const = 0; + virtual void appendSignatureProof(int64_t _nodeIdx, bytesConstRef _signatureData) = 0; + virtual void clearSignatureProof() = 0; +}; +using PBFTProposalList = std::vector; +using PBFTProposalListPtr = std::shared_ptr; + +template +inline std::string printPBFTProposal(T _proposal) +{ + std::ostringstream stringstream; + stringstream << LOG_KV("propHash", _proposal->hash().abridged()) + << LOG_KV("propIndex", _proposal->index()); + return stringstream.str(); +} +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTRequestInterface.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTRequestInterface.h" new file mode 100644 index 00000000..e5c2d3f0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTRequestInterface.h" @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for PBFT request + * @file PBFTRequestInterface.h + * @author: yujiechen + * @date 2021-04-28 + */ +#pragma once +#include "PBFTBaseMessageInterface.h" +namespace bcos +{ +namespace consensus +{ +class PBFTRequestInterface : virtual public PBFTBaseMessageInterface +{ +public: + using Ptr = std::shared_ptr; + PBFTRequestInterface() = default; + virtual ~PBFTRequestInterface() {} + + virtual void setSize(int64_t _size) = 0; + virtual int64_t size() const = 0; +}; +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTStorage.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTStorage.h" new file mode 100644 index 00000000..685a25d2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/PBFTStorage.h" @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2021 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. + * + * @brief StorageInterface for PBFT + * @file PBFTStorage.cpp + * @author: yujiechen + * @date 2021-04-26 + */ +#pragma once +#include "PBFTMessageInterface.h" +#include "PBFTProposalInterface.h" +#include +#include +#include +#include +namespace bcos +{ +namespace consensus +{ +class PBFTStorage +{ +public: + using Ptr = std::shared_ptr; + PBFTStorage() = default; + virtual ~PBFTStorage() {} + + virtual PBFTProposalListPtr loadState(bcos::protocol::BlockNumber _stabledIndex) = 0; + virtual int64_t maxCommittedProposalIndex() = 0; + virtual void asyncCommitProposal(PBFTProposalInterface::Ptr _commitProposal) = 0; + virtual void asyncCommitStableCheckPoint(PBFTProposalInterface::Ptr _stableProposal) = 0; + virtual void asyncRemoveStabledCheckPoint(size_t _stabledCheckPointIndex) = 0; + + // get the latest committed proposal from the storage + virtual void asyncGetCommittedProposals(bcos::protocol::BlockNumber _start, size_t _offset, + std::function _onSuccess) = 0; + virtual void registerFinalizeHandler( + std::function _finalizeHandler) = 0; + virtual void registerOnStableCheckPointCommitFailed( + std::function + _onStableCheckPointCommitFailed) = 0; +}; +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/ViewChangeMsgInterface.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/ViewChangeMsgInterface.h" new file mode 100644 index 00000000..6dc2b08c --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/interfaces/ViewChangeMsgInterface.h" @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for ViewChangeMsg + * @file ViewChangeMsgInterface.h + * @author: yujiechen + * @date 2021-04-15 + */ +#pragma once +#include "PBFTMessageInterface.h" + +namespace bcos +{ +namespace consensus +{ +class ViewChangeMsgInterface : virtual public PBFTBaseMessageInterface +{ +public: + using Ptr = std::shared_ptr; + ViewChangeMsgInterface() = default; + virtual ~ViewChangeMsgInterface() {} + + virtual PBFTProposalInterface::Ptr committedProposal() = 0; + virtual void setCommittedProposal(PBFTProposalInterface::Ptr _proposal) = 0; + + virtual PBFTMessageList const& preparedProposals() = 0; + virtual void setPreparedProposals(PBFTMessageList const& _preparedProposal) = 0; +}; +using ViewChangeMsgList = std::vector; +using ViewChangeMsgListPtr = std::shared_ptr; +} // namespace consensus +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTBaseMessage.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTBaseMessage.h" new file mode 100644 index 00000000..cd49dd8d --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTBaseMessage.h" @@ -0,0 +1,153 @@ +/** + * Copyright (C) 2021 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. + * + * @brief PB implementation for PBFTBaseMessage + * @file PBFTBaseMessage.h + * @author: yujiechen + * @date 2021-04-13 + */ +#pragma once +#include "bcos-pbft/pbft/interfaces/PBFTBaseMessageInterface.h" +#include "bcos-pbft/pbft/protocol/proto/PBFT.pb.h" +#include +namespace bcos +{ +namespace consensus +{ +class PBFTBaseMessage : virtual public PBFTBaseMessageInterface +{ +public: + using Ptr = std::shared_ptr; + PBFTBaseMessage() + : m_baseMessage(std::make_shared()), m_signatureData(std::make_shared()) + {} + + explicit PBFTBaseMessage(std::shared_ptr _baseMessage) + : m_baseMessage(_baseMessage), m_signatureData(std::make_shared()) + { + PBFTBaseMessage::deserializeToObject(); + } + + ~PBFTBaseMessage() override {} + + int64_t timestamp() const override { return m_baseMessage->timestamp(); } + int32_t version() const override { return m_baseMessage->version(); } + ViewType view() const override { return m_baseMessage->view(); } + IndexType generatedFrom() const override { return m_baseMessage->generatedfrom(); } + bcos::crypto::HashType const& hash() const override { return m_hash; } + + void setTimestamp(int64_t _timestamp) override { m_baseMessage->set_timestamp(_timestamp); } + void setVersion(int32_t _version) override { m_baseMessage->set_version(_version); } + void setView(ViewType _view) override { m_baseMessage->set_view(_view); } + void setGeneratedFrom(IndexType _generatedFrom) override + { + m_baseMessage->set_generatedfrom(_generatedFrom); + } + + void setHash(bcos::crypto::HashType const& _hash) override + { + m_hash = _hash; + m_baseMessage->set_hash(m_hash.data(), bcos::crypto::HashType::SIZE); + } + + PacketType packetType() const override { return m_packetType; } + void setPacketType(PacketType _packetType) override { m_packetType = _packetType; } + + bytesPointer encode( + bcos::crypto::CryptoSuite::Ptr, bcos::crypto::KeyPairInterface::Ptr) const override + { + return bcos::protocol::encodePBObject(m_baseMessage); + } + + bytesPointer encode() const { return bcos::protocol::encodePBObject(m_baseMessage); } + + void decode(bytesConstRef _data) override + { + bcos::protocol::decodePBObject(m_baseMessage, _data); + PBFTBaseMessage::deserializeToObject(); + } + + bytesConstRef signatureData() override + { + auto const& signatureData = m_baseMessage->signaturedata(); + return bytesConstRef((byte const*)signatureData.data(), signatureData.size()); + } + + bcos::crypto::HashType const& signatureDataHash() override { return m_dataHash; } + void setSignatureData(bytes&& _signatureData) override + { + auto size = _signatureData.size(); + m_baseMessage->set_signaturedata((std::move(_signatureData)).data(), size); + } + void setSignatureData(bytes const& _signatureData) override + { + m_baseMessage->set_signaturedata(_signatureData.data(), _signatureData.size()); + } + void setSignatureDataHash(bcos::crypto::HashType const& _hash) override + { + m_dataHash = _hash; + m_baseMessage->set_signaturehash(_hash.data(), bcos::crypto::HashType::SIZE); + } + bool verifySignature( + bcos::crypto::CryptoSuite::Ptr _cryptoSuite, bcos::crypto::PublicPtr _pubKey) override + { + return _cryptoSuite->signatureImpl()->verify(_pubKey, signatureDataHash(), signatureData()); + } + + int64_t index() const override { return m_baseMessage->index(); } + void setIndex(int64_t _index) override { m_baseMessage->set_index(_index); } + + bool operator==(PBFTBaseMessage const& _pbftMessage) const + { + return (timestamp() == _pbftMessage.timestamp()) && (version() == _pbftMessage.version()) && + (generatedFrom() == _pbftMessage.generatedFrom()) && + (view() == _pbftMessage.view()) && (hash() == _pbftMessage.hash()); + } + + void setFrom(bcos::crypto::PublicPtr _from) override { m_from = _from; } + bcos::crypto::PublicPtr from() const override { return m_from; } + +protected: + virtual void deserializeToObject() + { + auto const& hashData = m_baseMessage->hash(); + if (hashData.size() >= bcos::crypto::HashType::SIZE) + { + m_hash = + bcos::crypto::HashType((byte const*)hashData.c_str(), bcos::crypto::HashType::SIZE); + } + + auto const& signatureDataHash = m_baseMessage->signaturehash(); + if (signatureDataHash.size() >= bcos::crypto::HashType::SIZE) + { + m_dataHash = bcos::crypto::HashType( + (byte const*)signatureDataHash.c_str(), bcos::crypto::HashType::SIZE); + } + } + + std::shared_ptr baseMessage() { return m_baseMessage; } + void setBaseMessage(std::shared_ptr _baseMessage) { m_baseMessage = _baseMessage; } + + std::shared_ptr m_baseMessage; + bcos::crypto::HashType m_hash; + PacketType m_packetType = PacketType::PrePreparePacket; + + bcos::crypto::HashType m_dataHash; + bytesPointer m_signatureData; + + bcos::crypto::PublicPtr m_from; +}; +} // namespace consensus +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTCodec.cpp" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTCodec.cpp" new file mode 100644 index 00000000..bd43696f --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTCodec.cpp" @@ -0,0 +1,106 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for PBFTCodec + * @file PBFTCodec.cpp + * @author: yujiechen + * @date 2021-04-13 + */ +#include "PBFTCodec.h" +#include "bcos-pbft/pbft/protocol/proto/PBFT.pb.h" +#include + +using namespace bcos; +using namespace bcos::consensus; +using namespace bcos::crypto; + +bytesPointer PBFTCodec::encode(PBFTBaseMessageInterface::Ptr _pbftMessage, int32_t _version) const +{ + auto pbMessage = std::make_shared(); + + // set packetType + auto packetType = _pbftMessage->packetType(); + pbMessage->set_type((int32_t)packetType); + + // set payLoad + auto payLoad = _pbftMessage->encode(m_cryptoSuite, m_keyPair); + pbMessage->set_payload(payLoad->data(), payLoad->size()); + + // set signature + if (shouldHandleSignature(packetType)) + { + // get hash of the payLoad + auto hash = m_cryptoSuite->hashImpl()->hash(*payLoad); + // sign for the payload + auto signatureData = m_cryptoSuite->signatureImpl()->sign(*m_keyPair, hash, false); + pbMessage->set_signaturedata(signatureData->data(), signatureData->size()); + _pbftMessage->setSignatureDataHash(hash); + _pbftMessage->setSignatureData(*signatureData); + } + // set version + pbMessage->set_version(_version); + return bcos::protocol::encodePBObject(pbMessage); +} + +PBFTBaseMessageInterface::Ptr PBFTCodec::decode(bytesConstRef _data) const +{ + auto pbMessage = std::make_shared(); + bcos::protocol::decodePBObject(pbMessage, _data); + // get packetType + PacketType packetType = (PacketType)(pbMessage->type()); + // get payLoad + auto const& payLoad = pbMessage->payload(); + auto payLoadRefData = bytesConstRef((byte const*)payLoad.c_str(), payLoad.size()); + // decode the packet according to the packetType + PBFTBaseMessageInterface::Ptr decodedMsg = nullptr; + switch (packetType) + { + case PacketType::PrePreparePacket: + case PacketType::PreparePacket: + case PacketType::CommitPacket: + case PacketType::CommittedProposalResponse: + case PacketType::CheckPoint: + case PacketType::RecoverRequest: + case PacketType::RecoverResponse: + decodedMsg = m_pbftMessageFactory->createPBFTMsg(m_cryptoSuite, payLoadRefData); + break; + case PacketType::PreparedProposalResponse: + case PacketType::ViewChangePacket: + decodedMsg = m_pbftMessageFactory->createViewChangeMsg(payLoadRefData); + break; + case PacketType::NewViewPacket: + decodedMsg = m_pbftMessageFactory->createNewViewMsg(payLoadRefData); + break; + case PacketType::CommittedProposalRequest: + case PacketType::PreparedProposalRequest: + decodedMsg = m_pbftMessageFactory->createPBFTRequest(payLoadRefData); + break; + default: + BOOST_THROW_EXCEPTION(UnknownPBFTMsgType() << errinfo_comment( + "unknow pbft packetType: " + std::to_string(packetType))); + } + if (shouldHandleSignature(packetType)) + { + // set signature data for the message + auto hash = m_cryptoSuite->hashImpl()->hash(payLoadRefData); + decodedMsg->setSignatureDataHash(hash); + + auto const& signatureData = pbMessage->signaturedata(); + bytes signatureBytes(signatureData.begin(), signatureData.end()); + decodedMsg->setSignatureData(std::move(signatureBytes)); + } + decodedMsg->setPacketType(packetType); + return decodedMsg; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTCodec.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTCodec.h" new file mode 100644 index 00000000..05698027 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTCodec.h" @@ -0,0 +1,60 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for PBFTCodec + * @file PBFTCodec.h + * @author: yujiechen + * @date 2021-04-13 + */ +#pragma once +#include "../../interfaces/PBFTCodecInterface.h" +#include "../../interfaces/PBFTMessageFactory.h" +#include +#include +namespace bcos +{ +namespace consensus +{ +class PBFTCodec : public PBFTCodecInterface +{ +public: + using Ptr = std::shared_ptr; + PBFTCodec(bcos::crypto::KeyPairInterface::Ptr _keyPair, + bcos::crypto::CryptoSuite::Ptr _cryptoSuite, PBFTMessageFactory::Ptr _pbftMessageFactory) + : m_keyPair(_keyPair), m_cryptoSuite(_cryptoSuite), m_pbftMessageFactory(_pbftMessageFactory) + {} + + ~PBFTCodec() override {} + + bytesPointer encode( + PBFTBaseMessageInterface::Ptr _pbftMessage, int32_t _version = 0) const override; + + PBFTBaseMessageInterface::Ptr decode(bytesConstRef _data) const override; + +protected: + virtual bool shouldHandleSignature(PacketType _packetType) const + { + return (_packetType == PacketType::ViewChangePacket || + _packetType == PacketType::NewViewPacket); + } + +private: + bcos::crypto::KeyPairInterface::Ptr m_keyPair; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + + PBFTMessageFactory::Ptr m_pbftMessageFactory; +}; +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTMessage.cpp" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTMessage.cpp" new file mode 100644 index 00000000..0d9a2fef --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTMessage.cpp" @@ -0,0 +1,140 @@ +/** + * Copyright (C) 2021 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. + * + * @brief PB implementation for PBFT Message + * @file PBFTMessage.cpp + * @author: yujiechen + * @date 2021-04-13 + */ +#include "PBFTMessage.h" +#include "PBFTProposal.h" +#include "bcos-pbft/core/Proposal.h" + +using namespace bcos; +using namespace bcos::consensus; +using namespace bcos::crypto; +using namespace bcos::protocol; + +bytesPointer PBFTMessage::encode( + CryptoSuite::Ptr _cryptoSuite, KeyPairInterface::Ptr _keyPair) const +{ + // encode the PBFTBaseMessage + encodeHashFields(); + generateAndSetSignatureData(_cryptoSuite, _keyPair); + return encodePBObject(m_pbftRawMessage); +} + +void PBFTMessage::encodeHashFields() const +{ + auto hashFieldsData = PBFTBaseMessage::encode(); + m_pbftRawMessage->set_hashfieldsdata(hashFieldsData->data(), hashFieldsData->size()); +} + +void PBFTMessage::decode(bytesConstRef _data) +{ + decodePBObject(m_pbftRawMessage, _data); + PBFTMessage::deserializeToObject(); +} + +void PBFTMessage::deserializeToObject() +{ + auto const& hashFieldsData = m_pbftRawMessage->hashfieldsdata(); + auto baseMessageData = + bytesConstRef((byte const*)hashFieldsData.c_str(), hashFieldsData.size()); + PBFTBaseMessage::decode(baseMessageData); + + // decode the proposals + m_proposals->clear(); + if (m_pbftRawMessage->has_consensusproposal()) + { + auto consensusProposal = m_pbftRawMessage->mutable_consensusproposal(); + std::shared_ptr rawConsensusProposal(consensusProposal); + m_consensusProposal = std::make_shared(rawConsensusProposal); + } + for (int i = 0; i < m_pbftRawMessage->proposals_size(); i++) + { + std::shared_ptr rawProposal(m_pbftRawMessage->mutable_proposals(i)); + m_proposals->push_back(std::make_shared(rawProposal)); + } +} + +void PBFTMessage::decodeAndSetSignature(CryptoSuite::Ptr _cryptoSuite, bytesConstRef _data) +{ + decode(_data); + m_signatureDataHash = getHashFieldsDataHash(_cryptoSuite); +} + +void PBFTMessage::setConsensusProposal(PBFTProposalInterface::Ptr _consensusProposal) +{ + m_consensusProposal = _consensusProposal; + auto pbftProposal = std::dynamic_pointer_cast(_consensusProposal); + // set committed proposal + if (m_pbftRawMessage->has_consensusproposal()) + { + m_pbftRawMessage->unsafe_arena_release_consensusproposal(); + } + m_pbftRawMessage->unsafe_arena_set_allocated_consensusproposal( + pbftProposal->pbftRawProposal().get()); +} + +HashType PBFTMessage::getHashFieldsDataHash(CryptoSuite::Ptr _cryptoSuite) const +{ + auto const& hashFieldsData = m_pbftRawMessage->hashfieldsdata(); + auto hashFieldsDataRef = + bytesConstRef((byte const*)hashFieldsData.data(), hashFieldsData.size()); + return _cryptoSuite->hash(hashFieldsDataRef); +} + +void PBFTMessage::generateAndSetSignatureData( + CryptoSuite::Ptr _cryptoSuite, KeyPairInterface::Ptr _keyPair) const +{ + m_signatureDataHash = getHashFieldsDataHash(_cryptoSuite); + auto signature = _cryptoSuite->signatureImpl()->sign(*_keyPair, m_signatureDataHash, false); + // set the signature data + m_pbftRawMessage->set_signaturedata(signature->data(), signature->size()); +} + +void PBFTMessage::setProposals(PBFTProposalList const& _proposals) +{ + *m_proposals = _proposals; + m_pbftRawMessage->clear_proposals(); + for (auto proposal : _proposals) + { + auto proposalImpl = std::dynamic_pointer_cast(proposal); + assert(proposalImpl); + m_pbftRawMessage->mutable_proposals()->UnsafeArenaAddAllocated( + proposalImpl->pbftRawProposal().get()); + } +} + +bool PBFTMessage::operator==(PBFTMessage const& _pbftMessage) const +{ + if (!PBFTBaseMessage::operator==(_pbftMessage)) + { + return false; + } + // check proposal + for (size_t i = 0; i < _pbftMessage.proposals().size(); i++) + { + auto proposal = std::dynamic_pointer_cast((*m_proposals)[i]); + auto comparedProposal = + std::dynamic_pointer_cast((_pbftMessage.proposals())[i]); + if (*proposal != *comparedProposal) + { + return false; + } + } + return true; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTMessage.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTMessage.h" new file mode 100644 index 00000000..c950859f --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTMessage.h" @@ -0,0 +1,124 @@ +/** + * Copyright (C) 2021 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. + * + * @brief PB implementation for PBFT Message + * @file PBFTMessage.h + * @author: yujiechen + * @date 2021-04-13 + */ +#pragma once +#include "../../interfaces/PBFTMessageInterface.h" +#include "PBFTBaseMessage.h" +#include "bcos-pbft/pbft/protocol/proto/PBFT.pb.h" + +namespace bcos +{ +namespace consensus +{ +class PBFTMessage : public PBFTBaseMessage, public PBFTMessageInterface +{ +public: + using Ptr = std::shared_ptr; + PBFTMessage() + : PBFTBaseMessage(), + m_pbftRawMessage(std::make_shared()), + m_proposals(std::make_shared()) + {} + + explicit PBFTMessage(std::shared_ptr _pbftRawMessage) : PBFTBaseMessage() + { + m_pbftRawMessage = _pbftRawMessage; + m_proposals = std::make_shared(); + PBFTMessage::deserializeToObject(); + } + + PBFTMessage(bcos::crypto::CryptoSuite::Ptr _cryptoSuite, bytesConstRef _data) : PBFTMessage() + { + decodeAndSetSignature(_cryptoSuite, _data); + } + + ~PBFTMessage() override + { + // return back the ownership to m_consensusProposal + if (m_pbftRawMessage->has_consensusproposal()) + { + m_pbftRawMessage->unsafe_arena_release_consensusproposal(); + } + // return the ownership of rawProposal to the passed-in proposal + auto allocatedProposalSize = m_pbftRawMessage->proposals_size(); + for (int i = 0; i < allocatedProposalSize; i++) + { + m_pbftRawMessage->mutable_proposals()->UnsafeArenaReleaseLast(); + } + } + + std::shared_ptr pbftRawMessage() { return m_pbftRawMessage; } + bytesPointer encode(bcos::crypto::CryptoSuite::Ptr _cryptoSuite, + bcos::crypto::KeyPairInterface::Ptr _keyPair) const override; + void decode(bytesConstRef _data) override; + + void setProposals(PBFTProposalList const& _proposals) override; + PBFTProposalList const& proposals() const override { return *m_proposals; } + + void setConsensusProposal(PBFTProposalInterface::Ptr _consensusProposal) override; + PBFTProposalInterface::Ptr consensusProposal() override { return m_consensusProposal; } + + virtual void decodeAndSetSignature( + bcos::crypto::CryptoSuite::Ptr _pbftConfig, bytesConstRef _data); + + bool operator==(PBFTMessage const& _pbftMessage) const; + + bytesConstRef signatureData() override + { + auto const& signatureData = m_pbftRawMessage->signaturedata(); + return bytesConstRef((byte const*)signatureData.data(), signatureData.size()); + } + + bcos::crypto::HashType const& signatureDataHash() override { return m_signatureDataHash; } + + void setSignatureDataHash(bcos::crypto::HashType const& _hash) override + { + m_signatureDataHash = _hash; + } + + PBFTMessageInterface::Ptr populateWithoutProposal() override + { + auto pbftMessage = std::make_shared(); + encodeHashFields(); + auto const& hashFieldData = m_pbftRawMessage->hashfieldsdata(); + pbftMessage->pbftRawMessage()->set_hashfieldsdata( + hashFieldData.data(), hashFieldData.size()); + pbftMessage->deserializeToObject(); + return pbftMessage; + } + + void encodeHashFields() const; + void deserializeToObject() override; + +protected: + virtual bcos::crypto::HashType getHashFieldsDataHash( + bcos::crypto::CryptoSuite::Ptr _cryptoSuite) const; + virtual void generateAndSetSignatureData(bcos::crypto::CryptoSuite::Ptr _cryptoSuite, + bcos::crypto::KeyPairInterface::Ptr _keyPair) const; + +private: + std::shared_ptr m_pbftRawMessage; + PBFTProposalInterface::Ptr m_consensusProposal; + PBFTProposalListPtr m_proposals; + + mutable bcos::crypto::HashType m_signatureDataHash; +}; +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTMessageFactoryImpl.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTMessageFactoryImpl.h" new file mode 100644 index 00000000..7ef29b64 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTMessageFactoryImpl.h" @@ -0,0 +1,89 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for PBFTMessageFactory + * @file PBFTMessageFactoryImpl.h + * @author: yujiechen + * @date 2021-04-20 + */ +#pragma once +#include "../../interfaces/PBFTMessageFactory.h" +#include "PBFTMessage.h" +#include "PBFTNewViewMsg.h" +#include "PBFTProposal.h" +#include "PBFTRequest.h" +#include "PBFTViewChangeMsg.h" + +namespace bcos +{ +namespace consensus +{ +class PBFTMessageFactoryImpl : public PBFTMessageFactory +{ +public: + using Ptr = std::shared_ptr; + PBFTMessageFactoryImpl() = default; + ~PBFTMessageFactoryImpl() override {} + + PBFTMessageInterface::Ptr createPBFTMsg() override { return std::make_shared(); } + + ViewChangeMsgInterface::Ptr createViewChangeMsg() override + { + return std::make_shared(); + } + + NewViewMsgInterface::Ptr createNewViewMsg() override + { + return std::make_shared(); + } + + PBFTMessageInterface::Ptr createPBFTMsg( + bcos::crypto::CryptoSuite::Ptr _cryptoSuite, bytesConstRef _data) override + { + return std::make_shared(_cryptoSuite, _data); + } + + ViewChangeMsgInterface::Ptr createViewChangeMsg(bytesConstRef _data) override + { + return std::make_shared(_data); + } + + NewViewMsgInterface::Ptr createNewViewMsg(bytesConstRef _data) override + { + return std::make_shared(_data); + } + + PBFTProposalInterface::Ptr createPBFTProposal() override + { + return std::make_shared(); + } + + PBFTProposalInterface::Ptr createPBFTProposal(bytesConstRef _data) override + { + return std::make_shared(_data); + } + + PBFTRequestInterface::Ptr createPBFTRequest() override + { + return std::make_shared(); + } + + PBFTRequestInterface::Ptr createPBFTRequest(bytesConstRef _data) override + { + return std::make_shared(_data); + } +}; +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTNewViewMsg.cpp" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTNewViewMsg.cpp" new file mode 100644 index 00000000..82cbd2ad --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTNewViewMsg.cpp" @@ -0,0 +1,81 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for PBFTNewViewMsg + * @file PBFTNewViewMsg.cpp + * @author: yujiechen + * @date 2021-04-16 + */ + +#include "PBFTNewViewMsg.h" +#include "PBFTMessage.h" +#include "PBFTViewChangeMsg.h" +#include + +using namespace bcos; +using namespace bcos::consensus; +using namespace bcos::protocol; +using namespace bcos::crypto; +bytesPointer PBFTNewViewMsg::encode(CryptoSuite::Ptr, KeyPairInterface::Ptr) const +{ + return encodePBObject(m_rawNewView); +} + +void PBFTNewViewMsg::decode(bytesConstRef _data) +{ + decodePBObject(m_rawNewView, _data); + setBaseMessage(std::shared_ptr(m_rawNewView->mutable_message())); + PBFTNewViewMsg::deserializeToObject(); +} + +void PBFTNewViewMsg::deserializeToObject() +{ + PBFTBaseMessage::deserializeToObject(); + // decode into m_viewChangeList + for (int i = 0; i < m_rawNewView->viewchangemsglist_size(); i++) + { + std::shared_ptr pbRawViewChange( + m_rawNewView->mutable_viewchangemsglist(i)); + m_viewChangeList->push_back(std::make_shared(pbRawViewChange)); + } + // decode into m_prePrepareList + for (int i = 0; i < m_rawNewView->prepreparelist_size(); i++) + { + std::shared_ptr pbftRawMessage(m_rawNewView->mutable_prepreparelist(i)); + m_prePrepareList->push_back(std::make_shared(pbftRawMessage)); + } +} + +void PBFTNewViewMsg::setViewChangeMsgList(ViewChangeMsgList const& _viewChangeMsgList) +{ + *m_viewChangeList = _viewChangeMsgList; + for (auto viewChangeMsg : _viewChangeMsgList) + { + auto pbViewChangeMsg = std::dynamic_pointer_cast(viewChangeMsg); + m_rawNewView->mutable_viewchangemsglist()->AddAllocated( + pbViewChangeMsg->rawViewChange().get()); + } +} + +void PBFTNewViewMsg::setPrePrepareList(PBFTMessageList const& _prePrepareList) +{ + *m_prePrepareList = _prePrepareList; + for (auto prePrepare : _prePrepareList) + { + auto pbPrePrepare = std::dynamic_pointer_cast(prePrepare); + pbPrePrepare->encodeHashFields(); + m_rawNewView->mutable_prepreparelist()->AddAllocated(pbPrePrepare->pbftRawMessage().get()); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTNewViewMsg.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTNewViewMsg.h" new file mode 100644 index 00000000..de57c2e5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTNewViewMsg.h" @@ -0,0 +1,88 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for PBFTNewViewMsg + * @file PBFTNewViewMsg.h + * @author: yujiechen + * @date 2021-04-16 + */ +#pragma once +#include "../../interfaces/NewViewMsgInterface.h" +#include "../../interfaces/ViewChangeMsgInterface.h" +#include "PBFTBaseMessage.h" + +namespace bcos +{ +namespace consensus +{ +class PBFTNewViewMsg : public NewViewMsgInterface, public PBFTBaseMessage +{ +public: + using Ptr = std::shared_ptr; + PBFTNewViewMsg() : PBFTBaseMessage() + { + m_rawNewView = std::make_shared(); + m_rawNewView->set_allocated_message(PBFTBaseMessage::baseMessage().get()); + m_viewChangeList = std::make_shared(); + m_prePrepareList = std::make_shared(); + m_packetType = PacketType::NewViewPacket; + } + explicit PBFTNewViewMsg(bytesConstRef _data) : PBFTBaseMessage() + { + m_rawNewView = std::make_shared(); + m_viewChangeList = std::make_shared(); + m_prePrepareList = std::make_shared(); + m_packetType = PacketType::NewViewPacket; + decode(_data); + } + + ~PBFTNewViewMsg() override + { + // return back the ownership of message to the PBFTBaseMessage + m_rawNewView->unsafe_arena_release_message(); + // return back the ownership to m_viewChangeList + auto viewChangeSize = m_rawNewView->viewchangemsglist_size(); + for (auto i = 0; i < viewChangeSize; i++) + { + m_rawNewView->mutable_viewchangemsglist()->UnsafeArenaReleaseLast(); + } + auto preprepareSize = m_rawNewView->prepreparelist_size(); + for (auto i = 0; i < preprepareSize; i++) + { + m_rawNewView->mutable_prepreparelist()->UnsafeArenaReleaseLast(); + } + } + + bytesPointer encode(bcos::crypto::CryptoSuite::Ptr _cryptoSuite, + bcos::crypto::KeyPairInterface::Ptr _keyPair) const override; + void decode(bytesConstRef _data) override; + + void setViewChangeMsgList(ViewChangeMsgList const& _viewChangeMsgList) override; + ViewChangeMsgList const& viewChangeMsgList() const override { return *m_viewChangeList; } + + PBFTMessageList const& prePrepareList() override { return *m_prePrepareList; } + void setPrePrepareList(PBFTMessageList const& _preparedProposal) override; + +protected: + void deserializeToObject() override; + +private: + std::shared_ptr m_rawNewView; + // required and need to be verified + ViewChangeMsgListPtr m_viewChangeList; + PBFTMessageListPtr m_prePrepareList; +}; +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTProposal.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTProposal.h" new file mode 100644 index 00000000..e8b000c3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTProposal.h" @@ -0,0 +1,115 @@ +/** + * Copyright (C) 2021 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. + * + * @brief extend for PreparedProof + * @file PreparedPoof.h + * @author: yujiechen + * @date 2021-04-15 + */ +#pragma once +#include "bcos-pbft/core/Proposal.h" +#include "bcos-pbft/pbft/protocol/proto/PBFT.pb.h" +namespace bcos +{ +namespace consensus +{ +class PBFTProposal : public Proposal, virtual public PBFTProposalInterface +{ +public: + using Ptr = std::shared_ptr; + PBFTProposal() : Proposal() + { + m_pbftRawProposal = std::make_shared(); + m_pbftRawProposal->set_allocated_proposal(rawProposal().get()); + } + explicit PBFTProposal(bytesConstRef _data) : Proposal() + { + m_pbftRawProposal = std::make_shared(); + decode(_data); + } + explicit PBFTProposal(std::shared_ptr _pbftRawProposal) + : Proposal(std::shared_ptr(_pbftRawProposal->mutable_proposal())) + { + m_pbftRawProposal = _pbftRawProposal; + } + + ~PBFTProposal() override { m_pbftRawProposal->unsafe_arena_release_proposal(); } + + std::shared_ptr pbftRawProposal() { return m_pbftRawProposal; } + + size_t signatureProofSize() const override { return m_pbftRawProposal->signaturelist_size(); } + + std::pair signatureProof(size_t _index) const override + { + auto const& signatureData = m_pbftRawProposal->signaturelist(_index); + auto signatureDataRef = + bytesConstRef((byte const*)signatureData.c_str(), signatureData.size()); + return std::make_pair(m_pbftRawProposal->nodelist(_index), signatureDataRef); + } + + void appendSignatureProof(int64_t _nodeIdx, bytesConstRef _signatureData) override + { + m_pbftRawProposal->add_nodelist(_nodeIdx); + m_pbftRawProposal->add_signaturelist(_signatureData.data(), _signatureData.size()); + } + + void clearSignatureProof() override + { + m_pbftRawProposal->clear_nodelist(); + m_pbftRawProposal->clear_signaturelist(); + } + + bool operator==(PBFTProposal const& _proposal) const + { + if (!Proposal::operator==(_proposal)) + { + return false; + } + // check the signatureProof + if (_proposal.signatureProofSize() != signatureProofSize()) + { + return false; + } + size_t proofSize = signatureProofSize(); + for (size_t i = 0; i < proofSize; i++) + { + auto proof = _proposal.signatureProof(i); + auto comparedProof = signatureProof(i); + if (proof.first != comparedProof.first || + proof.second.toBytes() != comparedProof.second.toBytes()) + { + return false; + } + } + return true; + } + + bool operator!=(PBFTProposal const& _proposal) const { return !(operator==(_proposal)); } + + bytesPointer encode() const override + { + return bcos::protocol::encodePBObject(m_pbftRawProposal); + } + void decode(bytesConstRef _data) override + { + bcos::protocol::decodePBObject(m_pbftRawProposal, _data); + setRawProposal(std::shared_ptr(m_pbftRawProposal->mutable_proposal())); + } + +private: + std::shared_ptr m_pbftRawProposal; +}; +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTRequest.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTRequest.h" new file mode 100644 index 00000000..53bd2cce --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTRequest.h" @@ -0,0 +1,76 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for PBFT request + * @file PBFTRequest.h + * @author: yujiechen + * @date 2021-04-28 + */ +#pragma once +#include "bcos-pbft/pbft/interfaces/PBFTRequestInterface.h" +#include "PBFTBaseMessage.h" +#include "bcos-pbft/pbft/protocol/proto/PBFT.pb.h" +#include + +namespace bcos +{ +namespace consensus +{ +class PBFTRequest : virtual public PBFTRequestInterface, public PBFTBaseMessage +{ +public: + PBFTRequest() : PBFTBaseMessage() + { + m_pbRequest = std::make_shared(); + m_pbRequest->set_allocated_message(PBFTBaseMessage::baseMessage().get()); + } + explicit PBFTRequest(bytesConstRef _data) : PBFTBaseMessage() + { + m_pbRequest = std::make_shared(); + decode(_data); + } + + ~PBFTRequest() override { m_pbRequest->unsafe_arena_release_message(); } + + void setSize(int64_t _size) override { m_pbRequest->set_size(_size); } + int64_t size() const override { return m_pbRequest->size(); } + + bytesPointer encode( + bcos::crypto::CryptoSuite::Ptr, bcos::crypto::KeyPairInterface::Ptr) const override + { + return bcos::protocol::encodePBObject(m_pbRequest); + } + + void decode(bytesConstRef _data) override + { + bcos::protocol::decodePBObject(m_pbRequest, _data); + setBaseMessage(std::shared_ptr(m_pbRequest->mutable_message())); + PBFTBaseMessage::deserializeToObject(); + } + + bool operator==(PBFTRequest const& _pbftRequest) const + { + if (!PBFTBaseMessage::operator==(_pbftRequest)) + { + return false; + } + return _pbftRequest.size() == size(); + } + +private: + std::shared_ptr m_pbRequest; +}; +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTViewChangeMsg.cpp" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTViewChangeMsg.cpp" new file mode 100644 index 00000000..abc024a4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTViewChangeMsg.cpp" @@ -0,0 +1,89 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for PBFTViewChangeMsg + * @file PBFTViewChangeMsg.cpp + * @author: yujiechen + * @date 2021-04-15 + */ +#include "PBFTViewChangeMsg.h" +#include "PBFTMessage.h" +#include "PBFTProposal.h" +#include "bcos-pbft/pbft/protocol/proto/PBFT.pb.h" +#include + +using namespace bcos; +using namespace bcos::consensus; +using namespace bcos::protocol; +using namespace bcos::crypto; +PBFTViewChangeMsg::PBFTViewChangeMsg(std::shared_ptr _rawViewChange) + : PBFTBaseMessage(std::shared_ptr(_rawViewChange->mutable_message())) +{ + m_packetType = PacketType::ViewChangePacket; + m_preparedProposalList = std::make_shared(); + m_rawViewChange = _rawViewChange; + PBFTViewChangeMsg::deserializeToObject(); +} + +bytesPointer PBFTViewChangeMsg::encode(CryptoSuite::Ptr, KeyPairInterface::Ptr) const +{ + return encodePBObject(m_rawViewChange); +} + +void PBFTViewChangeMsg::decode(bytesConstRef _data) +{ + decodePBObject(m_rawViewChange, _data); + setBaseMessage(std::shared_ptr(m_rawViewChange->mutable_message())); + PBFTViewChangeMsg::deserializeToObject(); + m_packetType = PacketType::ViewChangePacket; +} + +void PBFTViewChangeMsg::setCommittedProposal(PBFTProposalInterface::Ptr _proposal) +{ + m_committedProposal = _proposal; + auto pbftProposal = std::dynamic_pointer_cast(_proposal); + // set committed proposal + if (m_rawViewChange->has_committedproposal()) + { + m_rawViewChange->unsafe_arena_release_committedproposal(); + } + m_rawViewChange->unsafe_arena_set_allocated_committedproposal( + pbftProposal->pbftRawProposal().get()); +} + +void PBFTViewChangeMsg::setPreparedProposals(PBFTMessageList const& _preparedProposals) +{ + *m_preparedProposalList = _preparedProposals; + for (auto proposal : *m_preparedProposalList) + { + auto pbftMessage = std::dynamic_pointer_cast(proposal); + m_rawViewChange->mutable_preparedproposals()->AddAllocated( + pbftMessage->pbftRawMessage().get()); + } +} + +void PBFTViewChangeMsg::deserializeToObject() +{ + PBFTBaseMessage::deserializeToObject(); + m_preparedProposalList->clear(); + std::shared_ptr rawCommittedProposal( + m_rawViewChange->mutable_committedproposal()); + m_committedProposal = std::make_shared(rawCommittedProposal); + for (int i = 0; i < m_rawViewChange->preparedproposals_size(); i++) + { + std::shared_ptr preparedMsg(m_rawViewChange->mutable_preparedproposals(i)); + m_preparedProposalList->push_back(std::make_shared(preparedMsg)); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTViewChangeMsg.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTViewChangeMsg.h" new file mode 100644 index 00000000..20387495 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/PB/PBFTViewChangeMsg.h" @@ -0,0 +1,91 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for ViewChangeMsg + * @file PBFTViewChangeMsg.h + * @author: yujiechen + * @date 2021-04-15 + */ +#pragma once +#include "../..//interfaces/ViewChangeMsgInterface.h" +#include "PBFTBaseMessage.h" +namespace bcos +{ +namespace consensus +{ +class PBFTViewChangeMsg : public ViewChangeMsgInterface, public PBFTBaseMessage +{ +public: + using Ptr = std::shared_ptr; + PBFTViewChangeMsg() : PBFTBaseMessage() + { + m_preparedProposalList = std::make_shared(); + m_rawViewChange = std::make_shared(); + m_rawViewChange->set_allocated_message(PBFTBaseMessage::baseMessage().get()); + m_packetType = PacketType::ViewChangePacket; + } + + explicit PBFTViewChangeMsg(std::shared_ptr _rawViewChange); + explicit PBFTViewChangeMsg(bytesConstRef _data) : PBFTBaseMessage() + { + m_preparedProposalList = std::make_shared(); + m_rawViewChange = std::make_shared(); + decode(_data); + } + + virtual ~PBFTViewChangeMsg() + { + // return back the ownership of message to PBFTBaseMessage + m_rawViewChange->unsafe_arena_release_message(); + // return back the ownership to m_committedProposal + if (m_rawViewChange->has_committedproposal()) + { + m_rawViewChange->unsafe_arena_release_committedproposal(); + } + // return back the ownership to m_preparedProposalList + auto preparedProposalSize = m_rawViewChange->preparedproposals_size(); + for (auto i = 0; i < preparedProposalSize; i++) + { + m_rawViewChange->mutable_preparedproposals()->UnsafeArenaReleaseLast(); + } + } + + std::shared_ptr rawViewChange() { return m_rawViewChange; } + + PBFTProposalInterface::Ptr committedProposal() override { return m_committedProposal; } + PBFTMessageList const& preparedProposals() override { return *m_preparedProposalList; } + + void setCommittedProposal(PBFTProposalInterface::Ptr _proposal) override; + void setPreparedProposals(PBFTMessageList const& _preparedProposals) override; + + bytesPointer encode( + bcos::crypto::CryptoSuite::Ptr, bcos::crypto::KeyPairInterface::Ptr) const override; + void decode(bytesConstRef _data) override; + +protected: + // deserialize RawViewChangeMessage to Object + void deserializeToObject() override; + +private: + std::shared_ptr m_rawViewChange; + // required and need to be verified + PBFTProposalInterface::Ptr m_committedProposal; + // optional + PBFTMessageListPtr m_preparedProposalList; +}; +using PBFTViewChangeMsgList = std::vector; +using PBFTViewChangeMsgListPtr = std::shared_ptr; +} // namespace consensus +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/proto/PBFT.proto" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/proto/PBFT.proto" new file mode 100644 index 00000000..0eb17273 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/protocol/proto/PBFT.proto" @@ -0,0 +1,69 @@ +syntax = "proto3"; +import "bcos-pbft/core/proto/Consensus.proto"; +package bcos.consensus; + +message BaseMessage +{ + int32 version = 1; + int64 index = 2; + // the hash of the proposals or the committed proposal + bytes hash = 3; + int64 view = 4; + int64 timestamp = 5; + // the index of the node that generated the request + int64 generatedFrom = 6; + bytes signatureHash = 7; + bytes signatureData = 8; +} + +message PBFTRawProposal +{ + RawProposal proposal = 1; + // proof for the prepared proposal + repeated int64 nodeList = 2; + repeated bytes signatureList = 3; +} + +message PBFTRawMessage +{ + bytes hashFieldsData = 1; + // for proposal consensus + PBFTRawProposal consensusProposal = 2; + // for fetch proposals + repeated PBFTRawProposal proposals = 3; + bytes signatureData = 4; +} + +message RawViewChangeMessage +{ + BaseMessage message = 1; + // used to verify the validity of the latest committed proposal + PBFTRawProposal committedProposal = 2; + // prepared but not commit proposals + // (no need to include the proposalsData, can obtain the missed proposal from other nodes) + repeated PBFTRawMessage preparedProposals = 3; +} + +message RawNewViewMessage +{ + BaseMessage message = 1; + // 2*f+1 view change message packets collected by the leader corresponding to toView + repeated RawViewChangeMessage viewChangeMsgList = 2; + repeated PBFTRawMessage prePrepareList = 3; +} + +message ProposalRequest +{ + BaseMessage message = 1; + int64 size = 2; +} + +message RawMessage +{ + int32 version = 1; + int32 type = 2; + // used to verify proposal-irrelevant request + // eg. ViewChange, NewView requests + bytes signatureData = 3; + bytes payLoad = 4; +} diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/storage/LedgerStorage.cpp" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/storage/LedgerStorage.cpp" new file mode 100644 index 00000000..108996c1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/storage/LedgerStorage.cpp" @@ -0,0 +1,457 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Storage for the ledger + * @file LedgerStorage.cpp + * @author: yujiechen + * @date 2021-04-26 + */ +#include "LedgerStorage.h" +#include "../utilities/Common.h" +#include +#include +#include + +using namespace bcos; +using namespace bcos::consensus; +using namespace bcos::ledger; +using namespace bcos::protocol; +using namespace bcos::storage; + +PBFTProposalListPtr LedgerStorage::loadState(BlockNumber _stabledIndex) +{ + m_maxCommittedProposalIndexFetched = false; + asyncGetLatestCommittedProposalIndex(); + auto startT = utcSteadyTime(); + while (utcSteadyTime() - startT < m_timeout) + { + if (m_maxCommittedProposalIndexFetched) + { + break; + } + boost::unique_lock l(x_signalled); + m_signalled.wait_for(l, boost::chrono::milliseconds(10)); + } + if (!m_maxCommittedProposalIndexFetched) + { + PBFT_STORAGE_LOG(WARNING) << LOG_DESC( + "loadState failed for fetch maxCommittedProposalIndex failed"); + BOOST_THROW_EXCEPTION(InitPBFTException() << errinfo_comment( + "loadState failed for fetch maxCommittedProposalIndex failed")); + } + // fetch the committed proposals + if (m_maxCommittedProposalIndex <= _stabledIndex) + { + PBFT_STORAGE_LOG(INFO) << LOG_DESC("no need to fetch committed proposal") + << LOG_KV("maxCommittedProposal", m_maxCommittedProposalIndex) + << LOG_KV("stableCheckPoint", _stabledIndex); + m_maxCommittedProposalIndex = _stabledIndex; + return nullptr; + } + auto offset = (m_maxCommittedProposalIndex - _stabledIndex); + PBFT_STORAGE_LOG(INFO) << LOG_DESC("recover committed proposal from the storage") + << LOG_KV("start", _stabledIndex + 1) + << LOG_KV("end", m_maxCommittedProposalIndex) << LOG_KV("size", offset); + + m_stateFetched = false; + auto self = weak_from_this(); + asyncGetCommittedProposals( + _stabledIndex + 1, offset, [self](PBFTProposalListPtr _proposalList) { + try + { + auto storage = self.lock(); + if (!storage) + { + return; + } + if (_proposalList) + { + storage->m_stateProposals = _proposalList; + } + storage->m_stateFetched = true; + storage->m_signalled.notify_all(); + } + catch (std::exception const& e) + { + PBFT_STORAGE_LOG(WARNING) + << LOG_DESC( + "The committedProposals have been received, but the " + "callback is called exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); + startT = utcSteadyTime(); + while (utcSteadyTime() - startT < m_timeout) + { + if (m_stateFetched) + { + break; + } + boost::unique_lock l(x_signalled); + m_signalled.wait_for(l, boost::chrono::milliseconds(10)); + } + if (!m_stateFetched) + { + PBFT_STORAGE_LOG(WARNING) << LOG_DESC( + "loadState failed for fetch committedProposal failed"); + BOOST_THROW_EXCEPTION(InitPBFTException() << errinfo_comment( + "loadState failed for fetch committedProposal failed")); + } + if (!m_stateProposals || m_stateProposals->empty()) + { + m_maxCommittedProposalIndex = _stabledIndex; + } + return m_stateProposals; +} + +void LedgerStorage::asyncGetCommittedProposals( + BlockNumber _start, size_t _offset, std::function _onSuccess) +{ + // Note: The called program must effectively handle exceptions + if (_start > m_maxCommittedProposalIndex) + { + PBFT_STORAGE_LOG(WARNING) << LOG_DESC("asyncGetCommittedProposals failed") + << LOG_KV( + "maxCommittedProposalIndex", m_maxCommittedProposalIndex) + << LOG_KV("requestedMinIndex", _start); + return; + } + auto keys = std::make_shared>(); + auto endIndex = + std::min((int64_t)(_start + _offset - 1), (int64_t)m_maxCommittedProposalIndex.load()); + for (int64_t i = _start; i <= endIndex; i++) + { + keys->push_back(boost::lexical_cast(i)); + } + auto self = weak_from_this(); + m_storage->asyncGetBatch(m_pbftCommitDB, keys, + [self, _onSuccess]( + Error::UniquePtr&& _error, std::shared_ptr>&& _values) { + if (_error != nullptr) + { + PBFT_STORAGE_LOG(WARNING) + << LOG_DESC("asyncGetCommittedProposals: get proposals failed") + << LOG_KV("error", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()); + return; + } + try + { + auto storage = self.lock(); + if (!storage) + { + return; + } + auto proposalList = std::make_shared(); + for (auto const& value : *_values) + { + if (value.empty()) + { + PBFT_STORAGE_LOG(INFO) + << LOG_DESC("asyncGetCommittedProposals: empty committed proposal") + << LOG_KV("valuesSize", _values->size()); + _onSuccess(nullptr); + return; + } + auto proposalData = bytesConstRef((byte const*)value.data(), value.size()); + proposalList->push_back( + storage->m_messageFactory->createPBFTProposal(proposalData)); + } + _onSuccess(proposalList); + PBFT_STORAGE_LOG(INFO) << LOG_DESC("asyncGetCommittedProposals success") + << LOG_KV("proposals", proposalList->size()); + } + catch (std::exception const& e) + { + PBFT_STORAGE_LOG(WARNING) << LOG_DESC("asyncGetCommittedProposals exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +void LedgerStorage::asyncGetLatestCommittedProposalIndex() +{ + auto self = weak_from_this(); + m_storage->asyncGet(m_pbftCommitDB, m_maxCommittedProposalKey, + [self](Error::UniquePtr&& _error, std::string_view&& _value) { + try + { + auto storage = self.lock(); + if (!storage) + { + storage->m_signalled.notify_all(); + return; + } + if (_value.empty()) + { + storage->m_maxCommittedProposalIndexFetched = true; + storage->m_signalled.notify_all(); + return; + } + if (_error != nullptr) + { + PBFT_STORAGE_LOG(WARNING) + << LOG_DESC("asyncGetLatestCommittedProposalIndex failed") + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()); + storage->m_signalled.notify_all(); + return; + } + auto latestCommittedProposalIndex = boost::lexical_cast(_value); + if (storage->m_maxCommittedProposalIndex < latestCommittedProposalIndex) + { + storage->m_maxCommittedProposalIndex = latestCommittedProposalIndex; + } + storage->m_maxCommittedProposalIndexFetched = true; + storage->m_signalled.notify_all(); + PBFT_STORAGE_LOG(INFO) + << LOG_DESC("asyncGetLatestCommittedProposalIndex") + << LOG_KV("latestCommittedProposalIndex", storage->m_maxCommittedProposalIndex); + } + catch (std::exception const& e) + { + PBFT_STORAGE_LOG(WARNING) + << LOG_DESC("asyncGetLatestCommittedProposalIndex exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +void LedgerStorage::asyncCommitProposal(PBFTProposalInterface::Ptr _committedProposal) +{ + if (m_maxCommittedProposalIndex.load() >= _committedProposal->index()) + { + return; + } + m_maxCommittedProposalIndex.store(_committedProposal->index()); + PBFT_STORAGE_LOG(INFO) << LOG_DESC("asyncCommitProposal: write the committed proposal into db") + << LOG_KV("index", _committedProposal->index()); + // commit the max-index proposal information + auto maxIndexStr = boost::lexical_cast(m_maxCommittedProposalIndex); + auto maxIndexBytes = std::make_shared(maxIndexStr.begin(), maxIndexStr.end()); + asyncPutProposal( + m_pbftCommitDB, m_maxCommittedProposalKey, maxIndexBytes, _committedProposal->index()); + + // commit the data + auto encodedData = _committedProposal->encode(); + asyncPutProposal(m_pbftCommitDB, boost::lexical_cast(_committedProposal->index()), + encodedData, _committedProposal->index()); +} + +void LedgerStorage::asyncPutProposal(std::string const& _dbName, std::string const& _key, + bytesPointer _committedData, BlockNumber _proposalIndex, size_t _retryTime) +{ + auto startT = utcTime(); + auto self = weak_from_this(); + // TODO: optimize here to decrease copy overhead + // Note: asyncPut now is a sync implementation, but no need to async here since this + // timeout-head is only between 5-10ms + m_storage->asyncPut(_dbName, _key, + std::string((const char*)_committedData->data(), _committedData->size()), + [startT, _dbName, _committedData, _key, _proposalIndex, _retryTime, self]( + Error::UniquePtr&& _error) { + if (_error == nullptr) + { + PBFT_STORAGE_LOG(INFO) + << LOG_DESC("asyncPutProposal: commit success") << LOG_KV("dbName", _dbName) + << LOG_KV("key", _key) << LOG_KV("number", _proposalIndex) + << LOG_KV("timecost", (utcTime() - startT)) + << LOG_KV("dataSize", _committedData->size()); + return; + } + PBFT_STORAGE_LOG(WARNING) + << LOG_DESC("asyncPutProposal failed") << LOG_KV("proposalIndex", _proposalIndex) + << LOG_KV("key", _key) << LOG_KV("dbName", _dbName) + << LOG_KV("code", _error->errorCode()) << LOG_KV("msg", _error->errorMessage()); + try + { + auto ledgerStorage = self.lock(); + if (!ledgerStorage) + { + return; + } + if (_retryTime >= 3) + { + return; + } + ledgerStorage->asyncPutProposal( + _dbName, _key, _committedData, _proposalIndex, (_retryTime + 1)); + } + catch (std::exception const& e) + { + PBFT_STORAGE_LOG(WARNING) << LOG_DESC("asyncPutProposal exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +void LedgerStorage::asyncCommitStableCheckPoint(PBFTProposalInterface::Ptr _stableProposal) +{ + std::shared_ptr> signatureList = + std::make_shared>(); + for (size_t i = 0; i < _stableProposal->signatureProofSize(); i++) + { + auto proof = _stableProposal->signatureProof(i); + Signature signature; + signature.index = proof.first; + signature.signature = proof.second.toBytes(); + signatureList->push_back(signature); + } + auto blockHeader = + m_blockFactory->blockHeaderFactory()->createBlockHeader(_stableProposal->data()); + blockHeader->setSignatureList(*signatureList); + auto blockSignatureList = blockHeader->signatureList(); + PBFT_LOG(INFO) << LOG_DESC("asyncCommitStableCheckPoint: set signatureList") + << LOG_KV("index", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("proofSize", signatureList->size()) + << LOG_KV("blockProofSize", blockSignatureList.size()); + // Note: enqueue here to increase the performance since commitBlock is a sync implementation + auto self = weak_from_this(); + m_commitBlockWorker->enqueue([self, blockHeader, _stableProposal]() { + auto storage = self.lock(); + if (!storage) + { + return; + } + // get the transactions list + auto txsInfo = storage->m_blockFactory->createBlock(_stableProposal->extraData()); + storage->commitStableCheckPoint(_stableProposal, blockHeader, txsInfo); + }); +} +void LedgerStorage::onStableCheckPointCommitted( + size_t _txsSize, BlockHeader::Ptr _blockHeader, LedgerConfig::Ptr _ledgerConfig) +{ + _ledgerConfig->setSealerId(_blockHeader->sealer()); + _ledgerConfig->setTxsSize(_txsSize); + // reset the blockNumber + _ledgerConfig->setBlockNumber(_blockHeader->number()); + _ledgerConfig->setHash(_blockHeader->hash()); + // finalize consensus + if (m_finalizeHandler) + { + m_finalizeHandler(_ledgerConfig, false); + } + // remove the proposal committed into the ledger, + // don't remove the latest stabled checkpoint for checkpoint msg response consideration + if (_blockHeader->number() > c_reservedCheckPointSize) + { + asyncRemoveStabledCheckPoint(_blockHeader->number() - c_reservedCheckPointSize); + } +} +void LedgerStorage::commitStableCheckPoint(PBFTProposalInterface::Ptr _stableProposal, + BlockHeader::Ptr _blockHeader, Block::Ptr _blockInfo) +{ + auto self = weak_from_this(); + auto startT = utcTime(); + m_scheduler->commitBlock(_blockHeader, [_stableProposal, _blockHeader, _blockInfo, startT, + self](Error::Ptr&& _error, + LedgerConfig::Ptr _ledgerConfig) { + try + { + auto ledgerStorage = self.lock(); + if (!ledgerStorage) + { + return; + } + if (_error != nullptr) + { + PBFT_STORAGE_LOG(ERROR) << LOG_DESC("commitStableCheckPoint failed") + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorInfo", _error->errorMessage()) + << LOG_KV("proposalIndex", _blockHeader->number()) + << LOG_KV("timecost", utcTime() - startT); + ledgerStorage->m_onStableCheckPointCommitFailed(std::move(_error), _stableProposal); + return; + } + auto commitPerTx = + (double)(utcTime() - startT) / (double)(_blockInfo->transactionsHashSize()); + PBFT_STORAGE_LOG(INFO) + << METRIC << LOG_DESC("commitStableCheckPoint success") + << LOG_KV("index", _blockHeader->number()) + << LOG_KV("hash", _ledgerConfig->hash().abridged()) + << LOG_KV("txs", _blockInfo->transactionsHashSize()) + << LOG_KV("timeCost", utcTime() - startT) << LOG_KV("commitPerTx", commitPerTx); + auto txsSize = _blockInfo->transactionsHashSize(); + // Note:Here the thread pool is used to asynchronize the operation of PBFT finalize to + // prevent the commitBlock from calling the callback synchronously and affecting the + // performance. + ledgerStorage->m_commitBlockWorker->enqueue( + [self, txsSize, _blockHeader, _ledgerConfig]() { + auto storage = self.lock(); + if (!storage) + { + return; + } + storage->onStableCheckPointCommitted(txsSize, _blockHeader, _ledgerConfig); + }); + } + catch (std::exception const& e) + { + PBFT_STORAGE_LOG(WARNING) << LOG_DESC("commitStableCheckPoint exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +void LedgerStorage::asyncRemoveStabledCheckPoint(size_t _stabledCheckPointIndex) +{ + PBFT_STORAGE_LOG(INFO) << LOG_DESC("asyncRemoveStabledCheckPoint") + << LOG_KV("index", _stabledCheckPointIndex); + asyncRemove(m_pbftCommitDB, boost::lexical_cast(_stabledCheckPointIndex)); +} + +void LedgerStorage::asyncRemove(std::string const& _dbName, std::string const& _key) +{ + m_storage->asyncRemove(_dbName, _key, [_dbName, _key](const Error::Ptr& _error) { + if (_error == nullptr) + { + PBFT_STORAGE_LOG(INFO) << LOG_DESC("asyncRemove success") << LOG_KV("dbName", _dbName) + << LOG_KV("key", _key); + return; + } + // TODO: remove failed + PBFT_STORAGE_LOG(WARNING) << LOG_DESC("asyncRemove failed") << LOG_KV("dbName", _dbName) + << LOG_KV("key", _key); + }); +} + +void LedgerStorage::createKVTable(std::string const& _dbName) +{ + auto ret = std::make_shared>(); + auto future = ret->get_future(); + std::string valueFields = "value"; + m_storage->storage()->asyncCreateTable( + _dbName, valueFields, [_dbName, ret](Error::UniquePtr&& _error, std::optional
&&) { + if (_error && _error->errorCode() != bcos::storage::StorageError::TableExists) + { + PBFT_STORAGE_LOG(WARNING) + << LOG_DESC("createKVTable error") << LOG_KV("table", _dbName) + << LOG_KV("code", _error->errorCode()) << LOG_KV("msg", _error->errorMessage()); + ret->set_value(std::move(_error)); + return; + } + ret->set_value(nullptr); + PBFT_STORAGE_LOG(INFO) << LOG_DESC("createKVTable success") << LOG_KV("table", _dbName); + }); + auto error = future.get(); + if (error) + { + BOOST_THROW_EXCEPTION( + InitPBFTException() << errinfo_comment( + "Create PBFT backup DB failed, code: " + std::to_string(error->errorCode()) + + ", message:" + error->errorMessage())); + } +} diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/storage/LedgerStorage.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/storage/LedgerStorage.h" new file mode 100644 index 00000000..401bdcfb --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/storage/LedgerStorage.h" @@ -0,0 +1,122 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Storage for the ledger + * @file LedgerStorage.h + * @author: yujiechen + * @date 2021-04-26 + */ +#pragma once +#include "../interfaces/PBFTMessageFactory.h" +#include "../interfaces/PBFTStorage.h" +#include +#include +#include +#include + +#include + +namespace bcos::consensus +{ +class LedgerStorage : public PBFTStorage, public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + LedgerStorage(bcos::scheduler::SchedulerInterface::Ptr _scheduler, + std::shared_ptr _storage, + bcos::protocol::BlockFactory::Ptr _blockFactory, PBFTMessageFactory::Ptr _messageFactory) + : m_scheduler(std::move(_scheduler)), + m_storage(std::move(_storage)), + m_blockFactory(std::move(_blockFactory)), + m_messageFactory(std::move(_messageFactory)) + { + createKVTable(m_pbftCommitDB); + m_commitBlockWorker = std::make_shared("blockSubmit", 1); + } + ~LedgerStorage() override + { + if (m_commitBlockWorker) + { + m_commitBlockWorker->stop(); + } + } + void createKVTable(std::string const& _dbName); + PBFTProposalListPtr loadState(bcos::protocol::BlockNumber _stabledIndex) override; + + // commit the committed proposal into the kv-storage + void asyncCommitProposal(PBFTProposalInterface::Ptr _proposal) override; + // commit the executed-block into the blockchain + void asyncCommitStableCheckPoint(PBFTProposalInterface::Ptr _stableProposal) override; + void registerFinalizeHandler( + std::function _finalizeHandler) + override + { + m_finalizeHandler = _finalizeHandler; + } + void registerOnStableCheckPointCommitFailed( + std::function + _onStableCheckPointCommitFailed) override + { + m_onStableCheckPointCommitFailed = _onStableCheckPointCommitFailed; + } + + void asyncGetCommittedProposals(bcos::protocol::BlockNumber _start, size_t _offset, + std::function _onSuccess) override; + + int64_t maxCommittedProposalIndex() override { return m_maxCommittedProposalIndex; } + + void asyncRemoveStabledCheckPoint(size_t _stabledCheckPointIndex) override; + +protected: + virtual void asyncPutProposal(std::string const& _dbName, std::string const& _key, + bytesPointer _committedData, bcos::protocol::BlockNumber _proposalIndex, + size_t _retryTime = 0); + + virtual void asyncRemove(std::string const& _dbName, std::string const& _key); + + virtual void commitStableCheckPoint(PBFTProposalInterface::Ptr _stableProposal, + bcos::protocol::BlockHeader::Ptr _blockHeader, bcos::protocol::Block::Ptr _blockInfo); + virtual void asyncGetLatestCommittedProposalIndex(); + + virtual void onStableCheckPointCommitted(size_t _txsSize, + bcos::protocol::BlockHeader::Ptr _blockHeader, + bcos::ledger::LedgerConfig::Ptr _ledgerConfig); + +protected: + bcos::scheduler::SchedulerInterface::Ptr m_scheduler; + std::shared_ptr m_storage; + bcos::protocol::BlockFactory::Ptr m_blockFactory; + PBFTMessageFactory::Ptr m_messageFactory; + + std::string m_maxCommittedProposalKey = "max_committed_proposal"; + std::string m_pbftCommitDB = "pbftCommitDB"; + + + std::atomic m_maxCommittedProposalIndex = {0}; + std::atomic_bool m_maxCommittedProposalIndexFetched = {false}; + + PBFTProposalListPtr m_stateProposals = nullptr; + std::atomic_bool m_stateFetched = {false}; + size_t m_timeout = 10000; + bcos::protocol::BlockNumber c_reservedCheckPointSize = 5; + + boost::condition_variable m_signalled; + boost::mutex x_signalled; + std::function m_finalizeHandler; + std::function + m_onStableCheckPointCommitFailed; + std::shared_ptr m_commitBlockWorker; +}; +} // namespace bcos::consensus \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/utilities/Common.h" "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/utilities/Common.h" new file mode 100644 index 00000000..df6390af --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/bcos-pbft/pbft/utilities/Common.h" @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2021 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: yujiechen + * @date 2021-04-12 + */ +#pragma once +#include +#include +#include + +#define PBFT_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("CONSENSUS") << LOG_BADGE("PBFT") +#define PBFT_STORAGE_LOG(LEVEL) \ + BCOS_LOG(LEVEL) << LOG_BADGE("CONSENSUS") << LOG_BADGE("PBFT") << LOG_BADGE("STORAGE") + +namespace bcos::consensus +{ +enum PacketType : uint32_t +{ + PrePreparePacket = 0x00, + PreparePacket = 0x01, + CommitPacket = 0x02, + ViewChangePacket = 0x03, + NewViewPacket = 0x04, + CommittedProposalRequest = 0x5, + CommittedProposalResponse = 0x6, + PreparedProposalRequest = 0x7, + PreparedProposalResponse = 0x8, + CheckPoint = 0x9, + RecoverRequest = 0xa, + RecoverResponse = 0xb, +}; +DERIVE_BCOS_EXCEPTION(UnknownPBFTMsgType); +DERIVE_BCOS_EXCEPTION(InitPBFTException); +} // namespace bcos::consensus \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-pbft/test/CMakeLists.txt" new file mode 100644 index 00000000..7dee2194 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/test/CMakeLists.txt" @@ -0,0 +1,30 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of bcos-framework +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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(GLOB_RECURSE SOURCES "*.cpp" "*.h" "*.sol") + +# cmake settings +set(TEST_BINARY_NAME test-bcos-pbft) +find_package(Protobuf REQUIRED) + +add_executable(${TEST_BINARY_NAME} ${SOURCES}) +target_include_directories(${TEST_BINARY_NAME} PRIVATE . ${CMAKE_SOURCE_DIR}) + +find_package(Boost REQUIRED unit_test_framework) + +target_link_libraries(${TEST_BINARY_NAME} ${PBFT_TARGET} ${TABLE_TARGET} bcos-crypto ${TARS_PROTOCOL_TARGET} protobuf::libprotobuf Boost::unit_test_framework) +add_test(NAME test-pbft WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/test/unittests/core/TimerTest.cpp" "b/BFPL\345\243\271/bcos-pbft/test/unittests/core/TimerTest.cpp" new file mode 100644 index 00000000..d8891bd7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/test/unittests/core/TimerTest.cpp" @@ -0,0 +1,117 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for Timer + * @file TimerTest.cpp + * @author: yujiechen + * @date 2021-04-26 + */ +#include "bcos-pbft/pbft/engine/PBFTTimer.h" +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::consensus; +namespace bcos +{ +namespace test +{ +class FakeTimer : public Timer +{ +public: + explicit FakeTimer(uint64_t _timeout) : Timer(_timeout) {} + ~FakeTimer() override {} + void setTriggerTimeout(bool _triggerTimeout) { m_triggerTimeout = _triggerTimeout; } + bool triggerTimeout() { return m_triggerTimeout; } + void registerTimeoutHandler(std::function) override {} + +protected: + // invoked everytime when it reaches the timeout + void run() override + { + std::cout << "### run timeout handler now" << std::endl; + m_triggerTimeout = true; + } + +private: + std::atomic_bool m_triggerTimeout = false; +}; + +BOOST_FIXTURE_TEST_SUITE(TimerTest, TestPromptFixture) +BOOST_AUTO_TEST_CASE(testTimer) +{ + uint64_t timeoutInterval = 200; + auto timer = std::make_shared(timeoutInterval); + auto startT = utcTime(); + for (size_t i = 0; i < 4; i++) + { + timer->setTriggerTimeout(false); + // start the timer + timer->start(); + // sleep + startT = utcTime(); + std::this_thread::sleep_for(std::chrono::milliseconds(timeoutInterval + 200)); + std::cout << "#### sleep eclipse:" << utcTime() - startT; + // stop the timer + timer->stop(); + // check the value + std::cout << "##### testTimer: index" << i << std::endl; + std::cout << std::endl; + auto eclipse = utcTime() - startT; + if (eclipse > timeoutInterval) + { + BOOST_CHECK(timer->triggerTimeout() == true); + } + } + + std::cout << std::endl; + std::cout << "#### case1" << std::endl; + timer->setTriggerTimeout(false); + timer->start(); + startT = utcTime(); + std::this_thread::sleep_for(std::chrono::milliseconds(timeoutInterval - 100)); + std::cout << "#### sleep eclipse:" << utcTime() - startT; + auto eclipse = utcTime() - startT; + if (eclipse < timeoutInterval) + { + BOOST_CHECK(timer->triggerTimeout() == false); + } + timer->stop(); + + std::cout << std::endl; + std::cout << "#### case2" << std::endl; + // reset the timer + timer->setTriggerTimeout(false); + timer->reset(60); + timer->start(); + startT = utcTime(); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + std::cout << "#### sleep eclipse:" << utcTime() - startT; + BOOST_CHECK(timer->triggerTimeout() == true); + timer->stop(); +} +BOOST_AUTO_TEST_CASE(testPBFTTimer) +{ + uint64_t timeoutInterval = 100; + auto timer = std::make_shared(timeoutInterval); + timer->start(); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/test/unittests/main/main.cpp" "b/BFPL\345\243\271/bcos-pbft/test/unittests/main/main.cpp" new file mode 100644 index 00000000..470010ce --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/test/unittests/main/main.cpp" @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 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 main.cpp + * @author: yujiechen, jimmyshi + * @date 2021-02-24 + */ +#define BOOST_TEST_MODULE FISCO_BCOS_Tests +#define BOOST_TEST_MAIN + +#include +#include \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/test/unittests/pbft/PBFTConfigTest.cpp" "b/BFPL\345\243\271/bcos-pbft/test/unittests/pbft/PBFTConfigTest.cpp" new file mode 100644 index 00000000..6d48f3b2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/test/unittests/pbft/PBFTConfigTest.cpp" @@ -0,0 +1,173 @@ +/** + * Copyright (C) 2021 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. + * + * @brief unit tests for PBFTConfig + * @file PBFTConfigTest.cpp + * @author: yujiechen + * @date 2021-05-28 + */ +#include +#include + +#include "bcos-crypto/interfaces/crypto/KeyPairInterface.h" +#include "test/unittests/pbft/PBFTFixture.h" +#include "test/unittests/protocol/FakePBFTMessage.h" +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::consensus; +using namespace bcos::crypto; +using namespace bcos::protocol; +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(PBFTConfigTest, TestPromptFixture) +BOOST_AUTO_TEST_CASE(testPBFTInit) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + bcos::crypto::KeyPairInterface::Ptr keyPair = signatureImpl->generateKeyPair(); + auto gateWay = std::make_shared(); + + size_t consensusTimeout = 4; + size_t txCountLimit = 2000; + auto faker = std::make_shared(cryptoSuite, keyPair, nullptr, txCountLimit); + faker->frontService()->setGateWay(gateWay); + + // case1: with zero consensus node + BOOST_CHECK_THROW(faker->init(), InitConsensusException); + + // case2: with 10 consensus nodes + auto pbftConfig = faker->pbftConfig(); + auto ledgerConfig = faker->ledger()->ledgerConfig(); + auto pbftEngine = faker->pbftEngine(); + + auto proposalIndex = ledgerConfig->blockNumber() + 1; + auto parent = (faker->ledger()->ledgerData())[ledgerConfig->blockNumber()]; + auto block = faker->ledger()->init(parent->blockHeader(), true, proposalIndex, 0, 0); + auto blockData = std::make_shared(); + block->encode(*blockData); + + faker->appendConsensusNode(faker->nodeID()); + + // case3: with expired state + auto pbftMsgFixture = std::make_shared(cryptoSuite, keyPair); + auto fakedProposal = pbftMsgFixture->fakePBFTProposal(faker->ledger()->blockNumber() - 1, + ledgerConfig->hash(), *blockData, std::vector(), std::vector()); + pbftConfig->storage()->asyncCommitProposal(fakedProposal); + faker->init(); + + BOOST_CHECK(pbftConfig->progressedIndex() == faker->ledger()->blockNumber() + 1); + auto cacheProcessor = + std::dynamic_pointer_cast(pbftEngine->cacheProcessor()); + BOOST_CHECK(cacheProcessor->stableCheckPointQueueSize() == 0); + + fakedProposal = pbftMsgFixture->fakePBFTProposal(faker->ledger()->blockNumber(), + ledgerConfig->hash(), *blockData, std::vector(), std::vector()); + pbftConfig->storage()->asyncCommitProposal(fakedProposal); + faker->init(); + + BOOST_CHECK(pbftConfig->progressedIndex() == faker->ledger()->blockNumber() + 1); + BOOST_CHECK(cacheProcessor->stableCheckPointQueueSize() == 0); + + faker->init(); + BOOST_CHECK(pbftConfig->nodeIndex() == 0); + BOOST_CHECK( + pbftConfig->getConsensusNodeByIndex(0)->nodeID()->data() == faker->nodeID()->data()); + + // check nodeIndex + size_t consensusNodesSize = 9; + for (size_t i = 0; i < consensusNodesSize; i++) + { + auto peerKeyPair = signatureImpl->generateKeyPair(); + faker->appendConsensusNode(peerKeyPair->publicKey()); + faker->init(); + auto nodeIndex = pbftConfig->nodeIndex(); + auto node = pbftConfig->getConsensusNodeByIndex(nodeIndex); + BOOST_CHECK(node->nodeID()->data() == faker->nodeID()->data()); + } + BOOST_CHECK(pbftConfig->consensusNodeList().size() == (consensusNodesSize + 1)); + BOOST_CHECK(pbftConfig->nodeID()->data() == faker->nodeID()->data()); + + // check params + BOOST_CHECK(pbftConfig->isConsensusNode()); + pbftConfig->setConsensusTimeout(consensusTimeout); + BOOST_CHECK(pbftConfig->consensusTimeout() == consensusTimeout); + BOOST_CHECK(pbftConfig->blockTxCountLimit() == txCountLimit); + // Note: should update this check if consensusNodesSize has been changed + BOOST_CHECK(pbftConfig->minRequiredQuorum() == 7); + BOOST_CHECK(pbftConfig->committedProposal()->index() == faker->ledger()->blockNumber()); + BOOST_CHECK(pbftConfig->committedProposal()->hash() == ledgerConfig->hash()); + // check PBFT related information + BOOST_CHECK(pbftConfig->progressedIndex() == faker->ledger()->blockNumber() + 1); + BOOST_CHECK(pbftConfig->view() == 0); + BOOST_CHECK(pbftConfig->toView() == 0); + // check object + BOOST_CHECK(pbftConfig->cryptoSuite()); + BOOST_CHECK(pbftConfig->pbftMessageFactory()); + BOOST_CHECK(pbftConfig->frontService()); + BOOST_CHECK(pbftConfig->codec()); + BOOST_CHECK(pbftConfig->validator()); + BOOST_CHECK(pbftConfig->storage()); + BOOST_CHECK(pbftConfig->highWaterMark() == + pbftConfig->progressedIndex() + pbftConfig->waterMarkLimit()); + BOOST_CHECK(pbftConfig->stateMachine()); + BOOST_CHECK(pbftConfig->expectedCheckPoint() == faker->ledger()->blockNumber() + 1); + + +#if 0 + // case4: with new committed index, but invalid data + auto hash = hashImpl->hash(std::string("checkpoint")); + fakedProposal = pbftMsgFixture->fakePBFTProposal(faker->ledger()->blockNumber() + 1, hash, + bytes(), std::vector(), std::vector()); + pbftConfig->storage()->asyncCommitProposal(fakedProposal); + faker->init(); + BOOST_CHECK(pbftConfig->progressedIndex() == faker->ledger()->blockNumber() + 1); + BOOST_CHECK(pbftEngine->cacheProcessor()->committedQueue() == 0); + BOOST_CHECK(pbftConfig->expectedCheckPoint() == faker->ledger()->blockNumber() + 1); +#endif + + // case5: with new committed index and valid data, collect enough checkpoint proposal and commit + // it + std::cout << "##### case5: with new committed index and valid data" << std::endl; + faker->clearConsensusNodeList(); + faker->appendConsensusNode(faker->nodeID()); + auto blockHeader = block->blockHeader(); + fakedProposal = pbftMsgFixture->fakePBFTProposal(proposalIndex, blockHeader->hash(), *blockData, + std::vector(), std::vector()); + pbftConfig->storage()->asyncCommitProposal(fakedProposal); + faker->init(); + BOOST_CHECK(pbftConfig->minRequiredQuorum() == 1); + auto startT = utcTime(); + while (pbftConfig->committedProposal()->index() != proposalIndex && + (utcTime() - startT <= 60 * 1000)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + BOOST_CHECK(faker->ledger()->blockNumber() == proposalIndex); + BOOST_CHECK(pbftConfig->progressedIndex() == proposalIndex + 1); + BOOST_CHECK(cacheProcessor->committedQueueSize() == 0); + BOOST_CHECK(cacheProcessor->stableCheckPointQueueSize() == 0); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-pbft/test/unittests/pbft/PBFTEngineTest.cpp" "b/BFPL\345\243\271/bcos-pbft/test/unittests/pbft/PBFTEngineTest.cpp" new file mode 100644 index 00000000..adcd08ae --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/test/unittests/pbft/PBFTEngineTest.cpp" @@ -0,0 +1,279 @@ +/** + * Copyright (C) 2021 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. + * + * @brief unit tests for PBFTEngine + * @file PBFTEngineTest.cpp + * @author: yujiechen + * @date 2021-05-31 + */ +#include +#include + +#include "test/unittests/pbft/PBFTFixture.h" +#include "test/unittests/protocol/FakePBFTMessage.h" +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::consensus; + +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(PBFTEngineTest, TestPromptFixture) +inline bool shouldExit(std::map& _consensusNodes, + BlockNumber _expectedNumber, size_t _connectedNodes) +{ + for (IndexType i = 0; i < _connectedNodes; i++) + { + auto faker = _consensusNodes[i]; + if (faker->ledger()->blockNumber() != _expectedNumber) + { + return false; + } + } + return true; +} + +void testPBFTEngineWithFaulty(size_t _consensusNodes, size_t _connectedNodes) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + + BlockNumber currentBlockNumber = 19; + std::cout << "### createFakers: " << currentBlockNumber << std::endl; + auto fakerMap = createFakers(cryptoSuite, _consensusNodes, currentBlockNumber, _connectedNodes); + std::cout << "### createFakers: " << currentBlockNumber << " success" << std::endl; + // check the leader notify the sealer to seal proposals + IndexType leaderIndex = 0; + auto leaderFaker = fakerMap[leaderIndex]; + size_t expectedProposal = (size_t)(leaderFaker->ledger()->blockNumber() + 1); + + // the leader submit proposals + auto pbftMsgFixture = std::make_shared(cryptoSuite, leaderFaker->keyPair()); + auto block = fakeBlock(cryptoSuite, leaderFaker, expectedProposal, 10); + auto blockData = std::make_shared(); + block->encode(*blockData); + auto blockHeader = block->blockHeader(); + BOOST_CHECK(blockHeader); + // handle pre-prepare message ,broadcast prepare messages and handle the collectted + // prepare-request + // check the duplicated case + for (size_t i = 0; i < 3; i++) + { + leaderFaker->pbftEngine()->asyncSubmitProposal( + false, ref(*blockData), blockHeader->number(), blockHeader->hash(), nullptr); + } + // Discontinuous case + auto faker = fakerMap[3]; + block = fakeBlock(cryptoSuite, faker, currentBlockNumber + 4, 10); + blockHeader = block->blockHeader(); + blockData = std::make_shared(); + block->encode(*blockData); + faker->pbftEngine()->asyncSubmitProposal( + false, ref(*blockData), blockHeader->number(), blockHeader->hash(), nullptr); + + // the next leader seal the next block + IndexType nextLeaderIndex = 1; + auto nextLeaderFacker = fakerMap[nextLeaderIndex]; + auto nextBlock = expectedProposal + 1; + block = fakeBlock(cryptoSuite, nextLeaderFacker, nextBlock, 10); + blockHeader = block->blockHeader(); + blockData = std::make_shared(); + block->encode(*blockData); + nextLeaderFacker->pbftEngine()->asyncSubmitProposal( + false, ref(*blockData), blockHeader->number(), blockHeader->hash(), nullptr); + + // handle prepare message and broadcast commit messages + auto startT = utcTime(); + while (!shouldExit(fakerMap, currentBlockNumber + 2, _connectedNodes) && + (utcTime() - startT <= 60 * 1000)) + { + for (auto const& node : fakerMap) + { + node.second->pbftEngine()->executeWorkerByRoundbin(); + } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + // supplement expectedProposal + 2 + faker = fakerMap[2]; + block = fakeBlock(cryptoSuite, faker, currentBlockNumber + 3, 10); + blockHeader = block->blockHeader(); + blockData = std::make_shared(); + block->encode(*blockData); + faker->pbftEngine()->asyncSubmitProposal( + false, ref(*blockData), blockHeader->number(), blockHeader->hash(), nullptr); + + startT = utcTime(); + while (!shouldExit(fakerMap, currentBlockNumber + 4, _connectedNodes) && + (utcTime() - startT <= 60 * 1000)) + { + for (auto const& node : fakerMap) + { + node.second->pbftEngine()->executeWorkerByRoundbin(); + } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} + +// TODO: Remove this test due to memory access violation +BOOST_AUTO_TEST_CASE(testPBFTEngineWithAllNonFaulty) +{ + size_t consensusNodeSize = 10; + // case1: all non-faulty + std::cout << "testPBFTEngineWithFaulty with 10 non-faulty" << std::endl; + testPBFTEngineWithFaulty(consensusNodeSize, consensusNodeSize); + std::cout << "testPBFTEngineWithFaulty with 10 non-faulty success" << std::endl; + // case2: with f=3 faulty + std::cout << "testPBFTEngineWithFaulty with 7 non-faulty" << std::endl; + testPBFTEngineWithFaulty(consensusNodeSize, 7); + std::cout << "testPBFTEngineWithFaulty with 7 non-faulty success" << std::endl; +} + +BOOST_AUTO_TEST_CASE(testHandlePrePrepareMsg) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + + size_t consensusNodeSize = 2; + size_t currentBlockNumber = 10; + auto fakerMap = + createFakers(cryptoSuite, consensusNodeSize, currentBlockNumber, consensusNodeSize); + + auto expectedIndex = (fakerMap[0])->pbftConfig()->progressedIndex(); + auto expectedLeader = (fakerMap[0])->pbftConfig()->leaderIndex(expectedIndex); + auto leaderFaker = fakerMap[expectedLeader]; + auto nonLeaderFaker = fakerMap[(expectedLeader + 1) % consensusNodeSize]; + + auto ledgerConfig = leaderFaker->ledger()->ledgerConfig(); + auto parent = (leaderFaker->ledger()->ledgerData())[ledgerConfig->blockNumber()]; + auto block = leaderFaker->ledger()->init(parent->blockHeader(), true, expectedIndex, 0, 0); + auto blockData = std::make_shared(); + block->encode(*blockData); + + // case1: invalid block number + auto hash = hashImpl->hash(std::string("invalidCase")); + auto leaderMsgFixture = + std::make_shared(cryptoSuite, leaderFaker->keyPair()); + auto index = (expectedIndex - 1); + + auto pbftMsg = fakePBFTMessage(utcTime(), 1, leaderFaker->pbftConfig()->view(), expectedLeader, + hash, index, bytes(), 0, leaderMsgFixture, PacketType::PrePreparePacket); + + auto fakedProposal = + leaderMsgFixture->fakePBFTProposal(leaderFaker->ledger()->blockNumber() + 1, hash, + *blockData, std::vector(), std::vector()); + pbftMsg->setConsensusProposal(fakedProposal); + auto data = leaderFaker->pbftConfig()->codec()->encode(pbftMsg); + + nonLeaderFaker->pbftEngine()->onReceivePBFTMessage( + nullptr, nonLeaderFaker->keyPair()->publicKey(), ref(*data), nullptr); + nonLeaderFaker->pbftEngine()->executeWorker(); + BOOST_CHECK(!nonLeaderFaker->pbftEngine()->cacheProcessor()->existPrePrepare(pbftMsg)); + + // case2: invalid view + index = expectedIndex; + ViewType view = 10; + for (auto node : fakerMap) + { + node.second->pbftConfig()->setView(view); + node.second->pbftConfig()->setToView(view); + } + expectedLeader = (fakerMap[0])->pbftConfig()->leaderIndex(index); + leaderFaker = fakerMap[expectedLeader]; + pbftMsg = fakePBFTMessage(utcTime(), 1, (leaderFaker->pbftConfig()->view() - 1), expectedLeader, + hash, index, bytes(), 0, leaderMsgFixture, PacketType::PrePreparePacket); + pbftMsg->setConsensusProposal(fakedProposal); + + data = leaderFaker->pbftConfig()->codec()->encode(pbftMsg); + nonLeaderFaker = fakerMap[(expectedLeader + 1) % consensusNodeSize]; + nonLeaderFaker->pbftEngine()->onReceivePBFTMessage( + nullptr, nonLeaderFaker->keyPair()->publicKey(), ref(*data), nullptr); + nonLeaderFaker->pbftEngine()->executeWorker(); + BOOST_CHECK(!nonLeaderFaker->pbftEngine()->cacheProcessor()->existPrePrepare(pbftMsg)); + + // case3: not from the leader + pbftMsg = fakePBFTMessage(utcTime(), 1, (nonLeaderFaker->pbftConfig()->view()), + (expectedLeader + 1) % consensusNodeSize, hash, index, bytes(), 0, leaderMsgFixture, + PacketType::PrePreparePacket); + pbftMsg->setConsensusProposal(fakedProposal); + + data = nonLeaderFaker->pbftConfig()->codec()->encode(pbftMsg); + leaderFaker->pbftEngine()->onReceivePBFTMessage( + nullptr, leaderFaker->keyPair()->publicKey(), ref(*data), nullptr); + leaderFaker->pbftEngine()->executeWorker(); + BOOST_CHECK(!leaderFaker->pbftEngine()->cacheProcessor()->existPrePrepare(pbftMsg)); + + // case4: invalid signature + pbftMsg = fakePBFTMessage(utcTime(), 1, (leaderFaker->pbftConfig()->view()), expectedLeader, + hash, index, bytes(), 0, leaderMsgFixture, PacketType::PrePreparePacket); + pbftMsg->setConsensusProposal(fakedProposal); + + data = nonLeaderFaker->pbftConfig()->codec()->encode(pbftMsg); + nonLeaderFaker->pbftEngine()->onReceivePBFTMessage( + nullptr, nonLeaderFaker->keyPair()->publicKey(), ref(*data), nullptr); + nonLeaderFaker->pbftEngine()->executeWorker(); + BOOST_CHECK(!nonLeaderFaker->pbftEngine()->cacheProcessor()->existPrePrepare(pbftMsg)); + + // case5: invalid pre-prepare for txpool verify failed + data = leaderFaker->pbftConfig()->codec()->encode(pbftMsg); + nonLeaderFaker->txpool()->setVerifyResult(false); + nonLeaderFaker->pbftEngine()->onReceivePBFTMessage( + nullptr, nonLeaderFaker->keyPair()->publicKey(), ref(*data), nullptr); + nonLeaderFaker->pbftEngine()->executeWorker(); + BOOST_CHECK(!nonLeaderFaker->pbftEngine()->cacheProcessor()->existPrePrepare(pbftMsg)); + + // case6: valid pre-prepare + nonLeaderFaker->txpool()->setVerifyResult(true); + nonLeaderFaker->pbftEngine()->onReceivePBFTMessage( + nullptr, nonLeaderFaker->keyPair()->publicKey(), ref(*data), nullptr); + nonLeaderFaker->pbftEngine()->executeWorker(); + auto startT = utcTime(); + while (!nonLeaderFaker->pbftEngine()->cacheProcessor()->existPrePrepare(pbftMsg) && + (utcTime() - startT <= 60 * 1000)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + BOOST_CHECK(nonLeaderFaker->pbftEngine()->cacheProcessor()->existPrePrepare(pbftMsg)); + nonLeaderFaker->pbftConfig()->setConsensusTimeout(200); + leaderFaker->pbftConfig()->setConsensusTimeout(200); + leaderFaker->pbftConfig()->timer()->start(); + startT = utcTime(); + while ( + (!leaderFaker->pbftEngine()->isTimeout() || !nonLeaderFaker->pbftEngine()->isTimeout()) && + (utcTime() - startT <= 60 * 1000)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + // wait to trigger viewchange since not reach consensus + startT = utcTime(); + while ((leaderFaker->pbftEngine()->isTimeout() || nonLeaderFaker->pbftEngine()->isTimeout()) && + (utcTime() - startT <= 60 * 1000)) + { + nonLeaderFaker->pbftEngine()->executeWorkerByRoundbin(); + leaderFaker->pbftEngine()->executeWorkerByRoundbin(); + } +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/test/unittests/pbft/PBFTFixture.h" "b/BFPL\345\243\271/bcos-pbft/test/unittests/pbft/PBFTFixture.h" new file mode 100644 index 00000000..5b5f342d --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/test/unittests/pbft/PBFTFixture.h" @@ -0,0 +1,418 @@ +/** + * Copyright (C) 2021 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. + * + * @brief fixture for the PBFT + * @file PBFTFixture.h + * @author: yujiechen + * @date 2021-05-28 + */ +#pragma once +#include "bcos-crypto/interfaces/crypto/KeyPairInterface.h" +#include "bcos-framework/storage/KVStorageHelper.h" +#include "bcos-pbft/core/StateMachine.h" +#include "bcos-pbft/pbft/PBFTFactory.h" +#include "bcos-pbft/pbft/PBFTImpl.h" +#include "bcos-pbft/pbft/storage/LedgerStorage.h" +#include +#include +#include +#include +#include +#include +#include +#include +// #include +// #include +// #include +// #include +#include +#include +#include +#include +#include + +using namespace bcos::crypto; +using namespace bcos::front; +using namespace bcos::storage; +using namespace bcos::ledger; +using namespace bcos::txpool; +using namespace bcos::sealer; +using namespace bcos::protocol; +using namespace bcos::scheduler; +using namespace bcos::consensus; + +namespace bcos +{ +namespace test +{ +class FakePBFTConfig : public PBFTConfig +{ +public: + using Ptr = std::shared_ptr; + FakePBFTConfig(bcos::crypto::CryptoSuite::Ptr _cryptoSuite, + bcos::crypto::KeyPairInterface::Ptr _keyPair, + std::shared_ptr _pbftMessageFactory, + std::shared_ptr _codec, std::shared_ptr _validator, + std::shared_ptr _frontService, + StateMachineInterface::Ptr _stateMachine, PBFTStorage::Ptr _storage) + : PBFTConfig(_cryptoSuite, _keyPair, _pbftMessageFactory, _codec, _validator, _frontService, + _stateMachine, _storage) + {} + + ~FakePBFTConfig() override {} + + virtual void setMinRequiredQuorum(uint64_t _quorum) { m_minRequiredQuorum = _quorum; } +}; +class FakePBFTCache : public PBFTCache +{ +public: + using Ptr = std::shared_ptr; + FakePBFTCache(PBFTConfig::Ptr _config, BlockNumber _index) : PBFTCache(_config, _index) {} + ~FakePBFTCache() override {} + + PBFTMessageInterface::Ptr prePrepare() { return m_prePrepare; } + void intoPrecommit() override { PBFTCache::intoPrecommit(); } +}; + +class FakePBFTCacheFactory : public PBFTCacheFactory +{ +public: + using Ptr = std::shared_ptr; + FakePBFTCacheFactory() = default; + ~FakePBFTCacheFactory() override {} + + PBFTCache::Ptr createPBFTCache(PBFTConfig::Ptr _config, BlockNumber _index, + std::function) override + { + return std::make_shared(_config, _index); + } +}; + +class FakeCacheProcessor : public PBFTCacheProcessor +{ +public: + using Ptr = std::shared_ptr; + explicit FakeCacheProcessor(PBFTCacheFactory::Ptr _cacheFactory, PBFTConfig::Ptr _config) + : PBFTCacheProcessor(_cacheFactory, _config) + {} + + ~FakeCacheProcessor() override {} + + PBFTCachesType& caches() { return m_caches; } + size_t stableCheckPointQueueSize() const { return m_stableCheckPointQueue.size(); } + size_t committedQueueSize() const { return m_committedQueue.size(); } + bool checkPrecommitWeight(PBFTMessageInterface::Ptr _precommitMsg) override + { + PBFTCacheProcessor::checkPrecommitWeight(_precommitMsg); + return true; + } +}; + + +class FakePBFTEngine : public PBFTEngine +{ +public: + using Ptr = std::shared_ptr; + explicit FakePBFTEngine(PBFTConfig::Ptr _config) : PBFTEngine(_config) + { + auto cacheFactory = std::make_shared(); + m_cacheProcessor = std::make_shared(cacheFactory, _config); + m_logSync = std::make_shared(_config, m_cacheProcessor); + m_cacheProcessor->registerProposalAppliedHandler( + boost::bind(&FakePBFTEngine::onProposalApplied, this, boost::placeholders::_1, + boost::placeholders::_2, boost::placeholders::_3)); + m_cacheProcessor->registerOnLoadAndVerifyProposalFinish( + boost::bind(&FakePBFTEngine::onLoadAndVerifyProposalFinish, this, + boost::placeholders::_1, boost::placeholders::_2, boost::placeholders::_3)); + initSendResponseHandler(); + _config->enableAsMasterNode(true); + } + ~FakePBFTEngine() override {} + + void onReceivePBFTMessage(bcos::Error::Ptr _error, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, std::function _sendResponse) override + { + PBFTEngine::onReceivePBFTMessage(_error, _nodeID, _data, _sendResponse); + } + + // PBFT main processing function + void executeWorker() override + { + while (!msgQueue()->empty()) + { + PBFTEngine::executeWorker(); + } + } + + void executeWorkerByRoundbin() { return PBFTEngine::executeWorker(); } + + void onRecvProposal(bool _containSysTxs, bytesConstRef _proposalData, + bcos::protocol::BlockNumber _proposalIndex, + bcos::crypto::HashType const& _proposalHash) override + { + PBFTEngine::onRecvProposal(_containSysTxs, _proposalData, _proposalIndex, _proposalHash); + } + + bool handlePrePrepareMsg(std::shared_ptr _prePrepareMsg, + bool _needVerifyProposal = true, bool _generatedFromNewView = false, + bool _needCheckSignature = true) override + { + return PBFTEngine::handlePrePrepareMsg( + _prePrepareMsg, _needVerifyProposal, _generatedFromNewView, _needCheckSignature); + } + + PBFTMsgQueuePtr msgQueue() { return m_msgQueue; } +}; + +class FakePBFTImpl : public PBFTImpl +{ +public: + explicit FakePBFTImpl(PBFTEngine::Ptr _pbftEngine) : PBFTImpl(_pbftEngine) + { + m_running = true; + m_masterNode.store(true); + } + void start() override { m_pbftEngine->recoverState(); } + void init() override + { + PBFTImpl::init(); + start(); + } + ~FakePBFTImpl() {} +}; + +class FakePBFTFactory : public PBFTFactory +{ +public: + using Ptr = std::shared_ptr; + FakePBFTFactory(bcos::crypto::CryptoSuite::Ptr _cryptoSuite, + bcos::crypto::KeyPairInterface::Ptr _keyPair, + std::shared_ptr _frontService, + std::shared_ptr _storage, + std::shared_ptr _ledger, + bcos::scheduler::SchedulerInterface::Ptr _scheduler, + bcos::txpool::TxPoolInterface::Ptr _txpool, bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::protocol::TransactionSubmitResultFactory::Ptr _txResultFactory) + : PBFTFactory(_cryptoSuite, _keyPair, _frontService, _storage, _ledger, _scheduler, _txpool, + _blockFactory, _txResultFactory) + {} + + PBFTImpl::Ptr createPBFT() override + { + auto pbft = PBFTFactory::createPBFT(); + auto orgPBFTConfig = pbft->pbftEngine()->pbftConfig(); + auto stateMachine = std::make_shared(m_scheduler, m_blockFactory); + + PBFT_LOG(DEBUG) << LOG_DESC("create pbftStorage"); + auto pbftStorage = std::make_shared( + m_scheduler, m_storage, m_blockFactory, orgPBFTConfig->pbftMessageFactory()); + + auto pbftConfig = std::make_shared(m_cryptoSuite, m_keyPair, + orgPBFTConfig->pbftMessageFactory(), orgPBFTConfig->codec(), orgPBFTConfig->validator(), + orgPBFTConfig->frontService(), stateMachine, pbftStorage); + PBFT_LOG(DEBUG) << LOG_DESC("create PBFTEngine"); + auto pbftEngine = std::make_shared(pbftConfig); + + PBFT_LOG(INFO) << LOG_DESC("create PBFT"); + auto fakedPBFT = std::make_shared(pbftEngine); + auto ledgerFetcher = std::make_shared(m_ledger); + fakedPBFT->setLedgerFetcher(ledgerFetcher); + pbftConfig->setTimeoutState(false); + pbftConfig->timer()->stop(); + return fakedPBFT; + } +}; + +class PBFTFixture +{ +public: + using Ptr = std::shared_ptr; + PBFTFixture(CryptoSuite::Ptr _cryptoSuite, KeyPairInterface::Ptr _keyPair, + FakeLedger::Ptr _ledger = nullptr, size_t _txCountLimit = 1000) + : m_cryptoSuite(_cryptoSuite), m_keyPair(_keyPair), m_nodeId(_keyPair->publicKey()) + { + // create block factory + m_blockFactory = createBlockFactory(_cryptoSuite); + + // create fakeFrontService + m_frontService = std::make_shared(_keyPair->publicKey()); + + // create KVStorageHelper + m_storage = std::make_shared(std::make_shared(nullptr)); + + // create fakeLedger + if (_ledger == nullptr) + { + m_ledger = std::make_shared(m_blockFactory, 20, 10, 10); + 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->ledgerConfig()->setConsensusTimeout(_consensusTimeout * 20); + m_ledger->ledgerConfig()->setBlockTxCountLimit(_txCountLimit); + } + else + { + m_ledger = _ledger; + } + // create fakeTxPool + m_txpool = std::make_shared(); + // create FakeScheduler + m_scheduler = std::make_shared(m_ledger, m_blockFactory); + + auto txResultFactory = std::make_shared(); + + auto pbftFactory = std::make_shared(_cryptoSuite, _keyPair, m_frontService, + m_storage, m_ledger, m_scheduler, m_txpool, m_blockFactory, txResultFactory); + m_pbft = pbftFactory->createPBFT(); + m_pbftEngine = std::dynamic_pointer_cast(m_pbft->pbftEngine()); + m_pbft->registerFaultyDiscriminator([](bcos::crypto::NodeIDPtr) { return false; }); + } + + virtual ~PBFTFixture() {} + + void init() { m_pbft->init(); } + + void appendConsensusNode(ConsensusNode::Ptr _node) + { + m_ledger->ledgerConfig()->mutableConsensusNodeList().push_back(_node); + pbftConfig()->setConsensusNodeList(m_ledger->ledgerConfig()->mutableConsensusNodeList()); + bcos::crypto::NodeIDSet connectedNodeList; + for (auto const& node : m_ledger->ledgerConfig()->mutableConsensusNodeList()) + { + connectedNodeList.insert(node->nodeID()); + } + pbftConfig()->setConnectedNodeList(connectedNodeList); + m_frontService->setNodeIDList(connectedNodeList); + } + + void appendConsensusNode(PublicPtr _nodeId) + { + auto node = std::make_shared(_nodeId, 1); + appendConsensusNode(node); + } + + void updateSwitchPerid() {} + + void clearConsensusNodeList() { m_ledger->ledgerConfig()->mutableConsensusNodeList().clear(); } + + FakeFrontService::Ptr frontService() { return m_frontService; } + std::shared_ptr storage() { return m_storage; } + FakeLedger::Ptr ledger() { return m_ledger; } + FakeTxPool::Ptr txpool() { return m_txpool; } + FakeScheduler::Ptr scheduler() { return m_scheduler; } + PBFTImpl::Ptr pbft() { return m_pbft; } + PBFTConfig::Ptr pbftConfig() { return m_pbft->pbftEngine()->pbftConfig(); } + PublicPtr nodeID() { return m_nodeId; } + + FakePBFTEngine::Ptr pbftEngine() { return m_pbftEngine; } + KeyPairInterface::Ptr keyPair() { return m_keyPair; } + + void setFrontService(FakeFrontService::Ptr _fakeFrontService) + { + m_frontService = _fakeFrontService; + } + + BlockFactory::Ptr blockFactory() { return m_blockFactory; } + +private: + CryptoSuite::Ptr m_cryptoSuite; + KeyPairInterface::Ptr m_keyPair; + PublicPtr m_nodeId; + BlockFactory::Ptr m_blockFactory; + + FakeFrontService::Ptr m_frontService; + std::shared_ptr m_storage; + FakeLedger::Ptr m_ledger; + FakeTxPool::Ptr m_txpool; + FakeScheduler::Ptr m_scheduler; + FakePBFTEngine::Ptr m_pbftEngine; + PBFTImpl::Ptr m_pbft; +}; +using PBFTFixtureList = std::vector; + +inline PBFTFixture::Ptr createPBFTFixture( + CryptoSuite::Ptr _cryptoSuite, FakeLedger::Ptr _ledger = nullptr, size_t _txCountLimit = 1000) +{ + bcos::crypto::KeyPairInterface::Ptr keyPair = _cryptoSuite->signatureImpl()->generateKeyPair(); + return std::make_shared(_cryptoSuite, keyPair, _ledger, _txCountLimit); +} + +inline std::map createFakers(CryptoSuite::Ptr _cryptoSuite, + size_t _consensusNodeSize, size_t _currentBlockNumber, size_t _connectedNodes, + size_t _txCountLimit = 1000) +{ + PBFTFixtureList fakerList; + // create block factory + auto blockFactory = createBlockFactory(_cryptoSuite); + + auto ledger = std::make_shared(blockFactory, _currentBlockNumber + 1, 10, 0); + for (size_t i = 0; i < _consensusNodeSize; i++) + { + // ensure all the block are consistent + auto fakedLedger = std::make_shared( + blockFactory, _currentBlockNumber + 1, 10, 0, ledger->sealerList()); + fakedLedger->setSystemConfig(SYSTEM_KEY_TX_COUNT_LIMIT, std::to_string(_txCountLimit)); + fakedLedger->setSystemConfig(SYSTEM_KEY_CONSENSUS_LEADER_PERIOD, std::to_string(1)); + // fakedLedger->ledgerConfig()->setConsensusTimeout(_consensusTimeout * 1000); + fakedLedger->ledgerConfig()->setBlockTxCountLimit(_txCountLimit); + auto peerFaker = createPBFTFixture(_cryptoSuite, fakedLedger, _txCountLimit); + fakerList.push_back(peerFaker); + } + + for (size_t i = 0; i < _consensusNodeSize; i++) + { + auto faker = fakerList[i]; + for (size_t j = 0; j < _consensusNodeSize; j++) + { + faker->appendConsensusNode(fakerList[j]->keyPair()->publicKey()); + } + } + // init the fakers + auto fakeGateWay = std::make_shared(); + for (size_t i = 0; i < _consensusNodeSize; i++) + { + auto faker = fakerList[i]; + faker->init(); + } + std::map indexToFakerMap; + for (size_t i = 0; i < _consensusNodeSize; i++) + { + auto faker = fakerList[i]; + indexToFakerMap[faker->pbftConfig()->nodeIndex()] = faker; + faker->frontService()->setGateWay(fakeGateWay); + } + for (IndexType i = 0; i < _connectedNodes; i++) + { + auto faker = indexToFakerMap[i]; + fakeGateWay->addConsensusInterface(faker->keyPair()->publicKey(), faker->pbft()); + } + return indexToFakerMap; +} + +inline Block::Ptr fakeBlock(CryptoSuite::Ptr _cryptoSuite, PBFTFixture::Ptr _faker, + size_t _proposalIndex, size_t _txsHashSize) +{ + auto ledgerConfig = _faker->ledger()->ledgerConfig(); + auto parent = (_faker->ledger()->ledgerData())[ledgerConfig->blockNumber()]; + auto block = _faker->ledger()->init(parent->blockHeader(), true, _proposalIndex, 0, 0); + for (size_t i = 0; i < _txsHashSize; i++) + { + auto hash = _cryptoSuite->hashImpl()->hash(std::to_string(i)); + auto txMetaData = _faker->blockFactory()->createTransactionMetaData(hash, hash.abridged()); + block->appendTransactionMetaData(txMetaData); + } + return block; +} +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/test/unittests/pbft/PBFTViewChangeTest.cpp" "b/BFPL\345\243\271/bcos-pbft/test/unittests/pbft/PBFTViewChangeTest.cpp" new file mode 100644 index 00000000..344c3528 --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/test/unittests/pbft/PBFTViewChangeTest.cpp" @@ -0,0 +1,159 @@ +/** + * Copyright (C) 2021 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. + * + * @brief unit tests for PBFT viewchange + * @file PBFTViewChangeTest.cpp + * @author: yujiechen + * @date 2021-06-01 + */ +#include +#include + +#include "test/unittests/pbft/PBFTFixture.h" +#include "test/unittests/protocol/FakePBFTMessage.h" +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(PBFTViewChangeTest, TestPromptFixture) +bool shouldExit(std::map& _fakers, BlockNumber _number) +{ + for (IndexType i = 0; i < _fakers.size(); i++) + { + auto faker = _fakers[i]; + if (faker->ledger()->blockNumber() != _number) + { + return false; + } + } + return true; +} +BOOST_AUTO_TEST_CASE(testViewChangeWithPrecommitProposals) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + size_t consensusNodeSize = 10; + size_t _connectedNodes = 10; + size_t currentBlockNumber = 11; + auto fakerMap = + createFakers(cryptoSuite, consensusNodeSize, currentBlockNumber, _connectedNodes); + + // check the leader notify the sealer to seal proposals + BlockNumber expectedProposal = (size_t)(fakerMap[0]->ledger()->blockNumber() + 1); + IndexType leaderIndex = fakerMap[0]->pbftConfig()->leaderIndex(expectedProposal); + auto leaderFaker = fakerMap[leaderIndex]; + + auto pbftMsgFixture = std::make_shared(cryptoSuite, leaderFaker->keyPair()); + auto block = fakeBlock(cryptoSuite, leaderFaker, expectedProposal, 10); + auto blockData = std::make_shared(); + block->encode(*blockData); + + // blockNumber + 3 + BlockNumber futureBlockIndex = expectedProposal + 2; + IndexType futureLeaderIndex = fakerMap[0]->pbftConfig()->leaderIndex(futureBlockIndex); + auto futureLeader = fakerMap[futureLeaderIndex]; + auto futureBlock = fakeBlock(cryptoSuite, futureLeader, futureBlockIndex, 10); + auto futureBlockData = std::make_shared(); + futureBlock->encode(*futureBlockData); + + auto blockHeader = block->blockHeader(); + // the leader submit proposal + leaderFaker->pbftEngine()->asyncSubmitProposal( + false, ref(*blockData), blockHeader->number(), blockHeader->hash(), nullptr); + // the future leader submit the proposal + auto futureBlockHeader = futureBlock->blockHeader(); + futureLeader->pbftEngine()->asyncSubmitProposal(false, ref(*futureBlockData), + futureBlockHeader->number(), futureBlockHeader->hash(), nullptr); + + auto cacheProcessor = + std::dynamic_pointer_cast(leaderFaker->pbftEngine()->cacheProcessor()); + BOOST_CHECK(cacheProcessor->caches().size() == 1); + auto cache = + std::dynamic_pointer_cast((cacheProcessor->caches())[expectedProposal]); + BOOST_CHECK(cache->prePrepare()); + BOOST_CHECK(cache->index() == expectedProposal); + BOOST_CHECK(cache->prePrepare()); + + auto futureCacheProcessor = + std::dynamic_pointer_cast(futureLeader->pbftEngine()->cacheProcessor()); + auto futureCache = std::dynamic_pointer_cast( + (futureCacheProcessor->caches())[futureBlockIndex]); + BOOST_CHECK(futureCacheProcessor->caches().size() == 1); + BOOST_CHECK(futureCache->prePrepare()); + BOOST_CHECK(futureCache->index() == futureBlockIndex); + BOOST_CHECK(futureCache->prePrepare()); + + for (auto const& otherNode : fakerMap) + { + otherNode.second->pbftEngine()->executeWorker(); + } + // assume five nodes into preCommit + size_t precommitSize = 5; + for (size_t i = 0; i < std::min(precommitSize, fakerMap.size()); i++) + { + auto faker = fakerMap[i]; + FakeCacheProcessor::Ptr cacheProcessor = + std::dynamic_pointer_cast(faker->pbftEngine()->cacheProcessor()); + BOOST_CHECK(cacheProcessor->caches().size() == 2); + auto cache = + std::dynamic_pointer_cast((cacheProcessor->caches())[expectedProposal]); + BOOST_CHECK(cache->prePrepare()); + BOOST_CHECK(cache->index() == expectedProposal); + cache->intoPrecommit(); + + auto futureCache = + std::dynamic_pointer_cast((cacheProcessor->caches())[futureBlockIndex]); + BOOST_CHECK(futureCache->prePrepare()); + BOOST_CHECK(futureCache->index() == futureBlockIndex); + BOOST_CHECK(futureCache->prePrepare()); + futureCache->intoPrecommit(); + } + + for (size_t i = 0; i < fakerMap.size(); i++) + { + auto faker = fakerMap[i]; + faker->pbftConfig()->setConsensusTimeout(1000); + } + auto startT = utcTime(); + while (!shouldExit(fakerMap, futureBlockIndex) && (utcTime() - startT <= 10 * 3000)) + { + for (size_t i = 0; i < fakerMap.size(); i++) + { + auto faker = fakerMap[i]; + faker->pbftEngine()->executeWorkerByRoundbin(); + } + } + // check reach new view + for (size_t i = 0; i < fakerMap.size(); i++) + { + auto faker = fakerMap[i]; + BOOST_CHECK(faker->pbftConfig()->view() > 0); + BOOST_CHECK(faker->pbftConfig()->toView() == (faker->pbftConfig()->view())); + BOOST_CHECK(faker->pbftConfig()->timer()->changeCycle() == 0); + BOOST_CHECK(faker->pbftEngine()->isTimeout() == false); + BOOST_CHECK(faker->ledger()->blockNumber() == futureBlockIndex); + } +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/test/unittests/protocol/FakePBFTMessage.h" "b/BFPL\345\243\271/bcos-pbft/test/unittests/protocol/FakePBFTMessage.h" new file mode 100644 index 00000000..929159ca --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/test/unittests/protocol/FakePBFTMessage.h" @@ -0,0 +1,589 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Fake the PBFT related messages + * @file FakePBFTMessage.h + * @author: yujiechen + * @date 2021-04-16 + */ +#pragma once +#include "bcos-pbft/core/Proposal.h" +#include "bcos-pbft/pbft/protocol/PB/PBFTCodec.h" +#include "bcos-pbft/pbft/protocol/PB/PBFTMessage.h" +#include "bcos-pbft/pbft/protocol/PB/PBFTMessageFactoryImpl.h" +#include "bcos-pbft/pbft/protocol/PB/PBFTNewViewMsg.h" +#include "bcos-pbft/pbft/protocol/PB/PBFTProposal.h" +#include "bcos-pbft/pbft/protocol/PB/PBFTViewChangeMsg.h" +#include + +using namespace bcos::consensus; +using namespace bcos::crypto; +using namespace bcos::protocol; +using namespace bcos; + +namespace bcos +{ +namespace test +{ +class PBFTMessageFixture +{ +public: + using Ptr = std::shared_ptr; + PBFTMessageFixture(CryptoSuite::Ptr _cryptoSuite, KeyPairInterface::Ptr _keyPair) + : m_cryptoSuite(_cryptoSuite), m_keyPair(_keyPair) + {} + + ~PBFTMessageFixture() {} + + PBFTProposal::Ptr fakePBFTProposal(BlockNumber _index, HashType const& _hash, + bytes const& _data, std::vector const& _nodeList, + std::vector const& _signatureData) + { + auto pbftProposal = std::make_shared(); + pbftProposal->setIndex(_index); + pbftProposal->setHash(_hash); + pbftProposal->setData(_data); + // set signatureProof + for (size_t i = 0; i < _nodeList.size(); i++) + { + pbftProposal->appendSignatureProof(_nodeList[i], ref(_signatureData[i])); + } + // test proposal encode/decode + auto encodedData = pbftProposal->encode(); + auto decodedProposal = std::make_shared(ref(*encodedData)); + BOOST_CHECK(decodedProposal->index() == pbftProposal->index()); + BOOST_CHECK(decodedProposal->hash() == pbftProposal->hash()); + BOOST_CHECK(decodedProposal->data().toBytes() == pbftProposal->data().toBytes()); + BOOST_CHECK(decodedProposal->signatureProofSize() == pbftProposal->signatureProofSize()); + return decodedProposal; + } + + void fakeBasePBFTMessage(PBFTBaseMessageInterface::Ptr pfbtMessage, int64_t _timestamp, + int32_t _version, int64_t _view, int64_t _generatedFrom, HashType _hash) + { + pfbtMessage->setTimestamp(_timestamp); + pfbtMessage->setVersion(_version); + pfbtMessage->setView(_view); + pfbtMessage->setGeneratedFrom(_generatedFrom); + pfbtMessage->setHash(_hash); + } + + void checkBaseMessageField( + PBFTBaseMessage::Ptr _pfbtMessage, PBFTBaseMessage::Ptr _decodedPBFTMsg) + { + BOOST_CHECK(_pfbtMessage->timestamp() == _decodedPBFTMsg->timestamp()); + BOOST_CHECK(_pfbtMessage->version() == _decodedPBFTMsg->version()); + BOOST_CHECK(_pfbtMessage->view() == _decodedPBFTMsg->view()); + BOOST_CHECK(_pfbtMessage->generatedFrom() == _decodedPBFTMsg->generatedFrom()); + BOOST_CHECK(_pfbtMessage->hash() == _decodedPBFTMsg->hash()); + } + + void checkProposals( + PBFTProposalList const& _pbftProposals, PBFTProposalList const& _decodedProposals) + { + size_t i = 0; + for (auto proposal : _pbftProposals) + { + auto comparedProposal = std::dynamic_pointer_cast(_decodedProposals[i++]); + BOOST_CHECK(*(std::dynamic_pointer_cast(proposal)) == *comparedProposal); + } + } + + PBFTMessage::Ptr fakePBFTMessage(int64_t _timestamp, int32_t _version, int64_t _view, + int64_t _generatedFrom, HashType _hash, PBFTProposalList const& _proposals) + { + auto pfbtMessage = std::make_shared(); + fakeBasePBFTMessage(pfbtMessage, _timestamp, _version, _view, _generatedFrom, _hash); + pfbtMessage->setProposals(_proposals); + // encode + auto encodedData = pfbtMessage->encode(m_cryptoSuite, m_keyPair); + // decode + auto decodedPBFTMsg = std::make_shared(m_cryptoSuite, ref(*encodedData)); + // check the data fields + checkBaseMessageField(pfbtMessage, decodedPBFTMsg); + // check proposals + checkProposals(_proposals, pfbtMessage->proposals()); + return decodedPBFTMsg; + } + + PBFTNewViewMsg::Ptr fakePBFTNewViewMsg(int64_t _timestamp, int32_t _version, int64_t _view, + int64_t _generatedFrom, HashType _hash, ViewChangeMsgList const& _viewChangeList, + PBFTMessage::Ptr _generatedPrepare) + { + auto pbftNewViewChangeMsg = std::make_shared(); + fakeBasePBFTMessage( + pbftNewViewChangeMsg, _timestamp, _version, _view, _generatedFrom, _hash); + pbftNewViewChangeMsg->setViewChangeMsgList(_viewChangeList); + // encode + auto encodedData = pbftNewViewChangeMsg->encode(nullptr, nullptr); + // decode + auto decodedNewViewMsg = std::make_shared(ref(*encodedData)); + // check the basic field + checkBaseMessageField(pbftNewViewChangeMsg, decodedNewViewMsg); + BOOST_CHECK(decodedNewViewMsg->prePrepareList().size() == 0); + + // encode: with generatedPrePrepare + PBFTMessageList prePrepareList; + prePrepareList.push_back(_generatedPrepare); + pbftNewViewChangeMsg->setPrePrepareList(prePrepareList); + encodedData = pbftNewViewChangeMsg->encode(nullptr, nullptr); + // decode + decodedNewViewMsg = std::make_shared(ref(*encodedData)); + // check the basic field + checkBaseMessageField(pbftNewViewChangeMsg, decodedNewViewMsg); + prePrepareList = decodedNewViewMsg->prePrepareList(); + BOOST_CHECK(prePrepareList.size() == 1); + auto decodedPrePrepareMsg = std::dynamic_pointer_cast(prePrepareList[0]); + BOOST_CHECK(*_generatedPrepare == *decodedPrePrepareMsg); + return pbftNewViewChangeMsg; + } + + PBFTViewChangeMsg::Ptr fakePBFTViewChangeMsg(int64_t _timestamp, int32_t _version, + int64_t _view, int64_t _generatedFrom, HashType _hash, + PBFTProposalInterface::Ptr _committedProposal, PBFTProposalList const& _preparedProposals) + { + auto pbftViewChangeMsg = std::make_shared(); + // fake the base PBFT message + fakeBasePBFTMessage(pbftViewChangeMsg, _timestamp, _version, _view, _generatedFrom, _hash); + // fake the commmitted proposal + + auto committedProposal2 = std::dynamic_pointer_cast(_committedProposal); + pbftViewChangeMsg->setCommittedProposal(_committedProposal); + auto committedProposal = + std::dynamic_pointer_cast(pbftViewChangeMsg->committedProposal()); + // the preparedProposals + PBFTMessageList preparedMsgs; + for (size_t i = 0; i < _preparedProposals.size(); i++) + { + auto message = std::make_shared(); + message->setConsensusProposal(_preparedProposals[i]); + preparedMsgs.push_back(message); + } + pbftViewChangeMsg->setPreparedProposals(preparedMsgs); + // encode + auto encodedData = pbftViewChangeMsg->encode(nullptr, nullptr); + // decode + auto decodedViewChange = std::make_shared(ref(*encodedData)); + // check the basic field + checkBaseMessageField(pbftViewChangeMsg, decodedViewChange); + // check committedProposal + auto decodedCommittedProposal = + std::dynamic_pointer_cast(decodedViewChange->committedProposal()); + BOOST_CHECK(*committedProposal2 == *decodedCommittedProposal); + // check prepared proposals + preparedMsgs = decodedViewChange->preparedProposals(); + PBFTProposalList decodedProposalList; + for (auto msg : preparedMsgs) + { + decodedProposalList.push_back(msg->consensusProposal()); + } + checkProposals(_preparedProposals, decodedProposalList); + return decodedViewChange; + } + CryptoSuite::Ptr cryptoSuite() { return m_cryptoSuite; } + KeyPairInterface::Ptr keyPair() { return m_keyPair; } + +private: + CryptoSuite::Ptr m_cryptoSuite; + KeyPairInterface::Ptr m_keyPair; +}; + +inline PBFTProposalInterface::Ptr fakeSingleProposal(CryptoSuite::Ptr _cryptoSuite, + PBFTMessageFixture::Ptr faker, + std::vector> const& _nodeKeyPairList, + BlockNumber _index, HashType const& _hash, bytes const& _data) +{ + // sign for the proposal + std::vector nodeList; + std::vector signatureList; + for (auto const& _nodeKeypairInfo : _nodeKeyPairList) + { + auto signatureData = _cryptoSuite->signatureImpl()->sign(*_nodeKeypairInfo.second, _hash); + nodeList.push_back(_nodeKeypairInfo.first); + signatureList.push_back(*signatureData); + } + return faker->fakePBFTProposal(_index, _hash, _data, nodeList, signatureList); +} + +inline PBFTProposalList fakeProposals(CryptoSuite::Ptr _cryptoSuite, PBFTMessageFixture::Ptr faker, + std::vector> const& _nodeKeyPairList, + BlockNumber _index, bytes const& _data, size_t proposalSize) +{ + PBFTProposalList proposals; + auto index = _index; + auto data = _data; + for (size_t i = 0; i < proposalSize; i++) + { + auto hash = _cryptoSuite->hashImpl()->hash(std::to_string(index)); + proposals.push_back( + fakeSingleProposal(_cryptoSuite, faker, _nodeKeyPairList, index, hash, data)); + index++; + if (data.size() > 0) + { + data[0] += 1; + } + } + return proposals; +} + +inline void checkFakedBasePBFTMessage(PBFTBaseMessageInterface::Ptr fakedMessage, + int64_t orgTimestamp, int32_t version, ViewType view, IndexType generatedFrom, + HashType const& proposalHash) +{ + // check the content + BOOST_CHECK(fakedMessage->timestamp() == orgTimestamp); + BOOST_CHECK(fakedMessage->version() == version); + BOOST_CHECK(fakedMessage->hash() == proposalHash); + BOOST_CHECK(fakedMessage->view() == view); + BOOST_CHECK(fakedMessage->generatedFrom() == generatedFrom); +} + +inline void checkSingleProposal(PBFTProposalInterface::Ptr _proposal, HashType const& _hash, + BlockNumber _index, bytes const& /*_data*/) +{ + BOOST_CHECK(_proposal->index() == _index); + BOOST_CHECK(_proposal->hash() == _hash); + // BOOST_CHECK(_proposal->data().toBytes() == _data); +} + +inline void checkProposals(PBFTProposalList _proposals, CryptoSuite::Ptr _cryptoSuite, + BlockNumber _index, bytes const& _data) +{ + // check the proposal + auto data = _data; + auto index = _index; + for (auto proposal : _proposals) + { + auto hash = _cryptoSuite->hashImpl()->hash(std::to_string(index)); + checkSingleProposal(proposal, hash, index, data); + if (data.size() > 0) + { + data[0] += 1; + } + index++; + } +} + +inline void checkPBFTMessage(PBFTMessage::Ptr fakedMessage, int64_t orgTimestamp, int32_t version, + ViewType view, IndexType generatedFrom, HashType const& proposalHash, size_t proposalSize, + CryptoSuite::Ptr cryptoSuite, BlockNumber _index, bytes const& _data) +{ + checkFakedBasePBFTMessage( + fakedMessage, orgTimestamp, version, view, generatedFrom, proposalHash); + BOOST_CHECK(fakedMessage->proposals().size() == proposalSize); + // check the proposal + checkProposals(fakedMessage->proposals(), cryptoSuite, _index, _data); +} + +inline PBFTMessage::Ptr fakePBFTMessage(int64_t orgTimestamp, int32_t version, ViewType view, + IndexType generatedFrom, HashType const& proposalHash, BlockNumber _index, bytes const& _data, + size_t proposalSize, std::shared_ptr _faker, PacketType _packetType) +{ + auto cryptoSuite = _faker->cryptoSuite(); + size_t signatureProofSize = 4; + std::vector> nodeKeyPairList; + for (size_t i = 0; i < signatureProofSize; i++) + { + nodeKeyPairList.push_back( + std::make_pair(i, cryptoSuite->signatureImpl()->generateKeyPair())); + } + auto proposals = + fakeProposals(cryptoSuite, _faker, nodeKeyPairList, _index, _data, proposalSize); + auto fakedMessage = _faker->fakePBFTMessage( + orgTimestamp, version, view, generatedFrom, proposalHash, proposals); + fakedMessage->setIndex(_index); + fakedMessage->setPacketType(_packetType); + checkPBFTMessage(fakedMessage, orgTimestamp, version, view, generatedFrom, proposalHash, + proposalSize, cryptoSuite, _index, _data); + return fakedMessage; +} + + +inline void checkViewChangeMessage(PBFTViewChangeMsg::Ptr fakedViewChangeMessage, + int64_t orgTimestamp, int32_t version, ViewType view, IndexType generatedFrom, + HashType const& proposalHash, BlockNumber _index, bytes const& _data, + BlockNumber _committedIndex, HashType const& _committedHash, size_t _proposalSize, + CryptoSuite::Ptr _cryptoSuite) +{ + checkFakedBasePBFTMessage( + fakedViewChangeMessage, orgTimestamp, version, view, generatedFrom, proposalHash); + + // check prepared proposal + BOOST_CHECK(fakedViewChangeMessage->preparedProposals().size() == _proposalSize); + PBFTProposalList fakedProposals; + for (auto msg : fakedViewChangeMessage->preparedProposals()) + { + fakedProposals.push_back(msg->consensusProposal()); + } + checkProposals(fakedProposals, _cryptoSuite, _index, _data); + // check committed proposal + checkSingleProposal( + fakedViewChangeMessage->committedProposal(), _committedHash, _committedIndex, bytes()); +} + +inline PBFTViewChangeMsg::Ptr fakeViewChangeMessage(int64_t orgTimestamp, int32_t version, + ViewType view, IndexType generatedFrom, HashType const& proposalHash, BlockNumber _index, + bytes const& _data, BlockNumber _committedIndex, HashType const& _committedHash, + size_t _proposalSize, std::shared_ptr _faker) +{ + auto cryptoSuite = _faker->cryptoSuite(); + std::vector> nodeKeyPairList; + size_t signatureProofSize = 4; + for (size_t i = 0; i < signatureProofSize; i++) + { + nodeKeyPairList.push_back( + std::make_pair(i, cryptoSuite->signatureImpl()->generateKeyPair())); + } + auto preparedProposals = + fakeProposals(cryptoSuite, _faker, nodeKeyPairList, _index, _data, _proposalSize); + std::vector> nodeKeyPairList2; + auto committedProposal = fakeSingleProposal( + cryptoSuite, _faker, nodeKeyPairList2, _committedIndex, _committedHash, bytes()); + + auto fakedViewChangeMessage = _faker->fakePBFTViewChangeMsg(orgTimestamp, version, view, + generatedFrom, proposalHash, committedProposal, preparedProposals); + + checkViewChangeMessage(fakedViewChangeMessage, orgTimestamp, version, view, generatedFrom, + proposalHash, _index, _data, _committedIndex, _committedHash, _proposalSize, cryptoSuite); + return fakedViewChangeMessage; +} + +inline void testPBFTMessage(PacketType _packetType, CryptoSuite::Ptr _cryptoSuite) +{ + int64_t orgTimestamp = utcTime(); + int32_t version = 10; + ViewType view = 103423423423; + IndexType generatedFrom = 10; + auto proposalHash = _cryptoSuite->hashImpl()->hash("proposal"); + size_t proposalSize = 3; + KeyPairInterface::Ptr keyPair = _cryptoSuite->signatureImpl()->generateKeyPair(); + auto faker = std::make_shared(_cryptoSuite, keyPair); + // for the proposal + BlockNumber index = 100; + std::string dataStr = "werldksjflaskjffakesdfastadfakedaat"; + bytes data(dataStr.begin(), dataStr.end()); + + auto fakedMessage = fakePBFTMessage(orgTimestamp, version, view, generatedFrom, proposalHash, + index, data, proposalSize, faker, _packetType); + + // test PBFTCodec + PBFTMessageFactory::Ptr pbftMessageFactory = std::make_shared(); + auto pbftCodec = std::make_shared(keyPair, _cryptoSuite, pbftMessageFactory); + // encode and sign + auto encodedData = pbftCodec->encode(fakedMessage, 1); + // decode + auto message = pbftCodec->decode(ref(*encodedData)); + PBFTMessage::Ptr decodedMsg = std::dynamic_pointer_cast(message); + BOOST_CHECK(decodedMsg->packetType() == _packetType); + // check the decoded message + checkFakedBasePBFTMessage(decodedMsg, orgTimestamp, version, view, generatedFrom, proposalHash); + // verify the signature + BOOST_CHECK(decodedMsg->verifySignature(_cryptoSuite, keyPair->publicKey()) == true); + // case: another node fake the decodedMsg with error view + KeyPairInterface::Ptr keyPair2 = _cryptoSuite->signatureImpl()->generateKeyPair(); + auto pbftCodec2 = std::make_shared(keyPair2, _cryptoSuite, pbftMessageFactory); + decodedMsg->setView(view + 1); + encodedData = pbftCodec2->encode(decodedMsg, 1); + auto decodedMsg2 = + std::dynamic_pointer_cast(pbftCodec2->decode(ref(*encodedData))); + BOOST_CHECK(decodedMsg2->verifySignature(_cryptoSuite, keyPair->publicKey()) == false); + + // the signatureHash has been updated + auto fakedHash = _cryptoSuite->hashImpl()->hash("fakedHash"); + decodedMsg->setSignatureDataHash(fakedHash); + BOOST_CHECK(decodedMsg->verifySignature(_cryptoSuite, keyPair->publicKey()) == false); +} + +inline void testPBFTViewChangeMessage(CryptoSuite::Ptr _cryptoSuite) +{ + int64_t orgTimestamp = utcTime(); + int32_t version = 11; + ViewType view = 23423423432; + IndexType generatedFrom = 200; + auto proposalHash = _cryptoSuite->hashImpl()->hash("testPBFTViewChangeMessage"); + size_t proposalSize = 4; + BlockNumber index = 10003; + std::string dataStr = "werldksjflaskjffakesdfastadfakedaat"; + bytes data(dataStr.begin(), dataStr.end()); + + BlockNumber committedIndex = 10002; + HashType committedHash = _cryptoSuite->hash("10002"); + KeyPairInterface::Ptr keyPair = _cryptoSuite->signatureImpl()->generateKeyPair(); + auto faker = std::make_shared(_cryptoSuite, keyPair); + + auto fakedViewChangeMsg = fakeViewChangeMessage(orgTimestamp, version, view, generatedFrom, + proposalHash, index, data, committedIndex, committedHash, proposalSize, faker); + BOOST_CHECK(fakedViewChangeMsg->packetType() == PacketType::ViewChangePacket); + + // test PBFTCodec + PBFTMessageFactory::Ptr pbftMessageFactory = std::make_shared(); + auto pbftCodec = std::make_shared(keyPair, _cryptoSuite, pbftMessageFactory); + // encode and sign + auto encodedData = pbftCodec->encode(fakedViewChangeMsg, 1); + // decode + auto message = pbftCodec->decode(ref(*encodedData)); + auto decodedMsg = std::dynamic_pointer_cast(message); + // check + BOOST_CHECK(decodedMsg->packetType() == PacketType::ViewChangePacket); + // check the decoded message + checkViewChangeMessage(fakedViewChangeMsg, orgTimestamp, version, view, generatedFrom, + proposalHash, index, data, committedIndex, committedHash, proposalSize, _cryptoSuite); + // verify the signature + BOOST_CHECK(decodedMsg->verifySignature(_cryptoSuite, keyPair->publicKey()) == true); + // case: another node fake the decodedMsg with error view + KeyPairInterface::Ptr keyPair2 = _cryptoSuite->signatureImpl()->generateKeyPair(); + auto pbftCodec2 = std::make_shared(keyPair2, _cryptoSuite, pbftMessageFactory); + decodedMsg->setView(view - 100); + encodedData = pbftCodec2->encode(decodedMsg, 1); + auto decodedMsg2 = + std::dynamic_pointer_cast(pbftCodec2->decode(ref(*encodedData))); + BOOST_CHECK(decodedMsg2->verifySignature(_cryptoSuite, keyPair->publicKey()) == false); + + // the signatureHash has been updated + auto fakedHash = _cryptoSuite->hashImpl()->hash("fakedHash"); + decodedMsg->setSignatureDataHash(fakedHash); + BOOST_CHECK(decodedMsg->verifySignature(_cryptoSuite, keyPair->publicKey()) == false); +} + +inline void checkNewViewMessage(PBFTNewViewMsg::Ptr fakedNewViewMessage, int64_t orgTimestamp, + int32_t version, ViewType view, IndexType generatedFrom, HashType const& proposalHash, + BlockNumber _index, bytes const& _data, int64_t viewChangeSize, int64_t proposalSize, + BlockNumber committedIndex, CryptoSuite::Ptr _cryptoSuite) +{ + checkFakedBasePBFTMessage( + fakedNewViewMessage, orgTimestamp, version, view, generatedFrom, proposalHash); + + // check viewChangeMsgs + BOOST_CHECK((int64_t)(fakedNewViewMessage->viewChangeMsgList().size()) == viewChangeSize); + for (int64_t i = 0; i < viewChangeSize; i++) + { + auto committedHash = _cryptoSuite->hash(std::to_string(committedIndex)); + auto fakedViewChange = fakedNewViewMessage->viewChangeMsgList()[i]; + checkViewChangeMessage(std::dynamic_pointer_cast(fakedViewChange), + orgTimestamp, version, view, generatedFrom, proposalHash, _index, _data, committedIndex, + committedHash, proposalSize, _cryptoSuite); + committedIndex++; + committedHash = _cryptoSuite->hash(std::to_string(committedIndex)); + generatedFrom++; + view++; + } +} + +inline void testPBFTNewViewMessage(CryptoSuite::Ptr _cryptoSuite) +{ + int64_t orgTimestamp = utcTime(); + int32_t version = 11; + ViewType view = 23423423432; + auto orgView = view; + + IndexType generatedFrom = 200; + auto orgGeneratedFrom = generatedFrom; + + auto proposalHash = _cryptoSuite->hashImpl()->hash("testPBFTViewChangeMessage"); + size_t proposalSize = 4; + BlockNumber index = 10003; + std::string dataStr = "werldksjflaskjffakesdfastadfakedaat"; + bytes data(dataStr.begin(), dataStr.end()); + + BlockNumber committedIndex = 10002; + auto orgCommittedIndex = committedIndex; + + KeyPairInterface::Ptr keyPair = _cryptoSuite->signatureImpl()->generateKeyPair(); + auto faker = std::make_shared(_cryptoSuite, keyPair); + + int64_t viewChangeSize = 4; + ViewChangeMsgList viewChangeList; + for (int64_t i = 0; i < viewChangeSize; i++) + { + auto committedHash = _cryptoSuite->hash(std::to_string(committedIndex)); + viewChangeList.push_back(fakeViewChangeMessage(orgTimestamp, version, view, generatedFrom, + proposalHash, index, data, committedIndex, committedHash, proposalSize, faker)); + committedIndex++; + committedHash = _cryptoSuite->hash(std::to_string(committedIndex)); + generatedFrom++; + view++; + } + // fake NewViewMsg + auto fakedPreparedMsg = fakePBFTMessage(orgTimestamp, version, view, generatedFrom, + proposalHash, index, data, proposalSize, faker, PacketType::PrePreparePacket); + auto fakedNewViewMsg = faker->fakePBFTNewViewMsg(orgTimestamp, version, orgView, + orgGeneratedFrom, proposalHash, viewChangeList, fakedPreparedMsg); + BOOST_CHECK(int64_t(fakedNewViewMsg->viewChangeMsgList().size()) == viewChangeSize); + + // test PBFTCodec + PBFTMessageFactory::Ptr pbftMessageFactory = std::make_shared(); + auto pbftCodec = std::make_shared(keyPair, _cryptoSuite, pbftMessageFactory); + // encode and sign + auto encodedData = pbftCodec->encode(fakedNewViewMsg, 1); + // decode + auto message = pbftCodec->decode(ref(*encodedData)); + auto decodedMsg = std::dynamic_pointer_cast(message); + // check + BOOST_CHECK(decodedMsg->packetType() == PacketType::NewViewPacket); + // check the decoded message + checkNewViewMessage(decodedMsg, orgTimestamp, version, orgView, orgGeneratedFrom, proposalHash, + index, data, viewChangeSize, proposalSize, orgCommittedIndex, _cryptoSuite); + // verify the signature + BOOST_CHECK(decodedMsg->verifySignature(_cryptoSuite, keyPair->publicKey()) == true); + // the signatureHash has been updated + auto fakedHash = _cryptoSuite->hashImpl()->hash("fakedHash"); + decodedMsg->setSignatureDataHash(fakedHash); + BOOST_CHECK(decodedMsg->verifySignature(_cryptoSuite, keyPair->publicKey()) == false); +} + +inline void testPBFTRequest(CryptoSuite::Ptr _cryptoSuite, PacketType _packetType) +{ + auto timeStamp = utcTime(); + int32_t version = 12; + ViewType view = 234234234; + IndexType generatedFrom = 200; + + auto proposalHash = _cryptoSuite->hashImpl()->hash("testPBFTRequest"); + KeyPairInterface::Ptr keyPair = _cryptoSuite->signatureImpl()->generateKeyPair(); + auto faker = std::make_shared(_cryptoSuite, keyPair); + auto pbftMessageFactory = std::make_shared(); + + auto pbftRequest = pbftMessageFactory->createPBFTRequest(); + faker->fakeBasePBFTMessage(pbftRequest, timeStamp, version, view, generatedFrom, proposalHash); + int64_t startIndex = 1435345; + int64_t size = 10; + pbftRequest->setSize(size); + pbftRequest->setIndex(startIndex); + pbftRequest->setPacketType(_packetType); + // encode + auto encodedData = pbftRequest->encode(_cryptoSuite, keyPair); + // decode + auto decodedPBFTRequest = pbftMessageFactory->createPBFTRequest(ref(*encodedData)); + BOOST_CHECK(*(std::dynamic_pointer_cast(decodedPBFTRequest)) == + *(std::dynamic_pointer_cast(pbftRequest))); + checkFakedBasePBFTMessage( + decodedPBFTRequest, timeStamp, version, view, generatedFrom, proposalHash); + BOOST_CHECK(decodedPBFTRequest->index() == startIndex); + BOOST_CHECK(decodedPBFTRequest->size() == size); + + // encode/decode with codec + auto pbftCodec = std::make_shared(keyPair, _cryptoSuite, pbftMessageFactory); + // encode and sign + encodedData = pbftCodec->encode(pbftRequest, 1); + // decode + auto message = pbftCodec->decode(ref(*encodedData)); + auto decodedMsg = std::dynamic_pointer_cast(message); + // check the decoded message + checkFakedBasePBFTMessage(decodedMsg, timeStamp, version, view, generatedFrom, proposalHash); + BOOST_CHECK(decodedMsg->index() == startIndex); + BOOST_CHECK(decodedMsg->size() == size); +} +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-pbft/test/unittests/protocol/PBFTMessageTest.cpp" "b/BFPL\345\243\271/bcos-pbft/test/unittests/protocol/PBFTMessageTest.cpp" new file mode 100644 index 00000000..ac85ff6c --- /dev/null +++ "b/BFPL\345\243\271/bcos-pbft/test/unittests/protocol/PBFTMessageTest.cpp" @@ -0,0 +1,111 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for pbft related messages + * @file PBFTMessageTest.h + * @author: yujiechen + * @date 2021-04-16 + */ +#include "FakePBFTMessage.h" +#include +#include +#include +#include +#include +#include +using namespace bcos; +using namespace bcos::protocol; +using namespace bcos::crypto; +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(PBFTMessageTest, TestPromptFixture) +BOOST_AUTO_TEST_CASE(testNormalPBFTMessage) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + testPBFTMessage(PacketType::PrePreparePacket, cryptoSuite); + testPBFTMessage(PacketType::PreparePacket, cryptoSuite); + testPBFTMessage(PacketType::CommitPacket, cryptoSuite); + testPBFTMessage(PacketType::CommittedProposalResponse, cryptoSuite); + testPBFTMessage(PacketType::CheckPoint, cryptoSuite); +} + +BOOST_AUTO_TEST_CASE(testSMPBFTMessage) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + testPBFTMessage(PacketType::PrePreparePacket, cryptoSuite); + testPBFTMessage(PacketType::PreparePacket, cryptoSuite); + testPBFTMessage(PacketType::CommitPacket, cryptoSuite); + testPBFTMessage(PacketType::CommittedProposalResponse, cryptoSuite); + testPBFTMessage(PacketType::CheckPoint, cryptoSuite); +} + +BOOST_AUTO_TEST_CASE(testNormalViewChangeMessage) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + testPBFTViewChangeMessage(cryptoSuite); +} + +BOOST_AUTO_TEST_CASE(testSMViewChangeMessage) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + testPBFTViewChangeMessage(cryptoSuite); +} + +BOOST_AUTO_TEST_CASE(testNormalNewViewMessage) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + testPBFTNewViewMessage(cryptoSuite); +} + +BOOST_AUTO_TEST_CASE(testSMNewViewMessage) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + testPBFTNewViewMessage(cryptoSuite); +} + +BOOST_AUTO_TEST_CASE(testNormalPBFTRequest) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + testPBFTRequest(cryptoSuite, PacketType::CommittedProposalRequest); + testPBFTRequest(cryptoSuite, PacketType::PreparedProposalRequest); +} + +BOOST_AUTO_TEST_CASE(testSMPBFTRequest) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + testPBFTRequest(cryptoSuite, PacketType::CommittedProposalRequest); + testPBFTRequest(cryptoSuite, PacketType::PreparedProposalRequest); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-protocol/CMakeLists.txt" "b/BFPL\345\243\271/bcos-protocol/CMakeLists.txt" new file mode 100644 index 00000000..874d689d --- /dev/null +++ "b/BFPL\345\243\271/bcos-protocol/CMakeLists.txt" @@ -0,0 +1,41 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for bcos-protocol +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 bcos-pbft +# 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. +#------------------------------------------------------------------------------ + +cmake_minimum_required(VERSION 3.10) +set(CMAKE_OSX_DEPLOYMENT_TARGET "11.3" CACHE STRING "Minimum OS X deployment version") + +add_subdirectory(bcos-protocol) + +add_library(bcos-protocol INTERFACE) +target_include_directories(bcos-protocol INTERFACE + $ + $ + $ +) +target_link_libraries(bcos-protocol INTERFACE ${CODEC_TARGET}) + +if (TESTS) + # fetch bcos-test + enable_testing() + set(CTEST_OUTPUT_ON_FAILURE TRUE) + add_subdirectory(test) +endif() + +include(GNUInstallDirs) +install(TARGETS bcos-protocol EXPORT fiscobcosTargets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") +install(DIRECTORY "bcos-protocol" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" FILES_MATCHING PATTERN "*.h") \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-protocol/bcos-protocol/CMakeLists.txt" "b/BFPL\345\243\271/bcos-protocol/bcos-protocol/CMakeLists.txt" new file mode 100644 index 00000000..0c1eeec3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-protocol/bcos-protocol/CMakeLists.txt" @@ -0,0 +1,15 @@ +file(GLOB SRC_LIST "*.cpp") +file(GLOB HEADERS "*.h") + +aux_source_directory(. SRC_LIST) +aux_source_directory(./amop SRC_LIST) + +find_package(Threads REQUIRED) +add_library(${PROTOCOL_TARGET} ${SRC_LIST} ${HEADERS}) + +find_package(TBB CONFIG REQUIRED) +target_link_libraries(${PROTOCOL_TARGET} PUBLIC ${CRYPTO_TARGET} ${CODEC_TARGET} bcos-framework TBB::tbb) + +include(GNUInstallDirs) +install(TARGETS ${PROTOCOL_TARGET} EXPORT fiscobcosTargets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") +# install(DIRECTORY "bcos-protocol" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" FILES_MATCHING PATTERN "*.h") \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-protocol/bcos-protocol/Common.h" "b/BFPL\345\243\271/bcos-protocol/bcos-protocol/Common.h" new file mode 100644 index 00000000..b4ffdae3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-protocol/bcos-protocol/Common.h" @@ -0,0 +1,89 @@ +/** + * Copyright (C) 2021 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: yujiechen + * @date 2021-04-12 + */ +#pragma once + +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace protocol +{ +DERIVE_BCOS_EXCEPTION(PBObjectEncodeException); +DERIVE_BCOS_EXCEPTION(PBObjectDecodeException); +template +bytesPointer encodePBObject(T _pbObject) +{ + auto encodedData = std::make_shared(); + encodedData->resize(_pbObject->ByteSizeLong()); + if (_pbObject->SerializeToArray(encodedData->data(), encodedData->size())) + { + return encodedData; + } + BOOST_THROW_EXCEPTION( + PBObjectEncodeException() << errinfo_comment("encode PBObject into bytes data failed")); +} + +template +void encodePBObject(bytes& _encodedData, T _pbObject) +{ + auto encodedData = std::make_shared(); + _encodedData.resize(_pbObject->ByteSizeLong()); + if (!_pbObject->SerializeToArray(_encodedData.data(), _encodedData.size())) + { + BOOST_THROW_EXCEPTION( + PBObjectEncodeException() << errinfo_comment("encode PBObject into bytes data failed")); + } +} + +template +void decodePBObject(T _pbObject, bytesConstRef _data) +{ + if (!_pbObject->ParseFromArray(_data.data(), _data.size())) + { + BOOST_THROW_EXCEPTION( + PBObjectDecodeException() << errinfo_comment( + "decode bytes data into PBObject failed, data: " + *toHexString(_data))); + } +} + +inline std::vector encodeToCalculateRoot( + size_t _listSize, std::function _hashFunc) +{ + std::vector encodedList(_listSize); + tbb::parallel_for( + tbb::blocked_range(0, _listSize), [&](const tbb::blocked_range& _r) { + for (auto i = _r.begin(); i < _r.end(); ++i) + { + bcos::codec::scale::ScaleEncoderStream stream; + stream << i; + bytes encodedData = stream.data(); + auto hash = _hashFunc(i); + encodedData.insert(encodedData.end(), hash.begin(), hash.end()); + encodedList[i] = std::move(encodedData); + } + }); + return encodedList; +} +} // namespace protocol +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-protocol/bcos-protocol/ParallelMerkleProof.cpp" "b/BFPL\345\243\271/bcos-protocol/bcos-protocol/ParallelMerkleProof.cpp" new file mode 100644 index 00000000..b2ef690f --- /dev/null +++ "b/BFPL\345\243\271/bcos-protocol/bcos-protocol/ParallelMerkleProof.cpp" @@ -0,0 +1,119 @@ +/** + * Copyright (C) 2020 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. + * + * @brief: calc trie hash with merkle tree + * + * @file: ParallelMerkleProof.cpp + * @author: darrenyin + * @date 2019-09-24 + */ + +#include "ParallelMerkleProof.h" +#include +#include + +using namespace bcos; +using namespace bcos::crypto; + +const uint32_t MAX_CHILD_COUNT = 16; + +HashType bcos::protocol::calculateMerkleProofRoot( + CryptoSuite::Ptr _cryptoSuite, const std::vector& _bytesCaches) +{ + if (_bytesCaches.empty()) + { + return _cryptoSuite->hash(bytes()); + } + std::vector bytesCachesTemp; + bytesCachesTemp.insert(bytesCachesTemp.end(), + std::make_move_iterator(const_cast&>(_bytesCaches).begin()), + std::make_move_iterator(const_cast&>(_bytesCaches).end())); + + while (bytesCachesTemp.size() > 1) + { + std::vector higherLevelList; + int size = (bytesCachesTemp.size() + MAX_CHILD_COUNT - 1) / MAX_CHILD_COUNT; + higherLevelList.resize(size); + tbb::parallel_for( + tbb::blocked_range(0, size), [&](const tbb::blocked_range& _r) { + for (uint32_t i = _r.begin(); i < _r.end(); ++i) + { + bytes byteValue; + for (uint32_t j = 0; j < MAX_CHILD_COUNT; j++) + { + uint32_t index = i * MAX_CHILD_COUNT + j; + if (index < bytesCachesTemp.size()) + { + byteValue.insert(byteValue.end(), bytesCachesTemp[index].begin(), + bytesCachesTemp[index].end()); + } + } + higherLevelList[i] = _cryptoSuite->hash(byteValue).asBytes(); + } + }); + bytesCachesTemp = std::move(higherLevelList); + } + return _cryptoSuite->hash(bytesCachesTemp[0]); +} + +void bcos::protocol::calculateMerkleProof(bcos::crypto::CryptoSuite::Ptr _cryptoSuite, + const std::vector& _bytesCaches, + std::shared_ptr>> _parent2ChildList) +{ + if (_bytesCaches.empty()) + { + return; + } + std::vector bytesCachesTemp; + bytesCachesTemp.insert(bytesCachesTemp.end(), + std::make_move_iterator(const_cast&>(_bytesCaches).begin()), + std::make_move_iterator(const_cast&>(_bytesCaches).end())); + std::mutex mapMutex; + while (bytesCachesTemp.size() > 1) + { + std::vector higherLevelList; + int size = (bytesCachesTemp.size() + MAX_CHILD_COUNT - 1) / MAX_CHILD_COUNT; + higherLevelList.resize(size); + tbb::parallel_for( + tbb::blocked_range(0, size), [&](const tbb::blocked_range& _r) { + for (uint32_t i = _r.begin(); i < _r.end(); ++i) + { + bytes byteValue; + std::vector childList; + for (uint32_t j = 0; j < MAX_CHILD_COUNT; j++) + { + uint32_t index = i * MAX_CHILD_COUNT + j; + if (index < bytesCachesTemp.size()) + { + byteValue.insert(byteValue.end(), bytesCachesTemp[index].begin(), + bytesCachesTemp[index].end()); + childList.push_back(bytesCachesTemp[index]); + } + } + higherLevelList[i] = _cryptoSuite->hash(byteValue).asBytes(); + std::lock_guard l(mapMutex); + std::string parentNode = *toHexString(higherLevelList[i]); + for (const auto& child : childList) + { + (*_parent2ChildList)[parentNode].emplace_back(*toHexString(child)); + } + } + }); + bytesCachesTemp = std::move(higherLevelList); + } + + (*_parent2ChildList)[*toHexString(_cryptoSuite->hash(bytesCachesTemp[0]).asBytes())].push_back( + *toHexString(bytesCachesTemp[0])); +} diff --git "a/BFPL\345\243\271/bcos-protocol/bcos-protocol/ParallelMerkleProof.h" "b/BFPL\345\243\271/bcos-protocol/bcos-protocol/ParallelMerkleProof.h" new file mode 100644 index 00000000..5d8f8b74 --- /dev/null +++ "b/BFPL\345\243\271/bcos-protocol/bcos-protocol/ParallelMerkleProof.h" @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2020 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. + * + * @brief: calc trie hash with merkle tree + * + * @file: ParallelMerkleProof.h + * @author: darrenyin + * @date 2019-09-24 + */ +#pragma once + +#include +#include +#include + +namespace bcos +{ +namespace protocol +{ +bcos::crypto::HashType calculateMerkleProofRoot( + bcos::crypto::CryptoSuite::Ptr _cryptoSuite, const std::vector& _bytesCaches); +void calculateMerkleProof(bcos::crypto::CryptoSuite::Ptr _cryptoSuite, + const std::vector& _bytesCaches, + std::shared_ptr>> _parent2ChildList); +} // namespace protocol +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-protocol/bcos-protocol/TransactionStatus.h" "b/BFPL\345\243\271/bcos-protocol/bcos-protocol/TransactionStatus.h" new file mode 100644 index 00000000..6fb6c9de --- /dev/null +++ "b/BFPL\345\243\271/bcos-protocol/bcos-protocol/TransactionStatus.h" @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2021 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: TransactionStatus.h + * @author: xingqiangbai + * @date: 20200608 + */ + +#pragma once +#include +#include + +namespace bcos +{ +struct Exception; +namespace protocol +{ +enum class TransactionStatus : int32_t +{ + None = 0, + Unknown = 1, + OutOfGasLimit = 2, ///< Too little gas to pay for the base transaction cost. + NotEnoughCash = 7, // TODO: remove this? + BadInstruction = 10, + BadJumpDestination = 11, + OutOfGas = 12, ///< Ran out of gas executing code of the transaction. + OutOfStack = 13, ///< Ran out of stack executing code of the transaction. + StackUnderflow = 14, + PrecompiledError = 15, + RevertInstruction = 16, + ContractAddressAlreadyUsed = 17, + PermissionDenied = 18, + CallAddressError = 19, + GasOverflow = 20, + ContractFrozen = 21, + AccountFrozen = 22, + AccountAbolished = 23, + WASMValidationFailure = 32, + WASMArgumentOutOfRange = 33, + WASMUnreachableInstruction = 34, + WASMTrap = 35, + NonceCheckFail = 10000, /// txPool related errors + BlockLimitCheckFail = 10001, + TxPoolIsFull = 10002, + Malform = 10003, + AlreadyInTxPool = 10004, + TxAlreadyInChain = 10005, + InvalidChainId = 10006, + InvalidGroupId = 10007, + InvalidSignature = 10008, + RequestNotBelongToTheGroup = 10009, +}; + +inline std::string toString(protocol::TransactionStatus const& _i) +{ + std::stringstream stream; + stream << "0x" << std::hex << static_cast(_i); + return stream.str(); +} + +inline std::ostream& operator<<(std::ostream& _out, bcos::protocol::TransactionStatus const& _er) +{ + switch (_er) + { + case bcos::protocol::TransactionStatus::None: + _out << "None"; + break; + case bcos::protocol::TransactionStatus::OutOfGasLimit: + _out << "OutOfGasLimit"; + break; + case bcos::protocol::TransactionStatus::NotEnoughCash: + _out << "NotEnoughCash"; + break; + case bcos::protocol::TransactionStatus::BadInstruction: + _out << "BadInstruction"; + break; + case bcos::protocol::TransactionStatus::BadJumpDestination: + _out << "BadJumpDestination"; + break; + case bcos::protocol::TransactionStatus::OutOfGas: + _out << "OutOfGas"; + break; + case bcos::protocol::TransactionStatus::OutOfStack: + _out << "OutOfStack"; + break; + case bcos::protocol::TransactionStatus::StackUnderflow: + _out << "StackUnderflow"; + break; + case bcos::protocol::TransactionStatus::NonceCheckFail: + _out << "NonceCheckFail"; + break; + case bcos::protocol::TransactionStatus::BlockLimitCheckFail: + _out << "BlockLimitCheckFail"; + break; + case bcos::protocol::TransactionStatus::PrecompiledError: + _out << "PrecompiledError"; + break; + case bcos::protocol::TransactionStatus::RevertInstruction: + _out << "RevertInstruction"; + break; + case bcos::protocol::TransactionStatus::ContractAddressAlreadyUsed: + _out << "ContractAddressAlreadyUsed"; + break; + case bcos::protocol::TransactionStatus::PermissionDenied: + _out << "PermissionDenied"; + break; + case bcos::protocol::TransactionStatus::CallAddressError: + _out << "CallAddressError"; + break; + case bcos::protocol::TransactionStatus::GasOverflow: + _out << "GasOverflow"; + break; + case bcos::protocol::TransactionStatus::ContractFrozen: + _out << "ContractFrozen"; + break; + case bcos::protocol::TransactionStatus::AccountFrozen: + _out << "AccountFrozen"; + break; + case bcos::protocol::TransactionStatus::TxPoolIsFull: + _out << "TxPoolIsFull"; + break; + case bcos::protocol::TransactionStatus::Malform: + _out << "MalformTx"; + break; + case bcos::protocol::TransactionStatus::AlreadyInTxPool: + _out << "AlreadyInTxPool"; + break; + case bcos::protocol::TransactionStatus::TxAlreadyInChain: + _out << "TxAlreadyInChain"; + break; + case bcos::protocol::TransactionStatus::InvalidChainId: + _out << "InvalidChainId"; + break; + case bcos::protocol::TransactionStatus::InvalidGroupId: + _out << "InvalidGroupId"; + break; + case bcos::protocol::TransactionStatus::InvalidSignature: + _out << "InvalidSignature"; + break; + case bcos::protocol::TransactionStatus::RequestNotBelongToTheGroup: + _out << "RequestNotBelongToTheGroup"; + break; + default: + _out << "Unknown"; + break; + } + return _out; +} +} // namespace protocol +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-protocol/bcos-protocol/TransactionSubmitResultFactoryImpl.h" "b/BFPL\345\243\271/bcos-protocol/bcos-protocol/TransactionSubmitResultFactoryImpl.h" new file mode 100644 index 00000000..0d5cf502 --- /dev/null +++ "b/BFPL\345\243\271/bcos-protocol/bcos-protocol/TransactionSubmitResultFactoryImpl.h" @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 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 TransactionSubmitResultFactoryImpl.h + * @author: yujiechen + * @date: 2021-05-08 + */ +#pragma once +#include "TransactionSubmitResultImpl.h" +#include + +namespace bcos +{ +namespace protocol +{ +class TransactionSubmitResultFactoryImpl : public TransactionSubmitResultFactory +{ +public: + using Ptr = std::shared_ptr; + TransactionSubmitResultFactoryImpl() = default; + ~TransactionSubmitResultFactoryImpl() override {} + + TransactionSubmitResult::Ptr createTxSubmitResult() override + { + return std::make_shared(); + } +}; +} // namespace protocol +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-protocol/bcos-protocol/TransactionSubmitResultImpl.h" "b/BFPL\345\243\271/bcos-protocol/bcos-protocol/TransactionSubmitResultImpl.h" new file mode 100644 index 00000000..ca1b43f7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-protocol/bcos-protocol/TransactionSubmitResultImpl.h" @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2021 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 TransactionSubmitResultImpl.h + * @author: yujiechen + * @date: 2021-04-07 + */ +#pragma once +#include "bcos-protocol/TransactionStatus.h" +#include +#include +#include + +namespace bcos +{ +namespace protocol +{ +class TransactionSubmitResultImpl : public TransactionSubmitResult +{ +public: + using Ptr = std::shared_ptr; + ~TransactionSubmitResultImpl() override {} + + uint32_t status() const override { return m_status; } + void setStatus(uint32_t status) override { m_status = status; } + + bcos::crypto::HashType txHash() const override { return m_txHash; } + void setTxHash(bcos::crypto::HashType txHash) override { m_txHash = txHash; } + + bcos::crypto::HashType blockHash() const override { return m_blockHash; } + void setBlockHash(bcos::crypto::HashType blockHash) override { m_blockHash = blockHash; } + + int64_t transactionIndex() const override { return m_transactionIndex; } + void setTransactionIndex(int64_t transactionIndex) override + { + m_transactionIndex = transactionIndex; + } + + NonceType nonce() const override { return m_nonce; } + void setNonce(NonceType nonce) override { m_nonce = nonce; } + + TransactionReceipt::Ptr transactionReceipt() const override { return m_receipt; } + void setTransactionReceipt(TransactionReceipt::Ptr transactionReceipt) override + { + m_receipt = std::move(transactionReceipt); + } + + std::string const& sender() const override { return m_sender; } + void setSender(std::string const& _sender) override { m_sender = _sender; } + + std::string const& to() const override { return m_to; } + void setTo(std::string const& _to) override { m_to = _to; } + +private: + uint32_t m_status = (uint32_t)TransactionStatus::None; + bcos::crypto::HashType m_txHash; + bcos::crypto::HashType m_blockHash; + int64_t m_transactionIndex; + NonceType m_nonce = -1; + TransactionReceipt::Ptr m_receipt; + std::string m_sender; + std::string m_to; +}; +} // namespace protocol +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-protocol/bcos-protocol/amop/TopicItem.h" "b/BFPL\345\243\271/bcos-protocol/bcos-protocol/amop/TopicItem.h" new file mode 100644 index 00000000..3ea24a93 --- /dev/null +++ "b/BFPL\345\243\271/bcos-protocol/bcos-protocol/amop/TopicItem.h" @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2021 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 TopicItem.h + * @author: octopus + * @date 2021-06-21 + */ +#pragma once +#include +#include +#include +#include +#include +#include +namespace bcos +{ +namespace protocol +{ +class TopicItem +{ +public: + using Ptr = std::shared_ptr; + TopicItem() {} + TopicItem(const std::string& _topicName) : m_topicName(_topicName) {} + std::string topicName() const { return m_topicName; } + void setTopicName(const std::string& _topicName) { m_topicName = _topicName; } + +private: + std::string m_topicName; +}; + +inline bool operator<(const TopicItem& _topicItem0, const TopicItem& _topicItem1) +{ + return _topicItem0.topicName() < _topicItem1.topicName(); +} +using TopicItems = std::set; + +inline bool parseSubTopicsJson(const std::string& _json, TopicItems& _topicItems) +{ + Json::Value root; + Json::Reader jsonReader; + + try + { + if (!jsonReader.parse(_json, root)) + { + BCOS_LOG(ERROR) << LOG_BADGE("parseSubTopicsJson") << LOG_DESC("unable to parse json") + << LOG_KV("json:", _json); + return false; + } + + TopicItems topicItems; + + auto topicItemsSize = root["topics"].size(); + + for (unsigned int i = 0; i < topicItemsSize; i++) + { + std::string topic = root["topics"][i].asString(); + topicItems.insert(TopicItem(topic)); + } + + _topicItems = topicItems; + + BCOS_LOG(INFO) << LOG_BADGE("parseSubTopicsJson") + << LOG_KV("topicItems size", topicItems.size()) << LOG_KV("json", _json); + return true; + } + catch (const std::exception& e) + { + BCOS_LOG(ERROR) << LOG_BADGE("parseSubTopicsJson") + << LOG_KV("error", boost::diagnostic_information(e)) + << LOG_KV("json:", _json); + return false; + } +} +} // namespace protocol +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-protocol/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-protocol/test/CMakeLists.txt" new file mode 100644 index 00000000..f5751f37 --- /dev/null +++ "b/BFPL\345\243\271/bcos-protocol/test/CMakeLists.txt" @@ -0,0 +1,29 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of bcos-protocol +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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(GLOB_RECURSE SOURCES "*.cpp" "*.h" "*.sol") +# cmake settings +set(TEST_BINARY_NAME test-bcos-protocol) + +# No need to test pb protocol +# add_executable(${TEST_BINARY_NAME} ${SOURCES}) +# target_include_directories(${TEST_BINARY_NAME} PRIVATE . ${CMAKE_SOURCE_DIR}) + +# find_package(Boost QUIET REQUIRED unit_test_framework) + +# target_link_libraries(${TEST_BINARY_NAME} ${PROTOCOL_TARGET} ${PBPROTOCOL_TARGET} ${UTILITIES_TARGET} ${CRYPTO_TARGET} Boost::unit_test_framework) +# add_test(NAME test-protocol WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-protocol/test/unittests/main/main.cpp" "b/BFPL\345\243\271/bcos-protocol/test/unittests/main/main.cpp" new file mode 100644 index 00000000..5029377e --- /dev/null +++ "b/BFPL\345\243\271/bcos-protocol/test/unittests/main/main.cpp" @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 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 main.cpp + * @author: yujiechen, jimmyshi + * @date 2021-02-24 + */ +#define BOOST_TEST_MODULE FISCO_BCOS_Tests +#define BOOST_TEST_MAIN + +#include +#include diff --git "a/BFPL\345\243\271/bcos-rpc/CMakeLists.txt" "b/BFPL\345\243\271/bcos-rpc/CMakeLists.txt" new file mode 100644 index 00000000..27d71092 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/CMakeLists.txt" @@ -0,0 +1,46 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for bcos-rpc +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 bcos-rpc +# 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. +#------------------------------------------------------------------------------ + +cmake_minimum_required(VERSION 3.10) +set(CMAKE_OSX_DEPLOYMENT_TARGET "11.3" CACHE STRING "Minimum OS X deployment version") + +include(Version) +project(bcos-rpc VERSION ${VERSION}) + +find_package(jsoncpp CONFIG REQUIRED) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") + +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) + +# if (TESTS) +# enable_testing() +# set(CTEST_OUTPUT_ON_FAILURE TRUE) +# add_subdirectory(test) +# endif() + +# for code coverage +if (COVERAGE) + include(Coverage) + config_coverage("rpc-coverage" "'/usr*' '${CMAKE_CURRENT_SOURCE_DIR}/bcos-cmake-scripts*' '${CMAKE_SOURCE_DIR}/test/mock**' '${CMAKE_SOURCE_DIR}/test/main**'") +endif () diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/Common.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/Common.h" new file mode 100644 index 00000000..28d1daff --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/Common.h" @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 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: octopus + * @date 2021-07-02 + */ +#pragma once +#include +#include +#include + +#define RPC_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("RPC") + +namespace bcos +{ +namespace rpc +{ +enum AMOPClientMessageType +{ + AMOP_SUBTOPIC = 0x110, // 272 + AMOP_REQUEST = 0x111, // 273 + AMOP_BROADCAST = 0x112, // 274 + AMOP_RESPONSE = 0x113 // 275 +}; +} // namespace rpc +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/Rpc.cpp" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/Rpc.cpp" new file mode 100644 index 00000000..fb399f26 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/Rpc.cpp" @@ -0,0 +1,251 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for RPC + * @file Rpc.cpp + * @author: octopus + * @date 2021-07-15 + */ + +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::rpc; +using namespace bcos::group; +using namespace bcos::protocol; +using namespace bcos::boostssl::ws; + +Rpc::Rpc(std::shared_ptr _wsService, + bcos::rpc::JsonRpcImpl_2_0::Ptr _jsonRpcImpl, bcos::event::EventSub::Ptr _eventSub, + AMOPClient::Ptr _amopClient) + : m_wsService(_wsService), + m_jsonRpcImpl(_jsonRpcImpl), + m_eventSub(_eventSub), + m_amopClient(_amopClient), + m_groupManager(m_jsonRpcImpl->groupManager()) +{ + // init the local protocol + m_localProtocol = g_BCOSConfig.protocolInfo(ProtocolModuleID::RpcService); + // group information notification + m_jsonRpcImpl->groupManager()->registerGroupInfoNotifier( + [this](bcos::group::GroupInfo::Ptr _groupInfo) { notifyGroupInfo(_groupInfo); }); + // block number notification + m_jsonRpcImpl->groupManager()->registerBlockNumberNotifier( + [this](std::string const& _groupID, std::string const& _nodeName, + bcos::protocol::BlockNumber _blockNumber) { + asyncNotifyBlockNumber(_groupID, _nodeName, _blockNumber, [](Error::Ptr _error) { + if (_error) + { + RPC_LOG(ERROR) << LOG_DESC("asyncNotifyBlockNumber error") + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + } + }); + }); + + // handshake msgHandler + m_wsService->registerMsgHandler(bcos::protocol::MessageType::HANDESHAKE, + boost::bind( + &Rpc::onRecvHandshakeRequest, this, boost::placeholders::_1, boost::placeholders::_2)); +} + +void Rpc::start() +{ + // start event sub + m_eventSub->start(); + // start websocket service + m_wsService->start(); + m_amopClient->start(); + RPC_LOG(INFO) << LOG_DESC("start rpc successfully"); +} + +void Rpc::stop() +{ + // stop ws service + if (m_wsService) + { + m_wsService->stop(); + } + + // stop event sub + if (m_eventSub) + { + m_eventSub->stop(); + } + if (m_amopClient) + { + m_amopClient->stop(); + } + + RPC_LOG(INFO) << LOG_DESC("[RPC][RPC][stop]") << LOG_DESC("stop rpc successfully"); +} + +/** + * @brief: notify blockNumber to rpc + * @param _blockNumber: blockNumber + * @param _callback: resp callback + * @return void + */ +void Rpc::asyncNotifyBlockNumber(std::string const& _groupID, std::string const& _nodeName, + bcos::protocol::BlockNumber _blockNumber, std::function _callback) +{ + auto ss = m_wsService->sessions(); + for (const auto& s : ss) + { + if (s && s->isConnected()) + { + std::string group; + // eg: {"blockNumber": 11, "group": "group"} + Json::Value response; + response["group"] = _groupID; + response["nodeName"] = _nodeName; + response["blockNumber"] = _blockNumber; + auto resp = response.toStyledString(); + auto message = m_wsService->messageFactory()->buildMessage(); + message->setPacketType(bcos::protocol::MessageType::BLOCK_NOTIFY); + message->setPayload(std::make_shared(resp.begin(), resp.end())); + s->asyncSendMessage(message); + } + } + + if (_callback) + { + _callback(nullptr); + } + m_jsonRpcImpl->groupManager()->updateGroupBlockInfo(_groupID, _nodeName, _blockNumber); + RPC_LOG(TRACE) << LOG_BADGE("asyncNotifyBlockNumber") << LOG_KV("group", _groupID) + << LOG_KV("blockNumber", _blockNumber) << LOG_KV("sessions", ss.size()); +} + +void Rpc::asyncNotifyGroupInfo( + bcos::group::GroupInfo::Ptr _groupInfo, std::function _callback) +{ + m_jsonRpcImpl->groupManager()->updateGroupInfo(_groupInfo); + if (_callback) + { + _callback(nullptr); + } +} + +void Rpc::notifyGroupInfo(bcos::group::GroupInfo::Ptr _groupInfo) +{ + // notify the groupInfo to SDK + auto sdkSessions = m_wsService->sessions(); + for (auto const& session : sdkSessions) + { + if (!session || !session->isConnected()) + { + continue; + } + Json::Value groupInfoJson; + groupInfoToJson(groupInfoJson, _groupInfo); + auto response = groupInfoJson.toStyledString(); + auto message = m_wsService->messageFactory()->buildMessage(); + message->setPacketType(bcos::protocol::MessageType::GROUP_NOTIFY); + message->setPayload(std::make_shared(response.begin(), response.end())); + session->asyncSendMessage(message); + } +} + +bool Rpc::negotiatedVersion( + std::shared_ptr _msg, std::shared_ptr _session) +{ + auto seq = _msg->seq(); + HandshakeRequest handshakeRequest; + auto ret = handshakeRequest.decode(*(_msg->payload())); + if (!ret) + { + RPC_LOG(WARNING) << LOG_DESC( + "negotiatedVersion error for receive invalid handshake request") + << LOG_KV("seq", seq); + return false; + } + // negotiated protocol Version + auto const& protocolInfo = handshakeRequest.protocol(); + // negotiated failed + if (protocolInfo.minVersion() > m_localProtocol->maxVersion() || + protocolInfo.maxVersion() < m_localProtocol->minVersion()) + { + RPC_LOG(WARNING) << LOG_DESC("negotiatedVersion failed, disconnect the session") + << LOG_KV("peer", _session->endPoint()) + << LOG_KV("minVersion", protocolInfo.minVersion()) + << LOG_KV("maxVersion", protocolInfo.maxVersion()) + << LOG_KV("supportMinVersion", m_localProtocol->minVersion()) + << LOG_KV("supportMaxVersion", m_localProtocol->maxVersion()) + << LOG_KV("seq", seq); + _session->drop(WsError::UserDisconnect); + } + auto version = std::min(m_localProtocol->maxVersion(), protocolInfo.maxVersion()); + _session->setVersion(version); + RPC_LOG(INFO) << LOG_DESC("negotiatedVersion success") << LOG_KV("peer", _session->endPoint()) + << LOG_KV("minVersion", protocolInfo.minVersion()) + << LOG_KV("maxVersion", protocolInfo.maxVersion()) + << LOG_KV("supportMinVersion", m_localProtocol->minVersion()) + << LOG_KV("supportMaxVersion", m_localProtocol->maxVersion()) + << LOG_KV("negotiatedVersion", version) << LOG_KV("seq", seq); + return true; +} + +void Rpc::onRecvHandshakeRequest( + std::shared_ptr _msg, std::shared_ptr _session) +{ + if (!negotiatedVersion(_msg, _session)) + { + return; + } + auto self = std::weak_ptr(shared_from_this()); + + // notify the handshakeResponse + m_jsonRpcImpl->getGroupInfoList([_msg, _session, self]( + bcos::Error::Ptr _error, Json::Value& _groupListResponse) { + if (_error && _error->errorCode() != bcos::protocol::CommonError::SUCCESS) + { + RPC_LOG(ERROR) + << LOG_BADGE( + "onRecvHandshakeRequest and response failed for get groupInfoList failed") + << LOG_KV("endpoint", _session ? _session->endPoint() : "unknown") + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()); + return; + } + auto rpc = self.lock(); + if (!rpc) + { + return; + } + rpc->m_jsonRpcImpl->getGroupBlockNumber( + [_groupListResponse, _session, _msg](bcos::Error::Ptr, Json::Value& _blockNumber) { + Json::Value handshakeResponse; + handshakeResponse["groupInfoList"] = _groupListResponse; + handshakeResponse["groupBlockNumber"] = _blockNumber; + handshakeResponse["protocolVersion"] = _session->version(); + Json::FastWriter writer; + auto response = writer.write(handshakeResponse); + + _msg->setPayload(std::make_shared(response.begin(), response.end())); + _session->asyncSendMessage(_msg); + RPC_LOG(INFO) << LOG_DESC("onRecvHandshakeRequest success") + << LOG_KV("version", _session->version()) + << LOG_KV("endpoint", _session ? _session->endPoint() : "unknown") + << LOG_KV("handshakeResponse", response); + }); + }); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/Rpc.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/Rpc.h" new file mode 100644 index 00000000..af21fe39 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/Rpc.h" @@ -0,0 +1,110 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for RPC + * @file Rpc.h + * @author: octopus + * @date 2021-07-15 + */ + +#pragma once +#include "bcos-rpc/groupmgr/GroupManager.h" +#include +#include +#include +#include +namespace bcos +{ +namespace boostssl +{ +namespace ws +{ +class WsSession; +class WsService; +} // namespace ws +} // namespace boostssl +namespace rpc +{ +class Rpc : public RPCInterface, public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + Rpc(std::shared_ptr _wsService, + bcos::rpc::JsonRpcImpl_2_0::Ptr _jsonRpcImpl, bcos::event::EventSub::Ptr _eventSub, + AMOPClient::Ptr _amopClient); + + virtual ~Rpc() { stop(); } + + void start() override; + void stop() override; + + /** + * @brief: notify blockNumber to rpc + * @param _blockNumber: blockNumber + * @param _callback: resp callback + * @return void + */ + void asyncNotifyBlockNumber(std::string const& _groupID, std::string const& _nodeName, + bcos::protocol::BlockNumber _blockNumber, + std::function _callback) override; + + void asyncNotifyGroupInfo(bcos::group::GroupInfo::Ptr _groupInfo, + std::function _callback) override; + + std::shared_ptr wsService() const { return m_wsService; } + bcos::rpc::JsonRpcImpl_2_0::Ptr jsonRpcImpl() const { return m_jsonRpcImpl; } + bcos::event::EventSub::Ptr eventSub() const { return m_eventSub; } + + void asyncNotifyAMOPMessage(int16_t _type, std::string const& _topic, + bytesConstRef _requestData, + std::function _callback) override + { + m_amopClient->asyncNotifyAMOPMessage(_type, _topic, _requestData, _callback); + } + + void setClientID(std::string const& _clientID) + { + m_jsonRpcImpl->setClientID(_clientID); + m_amopClient->setClientID(_clientID); + } + void asyncNotifySubscribeTopic( + std::function _callback) override + { + m_amopClient->asyncNotifySubscribeTopic(_callback); + } + + GroupManager::Ptr groupManager() { return m_groupManager; } + +protected: + virtual void notifyGroupInfo(bcos::group::GroupInfo::Ptr _groupInfo); + + virtual void onRecvHandshakeRequest(std::shared_ptr _msg, + std::shared_ptr _session); + + virtual bool negotiatedVersion(std::shared_ptr _msg, + std::shared_ptr _session); + +private: + std::shared_ptr m_wsService; + bcos::rpc::JsonRpcImpl_2_0::Ptr m_jsonRpcImpl; + bcos::event::EventSub::Ptr m_eventSub; + AMOPClient::Ptr m_amopClient; + GroupManager::Ptr m_groupManager; + + bcos::protocol::ProtocolInfo::ConstPtr m_localProtocol; +}; + +} // namespace rpc +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/RpcFactory.cpp" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/RpcFactory.cpp" new file mode 100644 index 00000000..7500acaf --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/RpcFactory.cpp" @@ -0,0 +1,461 @@ +/** + * Copyright (C) 2021 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. + * + * @brief RpcFactory + * @file RpcFactory.h + * @author: octopus + * @date 2021-07-15 + */ + +#include "bcos-framework/gateway/GatewayTypeDef.h" +#include "bcos-rpc/groupmgr/TarsGroupManager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::rpc; +using namespace bcos::protocol; +using namespace bcos::crypto; +using namespace bcos::gateway; +using namespace bcos::group; +using namespace bcos::boostssl::ws; +using namespace bcos::protocol; +using namespace bcos::security; + +RpcFactory::RpcFactory(std::string const& _chainID, GatewayInterface::Ptr _gatewayInterface, + KeyFactory::Ptr _keyFactory, bcos::security::DataEncryptInterface::Ptr _dataEncrypt) + : m_chainID(_chainID), + m_gateway(_gatewayInterface), + m_keyFactory(_keyFactory), + m_dataEncrypt(_dataEncrypt) +{} + +std::shared_ptr RpcFactory::initConfig( + bcos::tool::NodeConfig::Ptr _nodeConfig) +{ + auto wsConfig = std::make_shared(); + wsConfig->setModel(bcos::boostssl::ws::WsModel::Server); + + wsConfig->setListenIP(_nodeConfig->rpcListenIP()); + wsConfig->setListenPort(_nodeConfig->rpcListenPort()); + wsConfig->setThreadPoolSize(_nodeConfig->rpcThreadPoolSize()); + wsConfig->setDisableSsl(_nodeConfig->rpcDisableSsl()); + if (_nodeConfig->rpcDisableSsl()) + { + RPC_LOG(INFO) << LOG_BADGE("initConfig") << LOG_DESC("rpc work in disable ssl model") + << LOG_KV("listenIP", wsConfig->listenIP()) + << LOG_KV("listenPort", wsConfig->listenPort()) + << LOG_KV("threadCount", wsConfig->threadPoolSize()) + << LOG_KV("asServer", wsConfig->asServer()); + return wsConfig; + } + + auto contextConfig = std::make_shared(); + if (!_nodeConfig->rpcSmSsl()) + { // ssl + boostssl::context::ContextConfig::CertConfig certConfig; + + std::shared_ptr keyContent; + + // caCert + if (false == _nodeConfig->caCert().empty()) + { + try + { + keyContent = readContents(boost::filesystem::path(_nodeConfig->caCert())); + if (nullptr != keyContent) + { + certConfig.caCert.resize(keyContent->size()); + memcpy(certConfig.caCert.data(), keyContent->data(), keyContent->size()); + } + } + catch (std::exception& e) + { + BCOS_LOG(ERROR) << LOG_BADGE("RpcFactory") << LOG_DESC("open caCert failed") + << LOG_KV("file", _nodeConfig->caCert()); + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "RpcFactory::initConfig: unable read content of key:" + + _nodeConfig->caCert())); + } + } + + // nodeCert + if (false == _nodeConfig->nodeCert().empty()) + { + try + { + keyContent = readContents(boost::filesystem::path(_nodeConfig->nodeCert())); + if (nullptr != keyContent) + { + certConfig.nodeCert.resize(keyContent->size()); + memcpy(certConfig.nodeCert.data(), keyContent->data(), keyContent->size()); + } + } + catch (std::exception& e) + { + BCOS_LOG(ERROR) << LOG_BADGE("RpcFactory") << LOG_DESC("open nodeCert failed") + << LOG_KV("file", _nodeConfig->nodeCert()); + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "RpcFactory::initConfig: unable read content of key:" + + _nodeConfig->nodeCert())); + } + } + + // nodeKey + if (false == _nodeConfig->nodeKey().empty()) + { + try + { + if (nullptr == m_dataEncrypt) // storage_security.enable = false + keyContent = readContents(boost::filesystem::path(_nodeConfig->nodeKey())); + else + keyContent = m_dataEncrypt->decryptFile(_nodeConfig->nodeKey()); + } + catch (std::exception& e) + { + BCOS_LOG(ERROR) << LOG_BADGE("RpcFactory") << LOG_DESC("open nodeKey failed") + << LOG_KV("file", _nodeConfig->nodeKey()); + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "RpcFactory::initConfig: unable read content of key:" + + _nodeConfig->nodeKey())); + } + } + certConfig.nodeKey.resize(keyContent->size()); + memcpy(certConfig.nodeKey.data(), keyContent->data(), keyContent->size()); + + contextConfig->setIsCertPath(false); + + contextConfig->setCertConfig(certConfig); + contextConfig->setSslType("ssl"); + + RPC_LOG(INFO) << LOG_DESC("rpc work in ssl model") + << LOG_KV("listenIP", wsConfig->listenIP()) + << LOG_KV("listenPort", wsConfig->listenPort()) + << LOG_KV("threadCount", wsConfig->threadPoolSize()) + << LOG_KV("asServer", wsConfig->asServer()) + << LOG_KV("caCert", _nodeConfig->caCert()) + << LOG_KV("nodeCert", _nodeConfig->nodeCert()) + << LOG_KV("nodeKey", _nodeConfig->nodeKey()); + } + else + { // sm ssl + boostssl::context::ContextConfig::SMCertConfig certConfig; + + std::shared_ptr keyContent; + + // caCert + if (false == _nodeConfig->smCaCert().empty()) + { + try + { + keyContent = readContents(boost::filesystem::path(_nodeConfig->smCaCert())); + if (nullptr != keyContent) + { + certConfig.caCert.resize(keyContent->size()); + memcpy(certConfig.caCert.data(), keyContent->data(), keyContent->size()); + } + } + catch (std::exception& e) + { + BCOS_LOG(ERROR) << LOG_BADGE("RpcFactory") << LOG_DESC("open smCaCert failed") + << LOG_KV("file", _nodeConfig->caCert()); + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "RpcFactory::initConfig: unable read content of key:" + + _nodeConfig->caCert())); + } + } + + // nodeCert + if (false == _nodeConfig->smNodeCert().empty()) + { + try + { + keyContent = readContents(boost::filesystem::path(_nodeConfig->smNodeCert())); + if (nullptr != keyContent) + { + certConfig.nodeCert.resize(keyContent->size()); + memcpy(certConfig.nodeCert.data(), keyContent->data(), keyContent->size()); + } + } + catch (std::exception& e) + { + BCOS_LOG(ERROR) << LOG_BADGE("RpcFactory") << LOG_DESC("open smNodeCert failed") + << LOG_KV("file", _nodeConfig->nodeCert()); + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "RpcFactory::initConfig: unable read content of key:" + + _nodeConfig->nodeCert())); + } + } + + // nodeKey + if (false == _nodeConfig->smNodeKey().empty()) + { + try + { + if (nullptr == m_dataEncrypt) // storage_security.enable = false + keyContent = readContents(boost::filesystem::path(_nodeConfig->smNodeKey())); + else + keyContent = m_dataEncrypt->decryptFile(_nodeConfig->smNodeKey()); + } + catch (std::exception& e) + { + BCOS_LOG(ERROR) << LOG_BADGE("RpcFactory") << LOG_DESC("open smNodeKey failed") + << LOG_KV("file", _nodeConfig->nodeKey()); + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "RpcFactory::initConfig: unable read content of key:" + + _nodeConfig->nodeKey())); + } + } + certConfig.nodeKey.resize(keyContent->size()); + memcpy(certConfig.nodeKey.data(), keyContent->data(), keyContent->size()); + + // enNodeCert + if (false == _nodeConfig->enSmNodeCert().empty()) + { + try + { + keyContent = readContents(boost::filesystem::path(_nodeConfig->enSmNodeCert())); + if (nullptr != keyContent) + { + certConfig.enNodeCert.resize(keyContent->size()); + memcpy(certConfig.enNodeCert.data(), keyContent->data(), keyContent->size()); + } + } + catch (std::exception& e) + { + BCOS_LOG(ERROR) << LOG_BADGE("RpcFactory") << LOG_DESC("open enSmNodeCert failed") + << LOG_KV("file", _nodeConfig->nodeCert()); + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "RpcFactory::initConfig: unable read content of key:" + + _nodeConfig->nodeCert())); + } + } + + // enNodeKey + if (false == _nodeConfig->enSmNodeKey().empty()) + { + try + { + if (nullptr == m_dataEncrypt) // storage_security.enable = false + keyContent = readContents(boost::filesystem::path(_nodeConfig->enSmNodeKey())); + else + keyContent = m_dataEncrypt->decryptFile(_nodeConfig->enSmNodeKey()); + } + catch (std::exception& e) + { + BCOS_LOG(ERROR) << LOG_BADGE("RpcFactory") << LOG_DESC("open enSmNodeKey failed") + << LOG_KV("file", _nodeConfig->nodeKey()); + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "RpcFactory::initConfig: unable read content of key:" + + _nodeConfig->nodeKey())); + } + } + certConfig.enNodeKey.resize(keyContent->size()); + memcpy(certConfig.enNodeKey.data(), keyContent->data(), keyContent->size()); + + contextConfig->setIsCertPath(false); + + contextConfig->setSmCertConfig(certConfig); + contextConfig->setSslType("sm_ssl"); + + RPC_LOG(INFO) << LOG_DESC("rpc work in sm ssl model") + << LOG_KV("listenIP", wsConfig->listenIP()) + << LOG_KV("listenPort", wsConfig->listenPort()) + << LOG_KV("threadCount", wsConfig->threadPoolSize()) + << LOG_KV("asServer", wsConfig->asServer()) + << LOG_KV("caCert", _nodeConfig->smCaCert()) + << LOG_KV("nodeCert", _nodeConfig->smNodeCert()) + << LOG_KV("nodeKey", _nodeConfig->smNodeKey()) + << LOG_KV("enNodeCert", _nodeConfig->enSmNodeCert()) + << LOG_KV("enNodeKey", _nodeConfig->enSmNodeKey()); + } + + wsConfig->setContextConfig(contextConfig); + + return wsConfig; +} + +bcos::boostssl::ws::WsService::Ptr RpcFactory::buildWsService( + bcos::boostssl::ws::WsConfig::Ptr _config) +{ + auto wsService = std::make_shared(); + auto initializer = std::make_shared(); + + initializer->setConfig(_config); + initializer->initWsService(wsService); + + return wsService; +} + +bcos::rpc::JsonRpcImpl_2_0::Ptr RpcFactory::buildJsonRpc(int sendTxTimeout, + std::shared_ptr _wsService, GroupManager::Ptr _groupManager) +{ + // JsonRpcImpl_2_0 + //* + auto jsonRpcInterface = + std::make_shared(_groupManager, m_gateway, _wsService); + jsonRpcInterface->setSendTxTimeout(sendTxTimeout); + /*/ + auto jsonRpcInterface = + std::make_shared(_groupManager, m_gateway, + _wsService); + //*/ + auto httpServer = _wsService->httpServer(); + if (httpServer) + { + httpServer->setHttpReqHandler(std::bind(&bcos::rpc::JsonRpcInterface::onRPCRequest, + jsonRpcInterface, std::placeholders::_1, std::placeholders::_2)); + } + return jsonRpcInterface; +} + +bcos::event::EventSub::Ptr RpcFactory::buildEventSub( + std::shared_ptr _wsService, GroupManager::Ptr _groupManager) +{ + auto eventSubFactory = std::make_shared(); + auto eventSub = eventSubFactory->buildEventSub(_wsService); + + auto matcher = std::make_shared(); + eventSub->setGroupManager(_groupManager); + eventSub->setMessageFactory(_wsService->messageFactory()); + eventSub->setMatcher(matcher); + RPC_LOG(INFO) << LOG_DESC("create event sub obj"); + return eventSub; +} + +Rpc::Ptr RpcFactory::buildRpc(std::string const& _gatewayServiceName, + std::string const& _rpcServiceName, bcos::election::LeaderEntryPointInterface::Ptr _entryPoint) +{ + auto config = initConfig(m_nodeConfig); + auto wsService = buildWsService(config); + auto groupManager = buildGroupManager(_rpcServiceName, _entryPoint); + auto amopClient = buildAMOPClient(wsService, _gatewayServiceName); + + RPC_LOG(INFO) << LOG_KV("listenIP", config->listenIP()) + << LOG_KV("listenPort", config->listenPort()) + << LOG_KV("threadCount", config->threadPoolSize()) + << LOG_KV("gatewayServiceName", _gatewayServiceName); + auto rpc = buildRpc(m_nodeConfig->sendTxTimeout(), wsService, groupManager, amopClient); + return rpc; +} + +Rpc::Ptr RpcFactory::buildLocalRpc( + bcos::group::GroupInfo::Ptr _groupInfo, NodeService::Ptr _nodeService) +{ + auto config = initConfig(m_nodeConfig); + auto wsService = buildWsService(config); + auto groupManager = buildAirGroupManager(_groupInfo, _nodeService); + auto amopClient = buildAirAMOPClient(wsService); + auto rpc = buildRpc(m_nodeConfig->sendTxTimeout(), wsService, groupManager, amopClient); + // Note: init groupManager after create rpc and register the handlers + groupManager->init(); + return rpc; +} + +Rpc::Ptr RpcFactory::buildRpc(int sendTxTimeout, + std::shared_ptr _wsService, GroupManager::Ptr _groupManager, + AMOPClient::Ptr _amopClient) +{ + // JsonRpc + auto jsonRpc = buildJsonRpc(sendTxTimeout, _wsService, _groupManager); + // EventSub + auto es = buildEventSub(_wsService, _groupManager); + return std::make_shared(_wsService, jsonRpc, es, _amopClient); +} + +// Note: _rpcServiceName is used to check the validation of groupInfo when groupManager update +// groupInfo +GroupManager::Ptr RpcFactory::buildGroupManager( + std::string const& _rpcServiceName, bcos::election::LeaderEntryPointInterface::Ptr _entryPoint) +{ + auto nodeServiceFactory = std::make_shared(); + if (!_entryPoint) + { + RPC_LOG(INFO) << LOG_DESC("buildGroupManager: using tars to manager the node info"); + return std::make_shared( + _rpcServiceName, m_chainID, nodeServiceFactory, m_nodeConfig); + } + RPC_LOG(INFO) << LOG_DESC("buildGroupManager with leaderEntryPoint to manager the node info"); + auto groupManager = std::make_shared( + _rpcServiceName, m_chainID, nodeServiceFactory, m_nodeConfig); + auto groupInfoCodec = std::make_shared(); + _entryPoint->addMemberChangeNotificationHandler( + [groupManager, groupInfoCodec]( + std::string const& _key, bcos::protocol::MemberInterface::Ptr _member) { + auto const& groupInfoStr = _member->memberConfig(); + auto groupInfo = groupInfoCodec->deserialize(groupInfoStr); + groupManager->updateGroupInfo(groupInfo); + RPC_LOG(INFO) << LOG_DESC("The leader entryPoint changed") << LOG_KV("key", _key) + << LOG_KV("memberID", _member->memberID()) + << LOG_KV("modifyIndex", _member->seq()) + << LOG_KV("groupID", groupInfo->groupID()); + }); + + _entryPoint->addMemberDeleteNotificationHandler( + [groupManager, groupInfoCodec]( + std::string const& _leaderKey, bcos::protocol::MemberInterface::Ptr _leader) { + auto const& groupInfoStr = _leader->memberConfig(); + auto groupInfo = groupInfoCodec->deserialize(groupInfoStr); + RPC_LOG(INFO) << LOG_DESC("The leader entryPoint has been deleted") + << LOG_KV("key", _leaderKey) << LOG_KV("memberID", _leader->memberID()) + << LOG_KV("modifyIndex", _leader->seq()) + << LOG_KV("groupID", groupInfo->groupID()); + groupManager->removeGroupNodeList(groupInfo); + }); + return groupManager; +} + +AirGroupManager::Ptr RpcFactory::buildAirGroupManager( + GroupInfo::Ptr _groupInfo, NodeService::Ptr _nodeService) +{ + return std::make_shared(m_chainID, _groupInfo, _nodeService); +} + +AMOPClient::Ptr RpcFactory::buildAMOPClient( + std::shared_ptr _wsService, std::string const& _gatewayServiceName) +{ + auto wsFactory = std::make_shared(); + auto requestFactory = std::make_shared(); + return std::make_shared( + _wsService, wsFactory, requestFactory, m_gateway, _gatewayServiceName); +} + +AMOPClient::Ptr RpcFactory::buildAirAMOPClient(std::shared_ptr _wsService) +{ + auto wsFactory = std::make_shared(); + auto requestFactory = std::make_shared(); + return std::make_shared(_wsService, wsFactory, requestFactory, m_gateway); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/RpcFactory.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/RpcFactory.h" new file mode 100644 index 00000000..b8b5db98 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/RpcFactory.h" @@ -0,0 +1,101 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for RPC + * @file Rpc.h + * @author: octopus + * @date 2021-07-15 + */ + +#pragma once +#include "bcos-rpc/amop/AMOPClient.h" +#include "bcos-rpc/amop/AirAMOPClient.h" +#include "bcos-rpc/groupmgr/AirGroupManager.h" +#include "bcos-rpc/groupmgr/GroupManager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace boostssl +{ +namespace ws +{ +class WsService; +class WsSession; +} // namespace ws +} // namespace boostssl + +namespace rpc +{ +class RpcFactory : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + RpcFactory(std::string const& _chainID, bcos::gateway::GatewayInterface::Ptr _gatewayInterface, + bcos::crypto::KeyFactory::Ptr _keyFactory, + bcos::security::DataEncryptInterface::Ptr _dataEncrypt = nullptr); + virtual ~RpcFactory() {} + + std::shared_ptr initConfig(bcos::tool::NodeConfig::Ptr _nodeConfig); + std::shared_ptr buildWsService( + bcos::boostssl::ws::WsConfig::Ptr _config); + + Rpc::Ptr buildRpc(std::string const& _gatewayServiceName, std::string const& _rpcServiceName, + bcos::election::LeaderEntryPointInterface::Ptr _entryPoint); + Rpc::Ptr buildLocalRpc(bcos::group::GroupInfo::Ptr _groupInfo, NodeService::Ptr _nodeService); + + Rpc::Ptr buildRpc(int sendTxTimeout, std::shared_ptr _wsService, + GroupManager::Ptr _groupManager, AMOPClient::Ptr _amopClient); + + bcos::tool::NodeConfig::Ptr nodeConfig() const { return m_nodeConfig; } + void setNodeConfig(bcos::tool::NodeConfig::Ptr _nodeConfig) { m_nodeConfig = _nodeConfig; } + +protected: + // for groupManager builder + GroupManager::Ptr buildGroupManager(std::string const& _rpcServiceName, + bcos::election::LeaderEntryPointInterface::Ptr _entryPoint); + AirGroupManager::Ptr buildAirGroupManager( + bcos::group::GroupInfo::Ptr _groupInfo, NodeService::Ptr _nodeService); + + // for AMOP builder + AMOPClient::Ptr buildAMOPClient(std::shared_ptr _wsService, + std::string const& _gatewayServiceName); + AMOPClient::Ptr buildAirAMOPClient(std::shared_ptr _wsService); + + + bcos::rpc::JsonRpcImpl_2_0::Ptr buildJsonRpc(int sendTxTimeout, + std::shared_ptr _wsService, GroupManager::Ptr _groupManager); + bcos::event::EventSub::Ptr buildEventSub( + std::shared_ptr _wsService, GroupManager::Ptr _groupManager); + +private: + std::string m_chainID; + bcos::gateway::GatewayInterface::Ptr m_gateway; + std::shared_ptr m_keyFactory; + bcos::tool::NodeConfig::Ptr m_nodeConfig; + bcos::security::DataEncryptInterface::Ptr m_dataEncrypt; +}; +} // namespace rpc +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/amop/AMOPClient.cpp" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/amop/AMOPClient.cpp" new file mode 100644 index 00000000..aefc8bb5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/amop/AMOPClient.cpp" @@ -0,0 +1,570 @@ +/** + * Copyright (C) 2021 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. + * + * @brief AMOP client + * @file AMOPClient.cpp + * @author: yujiechen + * @date 2021-10-28 + */ +#include + +#include "AMOPClient.h" +#include "bcos-tars-protocol/Common.h" +#include "fisco-bcos-tars-service/Common/TarsUtils.h" +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::rpc; +using namespace bcos::boostssl::ws; +using namespace bcostars; +using namespace tars; +using namespace bcos::gateway; +using namespace bcos::protocol; + +void AMOPClient::initMsgHandler() +{ + m_wsService->registerMsgHandler(AMOPClientMessageType::AMOP_SUBTOPIC, + boost::bind( + &AMOPClient::onRecvSubTopics, this, boost::placeholders::_1, boost::placeholders::_2)); + m_wsService->registerMsgHandler( + AMOPClientMessageType::AMOP_REQUEST, boost::bind(&AMOPClient::onRecvAMOPRequest, this, + boost::placeholders::_1, boost::placeholders::_2)); + m_wsService->registerMsgHandler(AMOPClientMessageType::AMOP_BROADCAST, + boost::bind(&AMOPClient::onRecvAMOPBroadcast, this, boost::placeholders::_1, + boost::placeholders::_2)); + m_wsService->registerDisconnectHandler( + boost::bind(&AMOPClient::onClientDisconnect, this, boost::placeholders::_1)); +} + +bool AMOPClient::updateTopicInfos( + std::string const& _topicInfo, std::shared_ptr _session) +{ + TopicItems topicItems; + auto ret = parseSubTopicsJson(_topicInfo, topicItems); + if (!ret) + { + return false; + } + { + WriteGuard l(x_topicToSessions); + for (auto const& item : topicItems) + { + m_topicToSessions[item.topicName()][_session->endPoint()] = _session; + } + } + return true; +} +/** + * @brief: receive sub topic message from sdk + */ +void AMOPClient::onRecvSubTopics( + std::shared_ptr _msg, std::shared_ptr _session) +{ + auto topicInfo = std::string(_msg->payload()->begin(), _msg->payload()->end()); + auto seq = _msg->seq(); + + if (gatewayInactivated()) + { + AMOP_CLIENT_LOG(WARNING) << LOG_BADGE("onRecvSubTopics: the gateway in-activated") + << LOG_KV("topicInfo", topicInfo) + << LOG_KV("endpoint", _session->endPoint()) << LOG_KV("seq", seq); + } + + auto ret = updateTopicInfos(topicInfo, _session); + if (!ret) + { + AMOP_CLIENT_LOG(WARNING) << LOG_BADGE("onRecvSubTopics: invalid topic info") + << LOG_KV("topicInfo", topicInfo) + << LOG_KV("endpoint", _session->endPoint()) << LOG_KV("seq", seq); + return; + } + subscribeTopicToAllNodes(); + AMOP_CLIENT_LOG(INFO) << LOG_BADGE("onRecvSubTopics") << LOG_KV("topicInfo", topicInfo) + << LOG_KV("endpoint", _session->endPoint()) << LOG_KV("seq", seq); +} + +/** + * @brief: receive amop request message from sdk + */ +void AMOPClient::onRecvAMOPRequest( + std::shared_ptr _msg, std::shared_ptr _session) +{ + auto seq = _msg->seq(); + auto amopReq = m_requestFactory->buildRequest( + bytesConstRef(_msg->payload()->data(), _msg->payload()->size())); + AMOP_CLIENT_LOG(DEBUG) << LOG_DESC("onRecvAMOPRequest") << LOG_KV("seq", seq) + << LOG_KV("topic", amopReq->topic()); + + auto topic = amopReq->topic(); + // gateway inactivated + if (onGatewayInactivated(_msg, _session)) + { + // try to send to local node + if (trySendAMOPRequestToLocalNode(_session, topic, _msg)) + { + return; + } + AMOP_CLIENT_LOG(WARNING) << LOG_BADGE( + "onRecvAMOPRequest: the gateway in-activated and try to " + "request to local nodes failed") + << LOG_KV("endpoint", _session->endPoint()) << LOG_KV("seq", seq); + return; + } + auto self = std::weak_ptr(shared_from_this()); + m_gateway->asyncSendMessageByTopic(amopReq->topic(), + bytesConstRef(_msg->payload()->data(), _msg->payload()->size()), + [self, seq, _msg, topic, _session]( + bcos::Error::Ptr&& _error, int16_t, bytesPointer _responseData) { + try + { + auto amopClient = self.lock(); + if (!amopClient) + { + return; + } + auto responseMsg = amopClient->m_wsMessageFactory->buildMessage(); + auto orgSeq = seq; + if (_error && _error->errorCode() != bcos::protocol::CommonError::SUCCESS) + { + auto ret = amopClient->trySendAMOPRequestToLocalNode(_session, topic, _msg); + // to local node + if (ret) + { + return; + } + // tars timeout + auto errorCode = _error->errorCode(); + auto errorMsg = _error->errorMessage(); + if ((_error->errorCode() == -7) || (_error->errorCode() == -8)) + { + errorMsg = "Access to gateway timed out, please check gateway alive"; + } + std::dynamic_pointer_cast(responseMsg) + ->setStatus(errorCode); + // constructor the response + responseMsg->setPayload( + std::make_shared(errorMsg.begin(), errorMsg.end())); + // recover the seq + responseMsg->setSeq(orgSeq); + AMOP_CLIENT_LOG(ERROR) + << LOG_BADGE("onRecvAMOPRequest error") + << LOG_DESC("AMOP async send message callback") << LOG_KV("seq", seq) + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + _session->asyncSendMessage(responseMsg); + return; + } + // Note: the decode function will recover m_seq of wsMessage, so it should be + // better not set orgSeq into the responseMsg before decode + auto size = responseMsg->decode(ref(*_responseData)); + AMOP_CLIENT_LOG(DEBUG) + << LOG_BADGE("onRecvAMOPRequest") + << LOG_DESC("AMOP async send message: receive message response for sdk") + << LOG_KV("size", size) << LOG_KV("seq", seq) + << LOG_KV("type", responseMsg->packetType()); + // recover the seq + responseMsg->setSeq(orgSeq); + _session->asyncSendMessage(responseMsg); + } + catch (std::exception const& e) + { + AMOP_CLIENT_LOG(WARNING) << LOG_DESC("onRecvAMOPRequest exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +bool AMOPClient::trySendAMOPRequestToLocalNode(std::shared_ptr _session, + std::string const& _topic, std::shared_ptr _msg) +{ + // the local node has no client subscribe to the topic + auto selectedSession = randomChooseSession(_topic); + if (!selectedSession) + { + return false; + } + auto self = std::weak_ptr(shared_from_this()); + sendMessageToClient( + _topic, selectedSession, _msg, [self, _session](Error::Ptr&&, bytesPointer _responseData) { + try + { + auto amopClient = self.lock(); + if (!amopClient) + { + return; + } + auto responseMsg = amopClient->m_wsMessageFactory->buildMessage(); + auto size = responseMsg->decode(ref(*_responseData)); + auto seq = responseMsg->seq(); + _session->asyncSendMessage(responseMsg); + AMOP_CLIENT_LOG(DEBUG) + << LOG_BADGE("trySendAMOPRequestToLocalNode") + << LOG_DESC("AMOP async send message: receive message response for sdk") + << LOG_KV("size", size) << LOG_KV("seq", seq) + << LOG_KV("type", responseMsg->packetType()); + } + catch (std::exception const& e) + { + AMOP_CLIENT_LOG(WARNING) << LOG_DESC("trySendAMOPRequestToLocalNode exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); + return true; +} + +/** + * @brief: receive amop broadcast message from sdk + */ +void AMOPClient::onRecvAMOPBroadcast( + std::shared_ptr _msg, std::shared_ptr) +{ + auto seq = _msg->seq(); + auto amopReq = m_requestFactory->buildRequest( + bytesConstRef(_msg->payload()->data(), _msg->payload()->size())); + // broadcast message to the sdks connected to the local node + broadcastAMOPMessage(amopReq->topic(), _msg); + // broadcast messsage to sdks connected to other nodes + m_gateway->asyncSendBroadcastMessageByTopic( + amopReq->topic(), bytesConstRef(_msg->payload()->data(), _msg->payload()->size())); + AMOP_CLIENT_LOG(DEBUG) << LOG_BADGE("onRecvAMOPBroadcast") << LOG_KV("seq", seq) + << LOG_KV("topic", amopReq->topic()); +} + +void AMOPClient::sendMessageToClient(std::string const& _topic, + std::shared_ptr _selectSession, std::shared_ptr _msg, + std::function _callback) +{ + _selectSession->asyncSendMessage(_msg, Options(30000), + [_msg, _topic, _callback](bcos::Error::Ptr _error, + std::shared_ptr _responseMsg, + std::shared_ptr _session) { + auto seq = _msg->seq(); + if (_error && _error->errorCode() != bcos::protocol::CommonError::SUCCESS) + { + AMOP_CLIENT_LOG(WARNING) + << LOG_BADGE("asyncNotifyAMOPMessage") + << LOG_DESC("asyncSendMessage callback error") + << LOG_KV("endpoint", (_session ? _session->endPoint() : std::string(""))) + << LOG_KV("topic", _topic) << LOG_KV("seq", seq) + << LOG_KV("errorCode", _error ? _error->errorCode() : -1) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : "success"); + } + + AMOP_CLIENT_LOG(DEBUG) + << LOG_BADGE("asyncNotifyAMOPMessage") + << LOG_DESC("asyncSendMessage callback response") << LOG_KV("seq", seq) + << LOG_KV("data size", _responseMsg ? _responseMsg->payload()->size() : 0); + auto buffer = std::make_shared(); + if (_responseMsg) + { + _responseMsg->encode(*buffer); + } + + if (_error) + { + _callback( + std::make_shared(_error->errorCode(), _error->errorMessage()), + std::move(buffer)); + } + else + { + _callback(nullptr, std::move(buffer)); + } + }); +} + +void AMOPClient::asyncNotifyAMOPMessage(std::string const& _topic, bytesConstRef _amopRequestData, + std::function _callback) +{ + auto clientSession = randomChooseSession(_topic); + + if (!clientSession) + { + auto responseMessage = m_wsMessageFactory->buildMessage(); + std::dynamic_pointer_cast(responseMessage) + ->setStatus(bcos::protocol::CommonError::NotFoundClientByTopicDispatchMsg); + responseMessage->setPacketType(AMOPClientMessageType::AMOP_RESPONSE); + auto buffer = std::make_shared(); + // Note: encode the message into buffer, response to the request-sdk + responseMessage->encode(*buffer); + _callback(std::make_shared(CommonError::NotFoundClientByTopicDispatchMsg, + "NotFoundClientByTopicDispatchMsg"), + buffer); + AMOP_CLIENT_LOG(DEBUG) << LOG_BADGE("asyncNotifyAMOPMessage: no client found") + << LOG_KV("topic", _topic); + return; + } + AMOP_CLIENT_LOG(DEBUG) << LOG_BADGE("asyncNotifyAMOPMessage") << LOG_KV("topic", _topic) + << LOG_KV("choosedSession", clientSession->endPoint()); + auto requestMsg = m_wsMessageFactory->buildMessage(); + // Note: m_wsMessageFactory buildMessage won't generate seq automatically, we should setSeq + // manually when need trigger callback after receive response message from the client + requestMsg->setSeq(m_wsMessageFactory->newSeq()); + requestMsg->setPacketType(AMOPClientMessageType::AMOP_REQUEST); + auto requestPayLoad = std::make_shared(_amopRequestData.begin(), _amopRequestData.end()); + requestMsg->setPayload(requestPayLoad); + sendMessageToClient(_topic, clientSession, requestMsg, _callback); +} + +void AMOPClient::asyncNotifyAMOPBroadcastMessage(std::string const& _topic, bytesConstRef _data, + std::function _callback) +{ + AMOP_CLIENT_LOG(DEBUG) << LOG_DESC("asyncNotifyAMOPBroadcastMessage") + << LOG_KV("topic", _topic); + auto requestMsg = m_wsMessageFactory->buildMessage(); + requestMsg->setPacketType(AMOPClientMessageType::AMOP_BROADCAST); + requestMsg->setPayload(std::make_shared(_data.begin(), _data.end())); + broadcastAMOPMessage(_topic, requestMsg); + if (_callback) + { + _callback(nullptr, nullptr); + } +} + +void AMOPClient::broadcastAMOPMessage( + std::string const& _topic, std::shared_ptr _msg) +{ + AMOP_CLIENT_LOG(DEBUG) << LOG_DESC("broadcastAMOPMessage") << LOG_KV("topic", _topic); + auto sessions = querySessionsByTopic(_topic); + for (auto const& session : sessions) + { + session.second->asyncSendMessage(_msg, Options(30000)); + } +} +std::shared_ptr AMOPClient::randomChooseSession(std::string const& _topic) +{ + ReadGuard l(x_topicToSessions); + AMOP_CLIENT_LOG(DEBUG) << LOG_DESC("randomChooseSession:") + << LOG_KV("sessionSize", m_topicToSessions.size()) + << LOG_KV("topic", _topic); + if (!m_topicToSessions.count(_topic)) + { + return nullptr; + } + std::shared_ptr selectedSession = nullptr; + auto const& sessions = m_topicToSessions[_topic]; + // no client subscribe the topic + if (sessions.size() == 0) + { + return selectedSession; + } + size_t retryTime = 0; + do + { + srand(utcTime()); + auto selectedClient = rand() % sessions.size(); + auto it = sessions.begin(); + if (selectedClient > 0) + { + std::advance(it, selectedClient); + } + selectedSession = it->second; + retryTime++; + } while ( + (!selectedSession || !(selectedSession->isConnected())) && (retryTime <= sessions.size())); + return selectedSession; +} + +void AMOPClient::onClientDisconnect(std::shared_ptr _session) +{ + std::vector topicsToRemove; + { + WriteGuard l(x_topicToSessions); + for (auto it = m_topicToSessions.begin(); it != m_topicToSessions.end();) + { + auto& sessions = it->second; + if (sessions.count(_session->endPoint())) + { + sessions.erase(_session->endPoint()); + } + if (sessions.size() == 0) + { + topicsToRemove.emplace_back(it->first); + it = m_topicToSessions.erase(it); + continue; + } + it++; + } + } + if (topicsToRemove.size() == 0) + { + return; + } + removeTopicFromAllNodes(topicsToRemove); +} + +std::vector AMOPClient::getActiveGatewayEndPoints() +{ + auto gatewayClient = std::dynamic_pointer_cast(m_gateway); + + auto endPoints = tarsProxyAvailableEndPoints(gatewayClient->prx()); + return std::vector(endPoints.begin(), endPoints.end()); +} + +void AMOPClient::subscribeTopicToAllNodes() +{ + auto activeEndPoints = getActiveGatewayEndPoints(); + auto topicInfo = generateTopicInfo(); + // set the notify topic flag to true when subscribeTopicToAllNodes + m_notifyTopicSuccess.store(true); + AMOP_CLIENT_LOG(INFO) << LOG_DESC("subscribeTopicToAllNodes") << LOG_KV("topicInfo", topicInfo) + << LOG_KV("activeEndPoints", activeEndPoints.size()); + for (auto const& endPoint : activeEndPoints) + { + auto servicePrx = bcostars::createServantProxy( + m_gatewayServiceName, endPoint.getEndpoint()); + + auto serviceClient = + std::make_shared(servicePrx, m_gatewayServiceName); + serviceClient->asyncSubscribeTopic( + m_clientID, topicInfo, [this, endPoint](Error::Ptr&& _error) { + if (_error) + { + AMOP_CLIENT_LOG(WARNING) << LOG_DESC("asyncSubScribeTopic error") + << LOG_KV("gateway", endPoint.getEndpoint().toString()) + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + // set the notify topic flag to false when subscribeTopic failed + m_notifyTopicSuccess.store(false); + return; + } + AMOP_CLIENT_LOG(INFO) << LOG_DESC("asyncSubScribeTopic success") + << LOG_KV("gateway", endPoint.getEndpoint().toString()); + }); + } +} +void AMOPClient::removeTopicFromAllNodes(std::vector const& topicsToRemove) +{ + auto activeEndPoints = getActiveGatewayEndPoints(); + for (auto const& endPoint : activeEndPoints) + { + auto servicePrx = bcostars::createServantProxy( + m_gatewayServiceName, endPoint.getEndpoint()); + + auto serviceClient = + std::make_shared(servicePrx, m_gatewayServiceName); + serviceClient->asyncRemoveTopic( + m_clientID, topicsToRemove, [topicsToRemove, endPoint](Error::Ptr&& _error) { + AMOP_CLIENT_LOG(INFO) << LOG_DESC("asyncRemoveTopic") + << LOG_KV("gateway", endPoint.getEndpoint().toString()) + << LOG_KV("removedSize", topicsToRemove.size()) + << LOG_KV("code", _error ? _error->errorCode() : 0) + << LOG_KV("msg", _error ? _error->errorMessage() : "success"); + }); + } +} + +void AMOPClient::pingGatewayAndNotifyTopics() +{ + m_gatewayStatusDetector->restart(); + auto activeEndPoints = getActiveGatewayEndPoints(); + // the gateway become inactived from active status + if (activeEndPoints.size() == 0) + { + if (m_gatewayActivated.load() == true) + { + AMOP_CLIENT_LOG(INFO) << LOG_DESC( + "pingGatewayAndNotifyTopics: gateway inactived, reset the status"); + m_gatewayActivated.store(false); + } + return; + } + // the gateway in active status, return directly + if (m_gatewayActivated.load() == true && m_notifyTopicSuccess) + { + return; + } + // if gateway become activated or notify topic failed before, should subscribeTopicToAllNodes + subscribeTopicToAllNodes(); + + AMOP_CLIENT_LOG(INFO) << LOG_DESC( + "pingGatewayAndNotifyTopics: the gateway become activated from " + "in-active status, re-subscribe the topics") + << LOG_KV("gatewayNodesSize", activeEndPoints.size()) + << LOG_KV("topicsSize", m_topicToSessions.size()); + m_gatewayActivated.store(true); +} + +bool AMOPClient::onGatewayInactivated( + std::shared_ptr _msg, std::shared_ptr _session) +{ + auto activeEndPoints = getActiveGatewayEndPoints(); + // the gateway is in-activated + if (activeEndPoints.size() > 0) + { + return false; + } + auto seq = _msg->seq(); + auto responseMsg = m_wsMessageFactory->buildMessage(); + // set error status + std::dynamic_pointer_cast(responseMsg)->setStatus(-1); + std::string errorMsg = "error for the gateway is in-activated"; + // set errorMesg + responseMsg->setPayload(std::make_shared(errorMsg.begin(), errorMsg.end())); + // set seq + responseMsg->setSeq(seq); + _session->asyncSendMessage(responseMsg); + + AMOP_CLIENT_LOG(INFO) << LOG_DESC( + "Gateway inactivated, notify error message to the client directly") + << LOG_KV("endPoint", _session->endPoint()) << LOG_KV("seq", seq); + + return true; +} + +bool AMOPClient::gatewayInactivated() +{ + auto activeEndPoints = getActiveGatewayEndPoints(); + return (activeEndPoints.size() == 0); +} + +std::string AMOPClient::generateTopicInfo() +{ + Json::Value topicInfo; + Json::Value topicItems(Json::arrayValue); + std::set topicList; + ReadGuard l(x_topicToSessions); + for (auto const& it : m_topicToSessions) + { + if (topicList.count(it.first)) + { + continue; + } + topicList.insert(it.first); + } + for (auto const& topicName : topicList) + { + topicItems.append(topicName); + } + topicInfo["topics"] = topicItems; + return topicInfo.toStyledString(); +} + +void AMOPClient::asyncNotifySubscribeTopic( + std::function _callback) +{ + auto topicInfo = generateTopicInfo(); + AMOP_CLIENT_LOG(INFO) << LOG_DESC( + "Receive asyncNotifySubscribeTopic request from the gateway, " + "re-subscribe topics now") + << LOG_KV("topic", topicInfo); + if (_callback) + { + _callback(nullptr, topicInfo); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/amop/AMOPClient.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/amop/AMOPClient.h" new file mode 100644 index 00000000..8493631b --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/amop/AMOPClient.h" @@ -0,0 +1,193 @@ +/** + * Copyright (C) 2021 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. + * + * @brief AMOP client + * @file AMOPClient.h + * @author: yujiechen + * @date 2021-10-28 + */ +#pragma once +#include +#include +#include +#include +#include +#include + +#define AMOP_CLIENT_LOG(level) BCOS_LOG(level) << LOG_BADGE("AMOPClient") +namespace bcos +{ +namespace rpc +{ +class AMOPClient : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + AMOPClient(std::shared_ptr _wsService, + std::shared_ptr _wsMessageFactory, + std::shared_ptr _requestFactory, + bcos::gateway::GatewayInterface::Ptr _gateway, std::string const& _gatewayServiceName) + : m_wsService(_wsService), + m_wsMessageFactory(_wsMessageFactory), + m_requestFactory(_requestFactory), + m_gateway(_gateway), + m_gatewayServiceName(_gatewayServiceName) + { + initMsgHandler(); + // create gatewayStatusDetector to detect status of gateway periodically + m_gatewayStatusDetector = std::make_shared(5000, "gatewayDetector"); + m_gatewayStatusDetector->registerTimeoutHandler([this]() { pingGatewayAndNotifyTopics(); }); + } + + virtual ~AMOPClient() {} + /** + * @brief receive amop request message from the gateway + * + */ + virtual void asyncNotifyAMOPMessage(int16_t _type, std::string const& _topic, + bytesConstRef _data, std::function _callback) + { + try + { + switch (_type) + { + case AMOPNotifyMessageType::Unicast: + asyncNotifyAMOPMessage(_topic, _data, _callback); + break; + case AMOPNotifyMessageType::Broadcast: + asyncNotifyAMOPBroadcastMessage(_topic, _data, _callback); + break; + default: + BCOS_LOG(WARNING) << LOG_DESC("asyncNotifyAMOPMessage: unknown message type") + << LOG_KV("type", _type); + } + } + catch (std::exception const& e) + { + BCOS_LOG(WARNING) << LOG_DESC("asyncNotifyAMOPMessage exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + } + + void setClientID(std::string const& _clientID) { m_clientID = _clientID; } + + // start m_gatewayStatusDetector + virtual void start() + { + if (m_gatewayStatusDetector) + { + auto activeEndPoints = getActiveGatewayEndPoints(); + if (activeEndPoints.size() == 0) + { + m_gatewayActivated.store(false); + } + m_gatewayStatusDetector->start(); + } + } + + virtual void stop() + { + if (m_gatewayStatusDetector) + { + m_gatewayStatusDetector->stop(); + } + } + + // the gateway notify the RPC client to subscribe topic if receive publish + virtual void asyncNotifySubscribeTopic( + std::function _callback); + +protected: + /// for AMOP requests from SDK + virtual void onRecvSubTopics(std::shared_ptr _msg, + std::shared_ptr _session); + /** + * @brief: receive amop request message from sdk + */ + virtual void onRecvAMOPRequest(std::shared_ptr _msg, + std::shared_ptr _session); + /** + * @brief: receive amop broadcast message from sdk + */ + virtual void onRecvAMOPBroadcast(std::shared_ptr _msg, + std::shared_ptr _session); + + std::shared_ptr randomChooseSession(std::string const& _topic); + + virtual void asyncNotifyAMOPMessage(std::string const& _topic, bytesConstRef _data, + std::function _callback); + virtual void asyncNotifyAMOPBroadcastMessage(std::string const& _topic, bytesConstRef _data, + std::function _callback); + + std::map> querySessionsByTopic( + std::string const& _topic) const + { + ReadGuard l(x_topicToSessions); + if (m_topicToSessions.count(_topic)) + { + return m_topicToSessions.at(_topic); + } + return std::map>(); + } + + void onClientDisconnect(std::shared_ptr _session); + + bool updateTopicInfos( + std::string const& _topicInfo, std::shared_ptr _session); + std::vector getActiveGatewayEndPoints(); + virtual bool gatewayInactivated(); + + virtual void subscribeTopicToAllNodes(); + virtual void removeTopicFromAllNodes(std::vector const& _topicName); + + virtual void initMsgHandler(); + + void sendMessageToClient(std::string const& _topic, + std::shared_ptr _selectSession, + std::shared_ptr _msg, + std::function _callback); + + bool trySendAMOPRequestToLocalNode(std::shared_ptr _session, + std::string const& _topic, std::shared_ptr _msg); + + void broadcastAMOPMessage( + std::string const& _topic, std::shared_ptr _msg); + + virtual void pingGatewayAndNotifyTopics(); + + virtual bool onGatewayInactivated(std::shared_ptr _msg, + std::shared_ptr _session); + std::string generateTopicInfo(); + +protected: + std::shared_ptr m_wsService; + std::shared_ptr m_wsMessageFactory; + std::shared_ptr m_requestFactory; + + bcos::gateway::GatewayInterface::Ptr m_gateway; + std::string m_clientID = "localAMOP"; + std::string m_gatewayServiceName; + + // for AMOP: [topic->[endpoint->session]] + std::map>> + m_topicToSessions; + mutable SharedMutex x_topicToSessions; + + std::shared_ptr m_gatewayStatusDetector; + std::atomic_bool m_gatewayActivated = {true}; + std::atomic_bool m_notifyTopicSuccess = {true}; +}; +} // namespace rpc +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/amop/AirAMOPClient.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/amop/AirAMOPClient.h" new file mode 100644 index 00000000..278ebd33 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/amop/AirAMOPClient.h" @@ -0,0 +1,78 @@ +/** + * Copyright (C) 2021 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. + * + * @brief AMOP client + * @file AirAMOPClient.h + * @author: yujiechen + * @date 2021-10-28 + */ +#pragma once +#include "AMOPClient.h" + +namespace bcos +{ +namespace rpc +{ +class AirAMOPClient : public AMOPClient +{ +public: + using Ptr = std::shared_ptr; + AirAMOPClient(std::shared_ptr _wsService, + std::shared_ptr _wsMessageFactory, + std::shared_ptr _requestFactory, + bcos::gateway::GatewayInterface::Ptr _gateway) + : AMOPClient(_wsService, _wsMessageFactory, _requestFactory, _gateway, "localGateway") + {} + + // Note: must with empty implementation to in case of start the m_gatewayStatusDetector + void start() override { m_gatewayActivated.store(true); } + + bool onGatewayInactivated( + std::shared_ptr, std::shared_ptr) override + { + return false; + } + + bool gatewayInactivated() override { return false; } + +protected: + void subscribeTopicToAllNodes() override + { + auto topicInfo = generateTopicInfo(); + AMOP_CLIENT_LOG(INFO) << LOG_DESC("subscribeTopicToAllNodes") + << LOG_KV("topicInfo", topicInfo); + m_gateway->asyncSubscribeTopic(m_clientID, topicInfo, [](Error::Ptr&& _error) { + if (_error) + { + BCOS_LOG(WARNING) << LOG_DESC("asyncSubScribeTopic error") + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + } + }); + } + + void removeTopicFromAllNodes(std::vector const& _topicsToRemove) override + { + m_gateway->asyncRemoveTopic( + m_clientID, _topicsToRemove, [_topicsToRemove](Error::Ptr&& _error) { + BCOS_LOG(INFO) << LOG_DESC("asyncRemoveTopic") + << LOG_KV("removedSize", _topicsToRemove.size()) + << LOG_KV("code", _error ? _error->errorCode() : 0) + << LOG_KV("msg", _error ? _error->errorMessage() : ""); + }); + } +}; +} // namespace rpc +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/Common.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/Common.h" new file mode 100644 index 00000000..50c38b62 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/Common.h" @@ -0,0 +1,39 @@ +/** + * @file Common.h + * @author: octopuswang + * @date 2019-08-13 + */ + +#pragma once + +#include + +// The largest number of topic in one event log +#define EVENT_LOG_TOPICS_MAX_INDEX (4) + +#define EVENT_REQUEST(LEVEL) BCOS_LOG(LEVEL) << "[EVENT][REQUEST]" +#define EVENT_RESPONSE(LEVEL) BCOS_LOG(LEVEL) << "[EVENT][RESPONSE]" +#define EVENT_TASK(LEVEL) BCOS_LOG(LEVEL) << "[EVENT][TASK]" +#define EVENT_SUB(LEVEL) BCOS_LOG(LEVEL) << "[EVENT][SUB]" +#define EVENT_MATCH(LEVEL) BCOS_LOG(LEVEL) << "[EVENT][MATCH]" + +namespace bcos +{ +namespace event +{ +enum EP_STATUS_CODE +{ + SUCCESS = 0, + PUSH_COMPLETED = 1, + INVALID_PARAMS = -41000, + INVALID_REQUEST = -41001, + GROUP_NOT_EXIST = -41002, + INVALID_REQUEST_RANGE = -41003, + INVALID_RESPONSE = -41004, + REQUST_TIMEOUT = -41005, + SDK_PERMISSION_DENIED = -41006, + NONEXISTENT_EVENT = -41007, +}; + +} // namespace event +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSub.cpp" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSub.cpp" new file mode 100644 index 00000000..9a9adad4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSub.cpp" @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2021 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 EventSub.cpp + * @author: octopus + * @date 2021-09-07 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::event; + +EventSub::EventSub(std::shared_ptr _wsService) + : bcos::Worker("t_event_sub"), m_wsService(_wsService) +{ + m_wsService->registerMsgHandler(bcos::protocol::MessageType::EVENT_SUBSCRIBE, + boost::bind(&EventSub::onRecvSubscribeEvent, this, boost::placeholders::_1, + boost::placeholders::_2)); + m_wsService->registerMsgHandler(bcos::protocol::MessageType::EVENT_UNSUBSCRIBE, + boost::bind(&EventSub::onRecvUnsubscribeEvent, this, boost::placeholders::_1, + boost::placeholders::_2)); +} + +void EventSub::start() +{ + if (m_running.load()) + { + EVENT_SUB(INFO) << LOG_BADGE("start") << LOG_DESC("event sub is running"); + return; + } + m_running.store(true); + startWorking(); + + EVENT_SUB(INFO) << LOG_BADGE("start") << LOG_DESC("start event sub successfully"); +} + +void EventSub::stop() +{ + if (!m_running.load()) + { + EVENT_SUB(INFO) << LOG_BADGE("stop") << LOG_DESC("event sub is not running"); + return; + } + m_running.store(false); + + finishWorker(); + stopWorking(); + // will not restart worker, so terminate it + terminate(); + + EVENT_SUB(INFO) << LOG_BADGE("stop") << LOG_DESC("stop event sub successfully"); +} + +void EventSub::onRecvSubscribeEvent(std::shared_ptr _msg, + std::shared_ptr _session) +{ + std::string seq = _msg->seq(); + std::string request = std::string(_msg->payload()->begin(), _msg->payload()->end()); + + EVENT_SUB(INFO) << LOG_BADGE("onRecvSubscribeEvent") << LOG_KV("endpoint", _session->endPoint()) + << LOG_KV("seq", seq) << LOG_KV("request", request); + + auto eventSubRequest = std::make_shared(); + if (!eventSubRequest->fromJson(request)) + { + sendResponse(_session, _msg, eventSubRequest->id(), EP_STATUS_CODE::INVALID_PARAMS); + return; + } + + auto nodeService = m_groupManager->getNodeService(eventSubRequest->group(), ""); + if (!nodeService) + { + sendResponse(_session, _msg, eventSubRequest->id(), EP_STATUS_CODE::GROUP_NOT_EXIST); + EVENT_SUB(ERROR) << LOG_BADGE("onRecvSubscribeEvent") << LOG_DESC("group not exist") + << LOG_KV("group", eventSubRequest->group()); + return; + } + + auto state = std::make_shared(); + + // TODO: check request parameters + auto task = std::make_shared(); + task->setGroup(eventSubRequest->group()); + task->setId(eventSubRequest->id()); + task->setParams(eventSubRequest->params()); + task->setState(state); + + auto eventSubWeakPtr = std::weak_ptr(shared_from_this()); + task->setCallback([eventSubWeakPtr, _session](const std::string& _id, bool _complete, + const Json::Value& _result) -> bool { + auto eventSub = eventSubWeakPtr.lock(); + if (eventSub) + { + return eventSub->sendEvents(_session, _complete, _id, _result); + } + return false; + }); + + subscribeEventSub(task); + sendResponse(_session, _msg, eventSubRequest->id(), EP_STATUS_CODE::SUCCESS); + return; +} + +void EventSub::onRecvUnsubscribeEvent(std::shared_ptr _msg, + std::shared_ptr _session) +{ + std::string seq = _msg->seq(); + std::string request = std::string(_msg->payload()->begin(), _msg->payload()->end()); + + EVENT_SUB(INFO) << LOG_BADGE("onRecvUnsubscribeEvent") << LOG_KV("seq", seq) + << LOG_KV("endpoint", _session->endPoint()) << LOG_KV("request", request); + + auto esRes = std::make_shared(); + if (!esRes->fromJson(request)) + { + sendResponse(_session, _msg, esRes->id(), EP_STATUS_CODE::INVALID_PARAMS); + return; + } + + unsubscribeEventSub(esRes->id()); + sendResponse(_session, _msg, esRes->id(), EP_STATUS_CODE::SUCCESS); + return; +} + + +/** + * @brief: send response + * @param _session: the peer session + * @param _msg: the msg + * @param _status: the response status + * @return bool: if _session is inactive, false will be return + */ +bool EventSub::sendResponse(std::shared_ptr _session, + std::shared_ptr _msg, const std::string& _id, int32_t _status) +{ + if (!_session->isConnected()) + { + EVENT_SUB(WARNING) << LOG_BADGE("sendResponse") << LOG_DESC("session has been inactive") + << LOG_KV("id", _id) << LOG_KV("status", _status) + << LOG_KV("endpoint", _session->endPoint()); + return false; + } + + auto esResp = std::make_shared(); + esResp->setId(_id); + esResp->setStatus(_status); + auto result = esResp->generateJson(); + + auto data = std::make_shared(result.begin(), result.end()); + _msg->setPayload(data); + + _session->asyncSendMessage(_msg); + return true; +} + +/** + * @brief: send event log list to client + * @param _session: the peer + * @param _complete: if task _completed + * @param _id: the EventSub id + * @param _result: + * @return bool: if _session is inactive, false will be return + */ +bool EventSub::sendEvents(std::shared_ptr _session, bool _complete, + const std::string& _id, const Json::Value& _result) +{ + // session disconnected + if (!_session->isConnected()) + { + EVENT_SUB(WARNING) << LOG_BADGE("sendEvents") << LOG_DESC("session has been inactive") + << LOG_KV("id", _id) << LOG_KV("endpoint", _session->endPoint()); + return false; + } + + // task completed + if (_complete) + { + auto msg = m_messageFactory->buildMessage(); + msg->setPacketType(bcos::protocol::MessageType::EVENT_LOG_PUSH); + sendResponse(_session, msg, _id, EP_STATUS_CODE::PUSH_COMPLETED); + return true; + } + + // null + if (0 == _result.size()) + { + return true; + } + + auto esResp = std::make_shared(); + esResp->setId(_id); + esResp->setStatus(EP_STATUS_CODE::SUCCESS); + esResp->generateJson(); + + auto jResp = esResp->jResp(); + jResp["result"] = _result; + + Json::FastWriter writer; + std::string strEventInfo = writer.write(jResp); + auto data = std::make_shared(strEventInfo.begin(), strEventInfo.end()); + + auto msg = m_messageFactory->buildMessage(); + msg->setPacketType(bcos::protocol::MessageType::EVENT_LOG_PUSH); + msg->setPayload(data); + _session->asyncSendMessage(msg); + + EVENT_SUB(TRACE) << LOG_BADGE("sendEvents") << LOG_DESC("send events to client") + << LOG_KV("endpoint", _session->endPoint()) << LOG_KV("id", _id) + << LOG_KV("events", strEventInfo); + + return true; +} + +void EventSub::subscribeEventSub(EventSubTask::Ptr _task) +{ + EVENT_SUB(INFO) << LOG_BADGE("subscribeEventSub") << LOG_KV("id", _task->id()) + << LOG_KV("startBlk", _task->state()->currentBlockNumber()); + std::unique_lock lock(x_addTasks); + m_addTasks.push_back(_task); + m_addTaskCount++; +} + +void EventSub::unsubscribeEventSub(const std::string& _id) +{ + EVENT_SUB(INFO) << LOG_BADGE("unsubscribeEventSub") << LOG_KV("id", _id); + std::unique_lock lock(x_cancelTasks); + m_cancelTasks.push_back(_id); + m_cancelTaskCount++; +} + +void EventSub::executeWorker() +{ + executeCancelTasks(); + executeAddTasks(); + executeEventSubTasks(); + reportEventSubTasks(); +} + +void EventSub::reportEventSubTasks() +{ + static auto start = std::chrono::high_resolution_clock::now(); + auto now = std::chrono::high_resolution_clock::now(); + auto elapsedMs = std::chrono::duration_cast(now - start).count(); + // + if (elapsedMs > 10 * 1000) + { + auto taskSize = m_tasks.size(); + if (taskSize > 0) + { + EVENT_SUB(INFO) << LOG_BADGE("eventSubTasks") + << LOG_DESC("all event sub tasks subscribed by client") + << LOG_KV("count", m_tasks.size()); + } + + start = std::chrono::high_resolution_clock::now(); + } +} + +void EventSub::executeAddTasks() +{ + if (m_addTaskCount.load() == 0) + { + return; + } + + std::unique_lock lock(x_addTasks); + for (auto& task : m_addTasks) + { + auto id = task->id(); + if (m_tasks.find(id) == m_tasks.end()) + { + m_tasks[id] = task; + EVENT_SUB(INFO) << LOG_BADGE("executeAddTasks") << LOG_KV("id", task->id()); + } + else + { + EVENT_SUB(ERROR) << LOG_BADGE("executeAddTasks") + << LOG_DESC("event sub task already exist") + << LOG_KV("id", task->id()); + } + } + m_addTaskCount.store(0); + m_addTasks.clear(); + + auto taskCount = m_tasks.size(); + EVENT_SUB(INFO) << LOG_BADGE("executeAddTasks") << LOG_DESC("event subscribe tasks ") + << LOG_KV("count", taskCount); +} + +void EventSub::executeCancelTasks() +{ + if (m_cancelTaskCount.load() == 0) + { + return; + } + + std::unique_lock lock(x_cancelTasks); + for (const auto& id : m_cancelTasks) + { + auto r = m_tasks.erase(id); + if (r) + { + EVENT_SUB(INFO) << LOG_BADGE("executeCancelTasks") << LOG_KV("id", id); + } + else + { + EVENT_SUB(WARNING) << LOG_BADGE("executeCancelTasks") + << LOG_DESC("event sub task not exist") << LOG_KV("id", id); + } + } + m_cancelTaskCount.store(0); + m_cancelTasks.clear(); + + auto taskCount = m_tasks.size(); + EVENT_SUB(INFO) << LOG_BADGE("executeCancelTasks") << LOG_DESC("event subscribe tasks ") + << LOG_KV("count", taskCount); +} + +bool EventSub::checkConnAvailable(EventSubTask::Ptr _task) +{ + Json::Value jResp(Json::arrayValue); + return _task->callback()(_task->id(), false, jResp); +} + +void EventSub::onTaskComplete(EventSubTask::Ptr _task) +{ + Json::Value jResp(Json::arrayValue); + _task->callback()(_task->id(), true, jResp); + + EVENT_SUB(INFO) << LOG_BADGE("onTaskComplete") << LOG_DESC("event sub completed") + << LOG_KV("id", _task->id()) + << LOG_KV("fromBlock", _task->params()->fromBlock()) + << LOG_KV("toBlock", _task->params()->toBlock()) + << LOG_KV("currentBlock", _task->state()->currentBlockNumber()); +} + +int64_t EventSub::executeEventSubTask(EventSubTask::Ptr _task, int64_t _blockNumber) +{ + bcos::protocol::BlockNumber currentBlockNumber = _task->state()->currentBlockNumber(); + if (currentBlockNumber < 0) + { + currentBlockNumber = + _task->params()->fromBlock() > 0 ? _task->params()->fromBlock() : _blockNumber; + } + + if (_blockNumber < currentBlockNumber) + { + _task->freeWork(); + // waiting for block to be sealed + return 0; + } + + /* + EVENT_SUB(TRACE) << LOG_BADGE("executeEventSubTask") << LOG_DESC("running") + << LOG_KV("id", _task->id()) + << LOG_KV("fromBlock", _task->params()->fromBlock()) + << LOG_KV("toBlock", _task->params()->toBlock()) + << LOG_KV("blockNumber", _blockNumber) + << LOG_KV("currentBlockNumber", _task->state()->currentBlockNumber()); + */ + + int64_t toBlockNumber = _task->params()->toBlock(); + if (toBlockNumber > 0 && toBlockNumber < _blockNumber) + { + _blockNumber = toBlockNumber; + } + + int64_t blockCanProcess = _blockNumber - currentBlockNumber + 1; + int64_t maxBlockProcessPerLoop = m_maxBlockProcessPerLoop; + blockCanProcess = + (blockCanProcess > maxBlockProcessPerLoop ? maxBlockProcessPerLoop : blockCanProcess); + + class RecursiveProcess : public std::enable_shared_from_this + { + public: + void process(int64_t _blockNumber) + { + if (_blockNumber > m_endBlockNumber) + { // all block has been proccessed + m_task->freeWork(); + return; + } + + EVENT_SUB(TRACE) << LOG_BADGE("executeEventSubTask:process") + << LOG_KV("id", m_task->id()) + << LOG_KV("fromBlock", m_task->params()->fromBlock()) + << LOG_KV("toBlock", m_task->params()->toBlock()) + << LOG_KV("blockNumber", _blockNumber); + + auto eventSub = m_eventSub; + auto task = m_task; + auto p = shared_from_this(); + eventSub->processNextBlock( + _blockNumber, task, [task, _blockNumber, p](Error::Ptr _error) { + if (_error && _error->errorCode() != bcos::protocol::CommonError::SUCCESS) + { + // error occur, wait for the next loop ??? + task->freeWork(); + return; + } + // next block + task->state()->setCurrentBlockNumber(_blockNumber + 1); + p->process(_blockNumber + 1); + }); + } + + public: + bcos::protocol::BlockNumber m_endBlockNumber; + std::shared_ptr m_eventSub; + EventSubTask::Ptr m_task; + }; + + auto p = std::make_shared(); + p->m_endBlockNumber = currentBlockNumber + blockCanProcess - 1; + p->m_eventSub = shared_from_this(); + p->m_task = _task; + p->process(currentBlockNumber); + + return blockCanProcess; +} + +int64_t EventSub::executeEventSubTask(EventSubTask::Ptr _task) +{ + // tests whether the connection of the session is available first + auto connAvailable = checkConnAvailable(_task); + if (!connAvailable) + { + unsubscribeEventSub(_task->id()); + return -1; + } + + if (_task->isCompleted()) + { + unsubscribeEventSub(_task->id()); + onTaskComplete(_task); + return 0; + } + + // task is working, waiting for done + if (!_task->tryWork()) + { + EVENT_SUB(DEBUG) << LOG_BADGE("executeEventSubTask") + << LOG_DESC("tryWork false, the previous is still going on") + << LOG_KV("id", _task->id()) << LOG_KV("group", _task->group()); + return 0; + } + + std::string group = _task->group(); + auto blockNumber = m_groupManager->getBlockNumberByGroup(group); + if (blockNumber < 0) + { // group not exist ??? + EVENT_SUB(ERROR) + << LOG_BADGE("executeEventSubTask") + << LOG_DESC("Cannot getBlockNumber from groupManager, maybe the group has been removed") + << LOG_KV("group", group); + unsubscribeEventSub(_task->id()); + return -1; + } + + executeEventSubTask(_task, blockNumber); + + return 0; +} + +void EventSub::processNextBlock( + int64_t _blockNumber, EventSubTask::Ptr _task, std::function _callback) +{ + auto self = std::weak_ptr(shared_from_this()); + auto matcher = m_matcher; + + std::string group = _task->group(); + auto nodeService = m_groupManager->getNodeService(group, ""); + if (!nodeService) + { + // group not exist??? + EVENT_SUB(ERROR) + << LOG_BADGE("processNextBlock") + << LOG_DESC("cannot get node service of the group maybe the group has been removed") + << LOG_KV("id", _task->id()) << LOG_KV("group", _task->group()); + unsubscribeEventSub(_task->id()); + return; + } + + auto ledger = nodeService->ledger(); + ledger->asyncGetBlockDataByNumber(_blockNumber, + bcos::ledger::RECEIPTS | bcos::ledger::TRANSACTIONS, + [matcher, _task, _blockNumber, _callback, self]( + Error::Ptr _error, protocol::Block::Ptr _block) { + if (_error && _error->errorCode() != bcos::protocol::CommonError::SUCCESS) + { + // Note: wait for next time + EVENT_SUB(ERROR) << LOG_BADGE("processNextBlock") + << LOG_DESC("asyncGetBlockDataByNumber") + << LOG_KV("id", _task->id()) << LOG_KV("blockNumber", _blockNumber) + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()); + _callback(_error); + return; + } + + Json::Value jResp(Json::arrayValue); + auto count = matcher->matches(_task->params(), _block, jResp); + if (count) + { + EVENT_SUB(TRACE) << LOG_BADGE("processNextBlock") + << LOG_DESC("asyncGetBlockDataByNumber") + << LOG_KV("blockNumber", _blockNumber) << LOG_KV("id", _task->id()) + << LOG_KV("count", count); + + _task->callback()(_task->id(), false, jResp); + } + + _callback(nullptr); + }); +} + +void EventSub::executeEventSubTasks() +{ + for (auto& task : m_tasks) + { + executeEventSubTask(task.second); + } + + // limiting speed + std::this_thread::sleep_for(std::chrono::milliseconds(1)); +} diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSub.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSub.h" new file mode 100644 index 00000000..cbe98f05 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSub.h" @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2021 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 EventSub.h + * @author: octopus + * @date 2021-09-07 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace ws +{ +class WsSession; +} // namespace ws +namespace boostssl +{ +class MessageFace; +} + +namespace event +{ +class EventSubMatcher; +class EventSub : bcos::Worker, public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + EventSub(std::shared_ptr _wsService); + virtual ~EventSub() { stop(); } + +public: + virtual void start(); + virtual void stop(); + + void executeWorker() override; + +public: + virtual void onRecvSubscribeEvent(std::shared_ptr _msg, + std::shared_ptr _session); + virtual void onRecvUnsubscribeEvent(std::shared_ptr _msg, + std::shared_ptr _session); + +public: + /** + * @brief: send response + * @param _session: the peer + * @param _msg: the msg + * @param _id: the EventSub id + * @param _status: the response status + * @return bool: if _session is inactive, false will be return + */ + bool sendResponse(std::shared_ptr _session, + std::shared_ptr _msg, const std::string& _id, int32_t _status); + + /** + * @brief: send event log list to client + * @param _session: the peer + * @param _complete: if task _completed + * @param _id: the event sub id + * @param _result: + * @return bool: if _session is inactive, false will be return + */ + bool sendEvents(std::shared_ptr _session, bool _complete, + const std::string& _id, const Json::Value& _result); + +public: + void executeAddTasks(); + void executeCancelTasks(); + void executeEventSubTasks(); + void reportEventSubTasks(); + +public: + int64_t executeEventSubTask(EventSubTask::Ptr _task); + void subscribeEventSub(EventSubTask::Ptr _task); + void unsubscribeEventSub(const std::string& _id); + +public: + int64_t executeEventSubTask(EventSubTask::Ptr _task, int64_t _currentBlockNumber); + void onTaskComplete(bcos::event::EventSubTask::Ptr _task); + bool checkConnAvailable(bcos::event::EventSubTask::Ptr _task); + void processNextBlock(int64_t _blockNumber, bcos::event::EventSubTask::Ptr _task, + std::function _callback); + +public: + std::shared_ptr matcher() const { return m_matcher; } + void setMatcher(std::shared_ptr _matcher) { m_matcher = _matcher; } + + int64_t maxBlockProcessPerLoop() const { return m_maxBlockProcessPerLoop; } + void setMaxBlockProcessPerLoop(int64_t _maxBlockProcessPerLoop) + { + m_maxBlockProcessPerLoop = _maxBlockProcessPerLoop; + } + + bcos::rpc::GroupManager::Ptr groupManager() { return m_groupManager; } + void setGroupManager(bcos::rpc::GroupManager::Ptr _groupManager) + { + m_groupManager = _groupManager; + } + + std::shared_ptr messageFactory() const + { + return m_messageFactory; + } + void setMessageFactory(std::shared_ptr _messageFactory) + { + m_messageFactory = _messageFactory; + } + +private: + // group manager + bcos::rpc::GroupManager::Ptr m_groupManager; + // match for event log compare + std::shared_ptr m_matcher; + // message factory + std::shared_ptr m_messageFactory; + +private: + std::shared_ptr m_wsService; + + std::atomic m_running{false}; + + // lock for m_addTasks + mutable std::shared_mutex x_addTasks; + // tasks to be add + std::vector m_addTasks; + // the number of tasks to be add + std::atomic m_addTaskCount{0}; + + // lock for m_cancelTasks + mutable std::shared_mutex x_cancelTasks; + // tasks to be cancel + std::vector m_cancelTasks; + // the number of tasks to be cancel + std::atomic m_cancelTaskCount{0}; + + // all subscribe event tasks + std::unordered_map m_tasks; + + // + int64_t m_maxBlockProcessPerLoop = 10; +}; + +class EventSubFactory : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + +public: + EventSub::Ptr buildEventSub(std::shared_ptr _wsService) + { + auto es = std::make_shared(_wsService); + return es; + } +}; + +} // namespace event +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubMatcher.cpp" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubMatcher.cpp" new file mode 100644 index 00000000..ad6315ed --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubMatcher.cpp" @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2021 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 EvenPushMatcher.cpp + * @author: octopus + * @date 2021-09-10 + */ + +#include +#include +#include + +using namespace bcos; +using namespace bcos::event; + +uint32_t EventSubMatcher::matches( + EventSubParams::ConstPtr _params, bcos::protocol::Block::ConstPtr _block, Json::Value& _result) +{ + uint32_t count = 0; + for (std::size_t index = 0; index < _block->transactionsSize(); index++) + { + count += + matches(_params, _block->receipt(index), _block->transaction(index), index, _result); + } + + return count; +} + +uint32_t EventSubMatcher::matches(EventSubParams::ConstPtr _params, + bcos::protocol::TransactionReceipt::ConstPtr _receipt, + bcos::protocol::Transaction::ConstPtr _tx, std::size_t _txIndex, Json::Value& _result) +{ + uint32_t count = 0; + const auto& logEntries = _receipt->logEntries(); + std::size_t logIndex = 0; + for (const auto& logEntry : logEntries) + { + if (matches(_params, logEntry)) + { + count++; + + Json::Value jResp; + jResp["blockNumber"] = _receipt->blockNumber(); + jResp["address"] = std::string(logEntry.address()); + jResp["data"] = toHexStringWithPrefix(logEntry.data()); + jResp["logIndex"] = (uint64_t)logIndex; + jResp["transactionHash"] = _tx->hash().hexPrefixed(); + jResp["transactionIndex"] = (uint64_t)_txIndex; + jResp["topics"] = Json::Value(Json::arrayValue); + for (const auto& topic : logEntry.topics()) + { + jResp["topics"].append(topic.hexPrefixed()); + } + _result.append(jResp); + } + + logIndex += 1; + } + + return count; +} + +bool EventSubMatcher::matches( + EventSubParams::ConstPtr _params, const bcos::protocol::LogEntry& _logEntry) +{ + const auto& addresses = _params->addresses(); + const auto& topics = _params->topics(); + + // EVENT_MATCH(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; + } + + bool isMatch = true; + for (unsigned i = 0; i < EVENT_LOG_TOPICS_MAX_INDEX; ++i) + { + const auto& logTopics = _logEntry.topics(); + if (topics.size() > i && !topics[i].empty() && + (logTopics.size() <= i || !topics[i].count(logTopics[i].hex()))) + { + isMatch = false; + break; + } + } + + return isMatch; +} diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubMatcher.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubMatcher.h" new file mode 100644 index 00000000..c3d4e194 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubMatcher.h" @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2021 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 EvenPushMatcher.h + * @author: octopus + * @date 2021-09-10 + */ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace event +{ +class EventSubMatcher +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + virtual ~EventSubMatcher() {} + +public: + virtual bool matches( + EventSubParams::ConstPtr _params, const bcos::protocol::LogEntry& _logEntry); + +public: + uint32_t matches(EventSubParams::ConstPtr _params, + bcos::protocol::TransactionReceipt::ConstPtr _receipt, + bcos::protocol::Transaction::ConstPtr _tx, std::size_t _txIndex, Json::Value& _result); + uint32_t matches(EventSubParams::ConstPtr _params, bcos::protocol::Block::ConstPtr _block, + Json::Value& _result); +}; + +} // namespace event +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubParams.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubParams.h" new file mode 100644 index 00000000..13ce6e71 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubParams.h" @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2021 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 EvenSubParams.h + * @author: octopus + * @date 2021-09-01 + */ + +#pragma once +#include +#include +#include +#include + +namespace bcos +{ +namespace event +{ +class EventSubParams +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + +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; } + + 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); } + bool addTopic(std::size_t _index, const std::string& _topic) + { + if (_index >= EVENT_LOG_TOPICS_MAX_INDEX) + { + return false; + } + + m_topics.resize(_index + 1); + m_topics[_index].insert(_topic); + return true; + } + +private: + bcos::protocol::BlockNumber m_fromBlock = -1; + bcos::protocol::BlockNumber m_toBlock = -1; + std::set m_addresses; + std::vector> m_topics; +}; + +} // namespace event +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubRequest.cpp" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubRequest.cpp" new file mode 100644 index 00000000..fc32e579 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubRequest.cpp" @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2021 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 EvenPushRequest.cpp + * @author: octopus + * @date 2021-09-03 + */ + +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::event; + +std::string EventSubUnsubRequest::generateJson() const +{ + /* + { + "id": "", + "group": "" + } + */ + Json::Value jResult; + // id + jResult["id"] = m_id; + // group + jResult["group"] = m_group; + + Json::FastWriter writer; + std::string result = writer.write(jResult); + return result; +} + +bool EventSubUnsubRequest::fromJson(const std::string& _request) +{ + std::string id; + std::string group; + EventSubParams::Ptr params = std::make_shared(); + + try + { + Json::Value root; + Json::Reader jsonReader; + std::string errorMessage; + do + { + if (!jsonReader.parse(_request, root)) + { + errorMessage = "invalid json object, parse request failed"; + break; + } + + if (!root.isMember("id")) + { // id field not exist + errorMessage = "\'id\' field not exist"; + break; + } + id = root["id"].asString(); + + if (!root.isMember("group")) + { + // group field not exist + errorMessage = "\'group\' field not exist"; + break; + } + group = root["group"].asString(); + + m_id = id; + m_group = group; + + EVENT_REQUEST(INFO) << LOG_BADGE("fromJson") + << LOG_DESC("parse event sub request success") + << LOG_KV("group", m_group) << LOG_KV("id", m_id); + + return true; + } while (0); + + EVENT_REQUEST(ERROR) << LOG_BADGE("fromJson") << LOG_DESC("invalid event sub request") + << LOG_KV("request", _request) << LOG_KV("errorMessage", errorMessage); + } + catch (const std::exception& e) + { + EVENT_REQUEST(ERROR) << LOG_BADGE("fromJson") << LOG_DESC("invalid json object") + + << LOG_KV("request", _request) + << LOG_KV("error", std::string(e.what())); + } + + return false; +} + + +std::string EventSubRequest::generateJson() const +{ + /* + { + "id": "", + "group": "", + "params": { + "fromBlock": -1, + "toBlock": -1, + "addresses": [ + "0xca5ed56862869c25da0bdf186e634aac6c6361ee" + ], + "topics": [ + "0x91c95f04198617c60eaf2180fbca88fc192db379657df0e412a9f7dd4ebbe95d" + ] + } + } + */ + Json::Value jResult; + // id + jResult["id"] = id(); + // group + jResult["group"] = group(); + + Json::Value jParams; + // fromBlock + jParams["fromBlock"] = m_state->currentBlockNumber() > 0 ? m_state->currentBlockNumber() + 1 : + m_params->fromBlock(); + // toBlock + jParams["toBlock"] = m_params->toBlock(); + // addresses + Json::Value jAddresses(Json::arrayValue); + for (const auto& addr : m_params->addresses()) + { + jAddresses.append(addr); + } + jParams["addresses"] = jAddresses; + // topics + Json::Value jTopics(Json::arrayValue); + for (const auto& inTopics : m_params->topics()) + { + if (inTopics.empty()) + { + Json::Value jInTopics(Json::nullValue); + jTopics.append(jInTopics); + continue; + } + + Json::Value jInTopics(Json::arrayValue); + for (const auto& topic : inTopics) + { + jInTopics.append(topic); + } + jTopics.append(jInTopics); + } + + jParams["topics"] = jTopics; + jResult["params"] = jParams; + + Json::FastWriter writer; + std::string result = writer.write(jResult); + return result; +} + +bool EventSubRequest::fromJson(const std::string& _request) +{ + std::string id; + std::string group; + EventSubParams::Ptr params = std::make_shared(); + + try + { + Json::Value root; + Json::Reader jsonReader; + std::string errorMessage; + do + { + if (!jsonReader.parse(_request, root)) + { + errorMessage = "invalid json object, parse request failed"; + break; + } + + if (!root.isMember("id")) + { // id field not exist + errorMessage = "\'id\' field not exist"; + break; + } + id = root["id"].asString(); + + if (!root.isMember("group")) + { + // group field not exist + errorMessage = "\'group\' field not exist"; + break; + } + group = root["group"].asString(); + + if (!root.isMember("params")) + { // params field not exist + errorMessage = "\'params\' field not exist"; + break; + } + + auto& jParams = root["params"]; + if (jParams.isMember("fromBlock")) + { + params->setFromBlock(jParams["fromBlock"].asInt64()); + } + + if (jParams.isMember("toBlock")) + { + params->setToBlock(jParams["toBlock"].asInt64()); + } + + if (jParams.isMember("addresses")) + { + auto& jAddresses = jParams["addresses"]; + for (Json::Value::ArrayIndex index = 0; index < jAddresses.size(); ++index) + { + std::string address = jAddresses[index].asString(); + if ((address.compare(0, 2, "0x") == 0) || (address.compare(0, 2, "0X") == 0)) + { + address = address.substr(2); + } + // std::transform(address.begin(), address.end(), address.begin(), ::tolower); + params->addAddress(address); + } + } + + if (jParams.isMember("topics")) + { + auto& jTopics = jParams["topics"]; + + 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) + { + std::string topic = jIndex[innerIndex].asString(); + if ((topic.compare(0, 2, "0x") == 0) || + (topic.compare(0, 2, "0XC") == 0)) + { + topic = topic.substr(2); + } + std::transform(topic.begin(), topic.end(), topic.begin(), ::tolower); + params->addTopic(index, topic); + } + } + else + { // single topic, string value + params->addTopic(index, jIndex.asString()); + } + } + } + + setId(id); + setGroup(group); + setParams(params); + + EVENT_REQUEST(INFO) << LOG_BADGE("fromJson") + << LOG_DESC("parse event sub request success") + << LOG_KV("group", group) << LOG_KV("id", id); + + return true; + } while (0); + + EVENT_REQUEST(ERROR) << LOG_BADGE("fromJson") << LOG_DESC("invalid event sub request") + << LOG_KV("request", _request) << LOG_KV("errorMessage", errorMessage); + } + catch (const std::exception& e) + { + EVENT_REQUEST(ERROR) << LOG_BADGE("fromJson") << LOG_DESC("invalid json object") + + << LOG_KV("request", _request) + << LOG_KV("error", std::string(e.what())); + } + + return false; +} diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubRequest.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubRequest.h" new file mode 100644 index 00000000..22a5d5ee --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubRequest.h" @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 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 EvenPushRequest.h + * @author: octopus + * @date 2021-09-01 + */ + +#pragma once +#include + +namespace bcos +{ +namespace event +{ +class EventSubTaskState; +class EventSubUnsubRequest +{ +public: + using Ptr = std::shared_ptr; + + virtual ~EventSubUnsubRequest() {} + +public: + void setId(const std::string& _id) { m_id = _id; } + std::string id() const { return m_id; } + + void setGroup(const std::string& _group) { m_group = _group; } + std::string group() const { return m_group; } + + virtual std::string generateJson() const; + virtual bool fromJson(const std::string& _request); + +private: + std::string m_id; + std::string m_group; +}; + +class EventSubRequest : public EventSubUnsubRequest +{ +public: + using Ptr = std::shared_ptr; + + virtual ~EventSubRequest() {} + +public: + void setParams(std::shared_ptr _params) { m_params = _params; } + std::shared_ptr params() const { return m_params; } + + std::string generateJson() const override; + bool fromJson(const std::string& _request) override; + +private: + std::shared_ptr m_params; + std::shared_ptr m_state; +}; + +} // namespace event +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubResponse.cpp" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubResponse.cpp" new file mode 100644 index 00000000..4008b98a --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubResponse.cpp" @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2021 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 EvenPushResponse.cpp + * @author: octopus + * @date 2021-09-09 + */ +#include +#include +#include + +using namespace bcos; +using namespace bcos::event; + +std::string EventSubResponse::generateJson() +{ + /* + { + "id": "0x123", + "status": 0, + "result": { + "blockNumber": 111, + "events":[] + } + } + */ + Json::Value jResult; + // id + jResult["id"] = m_id; + // status + jResult["status"] = m_status; + + m_jResp = jResult; + + Json::FastWriter writer; + std::string result = writer.write(jResult); + return result; +} + +bool EventSubResponse::fromJson(const std::string& _response) +{ + std::string id; + int status; + + try + { + Json::Value root; + Json::Reader jsonReader; + std::string errorMessage; + do + { + if (!jsonReader.parse(_response, root)) + { + errorMessage = "invalid json object, parse response failed"; + break; + } + + if (!root.isMember("id")) + { // id field not exist + errorMessage = "\'id\' field not exist"; + break; + } + id = root["id"].asString(); + + if (!root.isMember("status")) + { + // group field not exist + errorMessage = "\'status\' field not exist"; + break; + } + status = root["status"].asInt(); + + m_id = id; + m_status = status; + m_jResp = root; + + EVENT_RESPONSE(TRACE) << LOG_BADGE("fromJson") + << LOG_DESC("parse event sub response success") + << LOG_KV("id", m_id) << LOG_KV("status", m_status); + + return true; + } while (0); + + EVENT_RESPONSE(ERROR) << LOG_BADGE("fromJson") << LOG_DESC("invalid event sub response") + << LOG_KV("response", _response) << LOG_KV("error", errorMessage); + } + catch (const std::exception& e) + { + EVENT_RESPONSE(ERROR) << LOG_BADGE("fromJson") << LOG_DESC("invalid json object") + << LOG_KV("response", _response) + << LOG_KV("error", std::string(e.what())); + } + + return false; +} diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubResponse.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubResponse.h" new file mode 100644 index 00000000..c771edea --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubResponse.h" @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 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 EvenPushResponse.h + * @author: octopus + * @date 2021-09-09 + */ + +#pragma once + +#include +#include +#include +namespace bcos +{ +namespace event +{ +class EventSubResponse +{ +public: + using Ptr = std::shared_ptr; + +public: + std::string id() const { return m_id; } + void setId(const std::string& _id) { m_id = _id; } + int status() const { return m_status; } + void setStatus(int _status) { m_status = _status; } + + void setJResp(const Json::Value& _jResp) { m_jResp = _jResp; } + Json::Value jResp() const { return m_jResp; } + +public: + std::string generateJson(); + bool fromJson(const std::string& _response); + +private: + std::string m_id; + int m_status; + + Json::Value m_jResp; +}; +} // namespace event +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubTask.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubTask.h" new file mode 100644 index 00000000..9d139022 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/event/EventSubTask.h" @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2021 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 EvenPushRequest.h + * @author: octopus + * @date 2021-09-07 + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace event +{ +using Callback = std::function; + +class EventSubTaskState +{ +public: + using Ptr = std::shared_ptr; + +public: + int64_t currentBlockNumber() const { return m_currentBlockNumber.load(); } + void setCurrentBlockNumber(int64_t _currentBlockNumber) + { + if (_currentBlockNumber > m_currentBlockNumber.load()) + { + m_currentBlockNumber.store(_currentBlockNumber); + } + } + +private: + std::atomic m_currentBlockNumber = -1; +}; + +class EventSubTask +{ +public: + using Ptr = std::shared_ptr; + EventSubTask() { EVENT_TASK(INFO) << LOG_KV("[NEWOBJ][EventSubTask]", this); } + ~EventSubTask() { EVENT_TASK(INFO) << LOG_KV("[DELOBJ][EventSubTask]", this); } + +public: + void setSession(std::shared_ptr _session) + { + m_session = _session; + } + std::shared_ptr session() const { return m_session; } + + void setId(const std::string& _id) { m_id = _id; } + std::string id() const { return m_id; } + + void setGroup(const std::string& _group) { m_group = _group; } + std::string group() const { return m_group; } + + void setParams(std::shared_ptr _params) { m_params = _params; } + std::shared_ptr params() const { return m_params; } + + void setState(std::shared_ptr _state) { m_state = _state; } + std::shared_ptr state() const { return m_state; } + + void setCallback(Callback _callback) { m_callback = _callback; } + Callback callback() const { return m_callback; } + + bool work() { return m_work.load(); } + + bool tryWork() + { + bool trueValue = true; + bool falseValue = false; + return m_work.compare_exchange_strong(falseValue, trueValue); + } + + void freeWork() { m_work.store(false); } + + bool isCompleted() + { + if (m_params->toBlock() < 0) + { + return false; + } + + return m_state->currentBlockNumber() > m_params->toBlock(); + } + +private: + std::atomic m_work{false}; + + std::string m_id; + std::string m_group; + + std::shared_ptr m_session; + std::shared_ptr m_params; + std::shared_ptr m_state; + +private: + Callback m_callback; +}; + +using EventSubTaskPtrs = std::vector; +} // namespace event +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/AirGroupManager.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/AirGroupManager.h" new file mode 100644 index 00000000..a66401fa --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/AirGroupManager.h" @@ -0,0 +1,104 @@ +/** + * Copyright (C) 2021 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. + * + * @brief AirGroupManager.h + * @file AirGroupManager.h + * @author: yujiechen + * @date 2021-10-11 + */ +#pragma once +#include +#include +namespace bcos +{ +namespace rpc +{ +class AirGroupManager : public GroupManager +{ +public: + using Ptr = std::shared_ptr; + AirGroupManager(std::string const& _chainID, bcos::group::GroupInfo::Ptr _groupInfo, + NodeService::Ptr _nodeService) + : GroupManager(_chainID), m_nodeService(_nodeService), m_groupInfo(_groupInfo) + {} + + ~AirGroupManager() override {} + virtual void init() { initNodeInfo(m_groupInfo->groupID(), "localNode", m_nodeService); } + + NodeService::Ptr getNodeService(std::string_view _groupID, std::string_view) const override + { + ReadGuard l(x_groupInfo); + if (_groupID.size() > 0 && _groupID != m_groupInfo->groupID()) + { + return nullptr; + } + return m_nodeService; + } + + std::set groupList() override + { + ReadGuard l(x_groupInfo); + return std::set{m_groupInfo->groupID()}; + } + + bcos::group::GroupInfo::Ptr getGroupInfo(std::string_view _groupID) override + { + ReadGuard l(x_groupInfo); + if (m_groupInfo->groupID() == _groupID) + { + return m_groupInfo; + } + return nullptr; + } + + bcos::group::ChainNodeInfo::Ptr getNodeInfo( + std::string_view _groupID, std::string_view _nodeName) override + { + ReadGuard l(x_groupInfo); + if (m_groupInfo->groupID() != _groupID) + { + return nullptr; + } + return m_groupInfo->nodeInfo(_nodeName); + } + + std::vector groupInfoList() override + { + std::vector groupList; + groupList.emplace_back(m_groupInfo); + return groupList; + } + + bool updateGroupInfo(bcos::group::GroupInfo::Ptr _groupInfo) override + { + { + WriteGuard l(x_groupInfo); + m_groupInfo = _groupInfo; + } + ReadGuard l(x_groupInfo); + if (m_groupInfoNotifier) + { + m_groupInfoNotifier(_groupInfo); + } + return true; + } + +private: + NodeService::Ptr m_nodeService; + bcos::group::GroupInfo::Ptr m_groupInfo; + mutable SharedMutex x_groupInfo; +}; +} // namespace rpc +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/Common.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/Common.h" new file mode 100644 index 00000000..6a3bb473 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/Common.h" @@ -0,0 +1,66 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Common.h + * @file Common.h + * @author: yujiechen + * @date 2021-10-11 + */ +#pragma once +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +namespace bcos +{ +namespace rpc +{ +inline bcos::crypto::CryptoSuite::Ptr createCryptoSuite() +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto encryptImpl = std::make_shared(); + return std::make_shared(hashImpl, signatureImpl, encryptImpl); +} + +inline bcos::crypto::CryptoSuite::Ptr createSMCryptoSuite() +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto encryptImpl = std::make_shared(); + return std::make_shared(hashImpl, signatureImpl, encryptImpl); +} + +inline bcos::protocol::BlockFactory::Ptr createBlockFactory( + bcos::crypto::CryptoSuite::Ptr _cryptoSuite) +{ + auto blockHeaderFactory = + std::make_shared(_cryptoSuite); + auto transactionFactory = + std::make_shared(_cryptoSuite); + auto receiptFactory = + std::make_shared(_cryptoSuite); + return std::make_shared( + _cryptoSuite, blockHeaderFactory, transactionFactory, receiptFactory); +} +} // namespace rpc +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/GroupManager.cpp" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/GroupManager.cpp" new file mode 100644 index 00000000..761f5c20 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/GroupManager.cpp" @@ -0,0 +1,363 @@ +/** + * Copyright (C) 2021 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. + * + * @brief GroupManager.cpp + * @file GroupManager.cpp + * @author: yujiechen + * @date 2021-10-11 + */ +#include "GroupManager.h" +#include +#include +using namespace bcos; +using namespace bcos::group; +using namespace bcos::rpc; +using namespace bcos::protocol; + +bool GroupManager::updateGroupInfo(bcos::group::GroupInfo::Ptr _groupInfo) +{ + if (!checkGroupInfo(_groupInfo)) + { + return false; + } + bool enforceUpdate = false; + { + UpgradableGuard l(x_nodeServiceList); + auto const& groupID = _groupInfo->groupID(); + if (!m_groupInfos.count(groupID)) + { + UpgradeGuard ul(l); + m_groupInfos[groupID] = _groupInfo; + GROUP_LOG(INFO) << LOG_DESC("updateGroupInfo") << printGroupInfo(_groupInfo); + m_groupInfoNotifier(_groupInfo); + enforceUpdate = true; + } + } + return updateGroupServices(_groupInfo, enforceUpdate); +} + +bool GroupManager::checkGroupInfo(bcos::group::GroupInfo::Ptr _groupInfo) +{ + // check the serviceName + auto nodeList = _groupInfo->nodeInfos(); + for (auto const& node : nodeList) + { + auto const& expectedRpcService = node.second->serviceName(bcos::protocol::ServiceType::RPC); + if (expectedRpcService != m_rpcServiceName) + { + GROUP_LOG(INFO) << LOG_DESC("unfollowed groupInfo for inconsistent rpc service name") + << LOG_KV("expected", expectedRpcService) + << LOG_KV("selfName", m_rpcServiceName); + return false; + } + } + return true; +} +bool GroupManager::updateGroupServices(bcos::group::GroupInfo::Ptr _groupInfo, bool _enforce) +{ + auto ret = false; + auto nodeInfos = _groupInfo->nodeInfos(); + for (auto const& it : nodeInfos) + { + if (updateNodeService(_groupInfo->groupID(), it.second, _enforce)) + { + ret = true; + } + } + return ret; +} + +void GroupManager::removeGroupNodeList(bcos::group::GroupInfo::Ptr _groupInfo) +{ + GROUP_LOG(INFO) << LOG_DESC("removeGroupNodeList") << printGroupInfo(_groupInfo); + std::map> groupToUnreachableNodes; + std::set unreachableNodes; + auto nodeList = _groupInfo->nodeInfos(); + for (auto const& node : nodeList) + { + unreachableNodes.insert(node.second->nodeName()); + } + groupToUnreachableNodes[_groupInfo->groupID()] = unreachableNodes; + removeGroupBlockInfo(groupToUnreachableNodes); + removeUnreachableNodeService(groupToUnreachableNodes); +} + +bool GroupManager::shouldRebuildNodeService( + std::string const& _groupID, bcos::group::ChainNodeInfo::Ptr _nodeInfo) +{ + auto const& nodeAppName = _nodeInfo->nodeName(); + if (!m_nodeServiceList.count(_groupID) || !m_nodeServiceList[_groupID].count(nodeAppName)) + { + return true; + } + auto nodeInfo = m_groupInfos.at(_groupID)->nodeInfo(nodeAppName); + // update the compatibilityVersion(Note: the compatibility version maybe updated in-runtime) + if (nodeInfo->compatibilityVersion() != _nodeInfo->compatibilityVersion()) + { + GROUP_LOG(INFO) << LOG_DESC("update compatibilityVersion to ") + << _nodeInfo->compatibilityVersion(); + nodeInfo->setCompatibilityVersion(_nodeInfo->compatibilityVersion()); + } + // check the serviceInfo + auto const& originServiceInfo = nodeInfo->serviceInfo(); + auto const& serviceInfo = _nodeInfo->serviceInfo(); + if (originServiceInfo.size() != serviceInfo.size()) + { + GROUP_LOG(INFO) << LOG_DESC("shouldRebuildNodeService for serviceInfo changed") + << LOG_KV("originServiceInfo", originServiceInfo.size()) + << LOG_KV("serviceInfo", serviceInfo.size()); + return true; + } + for (auto const& it : serviceInfo) + { + if (!originServiceInfo.count(it.first) || originServiceInfo.at(it.first) != it.second) + { + GROUP_LOG(INFO) << LOG_DESC("shouldRebuildNodeService for serviceInfo changed") + << LOG_KV("orgService", originServiceInfo.at(it.first)) + << LOG_KV("updatedService", it.second); + return true; + } + } + return false; +} + +bool GroupManager::updateNodeService( + std::string const& _groupID, ChainNodeInfo::Ptr _nodeInfo, bool _enforceUpdate) +{ + UpgradableGuard l(x_nodeServiceList); + auto const& nodeAppName = _nodeInfo->nodeName(); + if (!_enforceUpdate && !shouldRebuildNodeService(_groupID, _nodeInfo)) + { + return false; + } + // a started node + auto nodeService = + m_nodeServiceFactory->buildNodeService(m_chainID, _groupID, _nodeInfo, m_nodeConfig); + if (!nodeService) + { + return false; + } + // fetch blockNumber to the node + initNodeInfo(_groupID, _nodeInfo->nodeName(), nodeService); + UpgradeGuard ul(l); + m_nodeServiceList[_groupID][nodeAppName] = nodeService; + auto groupInfo = m_groupInfos[_groupID]; + // will cover the old NodeInfo + groupInfo->updateNodeInfo(_nodeInfo); + m_groupInfoNotifier(groupInfo); + GROUP_LOG(INFO) << LOG_DESC("buildNodeService for the master node") << printNodeInfo(_nodeInfo) + << printGroupInfo(groupInfo) + << LOG_KV("nodeServiceObj", m_nodeServiceList.at(_groupID).at(nodeAppName)) + << LOG_KV("nodeServiceSize", m_nodeServiceList.size()); + return true; +} + +bcos::protocol::BlockNumber GroupManager::getBlockNumberByGroup(const std::string& _groupID) +{ + ReadGuard l(x_groupBlockInfos); + if (!m_groupBlockInfos.count(_groupID)) + { + return -1; + } + + return m_groupBlockInfos.at(_groupID); +} + +NodeService::Ptr GroupManager::selectNode(std::string_view _groupID) const +{ + auto nodeName = selectNodeByBlockNumber(_groupID); + if (nodeName.size() == 0) + { + return selectNodeRandomly(_groupID); + } + return queryNodeService(_groupID, nodeName); +} + +std::string GroupManager::selectNodeByBlockNumber(std::string_view _groupID) const +{ + ReadGuard l(x_groupBlockInfos); + + auto it = m_nodesWithLatestBlockNumber.find(_groupID); + if (it == m_nodesWithLatestBlockNumber.end() || it->second.size() == 0) + { + return ""; + } + srand(utcTime()); + + auto nodeListIt = m_nodesWithLatestBlockNumber.find(_groupID); + auto const& nodesList = nodeListIt->second; + auto selectNodeIndex = rand() % nodesList.size(); + auto nodeIt = nodesList.begin(); + if (selectNodeIndex > 0) + { + std::advance(nodeIt, selectNodeIndex); + } + return *nodeIt; +} + +NodeService::Ptr GroupManager::selectNodeRandomly(std::string_view _groupID) const +{ + ReadGuard l(x_nodeServiceList); + if (!m_groupInfos.count(_groupID)) + { + return nullptr; + } + if (!m_nodeServiceList.count(_groupID)) + { + return nullptr; + } + + auto it = m_groupInfos.find(_groupID); + auto const& groupInfo = it->second; + auto const& nodeInfos = groupInfo->nodeInfos(); + for (auto const& it : nodeInfos) + { + auto const& node = it.second; + auto serviceIt = m_nodeServiceList.find(_groupID); + if (serviceIt != m_nodeServiceList.end() && !serviceIt->second.empty()) + { + auto nodeService = serviceIt->second.at(node->nodeName()); + if (nodeService) + { + return nodeService; + } + } + } + return nullptr; +} + +NodeService::Ptr GroupManager::queryNodeService( + std::string_view _groupID, std::string_view _nodeName) const +{ + ReadGuard l(x_nodeServiceList); + auto it = m_nodeServiceList.find(_groupID); + if (it != m_nodeServiceList.end()) + { + auto const& serviceList = it->second; + auto it2 = serviceList.find(_nodeName); + if (it2 != serviceList.end()) + { + return it2->second; + } + } + return nullptr; +} + +NodeService::Ptr GroupManager::getNodeService( + std::string_view _groupID, std::string_view _nodeName) const +{ + if (_nodeName.size() > 0) + { + return queryNodeService(_groupID, _nodeName); + } + return selectNode(_groupID); +} + +void GroupManager::initNodeInfo( + std::string const& _groupID, std::string const& _nodeName, NodeService::Ptr _nodeService) +{ + GROUP_LOG(INFO) << LOG_DESC("initNodeInfo") << LOG_KV("group", _groupID) + << LOG_KV("nodeName", _nodeName); + auto ledger = _nodeService->ledger(); + auto self = std::weak_ptr(shared_from_this()); + ledger->asyncGetBlockNumber( + [self, _groupID, _nodeName](Error::Ptr _error, BlockNumber _blockNumber) { + if (_error) + { + GROUP_LOG(WARNING) + << LOG_DESC("initNodeInfo error") << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()) << LOG_KV("groupID", _groupID) + << LOG_KV("nodeName", _nodeName); + return; + } + try + { + auto groupMgr = self.lock(); + if (!groupMgr) + { + return; + } + groupMgr->updateGroupBlockInfo(_groupID, _nodeName, _blockNumber); + if (groupMgr->m_blockNumberNotifier) + { + groupMgr->m_blockNumberNotifier(_groupID, _nodeName, _blockNumber); + } + GROUP_LOG(INFO) << LOG_DESC("initNodeInfo success") << LOG_KV("group", _groupID) + << LOG_KV("nodeName", _nodeName) << LOG_KV("number", _blockNumber); + } + catch (std::exception const& e) + { + GROUP_LOG(WARNING) << LOG_DESC("initNodeInfo exception") + << LOG_KV("group", _groupID) << LOG_KV("nodeName", _nodeName) + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +void GroupManager::removeUnreachableNodeService( + std::map> const& _unreachableNodes) +{ + WriteGuard l(x_nodeServiceList); + for (auto const& it : _unreachableNodes) + { + auto groupID = it.first; + auto& groupInfo = m_groupInfos[groupID]; + if (!m_nodeServiceList.count(groupID)) + { + continue; + } + auto const& nodeList = it.second; + for (auto const& node : nodeList) + { + GROUP_LOG(INFO) << LOG_DESC("GroupManager: removeUnreachablNodeService") + << LOG_KV("group", groupID) << LOG_KV("node", node); + m_nodeServiceList[groupID].erase(node); + groupInfo->removeNodeInfo(node); + } + if (m_nodeServiceList[groupID].size() == 0) + { + m_nodeServiceList.erase(groupID); + } + } +} +void GroupManager::removeGroupBlockInfo( + std::map> const& _unreachableNodes) +{ + WriteGuard l(x_groupBlockInfos); + for (auto const& it : _unreachableNodes) + { + auto group = it.first; + if (!m_nodesWithLatestBlockNumber.count(group)) + { + m_groupBlockInfos.erase(group); + continue; + } + if (!m_groupBlockInfos.count(group)) + { + m_nodesWithLatestBlockNumber.erase(group); + continue; + } + auto const& nodeList = it.second; + for (auto const& node : nodeList) + { + m_nodesWithLatestBlockNumber[group].erase(node); + } + if (m_nodesWithLatestBlockNumber[group].size() == 0) + { + m_groupBlockInfos.erase(group); + m_nodesWithLatestBlockNumber.erase(group); + } + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/GroupManager.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/GroupManager.h" new file mode 100644 index 00000000..c0d087c9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/GroupManager.h" @@ -0,0 +1,212 @@ +/** + * Copyright (C) 2021 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. + * + * @brief GroupManager.h + * @file GroupManager.h + * @author: yujiechen + * @date 2021-10-11 + */ +#pragma once +#include "NodeService.h" +#include +#include +namespace bcos +{ +namespace rpc +{ +class GroupManager : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + GroupManager(std::string _rpcServiceName, std::string const& _chainID, + NodeServiceFactory::Ptr _nodeServiceFactory, bcos::tool::NodeConfig::Ptr _nodeConfig) + : m_rpcServiceName(_rpcServiceName), + m_chainID(_chainID), + m_nodeServiceFactory(_nodeServiceFactory), + m_nodeConfig(_nodeConfig) + {} + virtual ~GroupManager() {} + virtual bool updateGroupInfo(bcos::group::GroupInfo::Ptr _groupInfo); + virtual void removeGroupNodeList(bcos::group::GroupInfo::Ptr _groupInfo); + + virtual NodeService::Ptr getNodeService( + std::string_view _groupID, std::string_view _nodeName) const; + + std::string const& chainID() const { return m_chainID; } + + virtual bcos::group::GroupInfo::Ptr getGroupInfo(std::string_view _groupID) + { + ReadGuard l(x_nodeServiceList); + auto it = m_groupInfos.find(_groupID); + if (it != m_groupInfos.end()) + { + return it->second; + } + return nullptr; + } + + virtual bcos::group::ChainNodeInfo::Ptr getNodeInfo( + std::string_view _groupID, std::string_view _nodeName) + { + ReadGuard l(x_nodeServiceList); + auto it = m_groupInfos.find(_groupID); + if (it == m_groupInfos.end()) + { + return nullptr; + } + auto groupInfo = it->second; + + auto nodeInfos = groupInfo->nodeInfos(); + auto nodeIt = nodeInfos.find(_nodeName); + return nodeIt->second; + } + + virtual std::set groupList() + { + std::set groupList; + ReadGuard l(x_nodeServiceList); + for (auto const& it : m_groupInfos) + { + if (it.second->nodesNum() == 0) + { + continue; + } + groupList.insert(it.first); + } + return groupList; + } + + virtual std::vector groupInfoList() + { + std::vector groupList; + ReadGuard l(x_nodeServiceList); + for (auto const& it : m_groupInfos) + { + groupList.push_back(it.second); + } + return groupList; + } + + virtual void updateGroupBlockInfo(std::string const& _groupID, std::string const& _nodeName, + bcos::protocol::BlockNumber _blockNumber) + { + UpgradableGuard l(x_groupBlockInfos); + if (m_groupBlockInfos.count(_groupID)) + { + // expired block + if (m_groupBlockInfos[_groupID] > _blockNumber) + { + return; + } + // has already in the m_nodesWithLatestBlockNumber + if (m_groupBlockInfos[_groupID] == _blockNumber && + m_nodesWithLatestBlockNumber.count(_groupID) && + m_nodesWithLatestBlockNumber[_groupID].count(_nodeName)) + { + return; + } + } + UpgradeGuard ul(l); + bcos::protocol::BlockNumber oldBlockNumber = 0; + if (m_groupBlockInfos.count(_groupID)) + { + oldBlockNumber = m_groupBlockInfos[_groupID]; + } + if (!m_nodesWithLatestBlockNumber.count(_groupID)) + { + m_nodesWithLatestBlockNumber[_groupID] = std::set(); + } + if (!m_groupBlockInfos.count(_groupID)) + { + m_groupBlockInfos[_groupID] = _blockNumber; + } + // nodes with newer highest block + if (oldBlockNumber < _blockNumber) + { + m_groupBlockInfos[_groupID] = _blockNumber; + m_nodesWithLatestBlockNumber[_groupID].clear(); + } + + // nodes with the same highest block + (m_nodesWithLatestBlockNumber[_groupID]).insert(_nodeName); + BCOS_LOG(DEBUG) << LOG_DESC("updateGroupBlockInfo for receive block notify") + << LOG_KV("group", _groupID) << LOG_KV("node", _nodeName) + << LOG_KV("block", _blockNumber); + } + + virtual void registerGroupInfoNotifier( + std::function _callback) + { + m_groupInfoNotifier = _callback; + } + + void registerBlockNumberNotifier( + std::function + _blockNumberNotifier) + { + m_blockNumberNotifier = _blockNumberNotifier; + } + virtual bcos::protocol::BlockNumber getBlockNumberByGroup(const std::string& _groupID); + +protected: + GroupManager(std::string const& _chainID) : m_chainID(_chainID) {} + + bool updateGroupServices(bcos::group::GroupInfo::Ptr _groupInfo, bool _enforceUpdate); + bool updateNodeService(std::string const& _groupID, bcos::group::ChainNodeInfo::Ptr _nodeInfo, + bool _enforceUpdate); + bool shouldRebuildNodeService( + std::string const& _groupID, bcos::group::ChainNodeInfo::Ptr _nodeInfo); + virtual NodeService::Ptr selectNode(std::string_view _groupID) const; + virtual std::string selectNodeByBlockNumber(std::string_view _groupID) const; + virtual NodeService::Ptr selectNodeRandomly(std::string_view _groupID) const; + virtual NodeService::Ptr queryNodeService( + std::string_view _groupID, std::string_view _nodeName) const; + + virtual void initNodeInfo( + std::string const& _groupID, std::string const& _nodeName, NodeService::Ptr _nodeService); + + virtual void removeGroupBlockInfo( + std::map> const& _unreachableNodes); + virtual void removeUnreachableNodeService( + std::map> const& _unreachableNodes); + + bool checkGroupInfo(bcos::group::GroupInfo::Ptr _groupInfo); + +protected: + std::string m_rpcServiceName; + std::string m_chainID; + NodeServiceFactory::Ptr m_nodeServiceFactory; + + bcos::tool::NodeConfig::Ptr m_nodeConfig; + + // map between groupID to groupInfo + std::map> m_groupInfos; + + // map between nodeName to NodeService + std::map>, std::less<>> + m_nodeServiceList; + mutable SharedMutex x_nodeServiceList; + + std::map, std::less<>> m_nodesWithLatestBlockNumber; + std::map> m_groupBlockInfos; + mutable SharedMutex x_groupBlockInfos; + + std::function m_groupInfoNotifier; + + std::function + m_blockNumberNotifier; +}; +} // namespace rpc +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/NodeService.cpp" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/NodeService.cpp" new file mode 100644 index 00000000..91852b82 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/NodeService.cpp" @@ -0,0 +1,101 @@ +/** + * Copyright (C) 2021 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. + * + * @brief NodeService + * @file NodeService.cpp + * @author: yujiechen + * @date 2021-10-11 + */ +#include "NodeService.h" +#include "Common.h" +#include "bcos-tool/NodeConfig.h" +#include +#include +#include +#include +#include +#include +#include +using namespace bcos; +using namespace bcos::rpc; +using namespace bcos::crypto; +using namespace bcos::group; +using namespace bcos::protocol; + +NodeService::Ptr NodeServiceFactory::buildNodeService(std::string const&, std::string const&, + bcos::group::ChainNodeInfo::Ptr _nodeInfo, bcos::tool::NodeConfig::Ptr _nodeConfig) +{ + auto appName = _nodeInfo->nodeName(); + // create cryptoSuite + auto const& type = _nodeInfo->nodeCryptoType(); + CryptoSuite::Ptr cryptoSuite = nullptr; + if (type == NodeCryptoType::SM_NODE) + { + cryptoSuite = createSMCryptoSuite(); + } + else + { + cryptoSuite = createCryptoSuite(); + } + auto keyFactory = std::make_shared(); + cryptoSuite->setKeyFactory(keyFactory); + + auto blockFactory = createBlockFactory(cryptoSuite); + + auto ledgerClient = createServicePrx( + LEDGER, _nodeInfo, _nodeConfig, blockFactory); + if (!ledgerClient.first) + { + return nullptr; + } + + auto schedulerClient = + createServicePrx( + SCHEDULER, _nodeInfo, _nodeConfig, cryptoSuite); + if (!schedulerClient.first) + { + return nullptr; + } + + // create txpool client + auto txpoolClient = createServicePrx( + TXPOOL, _nodeInfo, _nodeConfig, cryptoSuite, blockFactory); + if (!txpoolClient.first) + { + return nullptr; + } + + // create consensus client + auto consensusClient = createServicePrx( + CONSENSUS, _nodeInfo, _nodeConfig); + if (!consensusClient.first) + { + return nullptr; + } + + // create sync client + auto syncClient = createServicePrx( + CONSENSUS, _nodeInfo, _nodeConfig); + if (!syncClient.first) + { + return nullptr; + } + + auto nodeService = std::make_shared(ledgerClient.first, schedulerClient.first, + txpoolClient.first, consensusClient.first, syncClient.first, blockFactory); + + nodeService->setLedgerPrx(ledgerClient.second); + return nodeService; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/NodeService.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/NodeService.h" new file mode 100644 index 00000000..a854c30a --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/NodeService.h" @@ -0,0 +1,115 @@ +/** + * Copyright (C) 2021 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. + * + * @brief NodeService.h + * @file NodeService.h + * @author: yujiechen + * @date 2021-10-11 + */ +#pragma once +#include "bcos-tars-protocol/Common.h" +#include "bcos-tool/NodeConfig.h" +#include "fisco-bcos-tars-service/Common/TarsUtils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +namespace bcos +{ +namespace rpc +{ +class NodeService +{ +public: + using Ptr = std::shared_ptr; + NodeService(bcos::ledger::LedgerInterface::Ptr _ledger, + std::shared_ptr _scheduler, + bcos::txpool::TxPoolInterface::Ptr _txpool, + bcos::consensus::ConsensusInterface::Ptr _consensus, + bcos::sync::BlockSyncInterface::Ptr _sync, bcos::protocol::BlockFactory::Ptr _blockFactory) + : m_ledger(_ledger), + m_scheduler(_scheduler), + m_txpool(_txpool), + m_consensus(_consensus), + m_sync(_sync), + m_blockFactory(_blockFactory) + {} + virtual ~NodeService() {} + + bcos::ledger::LedgerInterface::Ptr ledger() { return m_ledger; } + std::shared_ptr scheduler() { return m_scheduler; } + bcos::txpool::TxPoolInterface::Ptr txpool() { return m_txpool; } + bcos::consensus::ConsensusInterface::Ptr consensus() { return m_consensus; } + bcos::sync::BlockSyncInterface::Ptr sync() { return m_sync; } + bcos::protocol::BlockFactory::Ptr blockFactory() { return m_blockFactory; } + + void setLedgerPrx(bcostars::LedgerServicePrx const& _ledgerPrx) { m_ledgerPrx = _ledgerPrx; } + + bool unreachable() + { + return !bcostars::checkConnection( + "NodeService", "unreachable", m_ledgerPrx, nullptr, false); + } + +private: + bcos::ledger::LedgerInterface::Ptr m_ledger; + std::shared_ptr m_scheduler; + bcos::txpool::TxPoolInterface::Ptr m_txpool; + bcos::consensus::ConsensusInterface::Ptr m_consensus; + bcos::sync::BlockSyncInterface::Ptr m_sync; + bcos::protocol::BlockFactory::Ptr m_blockFactory; + + bcostars::LedgerServicePrx m_ledgerPrx; +}; + +class NodeServiceFactory +{ +public: + using Ptr = std::shared_ptr; + NodeServiceFactory() = default; + virtual ~NodeServiceFactory() {} + NodeService::Ptr buildNodeService(std::string const& _chainID, std::string const& _groupID, + bcos::group::ChainNodeInfo::Ptr _nodeInfo, bcos::tool::NodeConfig::Ptr _nodeConfig); + + template + inline std::pair, S> createServicePrx(bcos::protocol::ServiceType _type, + bcos::group::ChainNodeInfo::Ptr _nodeInfo, bcos::tool::NodeConfig::Ptr _nodeConfig, + const Args&... _args) + { + auto withoutTarsFramework = _nodeConfig->withoutTarsFramework(); + auto serviceName = _nodeInfo->serviceName(_type); + if (serviceName.size() == 0) + { + if (!withoutTarsFramework) + { + return std::make_pair(nullptr, nullptr); + } + } + auto prx = bcostars::createServantProxy(serviceName); + auto client = std::make_shared(prx, _args...); + + return std::make_pair(client, prx); + } +}; +} // namespace rpc +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/TarsGroupManager.cpp" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/TarsGroupManager.cpp" new file mode 100644 index 00000000..8c7789d9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/TarsGroupManager.cpp" @@ -0,0 +1,80 @@ +/** + * Copyright (C) 2021 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. + * + * @brief TarsGroupManager.cpp + * @file TarsGroupManager.cpp + * @author: yujiechen + * @date 2021-10-11 + */ +#include "TarsGroupManager.h" +#include +#include +using namespace bcos; +using namespace bcos::group; +using namespace bcos::rpc; +using namespace bcos::protocol; + +bool TarsGroupManager::updateGroupInfo(bcos::group::GroupInfo::Ptr _groupInfo) +{ + auto ret = GroupManager::updateGroupInfo(_groupInfo); + if (ret) + { + m_groupStatusUpdater->restart(); + } + return ret; +} + +void TarsGroupManager::updateGroupStatus() +{ + m_groupStatusUpdater->restart(); + auto unreachableNodes = checkNodeStatus(); + if (unreachableNodes.size() == 0) + { + return; + } + removeUnreachableNodeService(unreachableNodes); + removeGroupBlockInfo(unreachableNodes); +} + +std::map> TarsGroupManager::checkNodeStatus() +{ + ReadGuard l(x_nodeServiceList); + // groupID => {unreachableNodes} + std::map> unreachableNodes; + for (auto const& it : m_groupInfos) + { + bool groupInfoUpdated = false; + auto groupInfo = it.second; + auto groupID = groupInfo->groupID(); + auto const& groupNodeList = groupInfo->nodeInfos(); + for (auto const& nodeInfo : groupNodeList) + { + if (!m_nodeServiceList.count(groupID) || + !m_nodeServiceList[groupID].count(nodeInfo.first) || + m_nodeServiceList[groupID][nodeInfo.first]->unreachable()) + { + unreachableNodes[groupID].insert(nodeInfo.first); + groupInfoUpdated = true; + continue; + } + } + // notify the updated groupInfo to the sdk + if (m_groupInfoNotifier && groupInfoUpdated) + { + m_groupInfoNotifier(groupInfo); + } + } + return unreachableNodes; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/TarsGroupManager.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/TarsGroupManager.h" new file mode 100644 index 00000000..81c4487b --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/groupmgr/TarsGroupManager.h" @@ -0,0 +1,63 @@ +/** + * Copyright (C) 2021 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. + * + * @brief TarsGroupManager + * @file TarsGroupManager.h + * @author: yujiechen + * @date 2021-10-11 + */ +#pragma once +#include "GroupManager.h" +#include "NodeService.h" +#include +namespace bcos +{ +namespace rpc +{ +class TarsGroupManager : public GroupManager +{ +public: + using Ptr = std::shared_ptr; + TarsGroupManager(std::string _rpcServiceName, std::string const& _chainID, + NodeServiceFactory::Ptr _nodeServiceFactory, bcos::tool::NodeConfig::Ptr _nodeConfig) + : GroupManager(_rpcServiceName, _chainID, _nodeServiceFactory, _nodeConfig) + { + m_groupStatusUpdater = std::make_shared(c_tarsAdminRefreshTime, "gmrTimer"); + m_groupStatusUpdater->start(); + m_groupStatusUpdater->registerTimeoutHandler( + boost::bind(&TarsGroupManager::updateGroupStatus, this)); + } + virtual ~TarsGroupManager() + { + if (m_groupStatusUpdater) + { + m_groupStatusUpdater->stop(); + } + } + + bool updateGroupInfo(bcos::group::GroupInfo::Ptr _groupInfo) override; + +protected: + virtual void updateGroupStatus(); + virtual std::map> checkNodeStatus(); + +protected: + std::shared_ptr m_groupStatusUpdater; + // Note: since tars need at-least 1min to update the endpoint info, we schedule + // updateGroupStatus every 1min + uint64_t c_tarsAdminRefreshTime = 60 * 1000; +}; +} // namespace rpc +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/Common.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/Common.h" new file mode 100644 index 00000000..219e402f --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/Common.h" @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2021 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: octopus + * @date 2021-07-02 + */ +#pragma once + +#include +#include +#include +#include + +#define RPC_IMPL_LOG(LEVEL) BCOS_LOG(LEVEL) << "[RPC][JSONRPC]" + +namespace bcos +{ +namespace rpc +{ +struct NodeInfo +{ + std::string version; + std::string supportedVersion; + std::string nodeID; + std::string chainID; + std::string groupID; + std::string agency; + std::string buildTime; + std::string gitCommitHash; + bool isWasm; + bool isSM; +}; + +class JsonRpcException : public std::exception +{ +public: + JsonRpcException(int32_t _code, std::string const& _msg) : m_code(_code), m_msg(_msg) {} + const char* what() const noexcept override { return m_msg.c_str(); } + +public: + int32_t code() const noexcept { return m_code; } + std::string msg() const noexcept { return m_msg; } + +private: + int32_t m_code; + std::string m_msg; +}; + +enum JsonRpcError : int32_t +{ + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603, + // -32000 to -32099: Server error Reserved for implementation-defined server-errors. + NodeNotExistOrNotStarted = -32000, + GroupAlreadyExists = -32001, + NodeAlreadyExists = -32002, + OperationNotAllowed = -32003, + ServiceNotInitCompleted = -32004, + GroupNotExist = -32005, +}; + +struct JsonRequest +{ + std::string jsonrpc; + std::string method; + int64_t id; + Json::Value params; +}; + +struct JsonResponse +{ + struct Error + { + int32_t code{0}; + std::string message{"success"}; + + std::string toString() const + { + return "{\"code\":" + std::to_string(code) + "\"message\":" + message + "}"; + } + }; + std::string jsonrpc; + int64_t id; + Error error; + Json::Value result; +}; + +inline Json::Value generateResponse(Error::Ptr _error) +{ + Json::Value response; + response["code"] = _error ? _error->errorCode() : 0; + response["msg"] = _error ? _error->errorMessage() : "success"; + return response; +} + +inline void nodeInfoToJson(Json::Value& _response, bcos::group::ChainNodeInfo::Ptr _nodeInfo) +{ + _response["name"] = _nodeInfo->nodeName(); + _response["nodeID"] = _nodeInfo->nodeID(); + _response["type"] = _nodeInfo->nodeCryptoType(); + _response["microService"] = _nodeInfo->microService(); + _response["iniConfig"] = _nodeInfo->iniConfig(); + // set deployInfo + _response["serviceInfo"] = Json::Value(Json::arrayValue); + auto const& infos = _nodeInfo->serviceInfo(); + for (auto const& it : infos) + { + Json::Value item; + item["type"] = it.first; + item["serviceName"] = it.second; + _response["serviceInfo"].append(item); + } + // set protocol info + auto protocol = _nodeInfo->nodeProtocol(); + Json::Value protocolResponse; + protocolResponse["minSupportedVersion"] = protocol->minVersion(); + protocolResponse["maxSupportedVersion"] = protocol->maxVersion(); + protocolResponse["compatibilityVersion"] = _nodeInfo->compatibilityVersion(); + _response["protocol"] = protocolResponse; +} + +inline void groupInfoToJson(Json::Value& _response, bcos::group::GroupInfo::Ptr _groupInfo) +{ + _response["chainID"] = _groupInfo->chainID(); + _response["groupID"] = _groupInfo->groupID(); + _response["genesisConfig"] = _groupInfo->genesisConfig(); + _response["iniConfig"] = _groupInfo->iniConfig(); + _response["nodeList"] = Json::Value(Json::arrayValue); + auto nodeInfos = _groupInfo->nodeInfos(); + for (auto const& it : nodeInfos) + { + Json::Value nodeInfoResponse; + nodeInfoToJson(nodeInfoResponse, it.second); + _response["nodeList"].append(nodeInfoResponse); + } +} + +inline void groupInfoListToJson( + Json::Value& _response, const std::vector _groupInfoList) +{ + for (const auto& groupInfo : _groupInfoList) + { + Json::Value item; + groupInfoToJson(item, groupInfo); + _response.append(item); + } +} +} // namespace rpc +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/DupTestTxJsonRpcImpl_2_0.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/DupTestTxJsonRpcImpl_2_0.h" new file mode 100644 index 00000000..466284b4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/DupTestTxJsonRpcImpl_2_0.h" @@ -0,0 +1,109 @@ +// +// Created by Jimmy Shi on 2022/6/11. +// +#pragma once +#include "DuplicateTransactionFactory.h" +#include "JsonRpcImpl_2_0.h" +#include +#include + +namespace bcos +{ +namespace 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( + 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(txpool->submitTransaction(std::move(tx)), + [m_submitCallback = std::move(submitCallback)](auto&& result) { + using ResultType = std::decay_t; + if constexpr (std::is_same_v) + { + try + { + std::rethrow_exception(result); + } + catch (bcos::Error& e) + { + m_submitCallback(std::make_shared(e), nullptr); + } + } + else + { + m_submitCallback(nullptr, result); + } + }); + }); + } + + ~DupTestTxJsonRpcImpl_2_0() {} +}; + + +} // namespace rpc +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/DuplicateTransactionFactory.cpp" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/DuplicateTransactionFactory.cpp" new file mode 100644 index 00000000..79d89f3c --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/DuplicateTransactionFactory.cpp" @@ -0,0 +1,38 @@ +// +// Created by Jimmy Shi on 2022/6/11. +// + +#include "DuplicateTransactionFactory.h" + + +using namespace bcos::rpc; +using namespace bcos::protocol; + +void DuplicateTransactionFactory::multiBuild( + bcos::protocol::TransactionFactory::Ptr transactionFactory, bytes const& seedTxData, + bcos::crypto::KeyPairInterface::Ptr keyPair, int64_t num, + std::function onTxBuild) +{ + auto seedTx = transactionFactory->createTransaction(seedTxData, false); + int32_t version = seedTx->version(); + const std::string_view to = seedTx->to(); + const bytes input = seedTx->input().toBytes(); + auto seedBlockLimit = seedTx->blockLimit(); + const std::string chainId = std::string(seedTx->chainId()); + const std::string groupId = std::string(seedTx->groupId()); + std::atomic sentNum = 0; + + do + { + const u256 nonce = seedTx->nonce() + utcTimeUs(); + int64_t blockLimit = + seedBlockLimit + sentNum.fetch_add(1) / 10000; // maybe this block limit is not so + // fast nor so slow + int64_t importTime = utcTime(); + Transaction::Ptr tx = transactionFactory->createTransaction( + version, to, input, nonce, blockLimit, chainId, groupId, importTime, keyPair); + + onTxBuild(tx); + } while (num > sentNum); + BCOS_LOG(TRACE) << LOG_BADGE("FAKE") << "Generate " << num << " tx finished"; +} diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/DuplicateTransactionFactory.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/DuplicateTransactionFactory.h" new file mode 100644 index 00000000..720ea344 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/DuplicateTransactionFactory.h" @@ -0,0 +1,35 @@ +// +// Created by Jimmy Shi on 2022/6/11. +// +#pragma once +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace rpc +{ +class DuplicateTransactionFactory +{ +public: + static void multiBuild(bcos::protocol::TransactionFactory::Ptr transactionFactory, + bytes const& seedTxData, bcos::crypto::KeyPairInterface::Ptr keyPair, int64_t num, + std::function onTxBuild); + + static void asyncMultiBuild(bcos::protocol::TransactionFactory::Ptr transactionFactory, + bytes const& seedTxData, bcos::crypto::KeyPairInterface::Ptr keyPair, int64_t num, + std::function onTxBuild) + { + static ThreadPool pool("DuplicateTransactionFactory", std::thread::hardware_concurrency()); + pool.enqueue( + [transactionFactory, seedTxData, keyPair, num, onTxBuild = std::move(onTxBuild)]() { + multiBuild(transactionFactory, seedTxData, keyPair, num, std::move(onTxBuild)); + }); + } +}; +} // namespace rpc +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp" new file mode 100644 index 00000000..5a709618 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.cpp" @@ -0,0 +1,1351 @@ +/** + * Copyright (C) 2021 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. + * + * @brief: implement for the RPC + * @file: JsonRpcImpl_2_0.h + * @author: octopus + * @date: 2021-07-09 + */ +#include "bcos-crypto/ChecksumAddress.h" +#include "bcos-crypto/interfaces/crypto/CommonType.h" +#include "bcos-crypto/interfaces/crypto/Hash.h" +#include "bcos-utilities/BoostLog.h" +#include "bcos-utilities/Common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::rpc; +using namespace bcos::group; +using namespace boost::iterators; +using namespace boost::archive::iterators; + +JsonRpcImpl_2_0::JsonRpcImpl_2_0(GroupManager::Ptr _groupManager, + bcos::gateway::GatewayInterface::Ptr _gatewayInterface, + std::shared_ptr _wsService) + : m_groupManager(_groupManager), m_gatewayInterface(_gatewayInterface), m_wsService(_wsService) +{ + m_wsService->registerMsgHandler(bcos::protocol::MessageType::RPC_REQUEST, + boost::bind(&JsonRpcImpl_2_0::handleRpcRequest, this, boost::placeholders::_1, + boost::placeholders::_2)); +} + +void JsonRpcImpl_2_0::handleRpcRequest( + std::shared_ptr _msg, std::shared_ptr _session) +{ + auto buffer = _msg->payload(); + auto req = std::string_view((const char*)buffer->data(), buffer->size()); + + auto start = std::chrono::high_resolution_clock::now(); + auto endpoint = _session->endPoint(); + auto seq = _msg->seq(); + auto version = _msg->version(); + auto ext = _msg->ext(); + + auto weakptrSession = std::weak_ptr(_session); + auto messageFactory = m_wsService->messageFactory(); + + onRPCRequest(req, [ext, seq, version, weakptrSession, messageFactory, start](bcos::bytes resp) { + auto session = weakptrSession.lock(); + + auto end = std::chrono::high_resolution_clock::now(); + auto total = std::chrono::duration_cast(end - start).count(); + + if (!session) + { + BCOS_LOG(TRACE) << LOG_DESC("[RPC][FACTORY][buildJsonRpc]") + << LOG_DESC("unable to send response for session has been destroyed") + << LOG_KV("seq", seq) << LOG_KV("totalTime", total); + return; + } + + if (session->isConnected()) + { + // TODO: no need to copy resp + auto buffer = std::make_shared(std::move(resp)); + + auto msg = messageFactory->buildMessage(); + msg->setPayload(buffer); + msg->setVersion(version); + msg->setSeq(seq); + msg->setExt(ext); + session->asyncSendMessage(msg); + } + else + { + BCOS_LOG(WARNING) << LOG_DESC("[RPC][FACTORY][buildJsonRpc]") + << LOG_DESC("unable to send response for session has been inactive") + << LOG_KV("seq", seq) << LOG_KV("totalTime", total) + << LOG_KV("endpoint", session->endPoint()) + << LOG_KV("refCount", session.use_count()); + } + }); +} + +bcos::bytes JsonRpcImpl_2_0::decodeData(std::string_view _data) +{ + auto begin = _data.begin(); + auto end = _data.end(); + auto length = _data.size(); + + if ((length == 0) || (length % 2 != 0)) [[unlikely]] + { + BOOST_THROW_EXCEPTION(std::runtime_error{"Unexpect hex string"}); + } + + if (*begin == '0' && *(begin + 1) == 'x') + { + begin += 2; + length -= 2; + } + + bcos::bytes data; + data.reserve(length / 2); + boost::algorithm::unhex(begin, end, std::back_inserter(data)); + return data; +} + +void JsonRpcImpl_2_0::parseRpcResponseJson( + std::string_view _responseBody, JsonResponse& _jsonResponse) +{ + Json::Value root; + Json::Reader jsonReader; + std::string errorMessage; + + try + { + do + { + if (!jsonReader.parse(_responseBody.begin(), _responseBody.end(), root)) + { + errorMessage = "invalid response json object"; + break; + } + + if (!root.isMember("jsonrpc")) + { + errorMessage = "response has no jsonrpc field"; + break; + } + _jsonResponse.jsonrpc = root["jsonrpc"].asString(); + + if (!root.isMember("id")) + { + errorMessage = "response has no id field"; + break; + } + _jsonResponse.id = root["id"].asInt64(); + + if (root.isMember("error")) + { + auto jError = root["error"]; + _jsonResponse.error.code = jError["code"].asInt64(); + _jsonResponse.error.message = jError["message"].asString(); + } + + if (root.isMember("result")) + { + _jsonResponse.result = root["result"]; + } + + RPC_IMPL_LOG(TRACE) << LOG_BADGE("parseRpcResponseJson") + << LOG_KV("jsonrpc", _jsonResponse.jsonrpc) + << LOG_KV("id", _jsonResponse.id) + << LOG_KV("error", _jsonResponse.error.toString()) + << LOG_KV("responseBody", _responseBody); + + return; + } while (0); + } + catch (std::exception& e) + { + RPC_IMPL_LOG(ERROR) << LOG_BADGE("parseRpcResponseJson") + << LOG_KV("response", _responseBody) + << LOG_KV("error", boost::diagnostic_information(e)); + BOOST_THROW_EXCEPTION( + JsonRpcException(JsonRpcError::ParseError, "Invalid JSON was received by the server.")); + } + + RPC_IMPL_LOG(ERROR) << LOG_BADGE("parseRpcResponseJson") << LOG_KV("response", _responseBody) + << LOG_KV("errorMessage", errorMessage); + + BOOST_THROW_EXCEPTION(JsonRpcException( + JsonRpcError::InvalidRequest, "The JSON sent is not a valid Response object.")); +} + +void JsonRpcImpl_2_0::toJsonResp( + Json::Value& jResp, bcos::protocol::Transaction::ConstPtr _transactionPtr) +{ + // transaction version + jResp["version"] = _transactionPtr->version(); + // transaction hash + jResp["hash"] = toHexStringWithPrefix(_transactionPtr->hash()); + // transaction nonce + jResp["nonce"] = _transactionPtr->nonce().str(16); + // blockLimit + jResp["blockLimit"] = _transactionPtr->blockLimit(); + // the receiver address + jResp["to"] = string(_transactionPtr->to()); + // the sender address + jResp["from"] = toHexStringWithPrefix(_transactionPtr->sender()); + // the input data + jResp["input"] = toHexStringWithPrefix(_transactionPtr->input()); + // importTime + jResp["importTime"] = _transactionPtr->importTime(); + // the chainID + jResp["chainID"] = std::string(_transactionPtr->chainId()); + // the groupID + jResp["groupID"] = std::string(_transactionPtr->groupId()); + // the abi + jResp["abi"] = std::string(_transactionPtr->abi()); + // the signature + jResp["signature"] = toHexStringWithPrefix(_transactionPtr->signatureData()); +} + +void JsonRpcImpl_2_0::toJsonResp(Json::Value& jResp, std::string_view _txHash, + bcos::protocol::TransactionReceipt const& transactionReceipt, bool _isWasm, + crypto::Hash& hashImpl) +{ + jResp["version"] = transactionReceipt.version(); + + std::string contractAddress = string(transactionReceipt.contractAddress()); + + if (!contractAddress.empty() && !_isWasm) + { + std::string checksumContractAddr = contractAddress; + toChecksumAddress(checksumContractAddr, hashImpl.hash(contractAddress).hex()); + + if (!contractAddress.starts_with("0x") && !contractAddress.starts_with("0X")) + { + contractAddress = "0x" + contractAddress; + } + + if (!checksumContractAddr.starts_with("0x") && !checksumContractAddr.starts_with("0X")) + { + checksumContractAddr = "0x" + checksumContractAddr; + } + + jResp["contractAddress"] = contractAddress; + jResp["checksumContractAddress"] = checksumContractAddr; + } + else + { + jResp["contractAddress"] = contractAddress; + jResp["checksumContractAddress"] = contractAddress; + } + + jResp["gasUsed"] = transactionReceipt.gasUsed().str(16); + jResp["status"] = transactionReceipt.status(); + jResp["blockNumber"] = transactionReceipt.blockNumber(); + jResp["output"] = toHexStringWithPrefix(transactionReceipt.output()); + jResp["message"] = transactionReceipt.message(); + jResp["transactionHash"] = std::string(_txHash); + jResp["hash"] = transactionReceipt.hash().hexPrefixed(); + jResp["logEntries"] = Json::Value(Json::arrayValue); + for (const auto& logEntry : transactionReceipt.logEntries()) + { + Json::Value jLog; + jLog["address"] = std::string(logEntry.address()); + jLog["topics"] = Json::Value(Json::arrayValue); + for (const auto& topic : logEntry.topics()) + { + jLog["topics"].append(topic.hexPrefixed()); + } + jLog["data"] = toHexStringWithPrefix(logEntry.data()); + jResp["logEntries"].append(jLog); + } +} + + +void JsonRpcImpl_2_0::toJsonResp( + Json::Value& jResp, bcos::protocol::BlockHeader::Ptr _blockHeaderPtr) +{ + if (!_blockHeaderPtr) + { + return; + } + + jResp["hash"] = toHexStringWithPrefix(_blockHeaderPtr->hash()); + jResp["version"] = _blockHeaderPtr->version(); + jResp["txsRoot"] = toHexStringWithPrefix(_blockHeaderPtr->txsRoot()); + jResp["receiptsRoot"] = toHexStringWithPrefix(_blockHeaderPtr->receiptsRoot()); + jResp["stateRoot"] = toHexStringWithPrefix(_blockHeaderPtr->stateRoot()); + jResp["number"] = _blockHeaderPtr->number(); + jResp["gasUsed"] = _blockHeaderPtr->gasUsed().str(16); + jResp["timestamp"] = _blockHeaderPtr->timestamp(); + jResp["sealer"] = _blockHeaderPtr->sealer(); + jResp["extraData"] = toHexStringWithPrefix(_blockHeaderPtr->extraData()); + + jResp["consensusWeights"] = Json::Value(Json::arrayValue); + for (const auto& wei : _blockHeaderPtr->consensusWeights()) + { + jResp["consensusWeights"].append(wei); + } + + jResp["sealerList"] = Json::Value(Json::arrayValue); + for (const auto& sealer : _blockHeaderPtr->sealerList()) + { + jResp["sealerList"].append(toHexStringWithPrefix(sealer)); + } + + Json::Value jParentInfo(Json::arrayValue); + for (const auto& p : _blockHeaderPtr->parentInfo()) + { + Json::Value jp; + jp["blockNumber"] = p.blockNumber; + jp["blockHash"] = toHexStringWithPrefix(p.blockHash); + jParentInfo.append(jp); + } + jResp["parentInfo"] = jParentInfo; + + Json::Value jSignList(Json::arrayValue); + for (const auto& sign : _blockHeaderPtr->signatureList()) + { + Json::Value jSign; + jSign["sealerIndex"] = sign.index; + jSign["signature"] = toHexStringWithPrefix(sign.signature); + jSignList.append(jSign); + } + jResp["signatureList"] = jSignList; +} + +void JsonRpcImpl_2_0::toJsonResp( + Json::Value& jResp, bcos::protocol::Block::Ptr _blockPtr, bool _onlyTxHash) +{ + if (!_blockPtr) + { + return; + } + + // header + toJsonResp(jResp, _blockPtr->blockHeader()); + auto txSize = _blockPtr->transactionsSize(); + + Json::Value jTxs(Json::arrayValue); + for (std::size_t index = 0; index < txSize; ++index) + { + Json::Value jTx; + if (_onlyTxHash) + { + // Note: should not call transactionHash for in the common cases transactionHash maybe + // empty + jTx = toHexStringWithPrefix(_blockPtr->transaction(index)->hash()); + } + else + { + toJsonResp(jTx, _blockPtr->transaction(index)); + } + jTxs.append(jTx); + } + + jResp["transactions"] = jTxs; +} + +void JsonRpcImpl_2_0::call(std::string_view _groupID, std::string_view _nodeName, + std::string_view _to, std::string_view _data, RespFunc _respFunc) +{ + RPC_IMPL_LOG(TRACE) << LOG_DESC("call") << LOG_KV("to", _to) << LOG_KV("group", _groupID) + << LOG_KV("node", _nodeName) << LOG_KV("data", _data); + + auto nodeService = getNodeService(_groupID, _nodeName, "call"); + auto transactionFactory = nodeService->blockFactory()->transactionFactory(); + auto transaction = + transactionFactory->createTransaction(0, _to, decodeData(_data), u256(0), 0, "", "", 0); + nodeService->scheduler()->call(std::move(transaction), + [m_to = std::string(_to), m_respFunc = std::move(_respFunc)]( + Error::Ptr&& _error, protocol::TransactionReceipt::Ptr&& _transactionReceiptPtr) { + Json::Value jResp; + if (!_error || (_error->errorCode() == bcos::protocol::CommonError::SUCCESS)) + { + jResp["blockNumber"] = _transactionReceiptPtr->blockNumber(); + jResp["status"] = _transactionReceiptPtr->status(); + jResp["output"] = toHexStringWithPrefix(_transactionReceiptPtr->output()); + } + else + { + RPC_IMPL_LOG(ERROR) + << LOG_BADGE("call") << LOG_KV("to", m_to) + << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : "success"); + } + + m_respFunc(_error, jResp); + }); +} + +void JsonRpcImpl_2_0::sendTransaction(std::string_view groupID, std::string_view nodeName, + std::string_view data, bool requireProof, RespFunc respFunc) +{ + task::wait( + [](JsonRpcImpl_2_0* self, std::string_view groupID, std::string_view nodeName, + std::string_view data, bool requireProof, RespFunc respFunc) -> task::Task { + auto nodeService = self->getNodeService(groupID, nodeName, "sendTransaction"); + + auto txpool = nodeService->txpool(); + if (!txpool) [[unlikely]] + { + BOOST_THROW_EXCEPTION( + JsonRpcException(JsonRpcError::InternalError, "TXPool not available!")); + } + + auto groupInfo = self->m_groupManager->getGroupInfo(groupID); + if (!groupInfo) [[unlikely]] + { + BOOST_THROW_EXCEPTION(JsonRpcException(JsonRpcError::GroupNotExist, + "The group " + std::string(groupID) + " does not exist!")); + } + + auto isWasm = groupInfo->wasm(); + auto transactionData = decodeData(data); + auto transaction = nodeService->blockFactory()->transactionFactory()->createTransaction( + transactionData, false); + + RPC_IMPL_LOG(TRACE) << LOG_DESC("sendTransaction") << LOG_KV("group", groupID) + << LOG_KV("node", nodeName) << LOG_KV("isWasm", isWasm); + auto start = utcTime(); + auto sendTxTimeout = self->m_sendTxTimeout; + + Json::Value jResp; + try + { + auto submitResult = co_await txpool->submitTransaction(transaction); + + auto txHash = submitResult->txHash(); + auto hexPreTxHash = txHash.hexPrefixed(); + + auto end = utcTime(); + auto totalTime = end - start; // ms + if (sendTxTimeout > 0 && totalTime > (uint64_t)sendTxTimeout) + { + RPC_IMPL_LOG(WARNING) + << LOG_BADGE("sendTransaction") << LOG_DESC("submit callback timeout") + << LOG_KV("hexPreTxHash", hexPreTxHash) + << LOG_KV("requireProof", requireProof) << LOG_KV("txCostTime", totalTime); + } + else + { + RPC_IMPL_LOG(TRACE) + << LOG_BADGE("sendTransaction") << LOG_DESC("submit callback") + << LOG_KV("hexPreTxHash", hexPreTxHash) + << LOG_KV("requireProof", requireProof) << LOG_KV("txCostTime", totalTime); + } + + if (submitResult->status() != (int32_t)bcos::protocol::TransactionStatus::None) + { + std::stringstream errorMsg; + errorMsg << (bcos::protocol::TransactionStatus)(submitResult->status()); + jResp["errorMessage"] = errorMsg.str(); + } + + toJsonResp(jResp, hexPreTxHash, *(submitResult->transactionReceipt()), isWasm, + *(nodeService->blockFactory()->cryptoSuite()->hashImpl())); + jResp["to"] = string(submitResult->to()); + jResp["from"] = toHexStringWithPrefix(submitResult->sender()); + + // TODO: check if needed + // jResp["input"] = toHexStringWithPrefix(transaction->input()); + + respFunc(nullptr, jResp); + } + catch (bcos::Error& e) + { + auto info = boost::diagnostic_information(e); + RPC_IMPL_LOG(WARNING) << "RPC bcos error: " << info; + respFunc(std::make_shared(std::move(e)), jResp); + } + catch (std::exception& e) + { + auto info = boost::diagnostic_information(e); + RPC_IMPL_LOG(WARNING) << "RPC common error: " << info; + respFunc(std::make_shared(-1, std::move(info)), jResp); + } + }(this, groupID, nodeName, data, requireProof, std::move(respFunc))); +} + + +void JsonRpcImpl_2_0::addProofToResponse( + Json::Value& jResp, std::string_view _key, ledger::MerkleProofPtr _merkleProofPtr) +{ + // Nothing to do! + // if (!_merkleProofPtr) + // { + // return; + // } + + // RPC_IMPL_LOG(TRACE) << LOG_DESC("addProofToResponse") << LOG_KV("key", _key) + // << LOG_KV("key", _key) << LOG_KV("merkleProofPtr", + // _merkleProofPtr->size()); + + // uint32_t index = 0; + // for (const auto& merkleItem : *_merkleProofPtr) + // { + // jResp[_key][index]["left"] = Json::arrayValue; + // jResp[_key][index]["right"] = Json::arrayValue; + // const auto& left = merkleItem.first; + // for (const auto& item : left) + // { + // jResp[_key][index]["left"].append(item); + // } + + // const auto& right = merkleItem.second; + // for (const auto& item : right) + // { + // jResp[_key][index]["right"].append(item); + // } + // ++index; + // } +} + +void JsonRpcImpl_2_0::getTransaction(std::string_view _groupID, std::string_view _nodeName, + std::string_view _txHash, bool _requireProof, RespFunc _respFunc) +{ + RPC_IMPL_LOG(TRACE) << LOG_DESC("getTransaction") << LOG_KV("txHash", _txHash) + << LOG_KV("requireProof", _requireProof) << LOG_KV("group", _groupID) + << LOG_KV("node", _nodeName); + + auto hashListPtr = std::make_shared(); + + // TODO: Error hash here, need hex2bin + hashListPtr->push_back(bcos::crypto::HashType(_txHash, bcos::crypto::HashType::FromHex)); + + auto nodeService = getNodeService(_groupID, _nodeName, "getTransaction"); + auto ledger = nodeService->ledger(); + checkService(ledger, "ledger"); + ledger->asyncGetBatchTxsByHashList(hashListPtr, _requireProof, + [m_txHash = std::string(_txHash), _requireProof, m_respFunc = std::move(_respFunc)]( + Error::Ptr _error, bcos::protocol::TransactionsPtr _transactionsPtr, + std::shared_ptr> _transactionProofsPtr) { + Json::Value jResp; + if (!_error || (_error->errorCode() == bcos::protocol::CommonError::SUCCESS)) + { + if (!_transactionsPtr->empty()) + { + auto transactionPtr = (*_transactionsPtr)[0]; + toJsonResp(jResp, transactionPtr); + } + + RPC_IMPL_LOG(TRACE) + << LOG_DESC("getTransaction") << LOG_KV("txHash", m_txHash) + << LOG_KV("requireProof", _requireProof) + << LOG_KV("transactionProofsPtr size", + (_transactionProofsPtr ? (int64_t)_transactionProofsPtr->size() : -1)); + + + if (_requireProof && _transactionProofsPtr && !_transactionProofsPtr->empty()) + { + auto transactionProofPtr = _transactionProofsPtr->begin()->second; + addProofToResponse(jResp, "transactionProof", transactionProofPtr); + } + } + else + { + RPC_IMPL_LOG(ERROR) + << LOG_BADGE("getTransaction") << LOG_KV("txHash", m_txHash) + << LOG_KV("requireProof", _requireProof) + << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : "success"); + } + + m_respFunc(_error, jResp); + }); +} + +void JsonRpcImpl_2_0::getTransactionReceipt(std::string_view _groupID, std::string_view _nodeName, + std::string_view _txHash, bool _requireProof, RespFunc _respFunc) +{ + RPC_IMPL_LOG(TRACE) << LOG_DESC("getTransactionReceipt") << LOG_KV("txHash", _txHash) + << LOG_KV("requireProof", _requireProof) << LOG_KV("group", _groupID) + << LOG_KV("node", _nodeName); + + auto hash = bcos::crypto::HashType(_txHash, bcos::crypto::HashType::FromHex); + + auto nodeService = getNodeService(_groupID, _nodeName, "getTransactionReceipt"); + auto ledger = nodeService->ledger(); + + checkService(ledger, "ledger"); + auto hashImpl = nodeService->blockFactory()->cryptoSuite()->hashImpl(); + + auto groupInfo = m_groupManager->getGroupInfo(_groupID); + if (!groupInfo) + { + BOOST_THROW_EXCEPTION(JsonRpcException(JsonRpcError::GroupNotExist, + "The group " + std::string(_groupID) + " does not exist!")); + } + + bool isWasm = groupInfo->wasm(); + + auto self = std::weak_ptr(shared_from_this()); + ledger->asyncGetTransactionReceiptByHash(hash, _requireProof, + [m_group = std::string(_groupID), m_nodeName = std::string(_nodeName), + m_txHash = std::string(_txHash), hash, _requireProof, m_respFunc = std::move(_respFunc), + self, hashImpl, isWasm](Error::Ptr _error, + protocol::TransactionReceipt::ConstPtr _transactionReceiptPtr, + ledger::MerkleProofPtr _merkleProofPtr) { + auto rpc = self.lock(); + if (!rpc) + { + return; + } + Json::Value jResp; + if (_error && (_error->errorCode() != bcos::protocol::CommonError::SUCCESS)) + { + RPC_IMPL_LOG(ERROR) + << LOG_BADGE("getTransactionReceipt") << LOG_KV("txHash", m_txHash) + << LOG_KV("requireProof", _requireProof) + << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : "success"); + + m_respFunc(_error, jResp); + return; + } + + toJsonResp(jResp, hash.hexPrefixed(), *_transactionReceiptPtr, isWasm, *hashImpl); + + RPC_IMPL_LOG(TRACE) << LOG_DESC("getTransactionReceipt") << LOG_KV("txHash", m_txHash) + << LOG_KV("requireProof", _requireProof) + << LOG_KV("merkleProofPtr", _merkleProofPtr); + + if (_requireProof && _merkleProofPtr) + { + addProofToResponse(jResp, "receiptProof", _merkleProofPtr); + } + + // fetch transaction proof + rpc->getTransaction(m_group, m_nodeName, m_txHash, _requireProof, + [m_jResp = std::move(jResp), m_txHash, m_respFunc = std::move(m_respFunc)]( + bcos::Error::Ptr _error, Json::Value& _jTx) mutable { + if (_error && _error->errorCode() != bcos::protocol::CommonError::SUCCESS) + { + RPC_IMPL_LOG(WARNING) + << LOG_BADGE("getTransactionReceipt") << LOG_DESC("getTransaction") + << LOG_KV("hexPreTxHash", m_txHash) + << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : "success"); + } + m_jResp["input"] = _jTx["input"]; + m_jResp["from"] = _jTx["from"]; + m_jResp["to"] = _jTx["to"]; + m_jResp["transactionProof"] = _jTx["transactionProof"]; + + m_respFunc(nullptr, m_jResp); + }); + }); +} + +void JsonRpcImpl_2_0::getBlockByHash(std::string_view _groupID, std::string_view _nodeName, + std::string_view _blockHash, bool _onlyHeader, bool _onlyTxHash, RespFunc _respFunc) +{ + RPC_IMPL_LOG(TRACE) << LOG_DESC("getBlockByHash") << LOG_KV("blockHash", _blockHash) + << LOG_KV("onlyHeader", _onlyHeader) << LOG_KV("onlyTxHash", _onlyTxHash) + << LOG_KV("group", _groupID) << LOG_KV("node", _nodeName); + + auto nodeService = getNodeService(_groupID, _nodeName, "getBlockByHash"); + auto ledger = nodeService->ledger(); + checkService(ledger, "ledger"); + auto self = std::weak_ptr(shared_from_this()); + ledger->asyncGetBlockNumberByHash( + bcos::crypto::HashType(_blockHash, bcos::crypto::HashType::FromHex), + [m_groupID = std::string(_groupID), m_nodeName = std::string(_nodeName), + m_blockHash = std::string(_blockHash), _onlyHeader, _onlyTxHash, + m_respFunc = std::move(_respFunc), + self](Error::Ptr _error, protocol::BlockNumber blockNumber) { + if (!_error || _error->errorCode() == bcos::protocol::CommonError::SUCCESS) + { + auto rpc = self.lock(); + if (rpc) + { + // call getBlockByNumber + return rpc->getBlockByNumber(m_groupID, m_nodeName, blockNumber, _onlyHeader, + _onlyTxHash, std::move(m_respFunc)); + } + } + else + { + RPC_IMPL_LOG(ERROR) + << LOG_BADGE("getBlockByHash") << LOG_KV("blockHash", m_blockHash) + << LOG_KV("onlyHeader", _onlyHeader) << LOG_KV("onlyTxHash", _onlyTxHash) + << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : "success"); + Json::Value jResp; + m_respFunc(_error, jResp); + } + }); +} + +void JsonRpcImpl_2_0::getBlockByNumber(std::string_view _groupID, std::string_view _nodeName, + int64_t _blockNumber, bool _onlyHeader, bool _onlyTxHash, RespFunc _respFunc) +{ + RPC_IMPL_LOG(TRACE) << LOG_DESC("getBlockByNumber") << LOG_KV("_blockNumber", _blockNumber) + << LOG_KV("onlyHeader", _onlyHeader) << LOG_KV("onlyTxHash", _onlyTxHash) + << LOG_KV("group", _groupID) << LOG_KV("node", _nodeName); + + auto nodeService = getNodeService(_groupID, _nodeName, "getBlockByNumber"); + auto ledger = nodeService->ledger(); + checkService(ledger, "ledger"); + ledger->asyncGetBlockDataByNumber(_blockNumber, + _onlyHeader ? bcos::ledger::HEADER : bcos::ledger::HEADER | bcos::ledger::TRANSACTIONS, + [_blockNumber, _onlyHeader, _onlyTxHash, m_respFunc = std::move(_respFunc)]( + Error::Ptr _error, protocol::Block::Ptr _block) { + Json::Value jResp; + if (_error && _error->errorCode() != bcos::protocol::CommonError::SUCCESS) + { + RPC_IMPL_LOG(ERROR) + << LOG_BADGE("getBlockByNumber") << LOG_KV("blockNumber", _blockNumber) + << LOG_KV("onlyHeader", _onlyHeader) << LOG_KV("onlyTxHash", _onlyTxHash) + << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : "success"); + } + else + { + if (_onlyHeader) + { + toJsonResp(jResp, _block ? _block->blockHeader() : nullptr); + } + else + { + toJsonResp(jResp, _block, _onlyTxHash); + } + } + m_respFunc(_error, jResp); + }); +} + +void JsonRpcImpl_2_0::getBlockHashByNumber( + std::string_view _groupID, std::string_view _nodeName, int64_t _blockNumber, RespFunc _respFunc) +{ + RPC_IMPL_LOG(TRACE) << LOG_DESC("getBlockHashByNumber") << LOG_KV("blockNumber", _blockNumber) + << LOG_KV("group", _groupID) << LOG_KV("node", _nodeName); + + auto nodeService = getNodeService(_groupID, _nodeName, "getBlockHashByNumber"); + auto ledger = nodeService->ledger(); + checkService(ledger, "ledger"); + ledger->asyncGetBlockHashByNumber(_blockNumber, + [m_respFunc = std::move(_respFunc)](Error::Ptr _error, crypto::HashType const& _hashValue) { + if (_error && (_error->errorCode() != bcos::protocol::CommonError::SUCCESS)) + { + RPC_IMPL_LOG(ERROR) + << LOG_BADGE("getBlockHashByNumber") + << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : "success"); + } + + Json::Value jResp = _hashValue.hexPrefixed(); + m_respFunc(nullptr, jResp); + }); +} + +void JsonRpcImpl_2_0::getBlockNumber( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) +{ + RPC_IMPL_LOG(TRACE) << LOG_BADGE("getBlockNumber") << LOG_KV("group", _groupID) + << LOG_KV("node", _nodeName); + + auto nodeService = getNodeService(_groupID, _nodeName, "getBlockNumber"); + auto ledger = nodeService->ledger(); + checkService(ledger, "ledger"); + ledger->asyncGetBlockNumber( + [m_respFunc = std::move(_respFunc)](Error::Ptr _error, protocol::BlockNumber _blockNumber) { + if (_error && (_error->errorCode() != bcos::protocol::CommonError::SUCCESS)) + { + RPC_IMPL_LOG(ERROR) + << LOG_BADGE("getBlockNumber") + << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : "success") + << LOG_KV("blockNumber", _blockNumber); + } + + Json::Value jResp = _blockNumber; + m_respFunc(_error, jResp); + }); +} + +void JsonRpcImpl_2_0::getCode(std::string_view _groupID, std::string_view _nodeName, + std::string_view _contractAddress, RespFunc _callback) +{ + RPC_IMPL_LOG(TRACE) << LOG_BADGE("getCode") << LOG_KV("contractAddress", _contractAddress) + << LOG_KV("group", _groupID) << LOG_KV("node", _nodeName); + + auto nodeService = getNodeService(_groupID, _nodeName, "getCode"); + + auto groupInfo = m_groupManager->getGroupInfo(_groupID); + if (!groupInfo) [[unlikely]] + { + BOOST_THROW_EXCEPTION(JsonRpcException(JsonRpcError::GroupNotExist, + "The group " + std::string(_groupID) + " does not exist!")); + } + + auto isWasm = groupInfo->wasm(); + // trim 0x prefix for solidity contract + if (!isWasm && (_contractAddress.starts_with("0x") || _contractAddress.starts_with("0X"))) + { + _contractAddress = _contractAddress.substr(2); + } + + auto lowerAddress = boost::to_lower_copy(std::string(_contractAddress)); + + auto scheduler = nodeService->scheduler(); + scheduler->getCode(std::string_view(lowerAddress), + [lowerAddress, callback = std::move(_callback)](Error::Ptr _error, bcos::bytes _codeData) { + std::string code; + if (!_error || (_error->errorCode() == bcos::protocol::CommonError::SUCCESS)) + { + if (!_codeData.empty()) + { + code = toHexStringWithPrefix( + bcos::bytesConstRef(_codeData.data(), _codeData.size())); + } + } + else + { + RPC_IMPL_LOG(ERROR) + << LOG_BADGE("getCode") << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : "success") + << LOG_KV("contractAddress", lowerAddress); + } + + Json::Value jResp = std::move(code); + callback(_error, jResp); + }); +} + +void JsonRpcImpl_2_0::getABI(std::string_view _groupID, std::string_view _nodeName, + std::string_view _contractAddress, RespFunc _callback) +{ + RPC_IMPL_LOG(TRACE) << LOG_BADGE("getABI") << LOG_KV("contractAddress", _contractAddress) + << LOG_KV("group", _groupID) << LOG_KV("node", _nodeName); + + auto nodeService = getNodeService(_groupID, _nodeName, "getABI"); + + auto groupInfo = m_groupManager->getGroupInfo(_groupID); + if (!groupInfo) [[unlikely]] + { + BOOST_THROW_EXCEPTION(JsonRpcException(JsonRpcError::GroupNotExist, + "The group " + std::string(_groupID) + " does not exist!")); + } + + auto isWasm = groupInfo->wasm(); + // trim 0x prefix for solidity contract address + if (!isWasm && (_contractAddress.starts_with("0x") || _contractAddress.starts_with("0X"))) + { + _contractAddress = _contractAddress.substr(2); + } + + + auto lowerAddress = boost::to_lower_copy(std::string(_contractAddress)); + + auto scheduler = nodeService->scheduler(); + scheduler->getABI(std::string_view(lowerAddress), + [lowerAddress, callback = std::move(_callback)](Error::Ptr _error, std::string _abi) { + if (_error) + { + RPC_IMPL_LOG(ERROR) + << LOG_BADGE("getABI") << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : "success") + << LOG_KV("contractAddress", lowerAddress); + } + Json::Value jResp = std::move(_abi); + callback(_error, jResp); + }); +} + +void JsonRpcImpl_2_0::getSealerList( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) +{ + RPC_IMPL_LOG(TRACE) << LOG_BADGE("getSealerList") << LOG_KV("group", _groupID) + << LOG_KV("node", _nodeName); + + auto nodeService = getNodeService(_groupID, _nodeName, "getSealerList"); + auto ledger = nodeService->ledger(); + checkService(ledger, "ledger"); + ledger->asyncGetNodeListByType( + bcos::ledger::CONSENSUS_SEALER, [m_respFunc = std::move(_respFunc)](Error::Ptr _error, + consensus::ConsensusNodeListPtr _consensusNodeListPtr) { + Json::Value jResp = Json::Value(Json::arrayValue); + if (!_error || (_error->errorCode() == bcos::protocol::CommonError::SUCCESS)) + { + if (_consensusNodeListPtr) + { + for (const auto& consensusNodePtr : *_consensusNodeListPtr) + { + Json::Value node; + node["nodeID"] = consensusNodePtr->nodeID()->hex(); + node["weight"] = consensusNodePtr->weight(); + jResp.append(node); + } + } + } + else + { + RPC_IMPL_LOG(ERROR) + << LOG_BADGE("getSealerList") + << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : "success"); + } + + m_respFunc(_error, jResp); + }); +} + +void JsonRpcImpl_2_0::getObserverList( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) +{ + RPC_IMPL_LOG(TRACE) << LOG_BADGE("getObserverList") << LOG_KV("group", _groupID) + << LOG_KV("group", _groupID) << LOG_KV("node", _nodeName); + + auto nodeService = getNodeService(_groupID, _nodeName, "getObserverList"); + auto ledger = nodeService->ledger(); + checkService(ledger, "ledger"); + ledger->asyncGetNodeListByType(bcos::ledger::CONSENSUS_OBSERVER, + [m_respFunc = std::move(_respFunc)]( + Error::Ptr _error, consensus::ConsensusNodeListPtr _consensusNodeListPtr) { + Json::Value jResp = Json::Value(Json::arrayValue); + if (!_error || (_error->errorCode() == bcos::protocol::CommonError::SUCCESS)) + { + if (_consensusNodeListPtr) + { + for (const auto& consensusNodePtr : *_consensusNodeListPtr) + { + jResp.append(consensusNodePtr->nodeID()->hex()); + } + } + } + else + { + RPC_IMPL_LOG(ERROR) + << LOG_BADGE("getObserverList") + << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : "success"); + } + + m_respFunc(_error, jResp); + }); +} + +void JsonRpcImpl_2_0::getPbftView( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) +{ + RPC_IMPL_LOG(TRACE) << LOG_BADGE("getPbftView") << LOG_KV("group", _groupID) + << LOG_KV("node", _nodeName); + + auto nodeService = getNodeService(_groupID, _nodeName, "getPbftView"); + auto consensus = nodeService->consensus(); + checkService(consensus, "consensus"); + consensus->asyncGetPBFTView([m_respFunc = std::move(_respFunc)]( + Error::Ptr _error, bcos::consensus::ViewType _viewValue) { + Json::Value jResp; + if (!_error || (_error->errorCode() == bcos::protocol::CommonError::SUCCESS)) + { + jResp = _viewValue; + } + else + { + RPC_IMPL_LOG(ERROR) << LOG_BADGE("getPbftView") + << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV( + "errorMessage", _error ? _error->errorMessage() : "success"); + } + + m_respFunc(_error, jResp); + }); +} + +void JsonRpcImpl_2_0::getPendingTxSize( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) +{ + RPC_IMPL_LOG(TRACE) << LOG_BADGE("getPendingTxSize") << LOG_KV("group", _groupID) + << LOG_KV("node", _nodeName); + + auto nodeService = getNodeService(_groupID, _nodeName, "getPendingTxSize"); + auto txpool = nodeService->txpool(); + checkService(txpool, "txpool"); + txpool->asyncGetPendingTransactionSize( + [m_respFunc = std::move(_respFunc)](Error::Ptr _error, size_t _pendingTxSize) { + Json::Value jResp; + if (!_error || (_error->errorCode() == bcos::protocol::CommonError::SUCCESS)) + { + jResp = (int64_t)_pendingTxSize; + } + else + { + RPC_IMPL_LOG(ERROR) + << LOG_BADGE("getPendingTxSize") + << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : "success"); + } + + m_respFunc(_error, jResp); + }); +} + +void JsonRpcImpl_2_0::getSyncStatus( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) +{ + RPC_IMPL_LOG(TRACE) << LOG_BADGE("getSyncStatus") << LOG_KV("group", _groupID) + << LOG_KV("node", _nodeName); + + auto nodeService = getNodeService(_groupID, _nodeName, "getSyncStatus"); + auto sync = nodeService->sync(); + checkService(sync, "sync"); + sync->asyncGetSyncInfo( + [m_respFunc = std::move(_respFunc)](Error::Ptr _error, std::string _syncStatus) { + Json::Value jResp; + if (!_error || (_error->errorCode() == bcos::protocol::CommonError::SUCCESS)) + { + jResp = _syncStatus; + } + else + { + RPC_IMPL_LOG(ERROR) + << LOG_BADGE("getSyncStatus") + << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : "success"); + } + m_respFunc(_error, jResp); + }); +} + +void JsonRpcImpl_2_0::getConsensusStatus( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) +{ + RPC_IMPL_LOG(TRACE) << LOG_BADGE("getConsensusStatus") << LOG_KV("group", _groupID) + << LOG_KV("node", _nodeName); + + auto nodeService = getNodeService(_groupID, _nodeName, "getConsensusStatus"); + auto consensus = nodeService->consensus(); + checkService(consensus, "consensus"); + consensus->asyncGetConsensusStatus( + [m_respFunc = std::move(_respFunc)](Error::Ptr _error, std::string _consensusStatus) { + Json::Value jResp; + if (!_error || (_error->errorCode() == bcos::protocol::CommonError::SUCCESS)) + { + jResp = _consensusStatus; + } + else + { + RPC_IMPL_LOG(ERROR) + << LOG_BADGE("getConsensusStatus") + << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : "success"); + } + m_respFunc(_error, jResp); + }); +} + +void JsonRpcImpl_2_0::getSystemConfigByKey(std::string_view _groupID, std::string_view _nodeName, + std::string_view _keyValue, RespFunc _respFunc) +{ + RPC_IMPL_LOG(TRACE) << LOG_DESC("getSystemConfigByKey") << LOG_KV("keyValue", _keyValue) + << LOG_KV("group", _groupID) << LOG_KV("node", _nodeName); + + auto nodeService = getNodeService(_groupID, _nodeName, "getSystemConfigByKey"); + auto ledger = nodeService->ledger(); + checkService(ledger, "ledger"); + ledger->asyncGetSystemConfigByKey( + _keyValue, [m_respFunc = std::move(_respFunc)]( + Error::Ptr _error, std::string _value, protocol::BlockNumber _blockNumber) { + Json::Value jResp; + if (!_error || (_error->errorCode() == bcos::protocol::CommonError::SUCCESS)) + { + jResp["blockNumber"] = _blockNumber; + jResp["value"] = _value; + } + else + { + RPC_IMPL_LOG(ERROR) + << LOG_BADGE("asyncGetSystemConfigByKey") + << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : "success"); + } + + m_respFunc(_error, jResp); + }); +} + +void JsonRpcImpl_2_0::getTotalTransactionCount( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) +{ + RPC_IMPL_LOG(TRACE) << LOG_DESC("getTotalTransactionCount") << LOG_KV("group", _groupID) + << LOG_KV("node", _nodeName); + + auto nodeService = getNodeService(_groupID, _nodeName, "getTotalTransactionCount"); + auto ledger = nodeService->ledger(); + checkService(ledger, "ledger"); + ledger->asyncGetTotalTransactionCount( + [m_respFunc = std::move(_respFunc)](Error::Ptr _error, int64_t _totalTxCount, + int64_t _failedTxCount, protocol::BlockNumber _latestBlockNumber) { + Json::Value jResp; + if (!_error || (_error->errorCode() == bcos::protocol::CommonError::SUCCESS)) + { + jResp["blockNumber"] = _latestBlockNumber; + jResp["transactionCount"] = _totalTxCount; + jResp["failedTransactionCount"] = _failedTxCount; + } + else + { + RPC_IMPL_LOG(ERROR) + << LOG_BADGE("getTotalTransactionCount") + << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : "success"); + } + + m_respFunc(_error, jResp); + }); +} +void JsonRpcImpl_2_0::getPeers(RespFunc _respFunc) +{ + RPC_IMPL_LOG(TRACE) << LOG_DESC("getPeers"); + auto self = std::weak_ptr(shared_from_this()); + m_gatewayInterface->asyncGetPeers([m_respFunc = std::move(_respFunc), self](Error::Ptr _error, + bcos::gateway::GatewayInfo::Ptr _localP2pInfo, + bcos::gateway::GatewayInfosPtr _peersInfo) { + auto rpc = self.lock(); + if (!rpc) + { + return; + } + Json::Value jResp; + if (!_error || (_error->errorCode() == bcos::protocol::CommonError::SUCCESS)) + { + rpc->gatewayInfoToJson(jResp, _localP2pInfo, _peersInfo); + } + else + { + RPC_IMPL_LOG(ERROR) << LOG_BADGE("getPeers") + << LOG_KV("errorCode", _error ? _error->errorCode() : 0) + << LOG_KV( + "errorMessage", _error ? _error->errorMessage() : "success"); + } + + m_respFunc(_error, jResp); + }); +} + +NodeService::Ptr JsonRpcImpl_2_0::getNodeService( + std::string_view _groupID, std::string_view _nodeName, std::string_view _command) +{ + auto nodeService = m_groupManager->getNodeService(_groupID, _nodeName); + if (!nodeService) + { + std::stringstream errorMsg; + errorMsg << LOG_DESC( + "Invalid request for the specified node of doesn't exist or doesn't " + "started!") + << LOG_KV("request", _command) << LOG_KV("chain", m_groupManager->chainID()) + << LOG_KV("group", _groupID) << LOG_KV("node", _nodeName); + RPC_IMPL_LOG(WARNING) << errorMsg.str(); + BOOST_THROW_EXCEPTION( + JsonRpcException(JsonRpcError::NodeNotExistOrNotStarted, errorMsg.str())); + } + return nodeService; +} + +// get all the groupID list +void JsonRpcImpl_2_0::getGroupList(RespFunc _respFunc) +{ + auto response = generateResponse(nullptr); + auto groupList = m_groupManager->groupList(); + for (auto const& it : groupList) + { + response["groupList"].append(it); + } + _respFunc(nullptr, response); +} + +// get the group information of the given group +void JsonRpcImpl_2_0::getGroupInfo(std::string_view _groupID, RespFunc _respFunc) +{ + auto groupInfo = m_groupManager->getGroupInfo(_groupID); + Json::Value response; + if (groupInfo) + { + // can only recover the deleted group + groupInfoToJson(response, groupInfo); + } + _respFunc(nullptr, response); +} + +// get all the group info list +void JsonRpcImpl_2_0::getGroupInfoList(RespFunc _respFunc) +{ + auto groupInfoList = m_groupManager->groupInfoList(); + Json::Value response(Json::arrayValue); + groupInfoListToJson(response, groupInfoList); + _respFunc(nullptr, response); +} + +void JsonRpcImpl_2_0::getGroupBlockNumber(RespFunc _respFunc) +{ + Json::Value response(Json::arrayValue); + auto groupInfoList = m_groupManager->groupInfoList(); + for (auto groupInfo : groupInfoList) + { + auto blockNumber = m_groupManager->getBlockNumberByGroup(groupInfo->groupID()); + if (blockNumber < 0) + { + RPC_IMPL_LOG(WARNING) << LOG_BADGE("getGroupBlockNumber") + << LOG_DESC("getBlockNumberByGroup failed") + << LOG_KV("groupID", groupInfo->groupID()); + continue; + } + + Json::Value jValue; + jValue[groupInfo->groupID()] = blockNumber; + response.append(jValue); + } + + _respFunc(nullptr, response); +} + +// get the information of a given node +void JsonRpcImpl_2_0::getGroupNodeInfo( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) +{ + auto nodeInfo = m_groupManager->getNodeInfo(_groupID, _nodeName); + Json::Value response; + // hit the cache, response directly + if (nodeInfo) + { + nodeInfoToJson(response, nodeInfo); + } + _respFunc(nullptr, response); +} +void JsonRpcImpl_2_0::gatewayInfoToJson( + Json::Value& _response, bcos::gateway::GatewayInfo::Ptr _gatewayInfo) +{ + auto p2pInfo = _gatewayInfo->p2pInfo(); + _response["p2pNodeID"] = p2pInfo.p2pID; + if (!p2pInfo.nodeIPEndpoint.address().empty()) + { + _response["endPoint"] = + p2pInfo.nodeIPEndpoint.address() + ":" + std::to_string(p2pInfo.nodeIPEndpoint.port()); + } + // set the groupNodeIDInfo + auto groupNodeIDInfo = _gatewayInfo->nodeIDInfo(); + Json::Value groupInfo(Json::arrayValue); + for (auto const& it : groupNodeIDInfo) + { + Json::Value item; + item["group"] = it.first; + auto const& nodeIDList = it.second; + Json::Value nodeIDInfo(Json::arrayValue); + for (auto const& nodeID : nodeIDList) + { + nodeIDInfo.append(Json::Value(nodeID)); + } + item["nodeIDList"] = nodeIDInfo; + groupInfo.append(item); + } + _response["groupNodeIDInfo"] = groupInfo; +} + +void JsonRpcImpl_2_0::gatewayInfoToJson(Json::Value& _response, + bcos::gateway::GatewayInfo::Ptr _localP2pInfo, bcos::gateway::GatewayInfosPtr _peersInfo) +{ + if (_localP2pInfo) + { + gatewayInfoToJson(_response, _localP2pInfo); + } + if (!_peersInfo) + { + return; + } + Json::Value peersInfo(Json::arrayValue); + for (auto const& it : *_peersInfo) + { + Json::Value peerInfo; + gatewayInfoToJson(peerInfo, it); + peersInfo.append(peerInfo); + } + _response["peers"] = peersInfo; +} + +void JsonRpcImpl_2_0::getGroupPeers(Json::Value& _response, std::string_view _groupID, + bcos::gateway::GatewayInfo::Ptr _localP2pInfo, bcos::gateway::GatewayInfosPtr _peersInfo) +{ + _peersInfo->emplace_back(_localP2pInfo); + std::set nodeIDList; + for (auto const& info : *_peersInfo) + { + auto groupNodeIDInfo = info->nodeIDInfo(); + auto it = groupNodeIDInfo.find(_groupID); + + if (it != groupNodeIDInfo.end()) + { + auto const& nodeList = it->second; + for (auto const& node : nodeList) + { + nodeIDList.insert(node); + } + } + } + + if (nodeIDList.size() == 0) + { + return; + } + for (auto const& nodeID : nodeIDList) + { + _response.append((Json::Value)(nodeID)); + } +} + +void JsonRpcImpl_2_0::getGroupPeers(std::string_view _groupID, RespFunc _respFunc) +{ + auto self = std::weak_ptr(shared_from_this()); + m_gatewayInterface->asyncGetPeers([_respFunc, group = std::string(_groupID), self]( + Error::Ptr _error, + bcos::gateway::GatewayInfo::Ptr _localP2pInfo, + bcos::gateway::GatewayInfosPtr _peersInfo) { + Json::Value jResp(Json::arrayValue); + if (_error) + { + RPC_IMPL_LOG(ERROR) << LOG_BADGE("getGroupPeers") << LOG_KV("code", _error->errorCode()) + << LOG_KV("message", _error->errorMessage()); + _respFunc(_error, jResp); + return; + } + auto rpc = self.lock(); + if (!rpc) + { + return; + } + rpc->getGroupPeers(jResp, std::string_view(group), _localP2pInfo, _peersInfo); + _respFunc(_error, jResp); + }); +} diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h" new file mode 100644 index 00000000..9f63bdae --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcImpl_2_0.h" @@ -0,0 +1,197 @@ +/** + * Copyright (C) 2021 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. + * + * @brief: implement for the RPC + * @file: JsonRpcImpl_2_0.h + * @author: octopus + * @date: 2021-07-09 + */ + +#pragma once +#include "bcos-rpc/groupmgr/GroupManager.h" +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace rpc +{ +class JsonRpcImpl_2_0 : public JsonRpcInterface, + public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + JsonRpcImpl_2_0(GroupManager::Ptr _groupManager, + bcos::gateway::GatewayInterface::Ptr _gatewayInterface, + std::shared_ptr _wsService); + ~JsonRpcImpl_2_0() override {} + + + void setClientID(std::string_view _clientID) { m_clientID = _clientID; } + +public: + void call(std::string_view _groupID, std::string_view _nodeName, std::string_view _to, + std::string_view _data, RespFunc _respFunc) override; + + void sendTransaction(std::string_view _groupID, std::string_view _nodeName, + std::string_view _data, bool _requireProof, RespFunc _respFunc) override; + + void getTransaction(std::string_view _groupID, std::string_view _nodeName, + std::string_view _txHash, bool _requireProof, RespFunc _respFunc) override; + + void getTransactionReceipt(std::string_view _groupID, std::string_view _nodeName, + std::string_view _txHash, bool _requireProof, RespFunc _respFunc) override; + + void getBlockByHash(std::string_view _groupID, std::string_view _nodeName, + std::string_view _blockHash, bool _onlyHeader, bool _onlyTxHash, + RespFunc _respFunc) override; + + void getBlockByNumber(std::string_view _groupID, std::string_view _nodeName, + int64_t _blockNumber, bool _onlyHeader, bool _onlyTxHash, RespFunc _respFunc) override; + + void getBlockHashByNumber(std::string_view _groupID, std::string_view _nodeName, + int64_t _blockNumber, RespFunc _respFunc) override; + + void getBlockNumber( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override; + + void getCode(std::string_view _groupID, std::string_view _nodeName, + std::string_view _contractAddress, RespFunc _respFunc) override; + + void getABI(std::string_view _groupID, std::string_view _nodeName, + std::string_view _contractAddress, RespFunc _respFunc) override; + + void getSealerList( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override; + + void getObserverList( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override; + + void getPbftView( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override; + + void getPendingTxSize( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override; + + void getSyncStatus( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override; + + void getConsensusStatus( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override; + + void getSystemConfigByKey(std::string_view _groupID, std::string_view _nodeName, + std::string_view _keyValue, RespFunc _respFunc) override; + + void getTotalTransactionCount( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override; + + void getPeers(RespFunc _respFunc) override; + + // get all the groupID list + void getGroupList(RespFunc _respFunc) override; + // get the group information of the given group + void getGroupInfo(std::string_view _groupID, RespFunc _respFunc) override; + // get all the group info list + void getGroupInfoList(RespFunc _respFunc) override; + // get the information of a given node + void getGroupNodeInfo( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override; + + void getGroupBlockNumber(RespFunc _respFunc) override; + +public: + void setNodeInfo(const NodeInfo& _nodeInfo) { m_nodeInfo = _nodeInfo; } + NodeInfo nodeInfo() const { return m_nodeInfo; } + GroupManager::Ptr groupManager() { return m_groupManager; } + + int sendTxTimeout() { return m_sendTxTimeout; } + void setSendTxTimeout(int _sendTxTimeout) { m_sendTxTimeout = _sendTxTimeout; } + +protected: + static bcos::bytes decodeData(std::string_view _data); + + static void parseRpcResponseJson(std::string_view _responseBody, JsonResponse& _jsonResponse); + + static void toJsonResp( + Json::Value& jResp, bcos::protocol::Transaction::ConstPtr _transactionPtr); + + static void toJsonResp(Json::Value& jResp, bcos::protocol::BlockHeader::Ptr _blockHeaderPtr); + static void toJsonResp( + Json::Value& jResp, bcos::protocol::Block::Ptr _blockPtr, bool _onlyTxHash); + static void toJsonResp(Json::Value& jResp, std::string_view _txHash, + bcos::protocol::TransactionReceipt const& transactionReceiptPtr, bool _isWasm, + crypto::Hash& hashImpl); + static void addProofToResponse( + Json::Value& jResp, std::string_view _key, ledger::MerkleProofPtr _merkleProofPtr); + + virtual void handleRpcRequest(std::shared_ptr _msg, + std::shared_ptr _session); + + // TODO: check perf influence + NodeService::Ptr getNodeService( + std::string_view _groupID, std::string_view _nodeName, std::string_view _command); + + template + void checkService(T _service, std::string_view _serviceName) + { + if (!_service) + { + BOOST_THROW_EXCEPTION(JsonRpcException( + JsonRpcError::ServiceNotInitCompleted, "The service " + std::string(_serviceName) + + " has not been initted completed yet!")); + } + } + +private: + void gatewayInfoToJson(Json::Value& _response, bcos::gateway::GatewayInfo::Ptr _gatewayInfo); + void gatewayInfoToJson(Json::Value& _response, bcos::gateway::GatewayInfo::Ptr _localP2pInfo, + bcos::gateway::GatewayInfosPtr _peersInfo); + void getGroupPeers(Json::Value& _response, std::string_view _groupID, + bcos::gateway::GatewayInfo::Ptr _localP2pInfo, bcos::gateway::GatewayInfosPtr _peersInfo); + void getGroupPeers(std::string_view _groupID, RespFunc _respFunc) override; + +private: + // ms + int m_sendTxTimeout = -1; + + GroupManager::Ptr m_groupManager; + bcos::gateway::GatewayInterface::Ptr m_gatewayInterface; + std::shared_ptr m_wsService; + + NodeInfo m_nodeInfo; + // Note: here clientID must non-empty for the rpc will set clientID as source for the tx for + // tx-notify and the scheduler will not notify the tx-result if the tx source is empty + std::string m_clientID = "localRpc"; + + struct TxHasher + { + size_t hash(const bcos::crypto::HashType& hash) const { return hasher(hash); } + + bool equal(const bcos::crypto::HashType& lhs, const bcos::crypto::HashType& rhs) const + { + return lhs == rhs; + } + + std::hash hasher; + }; +}; + +} // namespace rpc +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp" new file mode 100644 index 00000000..9ad49c9b --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.cpp" @@ -0,0 +1,265 @@ +#include "JsonRpcInterface.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos::rpc; + +void JsonRpcInterface::initMethod() +{ + m_methodToFunc["call"] = + std::bind(&JsonRpcInterface::callI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["sendTransaction"] = std::bind( + &JsonRpcInterface::sendTransactionI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getTransaction"] = std::bind( + &JsonRpcInterface::getTransactionI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getTransactionReceipt"] = std::bind(&JsonRpcInterface::getTransactionReceiptI, + this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getBlockByHash"] = std::bind( + &JsonRpcInterface::getBlockByHashI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getBlockByNumber"] = std::bind( + &JsonRpcInterface::getBlockByNumberI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getBlockHashByNumber"] = std::bind(&JsonRpcInterface::getBlockHashByNumberI, + this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getBlockNumber"] = std::bind( + &JsonRpcInterface::getBlockNumberI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getCode"] = + std::bind(&JsonRpcInterface::getCodeI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getABI"] = + std::bind(&JsonRpcInterface::getABII, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getSealerList"] = std::bind( + &JsonRpcInterface::getSealerListI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getObserverList"] = std::bind( + &JsonRpcInterface::getObserverListI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getPbftView"] = std::bind( + &JsonRpcInterface::getPbftViewI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getPendingTxSize"] = std::bind( + &JsonRpcInterface::getPendingTxSizeI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getSyncStatus"] = std::bind( + &JsonRpcInterface::getSyncStatusI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getConsensusStatus"] = std::bind( + &JsonRpcInterface::getConsensusStatusI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getSystemConfigByKey"] = std::bind(&JsonRpcInterface::getSystemConfigByKeyI, + this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getTotalTransactionCount"] = + std::bind(&JsonRpcInterface::getTotalTransactionCountI, this, std::placeholders::_1, + std::placeholders::_2); + m_methodToFunc["getPeers"] = + std::bind(&JsonRpcInterface::getPeersI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getGroupPeers"] = std::bind( + &JsonRpcInterface::getGroupPeersI, this, std::placeholders::_1, std::placeholders::_2); + + m_methodToFunc["getGroupList"] = std::bind( + &JsonRpcInterface::getGroupListI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getGroupInfo"] = std::bind( + &JsonRpcInterface::getGroupInfoI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getGroupInfoList"] = std::bind( + &JsonRpcInterface::getGroupInfoListI, this, std::placeholders::_1, std::placeholders::_2); + m_methodToFunc["getGroupNodeInfo"] = std::bind( + &JsonRpcInterface::getGroupNodeInfoI, 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); + } + RPC_IMPL_LOG(INFO) << LOG_BADGE("initMethod") << LOG_KV("size", m_methodToFunc.size()); +} + +void JsonRpcInterface::onRPCRequest(std::string_view _requestBody, Sender _sender) +{ + JsonRequest request; + JsonResponse response; + try + { + parseRpcRequestJson(_requestBody, request); + + response.jsonrpc = request.jsonrpc; + response.id = request.id; + + const auto& method = request.method; + auto it = m_methodToFunc.find(method); + if (it == m_methodToFunc.end()) + { + 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); + it->second( + request.params, [response, _sender](Error::Ptr _error, Json::Value& _result) mutable { + if (_error && (_error->errorCode() != bcos::protocol::CommonError::SUCCESS)) + { + // error + response.error.code = _error->errorCode(); + response.error.message = _error->errorMessage(); + } + else + { + 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())); + _sender(std::move(strResp)); + }); + + // success response + return; + } + catch (const JsonRpcException& e) + { + response.error.code = e.code(); + response.error.message = std::string(e.what()); + } + catch (const std::exception& e) + { + // server internal error or unexpected error + response.error.code = JsonRpcError::InvalidRequest; + response.error.message = std::string(e.what()); + } + + auto strResp = toStringResponse(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*)strResp.data(), strResp.size())); + _sender(strResp); +} + +void JsonRpcInterface::parseRpcRequestJson(std::string_view _requestBody, JsonRequest& _jsonRequest) +{ + Json::Value root; + Json::Reader jsonReader; + std::string errorMessage; + + try + { + std::string jsonrpc = ""; + std::string method = ""; + int64_t id = 0; + do + { + if (!jsonReader.parse(_requestBody.begin(), _requestBody.end(), root)) + { + errorMessage = "invalid request json object"; + break; + } + + if (!root.isMember("jsonrpc")) + { + errorMessage = "request has no jsonrpc field"; + break; + } + jsonrpc = root["jsonrpc"].asString(); + + if (!root.isMember("method")) + { + errorMessage = "request has no method field"; + break; + } + method = root["method"].asString(); + + if (root.isMember("id")) + { + id = root["id"].asInt64(); + } + + if (!root.isMember("params")) + { + errorMessage = "request has no params field"; + break; + } + + if (!root["params"].isArray()) + { + errorMessage = "request params is not array object"; + break; + } + + auto jParams = root["params"]; + + _jsonRequest.jsonrpc = jsonrpc; + _jsonRequest.method = method; + _jsonRequest.id = id; + _jsonRequest.params = jParams; + + // RPC_IMPL_LOG(DEBUG) << LOG_BADGE("parseRpcRequestJson") << LOG_KV("method", method) + // << LOG_KV("requestMessage", _requestBody); + + // success return + return; + } while (0); + } + catch (const std::exception& e) + { + RPC_IMPL_LOG(ERROR) << LOG_BADGE("parseRpcRequestJson") << LOG_KV("request", _requestBody) + << LOG_KV("error", boost::diagnostic_information(e)); + BOOST_THROW_EXCEPTION( + JsonRpcException(JsonRpcError::ParseError, "Invalid JSON was received by the server.")); + } + + RPC_IMPL_LOG(ERROR) << LOG_BADGE("parseRpcRequestJson") << LOG_KV("request", _requestBody) + << LOG_KV("errorMessage", errorMessage); + + BOOST_THROW_EXCEPTION(JsonRpcException( + JsonRpcError::InvalidRequest, "The JSON sent is not a valid Request object.")); +} + + +bcos::bytes JsonRpcInterface::toStringResponse(JsonResponse _jsonResponse) +{ + auto jResp = toJsonResponse(std::move(_jsonResponse)); + std::unique_ptr writer(Json::StreamWriterBuilder().newStreamWriter()); + class JsonSink + { + public: + typedef char char_type; + typedef boost::iostreams::sink_tag category; + + 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; + }; + + bcos::bytes out; + boost::iostreams::stream outputStream(out); + + writer->write(jResp, &outputStream); + writer.reset(); + return out; +} + +Json::Value JsonRpcInterface::toJsonResponse(JsonResponse _jsonResponse) +{ + Json::Value jResp; + jResp["jsonrpc"] = std::move(_jsonResponse.jsonrpc); + jResp["id"] = std::move(_jsonResponse.id); + + if (_jsonResponse.error.code == 0) + { // success + jResp["result"] = std::move(_jsonResponse.result); + } + else + { // error + Json::Value jError; + jError["code"] = std::move(_jsonResponse.error.code); + jError["message"] = std::move(_jsonResponse.error.message); + jResp["error"] = std::move(jError); + } + + return jResp; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h" "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h" new file mode 100644 index 00000000..7ce4e46f --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/bcos-rpc/jsonrpc/JsonRpcInterface.h" @@ -0,0 +1,277 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for JsonRPC + * @file JsonRpcInterface.h + * @author: octopus + * @date 2021-07-09 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::rpc +{ +using Sender = std::function; +using RespFunc = std::function; + +class JsonRpcInterface +{ +public: + using Ptr = std::shared_ptr; + JsonRpcInterface() { initMethod(); } + JsonRpcInterface(const JsonRpcInterface&) = default; + JsonRpcInterface(JsonRpcInterface&&) = default; + JsonRpcInterface& operator=(const JsonRpcInterface&) = default; + JsonRpcInterface& operator=(JsonRpcInterface&&) = default; + virtual ~JsonRpcInterface() {} + +public: + virtual void call(std::string_view _groupID, std::string_view _nodeName, std::string_view _to, + std::string_view _data, RespFunc _respFunc) = 0; + + virtual void sendTransaction(std::string_view _groupID, std::string_view _nodeName, + std::string_view _data, bool _requireProof, RespFunc _respFunc) = 0; + + virtual void getTransaction(std::string_view _groupID, std::string_view _nodeName, + std::string_view _txHash, bool _requireProof, RespFunc _respFunc) = 0; + + virtual void getTransactionReceipt(std::string_view _groupID, std::string_view _nodeName, + std::string_view _txHash, bool _requireProof, RespFunc _respFunc) = 0; + + virtual void getBlockByHash(std::string_view _groupID, std::string_view _nodeName, + std::string_view _blockHash, bool _onlyHeader, bool _onlyTxHash, RespFunc _respFunc) = 0; + + virtual void getBlockByNumber(std::string_view _groupID, std::string_view _nodeName, + int64_t _blockNumber, bool _onlyHeader, bool _onlyTxHash, RespFunc _respFunc) = 0; + + virtual void getBlockHashByNumber(std::string_view _groupID, std::string_view _nodeName, + int64_t _blockNumber, RespFunc _respFunc) = 0; + + virtual void getBlockNumber( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) = 0; + + virtual void getCode(std::string_view _groupID, std::string_view _nodeName, + std::string_view _contractAddress, RespFunc _respFunc) = 0; + + virtual void getABI(std::string_view _groupID, std::string_view _nodeName, + std::string_view _contractAddress, RespFunc _respFunc) = 0; + + virtual void getSealerList( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) = 0; + + virtual void getObserverList( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) = 0; + + virtual void getPbftView( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) = 0; + + virtual void getPendingTxSize( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) = 0; + + virtual void getSyncStatus( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) = 0; + virtual void getConsensusStatus( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) = 0; + + virtual void getSystemConfigByKey(std::string_view _groupID, std::string_view _nodeName, + std::string_view _keyValue, RespFunc _respFunc) = 0; + + virtual void getTotalTransactionCount( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) = 0; + + virtual void getGroupPeers(std::string_view _groupID, RespFunc _respFunc) = 0; + virtual void getPeers(RespFunc _respFunc) = 0; + // get all the groupID list + virtual void getGroupList(RespFunc _respFunc) = 0; + // get the group information of the given group + virtual void getGroupInfo(std::string_view _groupID, RespFunc _respFunc) = 0; + // get all the group info list + virtual void getGroupInfoList(RespFunc _respFunc) = 0; + // get the information of a given node + virtual void getGroupNodeInfo( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) = 0; + + virtual void getGroupBlockNumber(RespFunc _respFunc) = 0; + +public: + void onRPCRequest(std::string_view _requestBody, Sender _sender); + +private: + void initMethod(); + + std::unordered_map> m_methodToFunc; + + static void parseRpcRequestJson(std::string_view _requestBody, JsonRequest& _jsonRequest); + static bcos::bytes toStringResponse(JsonResponse _jsonResponse); + static Json::Value toJsonResponse(JsonResponse _jsonResponse); + + std::string_view toView(const Json::Value& value) + { + const char* begin = nullptr; + const char* end = nullptr; + bool result = value.getString(&begin, &end); + if (!result) + { + return std::string_view(); + } + std::string_view view(begin, end - begin); + return view; + } + + void callI(const Json::Value& req, RespFunc _respFunc) + { + call(toView(req[0u]), toView(req[1u]), toView(req[2u]), toView(req[3u]), + std::move(_respFunc)); + } + + void sendTransactionI(const Json::Value& req, RespFunc _respFunc) + { + sendTransaction(toView(req[0u]), toView(req[1u]), toView(req[2u]), req[3u].asBool(), + std::move(_respFunc)); + } + + void getTransactionI(const Json::Value& req, RespFunc _respFunc) + { + getTransaction(toView(req[0u]), toView(req[1u]), toView(req[2u]), req[3u].asBool(), + std::move(_respFunc)); + } + + void getTransactionReceiptI(const Json::Value& req, RespFunc _respFunc) + { + getTransactionReceipt(toView(req[0u]), toView(req[1u]), toView(req[2u]), req[3u].asBool(), + std::move(_respFunc)); + } + + void getBlockByHashI(const Json::Value& req, RespFunc _respFunc) + { + getBlockByHash(toView(req[0u]), toView(req[1u]), toView(req[2u]), + (req.size() > 3 ? req[3u].asBool() : true), (req.size() > 4 ? req[4u].asBool() : true), + std::move(_respFunc)); + } + + void getBlockByNumberI(const Json::Value& req, RespFunc _respFunc) + { + getBlockByNumber(toView(req[0u]), toView(req[1u]), req[2u].asInt64(), + (req.size() > 3 ? req[3u].asBool() : true), (req.size() > 4 ? req[4u].asBool() : true), + std::move(_respFunc)); + } + + void getBlockHashByNumberI(const Json::Value& req, RespFunc _respFunc) + { + getBlockHashByNumber( + toView(req[0u]), toView(req[1u]), req[2u].asInt64(), std::move(_respFunc)); + } + + void getBlockNumberI(const Json::Value& req, RespFunc _respFunc) + { + getBlockNumber(toView(req[0u]), toView(req[1u]), std::move(_respFunc)); + } + + void getCodeI(const Json::Value& req, RespFunc _respFunc) + { + getCode(toView(req[0u]), toView(req[1u]), toView(req[2u]), std::move(_respFunc)); + } + + void getABII(const Json::Value& req, RespFunc _respFunc) + { + getABI(toView(req[0u]), toView(req[1u]), toView(req[2u]), std::move(_respFunc)); + } + + void getSealerListI(const Json::Value& req, RespFunc _respFunc) + { + getSealerList(toView(req[0u]), toView(req[1u]), std::move(_respFunc)); + } + + void getObserverListI(const Json::Value& req, RespFunc _respFunc) + { + getObserverList(toView(req[0u]), toView(req[1u]), std::move(_respFunc)); + } + + void getPbftViewI(const Json::Value& req, RespFunc _respFunc) + { + getPbftView(toView(req[0u]), toView(req[1u]), std::move(_respFunc)); + } + + void getPendingTxSizeI(const Json::Value& req, RespFunc _respFunc) + { + getPendingTxSize(toView(req[0u]), toView(req[1u]), std::move(_respFunc)); + } + + void getSyncStatusI(const Json::Value& req, RespFunc _respFunc) + { + getSyncStatus(toView(req[0u]), toView(req[1u]), std::move(_respFunc)); + } + + void getConsensusStatusI(const Json::Value& _req, RespFunc _respFunc) + { + getConsensusStatus(toView(_req[0u]), toView(_req[1u]), std::move(_respFunc)); + } + + void getSystemConfigByKeyI(const Json::Value& req, RespFunc _respFunc) + { + getSystemConfigByKey( + toView(req[0u]), toView(req[1u]), toView(req[2u]), std::move(_respFunc)); + } + + void getTotalTransactionCountI(const Json::Value& req, RespFunc _respFunc) + { + getTotalTransactionCount(toView(req[0u]), toView(req[1u]), std::move(_respFunc)); + } + + void getPeersI(const Json::Value& req, RespFunc _respFunc) + { + boost::ignore_unused(req); + getPeers(std::move(_respFunc)); + } + + void getGroupPeersI(const Json::Value& req, RespFunc _respFunc) + { + getGroupPeers(toView(req[0u]), std::move(_respFunc)); + } + + // get all the groupID list + void getGroupListI(const Json::Value& _req, RespFunc _respFunc) + { + (void)_req; + getGroupList(std::move(_respFunc)); + } + // get the group information of the given group + void getGroupInfoI(const Json::Value& _req, RespFunc _respFunc) + { + (void)_req; + getGroupInfo(toView(_req[0u]), std::move(_respFunc)); + } + // get the group information of the given group + void getGroupInfoListI(const Json::Value& _req, RespFunc _respFunc) + { + (void)_req; + getGroupInfoList(std::move(_respFunc)); + } + // get the information of a given node + void getGroupNodeInfoI(const Json::Value& _req, RespFunc _respFunc) + { + getGroupNodeInfo(toView(_req[0u]), toView(_req[1u]), std::move(_respFunc)); + } +}; + +} // namespace bcos::rpc diff --git "a/BFPL\345\243\271/bcos-rpc/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-rpc/test/CMakeLists.txt" new file mode 100644 index 00000000..968dbc83 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/test/CMakeLists.txt" @@ -0,0 +1,30 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of bcos-rpc +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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(GLOB_RECURSE SOURCES "unittests/*.cpp" "unittests/*.h" "unittests/*.sol") + +# cmake settings +set(TEST_BINARY_NAME test-bcos-rpc) + +find_package(Boost REQUIRED unit_test_framework) + +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} 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 diff --git "a/BFPL\345\243\271/bcos-rpc/test/unittests/main/main.cpp" "b/BFPL\345\243\271/bcos-rpc/test/unittests/main/main.cpp" new file mode 100644 index 00000000..0e27edc0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-rpc/test/unittests/main/main.cpp" @@ -0,0 +1,3 @@ +#define BOOST_TEST_MAIN + +#include \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/CMakeLists.txt" "b/BFPL\345\243\271/bcos-scheduler/CMakeLists.txt" new file mode 100644 index 00000000..8307d8f1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/CMakeLists.txt" @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.14) + +include(Version) +project(bcos-scheduler VERSION ${VERSION}) + +aux_source_directory(src SRCS) + +add_library(${SCHEDULER_TARGET} ${SRCS}) +target_link_libraries(${SCHEDULER_TARGET} PUBLIC ${TABLE_TARGET} ${TARS_PROTOCOL_TARGET}) + +if(TESTS) + enable_testing() + add_subdirectory(test) +endif() + +if (COVERAGE) + include(Coverage) + config_coverage("scheduler-coverage" "'/usr*' '${CMAKE_CURRENT_SOURCE_DIR}/bcos-cmake-scripts*' '${CMAKE_CURRENT_SOURCE_DIR}/test/bcos-test*'") +endif () \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/src/BlockExecutive.cpp" "b/BFPL\345\243\271/bcos-scheduler/src/BlockExecutive.cpp" new file mode 100644 index 00000000..e0a7d016 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/BlockExecutive.cpp" @@ -0,0 +1,1669 @@ +#include "BlockExecutive.h" +#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/protocol/Transaction.h" +#include "bcos-table/src/StateStorage.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos::scheduler; +using namespace bcos::ledger; + +BlockExecutive::BlockExecutive(bcos::protocol::Block::Ptr block, SchedulerImpl* scheduler, + size_t startContextID, + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory, + bool staticCall, bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::txpool::TxPoolInterface::Ptr _txPool) + : m_dmcRecorder(std::make_shared()), + m_block(std::move(block)), + m_scheduler(scheduler), + m_schedulerTermId(scheduler->getSchedulerTermId()), + m_startContextID(startContextID), + m_transactionSubmitResultFactory(std::move(transactionSubmitResultFactory)), + m_blockFactory(_blockFactory), + m_txPool(_txPool), + m_staticCall(staticCall) +{ + start(); +} + +void BlockExecutive::prepare() +{ + if (m_hasPrepared) + { + return; + } + WriteGuard lock(x_prepareLock); + if (m_hasPrepared) + { + return; + } + + auto startT = utcTime(); + + if (m_block->transactionsMetaDataSize() > 0) + { + buildExecutivesFromMetaData(); + } + else if (m_block->transactionsSize() > 0) + { + buildExecutivesFromNormalTransaction(); + } + else + { + SCHEDULER_LOG(DEBUG) << BLOCK_NUMBER(number()) << "BlockExecutive prepare: empty block"; + } + + // prepare all executors + if (!m_hasDAG) + { + // prepare DMC executor + serialPrepareExecutor(); + } + + m_hasPrepared = true; + + SCHEDULER_LOG(INFO) << METRIC << LOG_BADGE("prepareBlockExecutive") << BLOCK_NUMBER(number()) + << LOG_KV("blockHeader.timestamp", m_block->blockHeaderConst()->timestamp()) + << LOG_KV("meta tx count", m_block->transactionsMetaDataSize()) + << LOG_KV("timeCost", (utcTime() - startT)); +} + +bcos::protocol::ExecutionMessage::UniquePtr BlockExecutive::buildMessage( + ContextID contextID, bcos::protocol::Transaction::ConstPtr tx) +{ + auto message = m_scheduler->m_executionMessageFactory->createExecutionMessage(); + message->setType(protocol::ExecutionMessage::MESSAGE); + message->setContextID(contextID); + message->setTransactionHash(tx->hash()); + message->setOrigin(toHex(tx->sender())); + message->setFrom(std::string(message->origin())); + + if (!m_isSysBlock) + { + auto toAddress = tx->to(); + if (bcos::precompiled::c_systemTxsAddress.count( + std::string(toAddress.begin(), toAddress.end()))) + { + m_isSysBlock.store(true); + } + } + + if (tx->attribute() & bcos::protocol::Transaction::Attribute::LIQUID_SCALE_CODEC) + { + // LIQUID + if (tx->attribute() & bcos::protocol::Transaction::Attribute::LIQUID_CREATE) + { + message->setCreate(true); + } + message->setTo(std::string(tx->to())); + } + else + { + // SOLIDITY + if (tx->to().empty()) + { + message->setCreate(true); + } + else + { + if (m_scheduler->m_isAuthCheck && !m_staticCall && + isSysContractDeploy(m_block->blockHeaderConst()->number()) && + tx->to() == precompiled::AUTH_COMMITTEE_ADDRESS) + { + // if enable auth check, and first deploy auth contract + message->setCreate(true); + } + message->setTo(preprocessAddress(tx->to())); + } + } + message->setDepth(0); + message->setGasAvailable(m_gasLimit); + if (precompiled::c_systemTxsAddress.count({tx->to().data(), tx->to().size()})) + { + message->setGasAvailable(TRANSACTION_GAS); + } + message->setData(tx->input().toBytes()); + message->setStaticCall(m_staticCall); + + if (message->create()) + { + message->setABI(std::string(tx->abi())); + } + + return message; +} + +void BlockExecutive::buildExecutivesFromMetaData() +{ + SCHEDULER_LOG(DEBUG) << BLOCK_NUMBER(number()) + << "BlockExecutive prepare: buildExecutivesFromMetaData" + << LOG_KV("tx meta count", m_block->transactionsMetaDataSize()); + + m_blockTxs = fetchBlockTxsFromTxPool(m_block, m_txPool); // no need to async + m_executiveResults.resize(m_block->transactionsMetaDataSize()); + std::vector> results( + m_block->transactionsMetaDataSize()); + + if (m_blockTxs) + { + tbb::parallel_for(tbb::blocked_range(0U, m_block->transactionsMetaDataSize()), + [&](auto const& range) { + for (auto i = range.begin(); i < range.end(); ++i) + { + auto metaData = m_block->transactionMetaData(i); + if (metaData) + { + m_executiveResults[i].transactionHash = metaData->hash(); + m_executiveResults[i].source = metaData->source(); + } + auto contextID = i + m_startContextID; + + auto& [toAddress, message, enableDAG] = results[i]; + message = buildMessage(contextID, (*m_blockTxs)[i]); + toAddress = {message->to().data(), message->to().size()}; + enableDAG = metaData->attribute() & bcos::protocol::Transaction::Attribute::DAG; + } + }); + } + else + { + tbb::parallel_for(tbb::blocked_range(0U, m_block->transactionsMetaDataSize()), + [&](auto const& range) { + for (auto i = range.begin(); i < range.end(); ++i) + { + auto metaData = m_block->transactionMetaData(i); + if (metaData) + { + m_executiveResults[i].transactionHash = metaData->hash(); + m_executiveResults[i].source = metaData->source(); + } + + auto contextID = i + m_startContextID; + + auto& [to, message, enableDAG] = results[i]; + message = m_scheduler->m_executionMessageFactory->createExecutionMessage(); + message->setContextID(contextID); + message->setType(protocol::ExecutionMessage::TXHASH); + // Note: set here for fetching txs when send_back + message->setTransactionHash(metaData->hash()); + + if (metaData->attribute() & + bcos::protocol::Transaction::Attribute::LIQUID_SCALE_CODEC) + { + // LIQUID + if (metaData->attribute() & + bcos::protocol::Transaction::Attribute::LIQUID_CREATE) + { + message->setCreate(true); + } + message->setTo(std::string(metaData->to())); + } + else + { + // SOLIDITY + if (metaData->to().empty()) + { + message->setCreate(true); + } + else + { + message->setTo(preprocessAddress(metaData->to())); + } + } + + message->setDepth(0); + message->setGasAvailable(m_gasLimit); + if (precompiled::c_systemTxsAddress.count( + {metaData->to().data(), metaData->to().size()})) + { + message->setGasAvailable(TRANSACTION_GAS); + } + message->setStaticCall(false); + enableDAG = metaData->attribute() & bcos::protocol::Transaction::Attribute::DAG; + to = {message->to().data(), message->to().size()}; + } + }); + } + + for (auto& it : results) + { + auto& [to, message, enableDAG] = it; + if (message) + { + m_hasDAG = m_hasDAG || enableDAG; + saveMessage(std::move(to), std::move(message), enableDAG); + } + } +} + +void BlockExecutive::buildExecutivesFromNormalTransaction() +{ + SCHEDULER_LOG(INFO) << BLOCK_NUMBER(number()) + << "BlockExecutive prepare: buildExecutivesFromNormalTransaction" + << LOG_KV("block number", m_block->blockHeaderConst()->number()) + << LOG_KV("tx count", m_block->transactionsSize()); + + m_executiveResults.resize(m_block->transactionsSize()); + std::vector> results( + m_block->transactionsSize()); + + tbb::parallel_for( + tbb::blocked_range(0U, m_block->transactionsSize()), [&](auto const& range) { + for (auto i = range.begin(); i < range.end(); ++i) + { + auto tx = m_block->transaction(i); + m_executiveResults[i].transactionHash = tx->hash(); + m_executiveResults[i].source = tx->source(); + + auto contextID = i + m_startContextID; + auto& [to, message, enableDAG] = results[i]; + message = buildMessage(contextID, tx); + to = {message->to().data(), message->to().size()}; + enableDAG = tx->attribute() & bcos::protocol::Transaction::Attribute::DAG; + } + }); + + for (auto& it : results) + { + auto& [to, message, enableDAG] = it; + if (message) + { + m_hasDAG = m_hasDAG || enableDAG; + saveMessage(std::move(to), std::move(message), enableDAG); + } + } +} + +bcos::protocol::TransactionsPtr BlockExecutive::fetchBlockTxsFromTxPool( + bcos::protocol::Block::Ptr block, bcos::txpool::TxPoolInterface::Ptr txPool) +{ + SCHEDULER_LOG(INFO) << BLOCK_NUMBER(number()) << "BlockExecutive prepare: fillBlock start" + << LOG_KV("txNum", block->transactionsMetaDataSize()); + bcos::protocol::TransactionsPtr txs = nullptr; + auto lastT = utcTime(); + if (txPool != nullptr) + { + // Get tx hash list + auto txHashes = std::make_shared(); + for (size_t i = 0; i < block->transactionsMetaDataSize(); ++i) + { + txHashes->emplace_back(block->transactionMetaData(i)->hash()); + } + if (c_fileLogLevel <= TRACE) [[unlikely]] + { + for (auto const& tx : *txHashes) + { + SCHEDULER_LOG(TRACE) << "fetch: " << tx.abridged(); + } + } + std::shared_ptr> txsPromise = + std::make_shared>(); + txPool->asyncFillBlock( + txHashes, [txsPromise](Error::Ptr error, bcos::protocol::TransactionsPtr txs) { + if (!txsPromise) + { + return; + } + + if (error) + { + txsPromise->set_value(nullptr); + } + else + { + txsPromise->set_value(txs); + } + }); + auto future = txsPromise->get_future(); + if (future.wait_for(std::chrono::milliseconds(10 * 1000)) != std::future_status::ready) + { + // 10s timeout + SCHEDULER_LOG(ERROR) << BLOCK_NUMBER(number()) + << "BlockExecutive prepare: fillBlock timeout/error" + << LOG_KV("txNum", block->transactionsMetaDataSize()) + << LOG_KV("cost", utcTime() - lastT) + << LOG_KV("fetchNum", txs ? txs->size() : 0); + return nullptr; + } + txs = future.get(); + txsPromise = nullptr; + } + SCHEDULER_LOG(INFO) << BLOCK_NUMBER(number()) << "BlockExecutive prepare: fillBlock end" + << LOG_KV("txNum", block->transactionsMetaDataSize()) + << LOG_KV("cost", utcTime() - lastT) + << LOG_KV("fetchNum", txs ? txs->size() : 0); + return txs; +} + +void BlockExecutive::asyncCall( + std::function callback) +{ + asyncExecute([executive = shared_from_this(), callback]( + Error::UniquePtr&& _error, protocol::BlockHeader::Ptr, bool) { + // auto executive = self.lock(); + if (!executive) + { + callback( + BCOS_ERROR_UNIQUE_PTR(SchedulerError::UnknownError, "get block executive failed"), + nullptr); + return; + } + auto receipt = + std::const_pointer_cast(executive->block()->receipt(0)); + callback(std::move(_error), std::move(receipt)); + }); +} + +void BlockExecutive::asyncExecute( + std::function callback) +{ + if (m_result) + { + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::InvalidStatus, "Invalid status"), nullptr, + m_isSysBlock); + return; + } + + if (m_scheduler->executorManager()->size() == 0) + { + callback(BCOS_ERROR_UNIQUE_PTR( + SchedulerError::ExecutorNotEstablishedError, "The executor has not started!"), + nullptr, m_isSysBlock); + } + m_currentTimePoint = std::chrono::system_clock::now(); + + auto startT = utcTime(); + prepare(); + + auto createMsgT = utcTime() - startT; + startT = utcTime(); + if (!m_staticCall) + { + // Execute nextBlock + bool hasDAG = m_hasDAG; + batchNextBlock([this, hasDAG, startT, createMsgT, callback = std::move(callback)]( + Error::UniquePtr error) { + if (!m_isRunning) + { + callback( + BCOS_ERROR_UNIQUE_PTR(SchedulerError::Stopped, "BlockExecutive is stopped"), + nullptr, m_isSysBlock); + return; + } + + if (error) + { + SCHEDULER_LOG(ERROR) + << BLOCK_NUMBER(number()) << "Next block with error!" << error->errorMessage(); + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR( + SchedulerError::NextBlockError, "Next block error!", *error), + nullptr, m_isSysBlock); + return; + } + + if (hasDAG) + { + DAGExecute([this, startT, createMsgT, callback = std::move(callback)]( + Error::UniquePtr error) { + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR( + SchedulerError::Stopped, "BlockExecutive is stopped"), + nullptr, m_isSysBlock); + return; + } + + if (error) + { + SCHEDULER_LOG(ERROR) + << BLOCK_NUMBER(number()) << "DAG execute block with error!" + << error->errorMessage(); + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR( + SchedulerError::DAGError, "DAG execute error!", *error), + nullptr, m_isSysBlock); + return; + } + auto blockHeader = m_block->blockHeader(); + SCHEDULER_LOG(INFO) << BLOCK_NUMBER(number()) << LOG_DESC("DAGExecute success") + << LOG_KV("createMsgT", createMsgT) + << LOG_KV("dagExecuteT", (utcTime() - startT)) + << LOG_KV("hash", blockHeader->hash().abridged()); + + SCHEDULER_LOG(INFO) << BLOCK_NUMBER(number()) << LOG_BADGE("BlockTrace") + << LOG_DESC("DMCExecute begin after DAGExecute"); + DMCExecute(std::move(callback)); + }); + } + else + { + SCHEDULER_LOG(INFO) << BLOCK_NUMBER(number()) << LOG_BADGE("BlockTrace") + << LOG_DESC("DMCExecute begin without DAGExecute"); + DMCExecute(std::move(callback)); + } + }); + } + else + { + SCHEDULER_LOG(TRACE) << BLOCK_NUMBER(number()) << LOG_BADGE("BlockTrace") + << LOG_DESC("DMCExecute begin for call"); + DMCExecute(std::move(callback)); + } +} + +void BlockExecutive::asyncCommit(std::function callback) +{ + auto stateStorage = std::make_shared(m_scheduler->m_storage); + + m_currentTimePoint = std::chrono::system_clock::now(); + SCHEDULER_LOG(INFO) << BLOCK_NUMBER(number()) << LOG_DESC("BlockExecutive commit block"); + + m_scheduler->m_ledger->asyncPrewriteBlock(stateStorage, m_blockTxs, m_block, + [this, stateStorage, callback = std::move(callback)](Error::Ptr&& error) mutable { + if (error) + { + SCHEDULER_LOG(ERROR) << "Prewrite block error!" << error->errorMessage(); + + if (error->errorCode() == bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + { + triggerSwitch(); + } + + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(SchedulerError::PrewriteBlockError, + "Prewrite block error: " + error->errorMessage(), *error)); + + return; + } + + auto status = std::make_shared(); + status->total = 1 + m_scheduler->m_executorManager->size(); // self + all executors + 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) + << BLOCK_NUMBER(number()) << "Rollback storage failed!" + << LOG_KV("number", number()) << " " << error->errorMessage(); + // FATAL ERROR, NEED MANUAL FIX! + + callback(std::move(error)); + return; + } + else + { + callback(BCOS_ERROR_UNIQUE_PTR( + SchedulerError::CommitError, errorMessage)); + return; + } + }); + + return; + } + + SCHEDULER_LOG(INFO) << BLOCK_NUMBER(number()) << "batchCommitBlock begin"; + batchBlockCommit(status.startTS, [this, callback](Error::UniquePtr&& error) { + if (error) + { + SCHEDULER_LOG(ERROR) + << BLOCK_NUMBER(number()) << "Commit block to storage failed!" + << error->errorMessage(); + + if (error->errorCode() == + bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + { + triggerSwitch(); + } + + // FATAL ERROR, NEED MANUAL FIX! + callback(std::move(error)); + + return; + } + + m_commitElapsed = std::chrono::duration_cast( + std::chrono::system_clock::now() - m_currentTimePoint); + SCHEDULER_LOG(INFO) << 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 = ""; + m_scheduler->m_storage->asyncPrepare(params, *stateStorage, + [status, this, callback]( + Error::Ptr&& error, uint64_t startTimeStamp, const std::string& primaryKey) { + if (error) + { + ++status->failed; + SCHEDULER_LOG(ERROR) + << BLOCK_NUMBER(number()) + << "scheduler asyncPrepare storage error: " << error->errorMessage(); + callback(BCOS_ERROR_UNIQUE_PTR(error->errorCode(), + "asyncPrepare block error: " + error->errorMessage())); + return; + } + ++status->success; + + SCHEDULER_LOG(INFO) + << BLOCK_NUMBER(number()) + << "primary prepare finished, call executor prepare" + << LOG_KV("startTimeStamp", startTimeStamp) + << LOG_KV("executors", m_scheduler->m_executorManager->size()) + << 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) + { + ++status->failed; + SCHEDULER_LOG(ERROR) + << BLOCK_NUMBER(number()) + << "asyncPrepare executor failed: " << 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; + } + } + + status->checkAndCommit(*status); + }); + } + }); + }); +} + +void BlockExecutive::asyncNotify( + std::function)>& notifier, + std::function _callback) +{ + if (!notifier) + { + return; + } + auto results = std::make_shared(); + auto blockHeader = m_block->blockHeaderConst(); + auto blockHash = blockHeader->hash(); + size_t index = 0; + for (auto& it : m_executiveResults) + { + auto submitResult = m_transactionSubmitResultFactory->createTxSubmitResult(); + submitResult->setTransactionIndex(index); + submitResult->setBlockHash(blockHash); + submitResult->setTxHash(it.transactionHash); + submitResult->setStatus(it.receipt->status()); + submitResult->setTransactionReceipt(it.receipt); + if (m_syncBlock) + { + auto tx = m_block->transaction(index); + submitResult->setNonce(tx->nonce()); + } + index++; + results->emplace_back(submitResult); + } + auto txsSize = m_executiveResults.size(); + notifier(blockHeader->number(), results, [_callback, blockHeader, txsSize](Error::Ptr _error) { + if (_callback) + { + _callback(_error); + } + if (_error == nullptr) + { + SCHEDULER_LOG(INFO) << BLOCK_NUMBER(blockHeader->number()) + << LOG_DESC("notify block result success") + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("txsSize", txsSize); + return; + } + SCHEDULER_LOG(INFO) << BLOCK_NUMBER(blockHeader->number()) + << LOG_DESC("notify block result failed") + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + }); +} + +void BlockExecutive::saveMessage( + std::string address, protocol::ExecutionMessage::UniquePtr message, bool withDAG) +{ + registerAndGetDmcExecutor(address)->submit(std::move(message), withDAG); +} + +void BlockExecutive::DAGExecute(std::function callback) +{ + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::Stopped, "BlockExecutive is stopped")); + return; + } + + // dump executive states from DmcExecutor + for (auto it : m_dmcExecutors) + { + auto& address = it.first; + auto dmcExecutor = it.second; + dmcExecutor->forEachExecutive( + [this, &address](ContextID contextID, ExecutiveState::Ptr executiveState) { + m_executiveStates.emplace(std::make_tuple(address, contextID), executiveState); + }); + } + + // map< string => => ExecutiveState::Ptr>::it > + std::multimap requests; + + // filter enableDAG request to map + for (auto it = m_executiveStates.begin(); it != m_executiveStates.end(); ++it) + { + if (it->second->enableDAG) + { + requests.emplace(std::get<0>(it->first), it); + } + } + + if (requests.empty()) + { + callback(nullptr); + return; + } + + auto totalCount = std::make_shared(requests.size()); + auto failed = std::make_shared(0); + auto callbackPtr = std::make_shared(std::move(callback)); + SCHEDULER_LOG(INFO) << LOG_BADGE("DAG") << LOG_BADGE("Stat") << BLOCK_NUMBER(number()) + << LOG_BADGE("BlockTrace") << "DAGExecute.0:\t>>> Start send to executor"; + + // string => => ExecutiveState::Ptr>::it + for (auto it = requests.begin(); it != requests.end(); it = requests.upper_bound(it->first)) + { + auto contractAddress = it->first; + auto startT = utcTime(); + + auto executor = m_scheduler->m_executorManager->dispatchExecutor(contractAddress); + auto count = requests.count(it->first); + // get all same address request, + auto range = requests.equal_range(it->first); + + auto messages = std::make_shared>(count); + auto iterators = std::vector(count); + size_t i = 0; + // traverse range, messageIt: => + // ExecutiveState::Ptr>::it>::it + for (auto messageIt = range.first; messageIt != range.second; ++messageIt) + { + SCHEDULER_LOG(TRACE) << "DAG message: " << messageIt->second->second->message.get() + << " to: " << messageIt->first; + messageIt->second->second->callStack.push(messageIt->second->second->currentSeq++); + messages->at(i) = std::move(messageIt->second->second->message); + iterators[i] = messageIt->second; + + ++i; + } + SCHEDULER_LOG(INFO) << LOG_BADGE("DAG") << LOG_BADGE("Stat") << BLOCK_NUMBER(number()) + << "DAGExecute.1:\t--> Send to executor\t" + << LOG_KV("contract", contractAddress) + << LOG_KV("txNum", messages->size()); + auto prepareT = utcTime() - startT; + startT = utcTime(); + executor->dagExecuteTransactions(*messages, + [this, contractAddress, messages, startT, prepareT, iterators = std::move(iterators), + totalCount, failed, callbackPtr](bcos::Error::UniquePtr error, + std::vector responseMessages) { + SCHEDULER_LOG(INFO) + << LOG_BADGE("DAG") << LOG_BADGE("Stat") << BLOCK_NUMBER(number()) + << "DAGExecute.2:\t<-- Receive from executor\t" + << LOG_KV("contract", contractAddress) << LOG_KV("txNum", messages->size()) + << LOG_KV("costT", utcTime() - startT) << LOG_KV("failed", *failed) + << LOG_KV("totalCount", *totalCount) << LOG_KV("blockNumber", number()); + if (error) + { + ++(*failed); + SCHEDULER_LOG(ERROR) + << BLOCK_NUMBER(number()) << "DAG execute error: " << error->errorMessage() + << LOG_KV("failed", *failed) << LOG_KV("totalCount", *totalCount) + << LOG_KV("blockNumber", number()); + if (error->errorCode() == bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + { + triggerSwitch(); + } + } + else if (messages->size() != responseMessages.size()) + { + ++(*failed); + SCHEDULER_LOG(ERROR) << BLOCK_NUMBER(number()) + << "DAG messages size and response size mismatch!"; + } + else + { + tbb::parallel_for(tbb::blocked_range(0U, responseMessages.size()), + [&responseMessages, &iterators](auto const& range) { + for (auto j = range.begin(); j < range.end(); ++j) + { + assert(responseMessages[j]); + iterators[j]->second->message = std::move(responseMessages[j]); + } + }); + } + + if (totalCount->fetch_sub(messages->size()) == messages->size()) + { + // only one thread can get in this field + SCHEDULER_LOG(DEBUG) + << LOG_BADGE("DAG") << LOG_BADGE("Stat") << BLOCK_NUMBER(number()) + << "DAGExecute.3:\t<<< Joint all contract result\t" + << LOG_KV("costT", utcTime() - startT) << LOG_KV("failed", *failed) + << LOG_KV("totalCount", *totalCount) << LOG_KV("blockNumber", number()); + + if (*failed > 0) + { + (*callbackPtr)(BCOS_ERROR_UNIQUE_PTR( + SchedulerError::DAGError, "Execute dag with errors")); + return; + } + + SCHEDULER_LOG(INFO) + << BLOCK_NUMBER(number()) << LOG_BADGE("BlockTrace") + << LOG_DESC("DAGExecute finish") << LOG_KV("prepareT", prepareT) + << LOG_KV("execT", (utcTime() - startT)); + (*callbackPtr)(nullptr); + } + }); + } +} + +void BlockExecutive::DMCExecute( + std::function callback) +{ + try + { + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::Stopped, "BlockExecutive is stopped"), + nullptr, m_isSysBlock); + return; + } + + // update DMC recorder for debugging + m_dmcRecorder->nextDmcRound(); + + auto lastT = utcTime(); + DMC_LOG(INFO) << LOG_BADGE("Stat") << BLOCK_NUMBER(number()) + << "DMCExecute.0:\t [+] Start\t\t\t" + << LOG_KV("round", m_dmcRecorder->getRound()) + << LOG_KV("checksum", m_dmcRecorder->getChecksum()); + + // prepare all dmcExecutor + serialPrepareExecutor(); + DMC_LOG(INFO) << LOG_BADGE("Stat") << BLOCK_NUMBER(number()) + << "DMCExecute.1:\t [-] PrepareExecutor finish\t" + << LOG_KV("round", m_dmcRecorder->getRound()) + << LOG_KV("checksum", m_dmcRecorder->getChecksum()) + << LOG_KV("cost", utcTime() - lastT); + lastT = utcTime(); + + // dump address for omp parallelization + std::vector contractAddress; + contractAddress.reserve(m_dmcExecutors.size()); + for (auto it = m_dmcExecutors.begin(); it != m_dmcExecutors.end(); it++) + { + contractAddress.push_back(it->first); + } + auto batchStatus = std::make_shared(); + batchStatus->total = contractAddress.size(); + + // if is empty block, just return + if (contractAddress.size() == 0) + { + onDmcExecuteFinish(std::move(callback)); + return; + } + + auto executorCallback = [this, lastT, batchStatus = std::move(batchStatus), + callback = std::move(callback)]( + bcos::Error::UniquePtr error, DmcExecutor::Status status) { + if (error || status == DmcExecutor::Status::ERROR) + { + batchStatus->error++; + batchStatus->errorMessage = error.get()->errorMessage(); + SCHEDULER_LOG(ERROR) << BLOCK_NUMBER(number()) << LOG_BADGE("DmcExecutor") + << "dmcExecutor->go() with error" + << LOG_KV("code", error ? error->errorCode() : -1) + << LOG_KV("msg", error ? error.get()->errorMessage() : "null"); + } + else if (status == DmcExecutor::Status::PAUSED || + status == DmcExecutor::Status::NEED_PREPARE) + { + batchStatus->paused++; + } + else if (status == DmcExecutor::Status::FINISHED) + { + batchStatus->finished++; + } + + // check batch + if ((batchStatus->error + batchStatus->paused + batchStatus->finished) != + batchStatus->total) + { + return; + } + + // block many threads + if (batchStatus->callbackExecuted) + { + return; + } + { + WriteGuard lock(batchStatus->x_lock); + if (batchStatus->callbackExecuted) + { + return; + } + batchStatus->callbackExecuted = true; + } + + if (!m_isRunning) + { + callback( + BCOS_ERROR_UNIQUE_PTR(SchedulerError::Stopped, "BlockExecutive is stopped"), + nullptr, m_isSysBlock); + return; + } + + // handle batch result(only one thread can get in here) + DMC_LOG(INFO) << LOG_BADGE("Stat") << BLOCK_NUMBER(number()) + << "DMCExecute.5:\t <<< Join all executor result\t" + << LOG_KV("round", m_dmcRecorder->getRound()) + << LOG_KV("checksum", m_dmcRecorder->getChecksum()) + << LOG_KV("sendChecksum", m_dmcRecorder->getSendChecksum()) + << LOG_KV("receiveChecksum", m_dmcRecorder->getReceiveChecksum()) + << LOG_KV("cost(after prepare finish)", utcTime() - lastT); + + if (batchStatus->error != 0) + { + DMC_LOG(ERROR) << BLOCK_NUMBER(number()) + << "DMCExecute with errors: " << batchStatus->errorMessage; + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::DMCError, batchStatus->errorMessage), + nullptr, m_isSysBlock); + } + else if (batchStatus->paused != 0) // new contract + { + // Start next DMC round + DMCExecute(std::move(callback)); + } + else if (batchStatus->finished == batchStatus->total) + { + onDmcExecuteFinish(std::move(callback)); + } + else + { + // assume never goes here + SCHEDULER_LOG(FATAL) << "Invalid type"; + assert(false); + } + }; + + DMC_LOG(INFO) << LOG_BADGE("Stat") << BLOCK_NUMBER(number()) + << "DMCExecute.2:\t >>> Start send to executors\t" + << LOG_KV("round", m_dmcRecorder->getRound()) + << LOG_KV("checksum", m_dmcRecorder->getChecksum()) + << LOG_KV("cost", utcTime() - lastT) + << LOG_KV("contractNum", contractAddress.size()); + + // for each dmcExecutor + // Use isolate task_arena to avoid error + tbb::this_task_arena::isolate([this, &contractAddress, &executorCallback] { + tbb::parallel_for(tbb::blocked_range(0U, contractAddress.size()), + [this, &contractAddress, &executorCallback](auto const& range) { + for (auto i = range.begin(); i < range.end(); ++i) + { + auto dmcExecutor = m_dmcExecutors[contractAddress[i]]; + dmcExecutor->go(executorCallback); + } + }); + }); + } + catch (bcos::Error& e) + { + DMC_LOG(WARNING) << "DMCExecute exception: " << LOG_KV("code", e.errorCode()) + << LOG_KV("message", e.errorMessage()); + callback( + std::make_unique(e.errorCode(), e.errorMessage()), nullptr, m_isSysBlock); + } + catch (...) + { + DMC_LOG(WARNING) << "DMCExecute exception. "; + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::UnknownError, "DMCExecute exception"), + nullptr, m_isSysBlock); + } +} + +void BlockExecutive::onDmcExecuteFinish( + std::function callback) +{ + 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); + } + else + { + DMC_LOG(INFO) << LOG_BADGE("Stat") << "DMCExecute.6:" + << "\t " << LOG_BADGE("DMCRecorder") + << " DMCExecute for transaction finished " << LOG_KV("blockNumber", number()) + << LOG_KV("checksum", dmcChecksum); + + DMC_LOG(INFO) << BLOCK_NUMBER(number()) << LOG_BADGE("BlockTrace") + << LOG_BADGE("DMCRecorder") << " DMCExecute for transaction finished " + << LOG_KV("checksum", dmcChecksum); + } + + onExecuteFinish(std::move(callback)); +} + +void BlockExecutive::onExecuteFinish( + std::function callback) +{ + auto now = std::chrono::system_clock::now(); + m_executeElapsed = + std::chrono::duration_cast(now - m_currentTimePoint); + m_currentTimePoint = now; + + + if (m_staticCall) + { + // Set result to m_block + for (size_t i = 0; i < m_executiveResults.size(); i++) + { + if (i < m_block->receiptsSize()) + { + // bugfix: force update receipt of last executeBlock() remaining + m_block->setReceipt(i, m_executiveResults[i].receipt); + } + else + { + m_block->appendReceipt(m_executiveResults[i].receipt); + } + } + callback(nullptr, nullptr, m_isSysBlock); + } + else + { + // All Transaction finished, get hash + batchGetHashes([this, callback = std::move(callback)]( + Error::UniquePtr error, crypto::HashType hash) { + if (!m_isRunning) + { + callback( + BCOS_ERROR_UNIQUE_PTR(SchedulerError::Stopped, "BlockExecutive is stopped"), + nullptr, m_isSysBlock); + return; + } + + if (error) + { + SCHEDULER_LOG(ERROR) << "batchGetHashes error: " << error->errorMessage(); + callback(std::move(error), nullptr, m_isSysBlock); + return; + } + + m_hashElapsed = std::chrono::duration_cast( + std::chrono::system_clock::now() - m_currentTimePoint); + + // Set result to m_block + for (size_t i = 0; i < m_executiveResults.size(); i++) + { + if (i < m_block->receiptsSize()) + { + // bugfix: force update receipt of last executeBlock() remaining + m_block->setReceipt(i, m_executiveResults[i].receipt); + } + else + { + m_block->appendReceipt(m_executiveResults[i].receipt); + } + } + auto executedBlockHeader = + m_blockFactory->blockHeaderFactory()->populateBlockHeader(m_block->blockHeader()); + executedBlockHeader->setStateRoot(hash); + executedBlockHeader->setGasUsed(m_gasUsed); + executedBlockHeader->setTxsRoot(m_block->calculateTransactionRoot()); + executedBlockHeader->setReceiptsRoot(m_block->calculateReceiptRoot()); + + m_result = executedBlockHeader; + callback(nullptr, m_result, m_isSysBlock); + }); + } +} + +void BlockExecutive::batchNextBlock(std::function callback) +{ + auto startTime = utcTime(); + auto status = std::make_shared(); + status->total = m_scheduler->m_executorManager->size(); + status->checkAndCommit = [this, startTime, callback = std::move(callback)]( + const CommitStatus& status) { + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::Stopped, "BlockExecutive is stopped")); + return; + } + + if (status.failed > 0) + { + auto message = "Next block:" + boost::lexical_cast(number()) + + " with errors! " + boost::lexical_cast(status.failed); + SCHEDULER_LOG(ERROR) << BLOCK_NUMBER(number()) << message; + + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::BatchError, std::move(message))); + return; + } + + SCHEDULER_LOG(INFO) << BLOCK_NUMBER(number()) << LOG_BADGE("BlockTrace") + << "NextBlock success" + << LOG_KV("executorNum", m_scheduler->m_executorManager->size()) + << LOG_KV("timeCost", utcTime() - startTime); + callback(nullptr); + }; + SCHEDULER_LOG(INFO) << BLOCK_NUMBER(number()) << LOG_BADGE("BlockTrace") << "NextBlock request" + << LOG_KV("executorNum", m_scheduler->m_executorManager->size()); + + // for (auto& it : *(m_scheduler->m_executorManager)) + m_scheduler->m_executorManager->forEachExecutor( + [this, status]( + std::string, bcos::executor::ParallelTransactionExecutorInterface::Ptr executor) { + auto blockHeader = m_block->blockHeaderConst(); + executor->nextBlockHeader( + m_schedulerTermId, blockHeader, [this, status](bcos::Error::Ptr&& error) { + { + WriteGuard lock(status->x_lock); + if (error) + { + SCHEDULER_LOG(ERROR) + << BLOCK_NUMBER(number()) << "Next block executor error!" + << error->errorMessage(); + ++status->failed; + + if (error->errorCode() == + bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + { + triggerSwitch(); + } + } + else + { + ++status->success; + } + + if (status->success + status->failed < status->total) + { + return; + } + } + status->checkAndCommit(*status); + }); + }); +} + +void BlockExecutive::batchGetHashes( + std::function callback) +{ + auto totalHash = std::make_shared(); + + auto status = std::make_shared(); + status->total = m_scheduler->m_executorManager->size(); // all executors + status->checkAndCommit = [this, totalHash, callback = std::move(callback)]( + const CommitStatus& status) { + if (!m_isRunning) + { + callback( + BCOS_ERROR_UNIQUE_PTR(SchedulerError::Stopped, "BlockExecutive is stopped"), {}); + return; + } + + if (status.failed > 0) + { + auto message = "batchGetHashes" + boost::lexical_cast(number()) + + " with errors! " + boost::lexical_cast(status.failed); + SCHEDULER_LOG(WARNING) << BLOCK_NUMBER(number()) << message; + + callback( + BCOS_ERROR_UNIQUE_PTR(SchedulerError::BatchError, std::move(message)), h256(0)); + return; + } + + callback(nullptr, std::move(*totalHash)); + }; + + // for (auto& it : *(m_scheduler->m_executorManager)) + + m_scheduler->m_executorManager->forEachExecutor( + [this, status, totalHash]( + std::string, bcos::executor::ParallelTransactionExecutorInterface::Ptr executor) { + executor->getHash( + number(), [status, totalHash](bcos::Error::Ptr&& error, crypto::HashType&& hash) { + { + WriteGuard lock(status->x_lock); + if (error) + { + SCHEDULER_LOG(ERROR) << "GetHash error!" << error->errorMessage(); + ++status->failed; + } + else + { + ++status->success; + SCHEDULER_LOG(DEBUG) << "GetHash success, success: " << status->success; + + *totalHash ^= hash; + } + + if (status->success + status->failed < status->total) + { + return; + } + } + status->checkAndCommit(*status); + }); + }); +} + +void BlockExecutive::batchBlockCommit( + uint64_t rollbackVersion, std::function callback) +{ + auto status = std::make_shared(); + status->total = 1 + m_scheduler->m_executorManager->size(); // self + all executors + 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) + { + auto message = "Commit block:" + boost::lexical_cast(number()) + + " with errors! " + boost::lexical_cast(status.failed); + SCHEDULER_LOG(WARNING) << BLOCK_NUMBER(number()) << message; + + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::CommitError, std::move(message))); + return; + } + + callback(nullptr); + }; + + bcos::protocol::TwoPCParams params; + params.number = number(); + params.timestamp = 0; + m_scheduler->m_storage->asyncCommit( + params, [rollbackVersion, status, this, callback](Error::Ptr&& error, uint64_t commitTS) { + if (error) + { +//#define COMMIT_FAILED_NEED_ROLLBACK +#ifdef COMMIT_FAILED_NEED_ROLLBACK + SCHEDULER_LOG(ERROR) + << BLOCK_NUMBER(number()) << "Commit node storage error! need rollback" + << error->errorMessage(); + + batchBlockRollback(rollbackVersion, [this, callback](Error::UniquePtr&& error) { + if (error) + { + SCHEDULER_LOG(ERROR) + << BLOCK_NUMBER(number()) + << "Rollback storage(for commit scheduler storage error) failed!" + << LOG_KV("number", number()) << " " << error->errorMessage(); + // FATAL ERROR, NEED MANUAL FIX! + + callback(std::move(error)); + return; + } + else + { + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::CommitError, + "Commit scheduler storage error, rollback")); + return; + } + }); +#else + SCHEDULER_LOG(WARNING) + << BLOCK_NUMBER(number()) + << "Commit scheduler storage error, just return with no rollback" << error->errorMessage() + << LOG_KV("rollbackVersion", rollbackVersion); + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::CommitError, + "Commit scheduler storage error, just return with no rollback")); +#endif + return; + } + else + { + ++status->success; + } + + bcos::protocol::TwoPCParams executorParams; + executorParams.number = number(); + executorParams.timestamp = commitTS; + tbb::parallel_for_each(m_scheduler->m_executorManager->begin(), + m_scheduler->m_executorManager->end(), [&](auto const& executorIt) { + SCHEDULER_LOG(TRACE) << "Commit executor for block " << executorParams.number; + + executorIt->commit(executorParams, [this, status](bcos::Error::Ptr&& error) { + { + WriteGuard lock(status->x_lock); + if (error) + { + SCHEDULER_LOG(ERROR) + << BLOCK_NUMBER(number()) << "Commit executor error!" + << error->errorMessage(); + + // executor failed is also success++ + // because commit has been successful after ledger commit + // this executorIt->commit is just for clear storage cache. + ++status->success; + + if (error->errorCode() == + bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + { + triggerSwitch(); + } + } + else + { + ++status->success; + SCHEDULER_LOG(DEBUG) + << BLOCK_NUMBER(number()) + << "Commit executor success, success: " << status->success; + } + + if (status->success + status->failed < status->total) + { + return; + } + } + status->checkAndCommit(*status); + }); + }); + }); +} + +void BlockExecutive::batchBlockRollback( + uint64_t version, std::function callback) +{ + auto status = std::make_shared(); + status->total = 1 + m_scheduler->m_executorManager->size(); // self + all executors + status->checkAndCommit = [this, callback = std::move(callback)](const CommitStatus& status) { + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::Stopped, "BlockExecutive is stopped")); + return; + } + + if (status.failed > 0) + { + auto message = "Rollback block:" + boost::lexical_cast(number()) + + " with errors! " + boost::lexical_cast(status.failed); + SCHEDULER_LOG(WARNING) << BLOCK_NUMBER(number()) << message; + + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::RollbackError, std::move(message))); + return; + } + + callback(nullptr); + }; + + bcos::protocol::TwoPCParams params; + params.number = number(); + params.timestamp = version; + m_scheduler->m_storage->asyncRollback( + params, [this, version, number = params.number, status](Error::Ptr&& error) { + { + WriteGuard lock(status->x_lock); + if (error) + { + SCHEDULER_LOG(ERROR) << BLOCK_NUMBER(number) << "Rollback node storage error!" + << error->errorMessage(); + + ++status->failed; + } + else + { + ++status->success; + } + } + + if (status->failed > 0) + { + status->checkAndCommit(*status); + } + else + { + // for (auto& it : *(m_scheduler->m_executorManager)) + m_scheduler->m_executorManager->forEachExecutor( + [this, version, number, status](std::string, + bcos::executor::ParallelTransactionExecutorInterface::Ptr executor) { + bcos::protocol::TwoPCParams executorParams; + executorParams.number = number; + executorParams.timestamp = version; + executor->rollback( + executorParams, [this, number, status](bcos::Error::Ptr&& error) { + { + WriteGuard lock(status->x_lock); + if (error) + { + if (error->errorCode() == + bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + { + SCHEDULER_LOG(INFO) + << BLOCK_NUMBER(number) + << "Rollback a restarted executor. Ignore." + << error->errorMessage(); + ++status->success; + triggerSwitch(); + } + else + { + SCHEDULER_LOG(ERROR) << BLOCK_NUMBER(number) + << "Rollback executor error!" + << error->errorMessage(); + ++status->failed; + } + } + else + { + ++status->success; + } + + if (status->success + status->failed < status->total) + { + return; + } + } + status->checkAndCommit(*status); + }); + }); + } + }); +} + + +DmcExecutor::Ptr BlockExecutive::registerAndGetDmcExecutor(std::string contractAddress) +{ + { + bcos::ReadGuard l(x_dmcExecutorLock); + auto dmcExecutorIt = m_dmcExecutors.find(contractAddress); + if (dmcExecutorIt != m_dmcExecutors.end()) + { + return dmcExecutorIt->second; + } + } + { + bcos::WriteGuard lock(x_dmcExecutorLock); + auto dmcExecutorIt = m_dmcExecutors.find(contractAddress); + if (dmcExecutorIt != m_dmcExecutors.end()) + { + return dmcExecutorIt->second; + } + + std::string dispatchAddress; + if (number() == 0) + { + dispatchAddress = "genesis block use same executor to init"; + } + else + { + dispatchAddress = contractAddress; + } + + auto executor = m_scheduler->executorManager()->dispatchExecutor(dispatchAddress); + auto executorInfo = m_scheduler->executorManager()->getExecutorInfo(dispatchAddress); + + if (executor == nullptr || executorInfo == nullptr) + { + BOOST_THROW_EXCEPTION(BCOS_ERROR( + SchedulerError::ExecutorNotEstablishedError, "The executor has not started!")); + } + + if (!m_dmcRecorder) + { + m_dmcRecorder = std::make_shared(); + } + + auto dmcExecutor = std::make_shared(executorInfo->name, contractAddress, + m_block, executor, m_keyLocks, m_scheduler->m_hashImpl, m_dmcRecorder); + m_dmcExecutors.emplace(contractAddress, dmcExecutor); + + // register functions + dmcExecutor->setSchedulerOutHandler( + [this](ExecutiveState::Ptr executiveState) { scheduleExecutive(executiveState); }); + + dmcExecutor->setOnTxFinishedHandler( + [this](bcos::protocol::ExecutionMessage::UniquePtr output) { + onTxFinish(std::move(output)); + }); + dmcExecutor->setOnNeedSwitchEventHandler([this]() { triggerSwitch(); }); + + dmcExecutor->setOnGetCodeHandler([this](std::string_view address) { + auto executor = m_scheduler->executorManager()->dispatchExecutor(address); + if (!executor) + { + SCHEDULER_LOG(ERROR) << "Could not dispatch correspond executor during getCode(). " + << LOG_KV("address", address); + return bcos::bytes(); + } + else + { + // getCode from executor + std::promise codeFuture; + executor->getCode( + address, [&codeFuture, this](bcos::Error::Ptr error, bcos::bytes codes) { + if (error) + { + SCHEDULER_LOG(ERROR) + << "Could not getCode from correspond executor. Trigger switch." + << LOG_KV("code", error->errorCode()) + << LOG_KV("message", error->errorMessage()); + triggerSwitch(); + codeFuture.set_value(bcos::bytes()); + } + else + { + codeFuture.set_value(std::move(codes)); + } + }); + bcos::bytes codes = codeFuture.get_future().get(); + + return codes; + } + }); + + return dmcExecutor; + } +} + +void BlockExecutive::scheduleExecutive(ExecutiveState::Ptr executiveState) +{ + auto to = std::string(executiveState->message->to()); + + DmcExecutor::Ptr dmcExecutor = registerAndGetDmcExecutor(to); + + dmcExecutor->scheduleIn(executiveState); +} + +void BlockExecutive::onTxFinish(bcos::protocol::ExecutionMessage::UniquePtr output) +{ + auto txGasUsed = m_gasLimit - output->gasAvailable(); + // Calc the gas set to header + if (bcos::precompiled::c_systemTxsAddress.count({output->from().data(), output->from().size()})) + { + txGasUsed = 0; + } + m_gasUsed += txGasUsed; + auto receipt = m_scheduler->m_blockFactory->receiptFactory()->createReceipt(txGasUsed, + output->newEVMContractAddress(), + std::make_shared>(output->takeLogEntries()), + output->status(), output->takeData(), m_block->blockHeaderConst()->number()); + // write receipt in results + m_executiveResults[output->contextID() - m_startContextID].receipt = receipt; + SCHEDULER_LOG(TRACE) << " 6.GenReceipt:\t [^^] " << output->toString() + << " -> contextID:" << output->contextID() - m_startContextID + << ", receipt: " << receipt->hash() << ", gasUsed: " << receipt->gasUsed() + << ", version: " << receipt->version() + << ", status: " << receipt->status(); +} + + +void BlockExecutive::serialPrepareExecutor() +{ + // Notice: + // For the same DMC lock priority + // m_dmcExecutors must be prepared in contractAddress less<> serial order + + /// Handle normal message + bool hasScheduleOutMessage; + do + { + hasScheduleOutMessage = false; + + // dump current DmcExecutor (m_dmcExecutors may be modified during traversing) + std::set> currentExecutors; + for (auto it = m_dmcExecutors.begin(); it != m_dmcExecutors.end(); it++) + { + it->second->releaseOutdatedLock(); // release last round's lock + currentExecutors.insert(it->first); + } + + // for each current DmcExecutor + for (auto& address : currentExecutors) + { + DMC_LOG(TRACE) << " 0.Pre-DmcExecutor: \t----------------- addr:" << address + << " | number:" << m_block->blockHeaderConst()->number() + << " -----------------"; + hasScheduleOutMessage |= + m_dmcExecutors[address]->prepare(); // may generate new contract in m_dmcExecutors + } + + // must all schedule out message has been handled. + } while (hasScheduleOutMessage); + + + /// try to handle locked message + // try to unlock some locked tx + bool needDetectDeadlock = true; + bool allFinished = true; + for (auto it = m_dmcExecutors.begin(); it != m_dmcExecutors.end(); it++) + { + auto& address = it->first; + auto dmcExecutor = m_dmcExecutors[address]; + if (dmcExecutor->hasFinished()) + { + continue; // must jump finished executor + } + DMC_LOG(TRACE) << " 3.UnlockPrepare: \t |---------------- addr:" << address + << " | number:" << std::to_string(m_block->blockHeaderConst()->number()) + << " ----------------|"; + + allFinished = false; + bool need = dmcExecutor->unlockPrepare(); + needDetectDeadlock &= need; + // if there is an executor need detect deadlock, noNeedDetectDeadlock = false + } + + if (needDetectDeadlock && !allFinished) + { + bool needRevert = false; + // detect deadlock and revert the first tx TODO: revert many tx in one DMC round + for (auto it = m_dmcExecutors.begin(); it != m_dmcExecutors.end(); it++) + { + auto& address = it->first; + DMC_LOG(TRACE) << " --detect--revert-- " << address << " | " + << m_block->blockHeaderConst()->number() << " -----------------"; + if (m_dmcExecutors[address]->detectLockAndRevert()) + { + needRevert = true; + break; // Just revert the first found tx + } + } + + if (!needRevert) + { + std::string errorMsg = "Need detect deadlock but no deadlock detected! block: " + + toString(m_block->blockHeaderConst()->number()); + DMC_LOG(ERROR) << errorMsg; + BOOST_THROW_EXCEPTION(BCOS_ERROR(-1, errorMsg)); + } + } +} + +std::string BlockExecutive::preprocessAddress(const std::string_view& address) +{ + std::string out; + if (address[0] == '0' && address[1] == 'x') + { + out = std::string(address.substr(2)); + } + else + { + out = std::string(address); + } + + // boost::to_lower(out); no need to be lower + return out; +} diff --git "a/BFPL\345\243\271/bcos-scheduler/src/BlockExecutive.h" "b/BFPL\345\243\271/bcos-scheduler/src/BlockExecutive.h" new file mode 100644 index 00000000..464a5b79 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/BlockExecutive.h" @@ -0,0 +1,200 @@ +#pragma once + +#include "Executive.h" +#include "ExecutorManager.h" +#include "GraphKeyLocks.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/executor/ParallelTransactionExecutorInterface.h" +#include "bcos-framework/protocol/Block.h" +#include "bcos-framework/protocol/BlockHeader.h" +#include "bcos-framework/protocol/BlockHeaderFactory.h" +#include "bcos-framework/protocol/ProtocolTypeDef.h" +#include "bcos-framework/protocol/TransactionMetaData.h" +#include "bcos-framework/protocol/TransactionReceiptFactory.h" +#include "bcos-protocol/TransactionSubmitResultFactoryImpl.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::scheduler +{ +class SchedulerImpl; +class DmcExecutor; +class DmcStepRecorder; + +class BlockExecutive : public std::enable_shared_from_this +{ +public: + using UniquePtr = std::unique_ptr; + using Ptr = std::shared_ptr; + + BlockExecutive(bcos::protocol::Block::Ptr block, SchedulerImpl* scheduler, + size_t startContextID, + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory, + bool staticCall, bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::txpool::TxPoolInterface::Ptr _txPool); + + BlockExecutive(bcos::protocol::Block::Ptr block, SchedulerImpl* scheduler, + size_t startContextID, + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory, + bool staticCall, bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::txpool::TxPoolInterface::Ptr _txPool, uint64_t _gasLimit, bool _syncBlock) + : BlockExecutive(block, scheduler, startContextID, transactionSubmitResultFactory, staticCall, + _blockFactory, _txPool) + { + m_syncBlock = _syncBlock; + m_gasLimit = _gasLimit; + } + + BlockExecutive(const BlockExecutive&) = delete; + BlockExecutive(BlockExecutive&&) = delete; + BlockExecutive& operator=(const BlockExecutive&) = delete; + BlockExecutive& operator=(BlockExecutive&&) = delete; + + virtual ~BlockExecutive() { stop(); }; + + virtual void prepare(); + virtual void asyncExecute( + std::function callback); + virtual void asyncCall( + std::function callback); + virtual void asyncCommit(std::function callback); + + virtual void asyncNotify( + std::function)>& notifier, + std::function _callback); + + virtual void saveMessage( + std::string address, protocol::ExecutionMessage::UniquePtr message, bool withDAG); + + inline bcos::protocol::BlockNumber number() { return m_block->blockHeaderConst()->number(); } + + inline bcos::protocol::Block::Ptr block() { return m_block; } + inline bcos::protocol::BlockHeader::Ptr result() { return m_result; } + + bool isCall() { return m_staticCall; } + bool sysBlock() const { return m_isSysBlock; } + + void start() { m_isRunning = true; } + void stop() { m_isRunning = false; } + + void setOnNeedSwitchEventHandler(std::function onNeedSwitchEvent) + { + f_onNeedSwitchEvent = std::move(onNeedSwitchEvent); + } + + void triggerSwitch() + { + if (f_onNeedSwitchEvent) + { + f_onNeedSwitchEvent(); + } + } + + bool isSysBlock() { return m_isSysBlock; } + +protected: + struct CommitStatus + { + std::atomic_size_t total; + std::atomic_size_t success = 0; + std::atomic_size_t failed = 0; + uint64_t startTS = 0; + std::function checkAndCommit; + mutable SharedMutex x_lock; + }; + void batchNextBlock(std::function callback); + void batchGetHashes(std::function callback); + void batchBlockCommit(uint64_t rollbackVersion, std::function callback); + void batchBlockRollback(uint64_t version, std::function callback); + + struct BatchStatus // Batch state per batch + { + using Ptr = std::shared_ptr; + std::atomic_size_t total = 0; + std::atomic_size_t paused = 0; + std::atomic_size_t finished = 0; + std::atomic_size_t error = 0; + + std::atomic_bool callbackExecuted = false; + mutable SharedMutex x_lock; + std::string errorMessage; + }; + + void DAGExecute(std::function callback); // only used for DAG + std::map, ExecutiveState::Ptr, std::less<>> + m_executiveStates; // only used for DAG + + void DMCExecute( + std::function callback); + virtual std::shared_ptr registerAndGetDmcExecutor(std::string contractAddress); + void scheduleExecutive(ExecutiveState::Ptr executiveState); + void onTxFinish(bcos::protocol::ExecutionMessage::UniquePtr output); + void onDmcExecuteFinish( + std::function callback); + virtual void onExecuteFinish( + std::function callback); + + bcos::protocol::ExecutionMessage::UniquePtr buildMessage( + ContextID contextID, bcos::protocol::Transaction::ConstPtr tx); + void buildExecutivesFromMetaData(); + void buildExecutivesFromNormalTransaction(); + + virtual void serialPrepareExecutor(); + bcos::protocol::TransactionsPtr fetchBlockTxsFromTxPool( + bcos::protocol::Block::Ptr block, bcos::txpool::TxPoolInterface::Ptr txPool); + std::string preprocessAddress(const std::string_view& address); + + std::map, std::less<>> m_dmcExecutors; + std::shared_ptr m_dmcRecorder; + + std::vector m_executiveResults; + + size_t m_gasUsed = 0; + + GraphKeyLocks::Ptr m_keyLocks = std::make_shared(); + + std::chrono::system_clock::time_point m_currentTimePoint; + + std::chrono::milliseconds m_executeElapsed; + std::chrono::milliseconds m_hashElapsed; + std::chrono::milliseconds m_commitElapsed; + + bcos::protocol::Block::Ptr m_block; + bcos::protocol::TransactionsPtr m_blockTxs; + + bcos::protocol::BlockHeader::Ptr m_result; + SchedulerImpl* m_scheduler; + int64_t m_schedulerTermId; + size_t m_startContextID; + bcos::protocol::TransactionSubmitResultFactory::Ptr m_transactionSubmitResultFactory; + bcos::protocol::BlockFactory::Ptr m_blockFactory; + bcos::txpool::TxPoolInterface::Ptr m_txPool; + + size_t m_gasLimit = TRANSACTION_GAS; + std::atomic_bool m_isSysBlock = false; + + bool m_staticCall = false; + bool m_syncBlock = false; + bool m_hasPrepared = false; + bool m_hasDAG = false; + mutable SharedMutex x_prepareLock; + mutable SharedMutex x_dmcExecutorLock; + + bool m_isRunning = false; + + std::function f_onNeedSwitchEvent; +}; + +} // namespace bcos::scheduler \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/src/BlockExecutiveFactory.cpp" "b/BFPL\345\243\271/bcos-scheduler/src/BlockExecutiveFactory.cpp" new file mode 100644 index 00000000..71e97626 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/BlockExecutiveFactory.cpp" @@ -0,0 +1,72 @@ +/* + * 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. + * + * @brief The block executive(context) for serial transaction execution + * @file SerialBlockExecutive.h + * @author: wenlinli + * @date: 2022-07-13 + */ + +#include "BlockExecutiveFactory.h" +#include "BlockExecutive.h" +#include "SerialBlockExecutive.h" + + +using namespace std; +using namespace bcos::protocol; +using namespace bcos::scheduler; + + +std::shared_ptr BlockExecutiveFactory::build(bcos::protocol::Block::Ptr block, + SchedulerImpl* scheduler, size_t startContextID, + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory, + bool staticCall, bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::txpool::TxPoolInterface::Ptr _txPool) +{ + if (m_isSerialExecute) + { + auto serialBlockExecutive = std::make_shared(block, scheduler, + startContextID, transactionSubmitResultFactory, staticCall, _blockFactory, _txPool); + return serialBlockExecutive; + } + else + { + auto blockExecutive = std::make_shared(block, scheduler, startContextID, + transactionSubmitResultFactory, staticCall, _blockFactory, _txPool); + return blockExecutive; + } +} + +std::shared_ptr BlockExecutiveFactory::build(bcos::protocol::Block::Ptr block, + SchedulerImpl* scheduler, size_t startContextID, + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory, + bool staticCall, bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::txpool::TxPoolInterface::Ptr _txPool, uint64_t _gasLimit, bool _syncBlock) +{ + if (m_isSerialExecute) + { + auto serialBlockExecutive = std::make_shared(block, scheduler, + startContextID, transactionSubmitResultFactory, staticCall, _blockFactory, _txPool, + _gasLimit, _syncBlock); + return serialBlockExecutive; + } + else + { + auto blockExecutive = std::make_shared(block, scheduler, startContextID, + transactionSubmitResultFactory, staticCall, _blockFactory, _txPool, _gasLimit, + _syncBlock); + return blockExecutive; + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/src/BlockExecutiveFactory.h" "b/BFPL\345\243\271/bcos-scheduler/src/BlockExecutiveFactory.h" new file mode 100644 index 00000000..343d1476 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/BlockExecutiveFactory.h" @@ -0,0 +1,56 @@ +/* + * 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. + * + * @brief The block executive(context) for serial transaction execution + * @file SerialBlockExecutive.h + * @author: wenlinli + * @date: 2022-07-13 + */ + +#pragma once +#include "BlockExecutive.h" +#include "Common.h" +#include "bcos-framework/protocol/Block.h" +#include "bcos-framework/protocol/TransactionReceiptFactory.h" +#include "bcos-protocol/TransactionSubmitResultFactoryImpl.h" +#include +#include +#include + +namespace bcos::scheduler +{ +class BlockExecutiveFactory +{ +public: + using Ptr = std::shared_ptr; + BlockExecutiveFactory(bool isSerialExecute) : m_isSerialExecute(isSerialExecute) {} + virtual ~BlockExecutiveFactory() {} + + virtual std::shared_ptr build(bcos::protocol::Block::Ptr block, + SchedulerImpl* scheduler, size_t startContextID, + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory, + bool staticCall, bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::txpool::TxPoolInterface::Ptr _txPool); + + virtual std::shared_ptr build(bcos::protocol::Block::Ptr block, + SchedulerImpl* scheduler, size_t startContextID, + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory, + bool staticCall, bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::txpool::TxPoolInterface::Ptr _txPool, uint64_t _gasLimit, bool _syncBlock); + +private: + bool m_isSerialExecute; +}; +} // namespace bcos::scheduler diff --git "a/BFPL\345\243\271/bcos-scheduler/src/Common.h" "b/BFPL\345\243\271/bcos-scheduler/src/Common.h" new file mode 100644 index 00000000..cfff5799 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/Common.h" @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include +#define SCHEDULER_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("SCHEDULER") +#define SCHEDULER_BLK_LOG(LEVEL, NUMBER) \ + BCOS_LOG(LEVEL) << LOG_BADGE("SCHEDULER") << BLOCK_NUMBER(NUMBER) +namespace bcos::scheduler +{ +using ContextID = int64_t; +using Seq = int64_t; +using Context = std::tuple; +inline const uint64_t TRANSACTION_GAS = 3000000000; + +} // namespace bcos::scheduler \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/src/DmcExecutor.cpp" "b/BFPL\345\243\271/bcos-scheduler/src/DmcExecutor.cpp" new file mode 100644 index 00000000..15de8ac1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/DmcExecutor.cpp" @@ -0,0 +1,471 @@ +#include "DmcExecutor.h" +#include "bcos-crypto/bcos-crypto/ChecksumAddress.h" +#include "bcos-framework/executor/ExecuteError.h" +#include + + +using namespace bcos::scheduler; + +void DmcExecutor::releaseOutdatedLock() +{ + m_executivePool.forEach( + MessageHint::NEED_PREPARE, [this](int64_t, ExecutiveState::Ptr executiveState) { + auto& message = executiveState->message; + if (message->type() == bcos::protocol::ExecutionMessage::FINISHED || + message->type() == bcos::protocol::ExecutionMessage::REVERT) + { + m_keyLocks->releaseKeyLocks(message->contextID(), message->seq()); + } + return true; + }); +} + +bool DmcExecutor::prepare() +{ + // clear env + m_executivePool.refresh(); + + // logging + m_executivePool.forEach(MessageHint::ALL, [](int64_t, ExecutiveState::Ptr executiveState) { + DMC_LOG(TRACE) << " 1.PendingMsg: \t\t [--] " << executiveState->toString(); + return true; + }); + + // prepare all that need to + m_executivePool.forEachAndClear(ExecutivePool::MessageHint::NEED_PREPARE, + [this](int64_t contextID, ExecutiveState::Ptr executiveState) { + auto hint = handleExecutiveMessage(executiveState); + m_executivePool.markAs(contextID, hint); + DMC_LOG(TRACE) << " 2.AfterPrepare: \t [..] " << executiveState->toString() << " " + << ExecutivePool::toString(hint); + return true; + }); + + bool hasScheduleOut = !m_executivePool.empty(MessageHint::NEED_SCHEDULE_OUT) || + !m_executivePool.empty(MessageHint::NEED_PREPARE); + + // handle schedule out message + m_executivePool.forEach(ExecutivePool::MessageHint::NEED_SCHEDULE_OUT, + [this](int64_t, ExecutiveState::Ptr executiveState) { + scheduleOut(executiveState); + return true; + }); + + // clear env + m_executivePool.refresh(); + return hasScheduleOut; +} + +bool DmcExecutor::unlockPrepare() +{ + m_executivePool.forEach( + MessageHint::LOCKED, [this](int64_t contextID, ExecutiveState::Ptr executiveState) { + auto& message = executiveState->message; + auto seq = message->seq(); + + if (message->type() != protocol::ExecutionMessage::KEY_LOCK) + { + DMC_LOG(FATAL) << "Handle a normal message in locking pool" << message->toString(); + assert(false); + } + + // Try to acquire key lock + if (!m_keyLocks->acquireKeyLock( + message->from(), message->keyLockAcquired(), contextID, seq)) + { + DMC_LOG(TRACE) << " Waiting key, contract: " << contextID << " | " << seq << " | " + << message->from() + << " keyLockAcquired: " << toHex(message->keyLockAcquired()); + } + else + { + DMC_LOG(TRACE) << " Wait key lock success, " << contextID << " | " << seq << " | " + << message->from() + << " keyLockAcquired: " << toHex(message->keyLockAcquired()); + + m_executivePool.markAs(contextID, MessageHint::NEED_SEND); + DMC_LOG(TRACE) << " 3.AfterPrepare: \t [..] " << executiveState->toString() + << " UNLOCK"; + } + return true; + }); + + return m_executivePool.empty(MessageHint::NEED_SEND); // has locked tx but nothing to send, + // need to detect deadlock +} + +bool DmcExecutor::detectLockAndRevert() +{ + bool found = false; + m_executivePool.forEach( + MessageHint::LOCKED, [this, &found](int64_t contextID, ExecutiveState::Ptr executiveState) { + if (m_keyLocks->detectDeadLock(contextID)) + { + auto& message = executiveState->message; + + SCHEDULER_LOG(INFO) << "Detected dead lock at " << contextID << " | " + << message->seq() << " , revert"; + + message->setType(protocol::ExecutionMessage::REVERT); + message->setCreate(false); + message->setKeyLocks({}); + + m_executivePool.markAs(contextID, MessageHint::NEED_SEND); + DMC_LOG(TRACE) << " 3.AfterPrepare: \t [..] " << executiveState->toString() + << " REVERT"; + found = true; + return false; // break at once found a tx can be revert + // just detect one TODO: detect and unlock more deadlock + } + else + { + return true; // continue forEach + } + }); + + return found; // no detected +} + +void DmcExecutor::submit(protocol::ExecutionMessage::UniquePtr message, bool withDAG) +{ + auto contextID = message->contextID(); + /* + DMC_LOG(TRACE) << std::endl + << " ////// " << message->contextID() << " | " << message->seq() << " | " << + std::hex + << message->transactionHash() << " | " << message->to(); + */ + { + // bcos::ReadGuard lock(x_concurrentLock); + m_executivePool.add( + contextID, std::make_shared(contextID, std::move(message), withDAG)); + } +} + +void DmcExecutor::scheduleIn(ExecutiveState::Ptr executive) +{ + assert(executive->message->to() == m_contractAddress); // message must belongs to this executor + auto contextID = executive->message->contextID(); + m_executivePool.add(contextID, executive); +} + +void DmcExecutor::go(std::function callback) +{ + /* + this code may lead to inconsistency, because in parallel for go(), + some message sent by other DMCExecutor will be executed in executor and return before this + instance go(), so some needs send messages will be ignored in the code below + + if (!m_executivePool.empty(MessageHint::NEED_PREPARE)) + { + callback(nullptr, NEED_PREPARE); + return; + } + */ + + if (hasFinished()) + { + callback(nullptr, FINISHED); + return; + } + + if (m_executivePool.empty(MessageHint::NEED_SEND)) + { + callback(nullptr, PAUSED); + return; + } + + assert(f_onSchedulerOut != nullptr); + + auto messages = std::make_shared>(); + + m_executivePool.forEachAndClear(MessageHint::NEED_SEND, + [this, messages](int64_t contextID, ExecutiveState::Ptr executiveState) { + auto& message = executiveState->message; + + auto keyLocks = m_keyLocks->getKeyLocksNotHoldingByContext(message->to(), contextID); + message->setKeyLocks(std::move(keyLocks)); + DMC_LOG(TRACE) << " 4.SendToExecutor:\t >>>> " << executiveState->toString() + << " >>>> [" << m_name << "]:" << m_contractAddress + << ", staticCall:" << message->staticCall(); + messages->push_back(std::move(message)); + + return true; + }); + + // record all send message for debug + m_dmcRecorder->recordSends(m_contractAddress, *messages); + + if (messages->size() == 1 && (*messages)[0]->staticCall()) + { + DMC_LOG(TRACE) << "send call request, address:" << m_contractAddress + << LOG_KV("executor", m_name) << LOG_KV("to", (*messages)[0]->to()) + << LOG_KV("contextID", (*messages)[0]->contextID()) + << LOG_KV("internalCall", (*messages)[0]->internalCall()) + << LOG_KV("type", (*messages)[0]->type()); + // is static call + m_executor->dmcCall(std::move((*messages)[0]), + [this, callback = std::move(callback)]( + bcos::Error::UniquePtr error, bcos::protocol::ExecutionMessage::UniquePtr output) { + if (error) + { + SCHEDULER_LOG(ERROR) << "Call error: " << boost::diagnostic_information(*error); + + if (error->errorCode() == bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + { + triggerSwitch(); + } + callback(std::move(error), ERROR); + } + else + { + std::vector outputs; + outputs.push_back(std::move(output)); + handleExecutiveOutputs(std::move(outputs)); + callback(nullptr, PAUSED); + } + }); + } + else + { + // is transaction + auto lastT = utcTime(); + DMC_LOG(DEBUG) << LOG_BADGE("Stat") << "DMCExecute.3:\t --> Send to executor\t\t" + << LOG_KV("round", m_dmcRecorder->getRound()) << LOG_KV("name", m_name) + << LOG_KV("contract", m_contractAddress) << LOG_KV("txNum", messages->size()) + << LOG_KV("blockNumber", m_block->blockHeader()->number()) + << LOG_KV("cost", utcTime() - lastT); + + m_executor->dmcExecuteTransactions(m_contractAddress, *messages, + [this, lastT, messages, callback = std::move(callback)](bcos::Error::UniquePtr error, + std::vector outputs) { + // update batch + DMC_LOG(DEBUG) << LOG_BADGE("Stat") << "DMCExecute.4:\t <-- Receive from executor\t" + << LOG_KV("round", m_dmcRecorder ? m_dmcRecorder->getRound() : 0) + << LOG_KV("name", m_name) << LOG_KV("contract", m_contractAddress) + << LOG_KV("txNum", messages->size()) + << LOG_KV("blockNumber", m_block && m_block->blockHeader() ? + m_block->blockHeader()->number() : + 0) + << LOG_KV("cost", utcTime() - lastT); + + if (error) + { + SCHEDULER_LOG(ERROR) << "Execute transaction error: " << error->errorMessage(); + + if (error->errorCode() == bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + { + triggerSwitch(); + } + + callback(std::move(error), Status::ERROR); + } + else + { + handleExecutiveOutputs(std::move(outputs)); + callback(nullptr, PAUSED); + } + }); + } +} + +void DmcExecutor::handleCreateMessage(ExecutiveState::Ptr executiveState) +{ + auto& message = executiveState->message; + auto contextID = message->contextID(); + + if (message->type() == protocol::ExecutionMessage::SEND_BACK) + { + // must clear callstack + executiveState->callStack = std::stack>(); + + if (message->transactionHash() != h256(0)) + { + message->setType(protocol::ExecutionMessage::TXHASH); + } + else + { + message->setType(protocol::ExecutionMessage::MESSAGE); + } + } + + switch (message->type()) + { + case protocol::ExecutionMessage::MESSAGE: + case protocol::ExecutionMessage::TXHASH: + { + if (message->to().empty()) + { + auto newSeq = executiveState->currentSeq; + if (message->createSalt()) + { + // TODO: Add sender in this process(consider compat with ethereum) + message->setTo( + newEVMAddress(message->from(), message->data(), *(message->createSalt()))); + } + else + { + // TODO: Add sender in this process(consider compat with ethereum) + message->setTo( + newEVMAddress(m_block->blockHeaderConst()->number(), contextID, newSeq)); + } + } + break; + } + default: + break; + } +} + +DmcExecutor::MessageHint DmcExecutor::handleExecutiveMessage(ExecutiveState::Ptr executiveState) +{ + // handle create message first, create address and convert to normal message + handleCreateMessage(executiveState); + + // handle normal message + auto& message = executiveState->message; + + if (message->to() != m_contractAddress) + { + return MessageHint::NEED_SCHEDULE_OUT; + } + + switch (message->type()) + { + // Request type, push stack + case protocol::ExecutionMessage::MESSAGE: + case protocol::ExecutionMessage::TXHASH: + { + if (executiveState->message->data().toBytes() == bcos::protocol::GET_CODE_INPUT_BYTES) + { + auto newSeq = executiveState->currentSeq++; + executiveState->callStack.push(newSeq); + executiveState->message->setSeq(newSeq); + + // getCode + DMC_LOG(DEBUG) << "Get external code in scheduler" + << LOG_KV("codeAddress", executiveState->message->delegateCallAddress()); + bytes code = f_onGetCodeEvent(executiveState->message->delegateCallAddress()); + DMC_LOG(TRACE) << "Get external code success in scheduler" + << LOG_KV("codeAddress", executiveState->message->delegateCallAddress()) + << LOG_KV("codeSize", code.size()); + executiveState->message->setData(code); + executiveState->message->setType(protocol::ExecutionMessage::FINISHED); + return MessageHint::NEED_PREPARE; + } + + // update my key locks in m_keyLocks + m_keyLocks->batchAcquireKeyLock( + message->from(), message->keyLocks(), message->contextID(), message->seq()); + + auto newSeq = executiveState->currentSeq++; + executiveState->callStack.push(newSeq); + executiveState->message->setSeq(newSeq); + + if (executiveState->message->delegateCall()) + { + bytes code = f_onGetCodeEvent(message->delegateCallAddress()); + if (code.empty()) + { + DMC_LOG(DEBUG) + << "Could not getCode() from correspond executor during delegateCall: " + << message->toString(); + message->setType(protocol::ExecutionMessage::REVERT); + message->setCreate(false); + message->setKeyLocks({}); + return MessageHint::NEED_PREPARE; + } + else + { + executiveState->message->setDelegateCallCode(code); + } + } + + return MessageHint::NEED_SEND; + } + // Return type, pop stack + case protocol::ExecutionMessage::FINISHED: + case protocol::ExecutionMessage::REVERT: + { + executiveState->callStack.pop(); + if (executiveState->callStack.empty()) + { + // Empty stack, execution is finished + f_onTxFinished(std::move(message)); + return MessageHint::END; + } + else + { + message->setSeq(executiveState->callStack.top()); + message->setCreate(false); + return MessageHint::NEED_SEND; + } + } + // Retry type, send again + case protocol::ExecutionMessage::KEY_LOCK: + { + m_keyLocks->batchAcquireKeyLock( + message->from(), message->keyLocks(), message->contextID(), message->seq()); + + executiveState->message = std::move(message); + + return MessageHint::LOCKED; + } + default: + { + DMC_LOG(FATAL) << "Unrecognized message type: " << message->toString(); + assert(false); + break; + } + } + + // never goes here + DMC_LOG(FATAL) << "Message end with illegal manner" << message->toString(); + assert(false); + return MessageHint::END; +} + +void DmcExecutor::handleExecutiveOutputs( + std::vector outputs) +{ + m_dmcRecorder->recordReceives(m_contractAddress, outputs); + + for (auto& output : outputs) + { + std::string to = {output->to().data(), output->to().size()}; + auto contextID = output->contextID(); + + // bcos::ReadGuard lock(x_concurrentLock); + ExecutiveState::Ptr executiveState = m_executivePool.get(contextID); + executiveState->message = std::move(output); + DMC_LOG(TRACE) << " 5.RecvFromExecutor: <<<< [" << m_name << "]:" << m_contractAddress + << " <<<< " << executiveState->toString(); + if (to == m_contractAddress) + { + // bcos::ReadGuard lock(x_concurrentLock); + // is my output + m_executivePool.markAs(contextID, MessageHint::NEED_PREPARE); + } + else + { + m_executivePool.markAs(contextID, MessageHint::NEED_SCHEDULE_OUT); + scheduleOut(executiveState); + } + } +} + +void DmcExecutor::scheduleOut(ExecutiveState::Ptr executiveState) +{ + f_onSchedulerOut(std::move(executiveState)); // schedule out +} + +std::string DmcExecutor::newEVMAddress(int64_t blockNumber, int64_t contextID, int64_t seq) +{ + return bcos::newEVMAddress(m_hashImpl, blockNumber, contextID, seq); +} + +std::string DmcExecutor::newEVMAddress( + const std::string_view& _sender, bytesConstRef _init, u256 const& _salt) +{ + return bcos::newEVMAddress(m_hashImpl, _sender, _init, _salt); +} diff --git "a/BFPL\345\243\271/bcos-scheduler/src/DmcExecutor.h" "b/BFPL\345\243\271/bcos-scheduler/src/DmcExecutor.h" new file mode 100644 index 00000000..3751f714 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/DmcExecutor.h" @@ -0,0 +1,145 @@ +/* + * 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. + * + * @brief The executor of a contract + * @file DmcExecutor.h + * @author: jimmyshi + * @date: 2022-03-23 + */ + + +#pragma once +#include "DmcStepRecorder.h" +#include "Executive.h" +#include "ExecutivePool.h" +#include "ExecutorManager.h" +#include "GraphKeyLocks.h" +#include +#include +#include +#include + +#define DMC_LOG(LEVEL) SCHEDULER_LOG(LEVEL) << LOG_BADGE("DMC") +//#define DMC_LOG(LEVEL) std::cout << LOG_BADGE("DMC") +namespace bcos::scheduler +{ +class DmcExecutor +{ +public: + using MessageHint = bcos::scheduler::ExecutivePool::MessageHint; + + enum Status : int8_t + { + ERROR, + NEED_PREPARE = 1, + PAUSED = 2, + FINISHED = 3 + }; + + using Ptr = std::shared_ptr; + + DmcExecutor(std::string name, std::string contractAddress, bcos::protocol::Block::Ptr block, + bcos::executor::ParallelTransactionExecutorInterface::Ptr executor, + GraphKeyLocks::Ptr keyLocks, bcos::crypto::Hash::Ptr hashImpl, + DmcStepRecorder::Ptr dmcRecorder) + : m_name(name), + m_contractAddress(contractAddress), + m_block(block), + m_executor(executor), + m_keyLocks(keyLocks), + m_hashImpl(hashImpl), + m_dmcRecorder(dmcRecorder) + {} + + void submit(protocol::ExecutionMessage::UniquePtr message, bool withDAG); + bool prepare(); // return true if has schedule out message + bool unlockPrepare(); // return true if need to detect deadlock + void releaseOutdatedLock(); + bool detectLockAndRevert(); // return true if detect a tx and revert + + void go(std::function callback); + bool hasFinished() { return m_executivePool.empty(); } + + void scheduleIn(ExecutiveState::Ptr executive); + + void setSchedulerOutHandler(std::function onSchedulerOut) + { + f_onSchedulerOut = std::move(onSchedulerOut); + }; + + void setOnTxFinishedHandler( + std::function onTxFinished) + { + f_onTxFinished = std::move(onTxFinished); + } + + void setOnNeedSwitchEventHandler(std::function onNeedSwitchEvent) + { + f_onNeedSwitchEvent = std::move(onNeedSwitchEvent); + } + + void setOnGetCodeHandler(std::function onGetCodeEvent) + { + f_onGetCodeEvent = std::move(onGetCodeEvent); + } + + void triggerSwitch() + { + if (f_onNeedSwitchEvent) + { + f_onNeedSwitchEvent(); + } + } + + void forEachExecutive(std::function handler) + { + m_executivePool.forEach( + ExecutivePool::MessageHint::ALL, [handler = std::move(handler)](ContextID contextID, + ExecutiveState::Ptr executiveState) { + handler(contextID, executiveState); + return true; + }); + } + +private: + MessageHint handleExecutiveMessage(ExecutiveState::Ptr executive); + void handleExecutiveOutputs(std::vector outputs); + void scheduleOut(ExecutiveState::Ptr executiveState); + + void handleCreateMessage(ExecutiveState::Ptr executive); + std::string newEVMAddress(int64_t blockNumber, int64_t contextID, int64_t seq); + std::string newEVMAddress( + const std::string_view& _sender, bytesConstRef _init, u256 const& _salt); + +private: + std::string m_name; + std::string m_contractAddress; + bcos::protocol::Block::Ptr m_block; + bcos::executor::ParallelTransactionExecutorInterface::Ptr m_executor; + GraphKeyLocks::Ptr m_keyLocks; + bcos::crypto::Hash::Ptr m_hashImpl; + DmcStepRecorder::Ptr m_dmcRecorder; + ExecutivePool m_executivePool; + + + mutable SharedMutex x_concurrentLock; + + std::function f_onTxFinished; + std::function f_onNeedSwitchEvent; + std::function f_onSchedulerOut; + std::function f_onGetCodeEvent; +}; + +} // namespace bcos::scheduler diff --git "a/BFPL\345\243\271/bcos-scheduler/src/DmcStepRecorder.cpp" "b/BFPL\345\243\271/bcos-scheduler/src/DmcStepRecorder.cpp" new file mode 100644 index 00000000..6b9c2a70 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/DmcStepRecorder.cpp" @@ -0,0 +1,96 @@ +// +// Created by Jimmy Shi on 2022/6/6. +// + +#include "DmcStepRecorder.h" + +using namespace bcos::scheduler; + +uint32_t DmcStepRecorder::getMessageChecksum(const protocol::ExecutionMessage::UniquePtr& message) +{ + bool staticCall = message->staticCall(); + + uint32_t type = (uint32_t)message->type(); + uint32_t contextID = (uint32_t)message->contextID(); + uint32_t seq = (uint32_t)message->seq(); + uint32_t from = getAddressChecksum(message->from()); + uint32_t to = getAddressChecksum(message->to()); + uint32_t origin = getAddressChecksum(message->origin()); + + uint32_t sufferSum = (~from + type) * (~to + contextID) ^ (~origin + seq); + + return staticCall ? sufferSum : ~sufferSum; +} + +uint32_t DmcStepRecorder::getAddressChecksum(std::string_view address) +{ + uint32_t ret = 0; + + for (int i = 0; i < 4; i++) + { + ret <<= 8; + ret |= (uint8_t)address[i]; + } + return ret; +} + +uint32_t DmcStepRecorder::getRecordSum( + std::string_view address, uint32_t id, const protocol::ExecutionMessage::UniquePtr& message) +{ + uint32_t addrSum = getAddressChecksum(address); + uint32_t msgSum = getMessageChecksum(message); + + return addrSum * msgSum * (id + 1); +} + +void DmcStepRecorder::recordSend( + std::string_view address, uint32_t id, const protocol::ExecutionMessage::UniquePtr& message) +{ + uint32_t sum = getRecordSum(address, id, message); + + m_sendChecksum.fetch_add(sum); +} + +void DmcStepRecorder::recordReceive( + std::string_view address, uint32_t id, const protocol::ExecutionMessage::UniquePtr& message) +{ + uint32_t sum = getRecordSum(address, id, message); + + m_receiveChecksum.fetch_add(sum); +} + +void DmcStepRecorder::recordSends( + std::string_view address, const std::vector& message) +{ + size_t size = message.size(); + + uint32_t sum = 0; + // #pragma omp parallel for schedule(static, 1000) reduction(+ : sum) + for (size_t id = 0; id < size; id++) + { + sum += getRecordSum(address, id, message[id]); + } + + m_sendChecksum.fetch_add(sum); +} + +void DmcStepRecorder::recordReceives( + std::string_view address, const std::vector& message) +{ + size_t size = message.size(); + uint32_t sum = 0; + // #pragma omp parallel for schedule(static, 1000) reduction(+ : sum) + for (size_t id = 0; id < size; id++) + { + sum += getRecordSum(address, id, message[id]); + } + + m_receiveChecksum.fetch_add(sum); +} + + +void DmcStepRecorder::nextDmcRound() +{ + m_round++; + m_checksum = m_sendChecksum.fetch_xor(m_receiveChecksum.fetch_xor(m_checksum)) + m_round; +} diff --git "a/BFPL\345\243\271/bcos-scheduler/src/DmcStepRecorder.h" "b/BFPL\345\243\271/bcos-scheduler/src/DmcStepRecorder.h" new file mode 100644 index 00000000..758c53ee --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/DmcStepRecorder.h" @@ -0,0 +1,83 @@ +// +// Created by Jimmy Shi on 2022/6/6. +// + +#pragma once +#include +#include +#include +#include +#include + +namespace bcos::scheduler +{ + +class DmcStepRecorder +{ + // Record DMC step for debugging inconsistency bug + +public: + using Ptr = std::shared_ptr; + + void recordSend(std::string_view address, uint32_t id, + const protocol::ExecutionMessage::UniquePtr& message); + + void recordReceive(std::string_view address, uint32_t id, + const protocol::ExecutionMessage::UniquePtr& message); + + void recordSends(std::string_view address, + const std::vector& message); + + void recordReceives(std::string_view address, + const std::vector& message); + + void nextDmcRound(); + + uint32_t getRound() { return m_round; } + + std::string toHex(uint32_t x) + { + std::stringstream ss; + ss << std::setbase(16) << x; // to hex + return ss.str(); + } + + std::string getChecksum() + { + return toHex(m_checksum) + ":" + getSendChecksum() + ":" + getReceiveChecksum(); + } + + std::string getSendChecksum() { return toHex(m_sendChecksum); } + + std::string getReceiveChecksum() { return toHex(m_receiveChecksum); } + + void clear() + { + m_sendChecksum = 0; + m_receiveChecksum = 0; + m_checksum = 0; + m_round = 0; + } + + std::string dumpAndClearChecksum() + { + std::string checksum = getChecksum(); + clear(); + return checksum; + } + + // TODO: record execute history + + +private: + std::atomic m_sendChecksum = 0; + std::atomic m_receiveChecksum = 0; + uint32_t m_round = 0; + uint32_t m_checksum = 0; + + uint32_t getMessageChecksum(const protocol::ExecutionMessage::UniquePtr& message); + uint32_t getAddressChecksum(std::string_view address); + uint32_t getRecordSum(std::string_view address, uint32_t id, + const protocol::ExecutionMessage::UniquePtr& message); +}; +} // namespace bcos::scheduler diff --git "a/BFPL\345\243\271/bcos-scheduler/src/Executive.h" "b/BFPL\345\243\271/bcos-scheduler/src/Executive.h" new file mode 100644 index 00000000..bfa22939 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/Executive.h" @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace bcos::scheduler +{ +struct ExecutiveState // Executive state per tx +{ + using Ptr = std::shared_ptr; + ExecutiveState( + int64_t _contextID, bcos::protocol::ExecutionMessage::UniquePtr _message, bool _enableDAG) + : contextID(_contextID), message(std::move(_message)), enableDAG(_enableDAG), id(_contextID) + {} + + int64_t contextID; + std::stack> callStack; + bcos::protocol::ExecutionMessage::UniquePtr message; + bcos::Error::UniquePtr error; + int64_t currentSeq = 0; + bool enableDAG; + bool skip = false; + int64_t id; + + std::string toString() + { + std::stringstream ss; + ss << " " << contextID << " | " << callStack.size() << " | " + << (message ? message->toString() : "null"); + return ss.str(); + } +}; + +using ExecutiveStates = std::shared_ptr>; + +struct ExecutiveResult +{ + using Ptr = std::shared_ptr; + bcos::protocol::TransactionReceipt::Ptr receipt; + bcos::crypto::HashType transactionHash; + std::string source; +}; +} // namespace bcos::scheduler diff --git "a/BFPL\345\243\271/bcos-scheduler/src/ExecutivePool.cpp" "b/BFPL\345\243\271/bcos-scheduler/src/ExecutivePool.cpp" new file mode 100644 index 00000000..efa972bd --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/ExecutivePool.cpp" @@ -0,0 +1,190 @@ +// +// Created by Jimmy Shi on 2022/5/5. +// + +#include "ExecutivePool.h" + +using namespace bcos::scheduler; + +bool ExecutivePool::add(ContextID contextID, ExecutiveState::Ptr executiveState) +{ + m_pendingExecutives.insert({contextID, executiveState}); + return m_needPrepare->insert(contextID).second; // return if insert success +} + + +ExecutiveState::Ptr ExecutivePool::get(ContextID contextID) +{ + auto it = m_pendingExecutives.find(contextID); + if (it != m_pendingExecutives.end()) + { + return it->second; + } + else + { + return nullptr; + } +} + +void ExecutivePool::markAs(ContextID contextID, MessageHint type) +{ + switch (type) + { + case NEED_PREPARE: + { + m_needPrepare->insert(contextID); + break; + } + case LOCKED: + { + m_hasLocked->insert(contextID); + break; + } + case NEED_SEND: + { + m_needSend->insert(contextID); + break; + } + case NEED_SCHEDULE_OUT: + { + m_needScheduleOut->insert(contextID); + break; + } + case END: + { + m_needRemove->insert(contextID); + break; + } + case ALL: // do nothing + default: + break; + } +} + +void ExecutivePool::refresh() +{ + // handle need send + for (auto contextID : *m_needSend) + { + m_hasLocked->unsafe_erase(contextID); + } + + // remove all need remove + for (auto contextID : *m_needRemove) + { + m_pendingExecutives.unsafe_erase(contextID); + m_hasLocked->unsafe_erase(contextID); + m_needPrepare->unsafe_erase(contextID); + } + m_needRemove->clear(); + + for (auto contextID : *m_needScheduleOut) + { + m_pendingExecutives.unsafe_erase(contextID); + m_hasLocked->unsafe_erase(contextID); + m_needPrepare->unsafe_erase(contextID); + } + m_needScheduleOut->clear(); +} + +bool ExecutivePool::empty(MessageHint type) +{ + switch (type) + { + case NEED_PREPARE: + { + return m_needPrepare->empty(); + } + case LOCKED: + { + return m_hasLocked->empty(); + } + case NEED_SEND: + { + return m_needSend->empty(); + } + case NEED_SCHEDULE_OUT: + { + return m_needScheduleOut->empty(); + } + case END: + { + return m_needRemove->empty(); + } + case ALL: + { + return m_pendingExecutives.empty(); + } + } + return true; +} + + +void ExecutivePool::forEach(MessageHint type, ExecutiveStateHandler handler, bool needClear) +{ + SetPtr pool = nullptr; + switch (type) + { + case NEED_PREPARE: + { + pool = m_needPrepare; + break; + } + case LOCKED: + { + pool = m_hasLocked; + break; + } + case NEED_SEND: + { + pool = m_needSend; + break; + } + case END: + { + pool = m_needRemove; + break; + } + case NEED_SCHEDULE_OUT: + { + pool = m_needScheduleOut; + break; + } + case ALL: + { + SetPtr allID = std::make_shared>(); + for (auto it : m_pendingExecutives) + { + allID->insert(it.first); + } + pool = allID; + break; + } + default: + break; + } + + tbb::concurrent_set currentPool = *pool; + + if (needClear) + { + pool->clear(); + } + for (auto contextID : currentPool) + { + auto executiveState = get(contextID); + if (executiveState != nullptr) + { + if (!handler(contextID, executiveState)) + { + // break if no need to continue + break; + } + } + } +} + +void ExecutivePool::forEachAndClear(MessageHint type, ExecutiveStateHandler handler) +{ + forEach(type, std::move(handler), true); +} diff --git "a/BFPL\345\243\271/bcos-scheduler/src/ExecutivePool.h" "b/BFPL\345\243\271/bcos-scheduler/src/ExecutivePool.h" new file mode 100644 index 00000000..5598f14c --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/ExecutivePool.h" @@ -0,0 +1,101 @@ +/* + * 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. + * + * @brief The pool for executive states + * @file ExecutivePool.h + * @author: jimmyshi + * @date: 2022-05-05 + */ + + +#pragma once +#include "Common.h" +#include "Executive.h" + +#define TBB_PREVIEW_CONCURRENT_ORDERED_CONTAINERS 1 +#include +#include +#include + +namespace bcos::scheduler +{ +class ExecutivePool +{ +public: + using Ptr = std::shared_ptr; + using ExecutiveStateHandler = std::function; // return needContinue + using SetPtr = std::shared_ptr>; + enum MessageHint : int8_t + { + ALL = 0, + NEED_PREPARE = 1, + LOCKED = 2, + NEED_SEND = 3, + NEED_SCHEDULE_OUT = 4, + END = 5 + }; + + bool add(ContextID contextID, ExecutiveState::Ptr executiveState); // return if add success + ExecutiveState::Ptr get(ContextID contextID); + void markAs(ContextID contextID, MessageHint type); + void refresh(); + bool empty() { return m_pendingExecutives.empty() && m_hasLocked->empty(); } + bool empty(MessageHint type); + void forEach(MessageHint type, ExecutiveStateHandler handler, bool needClear = false); + void forEachAndClear(MessageHint type, ExecutiveStateHandler handler); + + static std::string toString(MessageHint type) + { + switch (type) + { + case NEED_PREPARE: + { + return "NEED_PREPARE"; + } + case LOCKED: + { + return "LOCKED"; + } + case NEED_SEND: + { + return "NEED_SEND"; + } + case NEED_SCHEDULE_OUT: + { + return "NEED_SCHEDULE_OUT"; + } + case END: + { + return "END"; + } + case ALL: + { + return "ALL"; + } + } + return "Unknown"; + } + +private: + tbb::concurrent_unordered_map m_pendingExecutives; + + SetPtr m_needPrepare = std::make_shared>(); + SetPtr m_hasLocked = std::make_shared>(); + SetPtr m_needScheduleOut = std::make_shared>(); + SetPtr m_needSend = std::make_shared>(); + SetPtr m_needRemove = std::make_shared>(); +}; +} // namespace bcos::scheduler \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/src/ExecutorManager.cpp" "b/BFPL\345\243\271/bcos-scheduler/src/ExecutorManager.cpp" new file mode 100644 index 00000000..0723b2d8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/ExecutorManager.cpp" @@ -0,0 +1,136 @@ +#include "ExecutorManager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::scheduler; + +void ExecutorManager::addExecutor( + std::string name, bcos::executor::ParallelTransactionExecutorInterface::Ptr executor) +{ + UpgradableGuard l(m_mutex); + if (m_name2Executors.count(name)) + { + return; + } + + auto executorInfo = std::make_shared(); + executorInfo->name = std::move(name); + executorInfo->executor = std::move(executor); + + UpgradeGuard ul(l); + auto [it, exists] = m_name2Executors.emplace(executorInfo->name, executorInfo); + boost::ignore_unused(it); + + if (!exists) + { + return; + } + + m_executorPriorityQueue.emplace(std::move(executorInfo)); +} + +bcos::executor::ParallelTransactionExecutorInterface::Ptr ExecutorManager::dispatchExecutor( + const std::string_view& _contract) +{ + auto contract = toLowerAddress(_contract); + UpgradableGuard l(m_mutex); + if (m_name2Executors.empty()) + { + return nullptr; + } + auto executorIt = m_contract2ExecutorInfo.find(contract); + if (executorIt != m_contract2ExecutorInfo.end()) + { + return executorIt->second->executor; + } + UpgradeGuard ul(l); + auto executorInfo = m_executorPriorityQueue.top(); + m_executorPriorityQueue.pop(); + + auto [contractStr, success] = executorInfo->contracts.insert(std::string(contract)); + if (!success) + { + BOOST_THROW_EXCEPTION(BCOS_ERROR(-1, "Insert into contracts fail!")); + } + m_executorPriorityQueue.push(executorInfo); + + (void)m_contract2ExecutorInfo.emplace(*contractStr, executorInfo); + + return executorInfo->executor; +} + +bcos::executor::ParallelTransactionExecutorInterface::Ptr +ExecutorManager::dispatchCorrespondExecutor(const std::string_view& _contract) +{ + auto contract = toLowerAddress(_contract); + UpgradableGuard l(m_mutex); + auto executorIt = m_contract2ExecutorInfo.find(contract); + if (executorIt != m_contract2ExecutorInfo.end()) + { + return executorIt->second->executor; + } + else + { + return nullptr; + } +} + + +bcos::scheduler::ExecutorManager::ExecutorInfo::Ptr ExecutorManager::getExecutorInfo( + const std::string_view& _contract) +{ + auto contract = toLowerAddress(_contract); + ReadGuard l(m_mutex); + auto it = m_contract2ExecutorInfo.find(contract); + if (it == m_contract2ExecutorInfo.end()) + { + return nullptr; + } + else + { + return it->second; + } +} + +void ExecutorManager::removeExecutor(const std::string_view& name) +{ + WriteGuard lock(m_mutex); + + auto it = m_name2Executors.find(name); + if (it != m_name2Executors.end()) + { + auto& executorInfo = it->second; + + for (auto contractIt = executorInfo->contracts.begin(); + contractIt != executorInfo->contracts.end(); ++contractIt) + { + auto count = m_contract2ExecutorInfo.unsafe_erase(*contractIt); + if (count < 1) + { + BOOST_THROW_EXCEPTION(bcos::Exception("Can't find contract in container")); + } + } + + m_name2Executors.erase(it); + + m_executorPriorityQueue = std::priority_queue, ExecutorInfoComp>(); + + for (auto& it : m_name2Executors) + { + m_executorPriorityQueue.push(it.second); + } + } + else + { + BOOST_THROW_EXCEPTION(BCOS_ERROR(-1, "Not found executor: " + std::string(name))); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/src/ExecutorManager.h" "b/BFPL\345\243\271/bcos-scheduler/src/ExecutorManager.h" new file mode 100644 index 00000000..eb888839 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/ExecutorManager.h" @@ -0,0 +1,153 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::scheduler +{ +#define EXECUTOR_MANAGER_LOG(LEVEL) \ + BCOS_LOG(LEVEL) << LOG_BADGE("EXECUTOR_MANAGER") << LOG_BADGE("Switch") + +class ExecutorManager +{ +public: + using Ptr = std::shared_ptr; + + virtual ~ExecutorManager() = default; + + void addExecutor( + std::string name, bcos::executor::ParallelTransactionExecutorInterface::Ptr executor); + + bcos::executor::ParallelTransactionExecutorInterface::Ptr dispatchExecutor( + const std::string_view& contract); + + // return nullptr if there is no executor dispatched in this contract before + bcos::executor::ParallelTransactionExecutorInterface::Ptr dispatchCorrespondExecutor( + const std::string_view& contract); + + void removeExecutor(const std::string_view& name); + + auto begin() const + { + return boost::make_transform_iterator(m_name2Executors.cbegin(), + std::bind(&ExecutorManager::executorView, this, std::placeholders::_1)); + } + + auto end() const + { + return boost::make_transform_iterator(m_name2Executors.cend(), + std::bind(&ExecutorManager::executorView, this, std::placeholders::_1)); + } + + void forEachExecutor( + std::function + handleExecutor) + { + ReadGuard lock(m_mutex); + if (m_name2Executors.empty()) + { + return; + } + + for (auto it : m_name2Executors) + { + handleExecutor(std::string(it.first), it.second->executor); + } + } + + size_t size() const + { + ReadGuard lock(m_mutex); + return m_name2Executors.size(); + } + + void clear() + { + WriteGuard lock(m_mutex); + m_contract2ExecutorInfo.clear(); + m_name2Executors.clear(); + m_executorPriorityQueue = std::priority_queue, ExecutorInfoComp>(); + }; + + virtual void stop() + { + EXECUTOR_MANAGER_LOG(INFO) << "Try to stop ExecutorManager"; + + + std::vector executors; + { + if (m_name2Executors.empty()) + { + return; + } + + WriteGuard lock(m_mutex); + for (auto it : m_name2Executors) + { + executors.push_back(it.second->executor); + } + } + + // no lock blocking to stop + for (auto executor : executors) + { + executor->stop(); + } + } + + struct ExecutorInfo + { + using Ptr = std::shared_ptr; + + std::string name; + bcos::executor::ParallelTransactionExecutorInterface::Ptr executor; + std::set contracts; + }; + + ExecutorInfo::Ptr getExecutorInfo(const std::string_view& contract); + ExecutorInfo::Ptr getExecutorInfoByName(const std::string_view& name) + { + return m_name2Executors[name]; + }; + +private: + struct ExecutorInfoComp + { + bool operator()(const ExecutorInfo::Ptr& lhs, const ExecutorInfo::Ptr& rhs) const + { + return lhs->contracts.size() > rhs->contracts.size(); + } + }; + + inline std::string toLowerAddress(const std::string_view& address) + { + return boost::algorithm::hex_lower(std::string(address)); + } + + tbb::concurrent_unordered_map> + m_contract2ExecutorInfo; + std::unordered_map> + m_name2Executors; + std::priority_queue, ExecutorInfoComp> + m_executorPriorityQueue; + mutable SharedMutex m_mutex; + + bcos::executor::ParallelTransactionExecutorInterface::Ptr const& executorView( + const decltype(m_name2Executors)::value_type& value) const + { + return value.second->executor; + } +}; +} // namespace bcos::scheduler \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/src/GraphKeyLocks.cpp" "b/BFPL\345\243\271/bcos-scheduler/src/GraphKeyLocks.cpp" new file mode 100644 index 00000000..a19203d5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/GraphKeyLocks.cpp" @@ -0,0 +1,277 @@ +#include "GraphKeyLocks.h" +#include "Common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos::scheduler; + +bool GraphKeyLocks::batchAcquireKeyLock( + std::string_view contract, gsl::span keys, ContextID contextID, Seq seq) +{ + if (!keys.empty()) + { + for (auto& it : keys) + { + if (!acquireKeyLock(contract, it, contextID, seq)) + { + auto message = (boost::format("Batch acquire lock failed, contract: %s" + ", key: %s, contextID: %ld, seq: %ld") % + contract % toHex(it) % contextID % seq) + .str(); + SCHEDULER_LOG(ERROR) << message; + BOOST_THROW_EXCEPTION(BCOS_ERROR(UnexpectedKeyLockError, message)); + } + } + } + + return true; +} + +bool GraphKeyLocks::acquireKeyLock( + std::string_view contract, std::string_view key, int64_t contextID, int64_t seq) +{ + auto keyVertex = touchKeyLock(std::make_tuple(contract, key)); + auto contextVertex = touchContext(contextID); + + auto range = boost::out_edges(keyVertex, m_graph); + for (auto it = range.first; it != range.second; ++it) + { + auto vertex = boost::get(VertexPropertyTag(), boost::target(*it, m_graph)); + if (std::get<0>(*vertex) != contextID) + { + KEY_LOCK_LOG(TRACE) << boost::format( + "Acquire key lock failed, request: [%s, %s, %ld, %ld] " + "exists: [%ld]") % + contract % toHex(key) % contextID % seq % + std::get<0>(*vertex); + + // Key lock holding by another context + addEdge(contextVertex, keyVertex, seq); + KEY_LOCK_LOG(TRACE) << " [[" << std::string(contract) << ":" << toHex(key) << "]] -> " + << contextID << " | " << seq; + return false; + } + } + + // Remove all request edge + boost::remove_edge(contextVertex, keyVertex, m_graph); + + // Add an own edge + addEdge(keyVertex, contextVertex, seq); + KEY_LOCK_LOG(TRACE) << " [" << std::string(contract) << ":" << toHex(key) << "] -> " + << contextID << " | " << seq; + + SCHEDULER_LOG(TRACE) << "Acquire key lock success, contract: " << contract << " key: " << key + << " contextID: " << contextID << " seq: " << seq; + + return true; +} + + +std::vector GraphKeyLocks::getKeyLocksNotHoldingByContext( + std::string_view contract, int64_t excludeContextID) const +{ + std::set uniqueKeyLocks; + + auto range = boost::edges(m_graph); + for (auto it = range.first; it != range.second; ++it) + { + auto sourceVertex = boost::get(VertexPropertyTag(), boost::source(*it, m_graph)); + auto targetVertex = boost::get(VertexPropertyTag(), boost::target(*it, m_graph)); + + if (targetVertex->index() == 0 && std::get<0>(*targetVertex) != excludeContextID && + sourceVertex->index() == 1 && std::get<0>(std::get<1>(*sourceVertex)) == contract) + { + uniqueKeyLocks.emplace(std::get<1>(std::get<1>(*sourceVertex))); + } + } + + std::vector keyLocks; + keyLocks.reserve(uniqueKeyLocks.size()); + for (auto& it : uniqueKeyLocks) + { + keyLocks.emplace_back(std::move(it)); + } + + return keyLocks; +} + +void GraphKeyLocks::releaseKeyLocks(int64_t contextID, int64_t seq) +{ + if (m_vertexes.count(Vertex(contextID)) == 0) + { + return; + } + + SCHEDULER_LOG(TRACE) << "Release key lock, contextID: " << contextID << " seq: " << seq; + + KEY_LOCK_LOG(TRACE) << " [*****] -> " << contextID << " | " << seq; + auto vertex = touchContext(contextID); + + auto edgeRemoveFunc = [seq, graph = &m_graph](auto range) mutable -> bool { + size_t total = 0; + size_t removed = 0; + + for (auto next = range.first; range.first != range.second; range.first = next) + { + ++total; + ++next; + auto edgeSeq = boost::get(EdgePropertyTag(), *range.first); + if (edgeSeq == seq) + { + ++removed; + if (bcos::LogLevel::TRACE >= bcos::c_fileLogLevel) + { + auto source = + boost::get(VertexPropertyTag(), boost::source(*range.first, *graph)); + auto target = + boost::get(VertexPropertyTag(), boost::target(*range.first, *graph)); + if (target->index() == 1) + { + source = target; + } + + const auto& [contract, key] = std::get<1>(*source); + SCHEDULER_LOG(TRACE) << "Releasing key lock, contract: " << contract + << " key: " << bcos::toHexString(key); + } + boost::remove_edge(*range.first, *graph); + } + } + + return total == removed; + }; + + auto clearedIn = edgeRemoveFunc(boost::in_edges(vertex, m_graph)); + auto clearedOut = edgeRemoveFunc(boost::out_edges(vertex, m_graph)); + + if (clearedIn && clearedOut) + { + // All edge had removed, delete the vertex + boost::remove_vertex(vertex, m_graph); + m_vertexes.erase(contextID); + } +} + +bool GraphKeyLocks::detectDeadLock(ContextID contextID) +{ + struct GraphVisitor + { + GraphVisitor(bool& backEdge) : m_backEdge(backEdge) {} + + void initialize_vertex(VertexID, const Graph&) {} + void start_vertex(VertexID, const Graph&) {} + void discover_vertex(VertexID, const Graph&) {} + void examine_edge(EdgeID, const Graph&) {} + void tree_edge(EdgeID, const Graph&) {} + void forward_or_cross_edge(EdgeID, const Graph&) {} + void finish_edge(EdgeID, const Graph&) {} + void finish_vertex(VertexID, const Graph&) {} + + void back_edge(EdgeID edgeID, const Graph&) const + { + auto edgeSeq = boost::get(EdgePropertyTag(), edgeID); + SCHEDULER_LOG(TRACE) << "Detected back edge seq: " << edgeSeq; + + m_backEdge = true; + } + + bool& m_backEdge; + }; + + auto it = m_vertexes.find(bcos::scheduler::GraphKeyLocks::Vertex(contextID)); + if (it == m_vertexes.end()) + { + // No vertex, may be removed + return false; + } + + if (boost::in_degree(it->second, m_graph) == 0) + { + // No in degree, not holding key lock + return false; + } + + std::map vertexColors; + bool hasDeadLock = false; + boost::depth_first_visit(m_graph, it->second, GraphVisitor(hasDeadLock), + boost::make_assoc_property_map(vertexColors), + [&hasDeadLock](VertexID, const Graph&) { return hasDeadLock; }); + + + return hasDeadLock; +} + +GraphKeyLocks::VertexID GraphKeyLocks::touchContext(int64_t contextID) +{ + auto [it, inserted] = m_vertexes.emplace(Vertex(contextID), VertexID()); + if (inserted) + { + it->second = boost::add_vertex(&(it->first), m_graph); + } + auto contextVertexID = it->second; + + return contextVertexID; +} + +GraphKeyLocks::VertexID GraphKeyLocks::touchKeyLock(KeyLockView keyLockView) +{ + auto contractKeyView = keyLockView; + auto it = m_vertexes.lower_bound(contractKeyView); + if (it != m_vertexes.end() && it->first == contractKeyView) + { + return it->second; + } + + auto inserted = m_vertexes.emplace_hint(it, + Vertex(std::make_tuple( + std::string(std::get<0>(contractKeyView)), std::string(std::get<1>(contractKeyView)))), + VertexID()); + inserted->second = boost::add_vertex(&(inserted->first), m_graph); + return inserted->second; +} + +void GraphKeyLocks::addEdge(VertexID source, VertexID target, int64_t seq) +{ + bool exists = false; + + auto range = boost::edge_range(source, target, m_graph); + for (auto it = range.first; it != range.second; ++it) + { + auto edgeSeq = boost::get(EdgePropertyTag(), *it); + if (edgeSeq == seq) + { + exists = true; + } + } + + if (!exists) + { + boost::add_edge(source, target, seq, m_graph); + } +} + +void GraphKeyLocks::removeEdge(VertexID source, VertexID target, int64_t seq) +{ + auto range = boost::edge_range(source, target, m_graph); + for (auto it = range.first; it != range.second; ++it) + { + auto edgeSeq = boost::get(EdgePropertyTag(), *it); + if (edgeSeq == seq) + { + boost::remove_edge(*it, m_graph); + break; + } + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/src/GraphKeyLocks.h" "b/BFPL\345\243\271/bcos-scheduler/src/GraphKeyLocks.h" new file mode 100644 index 00000000..e13a6720 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/GraphKeyLocks.h" @@ -0,0 +1,127 @@ +#pragma once + +#include "Common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KEY_LOCK_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("SCHEDULER") << LOG_BADGE("KEY_LOCK") +// #define KEY_LOCK_LOG(LEVEL) std::cout << LOG_BADGE("KEY_LOCK") + +namespace bcos::scheduler +{ +class GraphKeyLocks +{ +public: + using Ptr = std::shared_ptr; + using KeyLock = std::tuple; + using KeyLockView = std::tuple; + + using ContractView = std::string_view; + using KeyView = std::string_view; + + GraphKeyLocks() = default; + GraphKeyLocks(const GraphKeyLocks&) = delete; + GraphKeyLocks(GraphKeyLocks&&) = delete; + GraphKeyLocks& operator=(const GraphKeyLocks&) = delete; + GraphKeyLocks& operator=(GraphKeyLocks&&) = delete; + + bool batchAcquireKeyLock(std::string_view contract, gsl::span keyLocks, + ContextID contextID, Seq seq); + + bool acquireKeyLock( + std::string_view contract, std::string_view key, ContextID contextID, Seq seq); + + std::vector getKeyLocksNotHoldingByContext( + std::string_view contract, ContextID excludeContextID) const; + + void releaseKeyLocks(ContextID contextID, Seq seq); + + bool detectDeadLock(ContextID contextID); + + struct Vertex : public std::variant + { + using std::variant::variant; + + bool operator==(const KeyLockView& rhs) const + { + if (index() != 1) + { + return false; + } + + auto view = std::make_tuple(std::string_view(std::get<0>(std::get<1>(*this))), + std::string_view(std::get<1>(std::get<1>(*this)))); + return view == rhs; + } + }; + +private: + struct VertexIterator + { + using kind = boost::vertex_property_tag; + }; + using VertexProperty = boost::property; + struct EdgeSeq + { + using kind = boost::edge_property_tag; + }; + using EdgeProperty = boost::property; + + using Graph = boost::adjacency_list; + using VertexPropertyTag = boost::property_map::const_type; + using EdgePropertyTag = boost::property_map::const_type; + using VertexID = Graph::vertex_descriptor; + using EdgeID = Graph::edge_descriptor; + + Graph m_graph; + std::map> m_vertexes; + + VertexID touchContext(ContextID contextID); + VertexID touchKeyLock(KeyLockView keylockView); + + void addEdge(VertexID source, VertexID target, Seq seq); + void removeEdge(VertexID source, VertexID target, Seq seq); +}; + +} // namespace bcos::scheduler + +namespace std +{ +inline bool operator<(const bcos::scheduler::GraphKeyLocks::Vertex& lhs, + const bcos::scheduler::GraphKeyLocks::KeyLockView& rhs) +{ + if (lhs.index() != 1) + { + return true; + } + + auto view = std::make_tuple(std::string_view(std::get<0>(std::get<1>(lhs))), + std::string_view(std::get<1>(std::get<1>(lhs)))); + return view < rhs; +} + +inline bool operator<(const bcos::scheduler::GraphKeyLocks::KeyLockView& lhs, + const bcos::scheduler::GraphKeyLocks::Vertex& rhs) +{ + if (rhs.index() != 1) + { + return false; + } + + auto view = std::make_tuple(std::string_view(std::get<0>(std::get<1>(rhs))), + std::string_view(std::get<1>(std::get<1>(rhs)))); + return lhs < view; +} +} // namespace std \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/src/SchedulerFactory.h" "b/BFPL\345\243\271/bcos-scheduler/src/SchedulerFactory.h" new file mode 100644 index 00000000..dd465d22 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/SchedulerFactory.h" @@ -0,0 +1,80 @@ +#pragma once +#include "SchedulerImpl.h" + + +namespace bcos::scheduler +{ +class SchedulerFactory +{ +public: + using Ptr = std::shared_ptr; + + SchedulerFactory(ExecutorManager::Ptr executorManager, + bcos::ledger::LedgerInterface::Ptr ledger, + bcos::storage::TransactionalStorageInterface::Ptr storage, + bcos::protocol::ExecutionMessageFactory::Ptr executionMessageFactory, + bcos::protocol::BlockFactory::Ptr blockFactory, bcos::txpool::TxPoolInterface::Ptr txPool, + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory, + bcos::crypto::Hash::Ptr hashImpl, bool isAuthCheck, bool isWasm, bool isSerialExecute) + : m_executorManager(executorManager), + m_ledger(ledger), + m_storage(storage), + m_executionMessageFactory(executionMessageFactory), + m_blockFactory(blockFactory), + m_txPool(txPool), + m_transactionSubmitResultFactory(transactionSubmitResultFactory), + m_hashImpl(hashImpl), + m_isAuthCheck(isAuthCheck), + m_isWasm(isWasm), + m_isSerialExecute(isSerialExecute) + + {} + + scheduler::SchedulerImpl::Ptr build(int64_t schedulerTermId) + { + auto scheduler = std::make_shared(m_executorManager, m_ledger, + m_storage, m_executionMessageFactory, m_blockFactory, m_txPool, + m_transactionSubmitResultFactory, m_hashImpl, m_isAuthCheck, m_isWasm, + m_isSerialExecute, schedulerTermId); + scheduler->fetchGasLimit(); + + scheduler->registerBlockNumberReceiver(m_blockNumberReceiver); + scheduler->registerTransactionNotifier(m_txNotifier); + + return scheduler; + } + + void setBlockNumberReceiver(std::function callback) + { + m_blockNumberReceiver = std::move(callback); + } + + void setTransactionNotifier(std::function)> + txNotifier) + { + m_txNotifier = std::move(txNotifier); + } + + bcos::ledger::LedgerInterface::Ptr getLedger() { return m_ledger; } + +private: + ExecutorManager::Ptr m_executorManager; + bcos::ledger::LedgerInterface::Ptr m_ledger; + bcos::storage::TransactionalStorageInterface::Ptr m_storage; + bcos::protocol::ExecutionMessageFactory::Ptr m_executionMessageFactory; + bcos::protocol::BlockFactory::Ptr m_blockFactory; + bcos::txpool::TxPoolInterface::Ptr m_txPool; + bcos::protocol::TransactionSubmitResultFactory::Ptr m_transactionSubmitResultFactory; + bcos::crypto::Hash::Ptr m_hashImpl; + bool m_isAuthCheck; + bool m_isWasm; + bool m_isSerialExecute; + + std::function m_blockNumberReceiver; + std::function)> + m_txNotifier; +}; + +} // namespace bcos::scheduler diff --git "a/BFPL\345\243\271/bcos-scheduler/src/SchedulerImpl.cpp" "b/BFPL\345\243\271/bcos-scheduler/src/SchedulerImpl.cpp" new file mode 100644 index 00000000..766d8265 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/SchedulerImpl.cpp" @@ -0,0 +1,1036 @@ +#include "SchedulerImpl.h" +#include "BlockExecutive.h" +#include "Common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace bcos::scheduler; + +void SchedulerImpl::handleBlockQueue(bcos::protocol::BlockNumber requestBlockNumber, + std::function whenOlder, // whenOlder(frontNumber) + std::function whenQueueFront, std::function afterFront, + std::function whenNotFrontInQueue, std::function beforeBack, + std::function whenQueueBack, + std::function whenNewer, // whenNewer(backNumber) + std::function whenException) +{ + // [--------------- block queue --------------] + // ..95 96 97 [ 98 | 99 100 101 102 103 ] 104 | 105 106... + // whenOlder(98) [whenQueueFront() | whenNotFrontInQueue()..] whenQueueBack() | whenNewer(104) + // whenQueueFront() -> afterFront() + // beforeBack() -> whenQueueBack() + + std::unique_lock blocksLock(m_blocksMutex); + + // refresh block cache + bcos::protocol::BlockNumber currentNumber = getBlockNumberFromStorage(); + // note that genesis sysBlock is blockNumber 0, we need to ignore it + while (!m_blocks->empty() && currentNumber >= m_blocks->front()->number() && currentNumber != 0) + { + SCHEDULER_LOG(DEBUG) << "Remove committed block on handleBlockQueue : " + << m_blocks->front()->number() << " success"; + m_blocks->pop_front(); + } + + try + { + /* + if (!m_blocks->empty() && currentNumber >= m_blocks->front()->number() && + currentNumber != 0) + { + SCHEDULER_LOG(DEBUG) + << "Doesn't receive block commit success callback but block has committed" + << LOG_KV("cacheFrontNumber", m_blocks->front()->number()) + << LOG_KV("currentNumber", currentNumber); + + triggerSwitch(); + BOOST_THROW_EXCEPTION(std::runtime_error( + "Doesn't receive block commit success callback but block has committed")); + return; + } + */ + + if (m_blocks->empty()) + { + bcos::protocol::BlockNumber number = currentNumber; + if (requestBlockNumber == 0) + { + // handle genesis block, to execute or commit + beforeBack(); + blocksLock.unlock(); + whenQueueBack(); + } + else if (requestBlockNumber <= number) + { + blocksLock.unlock(); + whenOlder(number); + } + else if (requestBlockNumber == number + 1) + { + beforeBack(); + blocksLock.unlock(); + whenQueueBack(); + } + else + { + blocksLock.unlock(); + whenNewer(number); + } + } + else + { + // has block cache + bcos::protocol::BlockNumber frontNumber = m_blocks->front()->number(); + bcos::protocol::BlockNumber backNumber = m_blocks->back()->number(); + + if (requestBlockNumber < frontNumber) + { + blocksLock.unlock(); + whenOlder(frontNumber); + } + else if (requestBlockNumber == frontNumber) + { + auto frontBlock = m_blocks->front(); + blocksLock.unlock(); + whenQueueFront(frontBlock); + + blocksLock.lock(); + afterFront(); + blocksLock.unlock(); + } + else if (requestBlockNumber <= backNumber) + { + auto it = ++m_blocks->begin(); // it is begin next + while (it != m_blocks->end() && it->get()->number() != requestBlockNumber) + { + ++it; + } + if (it != m_blocks->end()) + { + blocksLock.unlock(); + whenNotFrontInQueue(*it); + } + } + else if (requestBlockNumber == backNumber + 1) + { + // Notice that whenQueueBack is backNumber + 1 + beforeBack(); + blocksLock.unlock(); + whenQueueBack(); + } + else + { + blocksLock.unlock(); + whenNewer(backNumber); + } + } + } + catch (std::exception const& e) + { + SCHEDULER_LOG(ERROR) << BLOCK_NUMBER(requestBlockNumber) << "handleBlockQueue exception" + << e.what(); + blocksLock.unlock(); + whenException(e); + } +} + + +void SchedulerImpl::executeBlock(bcos::protocol::Block::Ptr block, bool verify, + std::function + _callback) +{ + if (block->blockHeader()->version() > (uint32_t)g_BCOSConfig.maxSupportedVersion()) + { + auto errorMessage = "The block version is larger than maxSupportedVersion"; + SCHEDULER_LOG(WARNING) << BLOCK_NUMBER(block->blockHeaderConst()->number()) << errorMessage + << LOG_KV("version", block->version()) + << LOG_KV("maxSupportedVersion", g_BCOSConfig.maxSupportedVersion()); + _callback(std::make_shared(SchedulerError::InvalidBlockVersion, errorMessage), + nullptr, false); + return; + } + uint64_t waitT = 0; + if (m_lastExecuteFinishTime > 0) + { + waitT = utcTime() - m_lastExecuteFinishTime; + } + if (waitT > 3000) + { + waitT = 0; + m_lastExecuteFinishTime = 0; + } + auto signature = block->blockHeaderConst()->signatureList(); + auto requestBlockNumber = block->blockHeader()->number(); + try + { + fetchGasLimit(requestBlockNumber); + } + catch (std::exception& e) + { + SCHEDULER_LOG(ERROR) << BLOCK_NUMBER(block->blockHeaderConst()->number()) + << "fetchGasLimit exception: " << boost::diagnostic_information(e); + _callback(BCOS_ERROR_WITH_PREV_PTR( + SchedulerError::fetchGasLimitError, "fetchGasLimitError exception", e), + nullptr, false); + return; + } + + SCHEDULER_LOG(INFO) << METRIC << BLOCK_NUMBER(requestBlockNumber) << "ExecuteBlock request" + << LOG_KV("gasLimit", m_gasLimit) << LOG_KV("verify", verify) + << LOG_KV("signatureSize", signature.size()) + << LOG_KV("tx count", block->transactionsSize()) + << LOG_KV("meta tx count", block->transactionsMetaDataSize()) + << LOG_KV("version", (bcos::protocol::BlockVersion)(block->version())) + << LOG_KV("waitT", waitT); + + auto callback = [requestBlockNumber, _callback = std::move(_callback)](bcos::Error::Ptr&& error, + bcos::protocol::BlockHeader::Ptr&& blockHeader, bool _sysBlock) { + SCHEDULER_LOG(INFO) << METRIC << BLOCK_NUMBER(requestBlockNumber) << "ExecuteBlock response" + << LOG_KV(error ? "error" : "ok", error ? error->what() : "ok"); + _callback(error == nullptr ? nullptr : std::move(error), std::move(blockHeader), _sysBlock); + }; + + + auto whenOlder = [requestBlockNumber, callback](bcos::protocol::BlockNumber number) { + auto message = (boost::format("Execute an history block %d, current number is %d") % + requestBlockNumber % number) + .str(); + SCHEDULER_LOG(ERROR) << BLOCK_NUMBER(number) << message; + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::InvalidBlocks, message), nullptr, false); + }; + + auto whenInQueue = [this, callback, block, requestBlockNumber, &signature, verify]( + BlockExecutive::Ptr blockExecutive) { + auto blockHeader = blockExecutive->result(); + + if (blockHeader == nullptr) + { + auto message = "hit block cache, but block is executing!"; + SCHEDULER_LOG(ERROR) << BLOCK_NUMBER(requestBlockNumber) << "ExecuteBlock error, " + << message; + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::InvalidStatus, message), nullptr, false); + } + else + { + if (verify && blockHeader->hash() != block->blockHeader()->hash()) + { + SCHEDULER_LOG(WARNING) + << BLOCK_NUMBER(requestBlockNumber) + << "ExecuteBlock failed. The executed block has been cached " + "but request header hash is not the same. Trigger switch." + << LOG_KV("cachedHeaderHash", blockHeader->hash().abridged()) + << LOG_KV("requestHeaderHash", block->blockHeader()->hash().abridged()) + << LOG_KV("verify", verify); + triggerSwitch(); + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::InvalidBlocks, + "request header not the same with cached"), + nullptr, false); + } + else + { + SCHEDULER_LOG(INFO) + << BLOCK_NUMBER(requestBlockNumber) << LOG_BADGE("BlockTrace") + << "ExecuteBlock success, return executed block" + << LOG_KV("signatureSize", signature.size()) << LOG_KV("verify", verify); + callback(nullptr, std::move(blockHeader), blockExecutive->sysBlock()); + } + } + }; + + auto whenQueueFront = [whenInQueue]( + BlockExecutive::Ptr blockExecutive) { whenInQueue(blockExecutive); }; + + auto afterFront = []() { + // do nothing + }; + + auto whenNotFrontInQueue = [whenInQueue](BlockExecutive::Ptr blockExecutive) { + whenInQueue(blockExecutive); + }; + + auto whenNewer = [requestBlockNumber, callback](bcos::protocol::BlockNumber backNumber) { + auto message = + (boost::format( + "Try to execute an discontinuous block: %ld, queue back blockNumber: %ld") % + requestBlockNumber % backNumber) + .str(); + SCHEDULER_LOG(ERROR) << BLOCK_NUMBER(requestBlockNumber) << "ExecuteBlock error, " + << message; + callback( + BCOS_ERROR_UNIQUE_PTR(SchedulerError::InvalidBlockNumber, message), nullptr, false); + }; + auto whenException = [requestBlockNumber, callback](std::exception const& e) { + auto message = (boost::format("ExecuteBlock exception %s") % e.what()).str(); + SCHEDULER_LOG(WARNING) << BLOCK_NUMBER(requestBlockNumber) << message; + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::UnknownError, message), nullptr, false); + }; + + std::shared_ptr> executeLock = nullptr; + BlockExecutive::Ptr blockExecutive = nullptr; + + auto beforeBack = [this, block, verify, &executeLock, &blockExecutive, callback]() { + // update m_block + blockExecutive = getPreparedBlock( + block->blockHeaderConst()->number(), block->blockHeaderConst()->timestamp()); + + if (blockExecutive == nullptr) + { + // the block has not been prepared, just make a new one here + SCHEDULER_LOG(DEBUG) << LOG_BADGE("preExeBlock") + << BLOCK_NUMBER(block->blockHeaderConst()->number()) + << "Not hit prepared block executive, create."; + // blockExecutive = std::make_shared(std::move(block), this, 0, + // m_transactionSubmitResultFactory, false, m_blockFactory, m_txPool, m_gasLimit, + // verify); + blockExecutive = m_blockExecutiveFactory->build(std::move(block), this, 0, + m_transactionSubmitResultFactory, false, m_blockFactory, m_txPool, m_gasLimit, + verify); + + blockExecutive->setOnNeedSwitchEventHandler([this]() { triggerSwitch(); }); + } + else + { + SCHEDULER_LOG(DEBUG) << LOG_BADGE("preExeBlock") + << BLOCK_NUMBER(block->blockHeaderConst()->number()) + << "Hit prepared block executive cache, use it."; + blockExecutive->block()->setBlockHeader(block->blockHeader()); + } + + // acquire lock + executeLock = + std::make_shared>(m_executeMutex, std::try_to_lock); + if (!executeLock->owns_lock()) + { + executeLock = nullptr; + return; + } + + m_blocks->emplace_back(blockExecutive); + + // blockExecutive = m_blocks->back(); + }; + + // to execute the block + auto whenQueueBack = [this, &executeLock, &blockExecutive, callback, requestBlockNumber]() { + if (!executeLock) + { + // if not acquire the lock, return error + auto message = + "Another block is executing, maybe consensus and sync execute same block"; + SCHEDULER_LOG(DEBUG) << BLOCK_NUMBER(requestBlockNumber) << "ExecuteBlock error, " + << message; + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::InvalidStatus, message), nullptr, false); + return; + } + + SCHEDULER_LOG(INFO) << BLOCK_NUMBER(requestBlockNumber) << LOG_BADGE("BlockTrace") + << "ExecuteBlock start"; + auto startTime = utcTime(); + blockExecutive->asyncExecute( + [this, startTime, requestBlockNumber, callback = std::move(callback), executeLock]( + Error::UniquePtr error, protocol::BlockHeader::Ptr header, bool _sysBlock) { + if (!m_isRunning) + { + callback( + BCOS_ERROR_UNIQUE_PTR(SchedulerError::Stopped, "Scheduler is not running"), + nullptr, false); + return; + } + + if (error) + { + SCHEDULER_LOG(ERROR) << BLOCK_NUMBER(requestBlockNumber) + << "executeBlock error: " << error->what(); + { + std::unique_lock blocksLock(m_blocksMutex); + m_blocks->pop_back(); + blocksLock.unlock(); + } + executeLock->unlock(); + callback(BCOS_ERROR_WITH_PREV_PTR( + SchedulerError::UnknownError, "executeBlock error:", *error), + nullptr, _sysBlock); + return; + } + auto signature = header->signatureList(); + SCHEDULER_LOG(INFO) + << BLOCK_NUMBER(header->number()) << LOG_BADGE("BlockTrace") + << "ExecuteBlock success" << LOG_KV("hash", header->hash().abridged()) + << LOG_KV("state root", header->stateRoot().hex()) + << LOG_KV("receiptRoot", header->receiptsRoot().hex()) + << LOG_KV("txsRoot", header->txsRoot().abridged()) + << LOG_KV("gasUsed", header->gasUsed()) + << LOG_KV("signatureSize", signature.size()) + << LOG_KV("timeCost", utcTime() - startTime) + << LOG_KV("blockVersion", header->version()); + + m_lastExecuteFinishTime = utcTime(); + executeLock->unlock(); + callback(std::move(error), std::move(header), _sysBlock); + }); + }; + + // run all handler + handleBlockQueue(requestBlockNumber, whenOlder, whenQueueFront, afterFront, whenNotFrontInQueue, + beforeBack, whenQueueBack, whenNewer, whenException); +} + +void SchedulerImpl::commitBlock(bcos::protocol::BlockHeader::Ptr header, + std::function _callback) +{ + SCHEDULER_LOG(INFO) << BLOCK_NUMBER(header->number()) << "CommitBlock request"; + + auto requestBlockNumber = header->number(); + auto callback = [requestBlockNumber, _callback = std::move(_callback)]( + bcos::Error::Ptr&& error, bcos::ledger::LedgerConfig::Ptr&& config) { + SCHEDULER_LOG(INFO) << METRIC << BLOCK_NUMBER(requestBlockNumber) << "CommitBlock response" + << LOG_KV(error ? "error" : "ok", error ? error->what() : "ok"); + _callback(error == nullptr ? nullptr : std::move(error), std::move(config)); + }; + + auto whenOlder = [requestBlockNumber, callback]( + bcos::protocol::BlockNumber currentBlockNumber) { + auto message = (boost::format("commit an history block %d, current number is %d") % + requestBlockNumber % currentBlockNumber) + .str(); + SCHEDULER_LOG(ERROR) << BLOCK_NUMBER(requestBlockNumber) << message; + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::InvalidBlocks, message), nullptr); + }; + + auto whenAfterFront = [this, requestBlockNumber, callback]() { + auto message = + "A smaller block need to commit, requestBlockNumber " + + std::to_string(requestBlockNumber) + + +", need to commit blockNumber: " + std::to_string(getCurrentBlockNumber() + 1); + SCHEDULER_LOG(ERROR) << BLOCK_NUMBER(requestBlockNumber) << "CommitBlock error, " + << message; + + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::InvalidBlockNumber, message), nullptr); + }; + + auto whenNotFrontInQueue = [whenAfterFront](BlockExecutive::Ptr) { whenAfterFront(); }; + + auto beforeBack = []() { + // do nothing + }; + + auto whenQueueBack = [whenAfterFront]() { whenAfterFront(); }; + + auto whenNewer = [whenAfterFront](bcos::protocol::BlockNumber) { whenAfterFront(); }; + auto whenException = [callback, requestBlockNumber](std::exception const& e) { + auto message = (boost::format("CommitBlock exception %s") % e.what()).str(); + SCHEDULER_LOG(WARNING) << BLOCK_NUMBER(requestBlockNumber) << message; + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::UnknownError, message), nullptr); + }; + + auto whenQueueFront = [this, requestBlockNumber, header = std::move(header), callback]( + BlockExecutive::Ptr blockExecutive) { + // acquire lock + std::shared_ptr> commitLock = + std::make_shared>(m_commitMutex, std::try_to_lock); + + if (!commitLock->owns_lock()) + { + std::string message = (boost::format("commitBlock: Another block is committing! Block " + "number: %ld, hash: %s") % + requestBlockNumber % header->hash().abridged()) + .str(); + + + SCHEDULER_LOG(ERROR) << BLOCK_NUMBER(requestBlockNumber) << "CommitBlock error, " + << message; + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::BlockIsCommitting, message), nullptr); + return; + } + + auto currentBlockNumber = getCurrentBlockNumber(); + if (currentBlockNumber + 1 != requestBlockNumber) + { + // happens in some multi-thread scenario, the block has been commited + auto message = (boost::format("commit an committed block %d, current number is %d") % + requestBlockNumber % currentBlockNumber) + .str(); + SCHEDULER_LOG(ERROR) << message; + commitLock->unlock(); + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::InvalidBlocks, message), nullptr); + return; + } + + if (!blockExecutive->result()) + { + auto message = "Block is executing, number=" + std::to_string(blockExecutive->number()); + SCHEDULER_LOG(ERROR) << "CommitBlock error, " << message; + + commitLock->unlock(); + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::BlockIsCommitting, message), nullptr); + return; + } + + // Note: only when the signatureList is empty need to reset the header + // in case of the signatureList of the header is accessing by the sync module while + // frontBlock is setting newBlockHeader, which will cause the signatureList ilegal + auto executedHeader = blockExecutive->block()->blockHeader(); + auto signature = executedHeader->signatureList(); + if (signature.empty()) + { + blockExecutive->block()->setBlockHeader(std::move(header)); + } + + SCHEDULER_LOG(INFO) << BLOCK_NUMBER(requestBlockNumber) << LOG_BADGE("BlockTrace") + << "CommitBlock start"; + auto startTime = utcTime(); + blockExecutive->asyncCommit([this, startTime, callback = std::move(callback), + blockExecutive, block = blockExecutive->block(), + commitLock](Error::UniquePtr&& error) { + if (!m_isRunning) + { + commitLock->unlock(); + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::Stopped, "Scheduler is not running"), + nullptr); + return; + } + + if (error) + { + SCHEDULER_LOG(ERROR) << "CommitBlock error, " << error->errorMessage(); + + commitLock->unlock(); + callback(BCOS_ERROR_UNIQUE_PTR( + error->errorCode(), "CommitBlock error: " + error->errorMessage()), + nullptr); + return; + } + + { + std::unique_lock blocksLock(m_blocksMutex); + auto number = block->blockHeaderConst()->number(); + if (m_blocks && !m_blocks->empty() && m_blocks->front()->number() == number) + { + m_blocks->pop_front(); + SCHEDULER_LOG(DEBUG) + << "Remove committed block after commit: " << number << " success"; + } + removeAllOldPreparedBlock(number); + } + + + asyncGetLedgerConfig([this, startTime, commitLock = std::move(commitLock), + blockExecutive, callback = std::move(callback)]( + Error::Ptr error, ledger::LedgerConfig::Ptr ledgerConfig) { + if (!m_isRunning) + { + commitLock->unlock(); + callback( + BCOS_ERROR_UNIQUE_PTR(SchedulerError::Stopped, "Scheduler is not running"), + nullptr); + return; + } + if (error) + { + SCHEDULER_LOG(ERROR) << "Get system config error, " << error->errorMessage(); + + commitLock->unlock(); + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR( + SchedulerError::UnknownError, "Get system config error", *error), + nullptr); + return; + } + + auto blockNumber = ledgerConfig->blockNumber(); + auto gasNumber = ledgerConfig->gasLimit(); + // Note: takes effect in next block. we query the enableNumber of blockNumber + // + 1. + if (std::get<1>(gasNumber) <= (blockNumber + 1)) + { + m_gasLimit = std::get<0>(gasNumber); + } + + if (blockExecutive->isSysBlock()) + { + removeAllPreparedBlock(); // must clear prepared cacche + } + + SCHEDULER_LOG(INFO) + << BLOCK_NUMBER(blockNumber) << LOG_BADGE("BlockTrace") << "CommitBlock success" + << LOG_KV("gas limit", m_gasLimit) << LOG_KV("timeCost", utcTime() - startTime); + + commitLock->unlock(); // just unlock here + + + // Note: blockNumber = 0, means system deploy, and tx is not existed in txpool. + // So it should not exec tx notifier + if (m_txNotifier && blockNumber != 0) + { + SCHEDULER_LOG(INFO) << "Start notify block result: " << blockNumber; + blockExecutive->asyncNotify(m_txNotifier, + [this, blockNumber, callback = std::move(callback), + ledgerConfig = std::move(ledgerConfig)](Error::Ptr _error) mutable { + if (!m_isRunning) + { + callback(BCOS_ERROR_UNIQUE_PTR( + SchedulerError::Stopped, "Scheduler is not running"), + nullptr); + return; + } + + if (m_blockNumberReceiver) + { + m_blockNumberReceiver(blockNumber); + } + + SCHEDULER_LOG(INFO) << "End notify block result: " << blockNumber; + // Note: only after the block notify finished can call the callback + callback(std::move(_error), std::move(ledgerConfig)); + }); + } + else + { + callback(nullptr, std::move(ledgerConfig)); + } + }); + }); + }; + + auto afterFront = []() { + // do nothing + }; + + // run all handler + handleBlockQueue(requestBlockNumber, whenOlder, whenQueueFront, afterFront, whenNotFrontInQueue, + beforeBack, whenQueueBack, whenNewer, whenException); +} + +void SchedulerImpl::status( + std::function callback) +{ + (void)callback; +} + +void SchedulerImpl::call(protocol::Transaction::Ptr tx, + std::function callback) +{ + // call but to is empty, + // it will cause tx message be marked as 'create' falsely when asyncExecute tx + if (tx->to().empty()) + { + SCHEDULER_LOG(DEBUG) << LOG_BADGE("call") << LOG_DESC("call address empty"); + callback(BCOS_ERROR_PTR(SchedulerError::UnknownError, "Call address is empty"), nullptr); + return; + } + + auto blockNumber = getCurrentBlockNumber(); + SCHEDULER_LOG(DEBUG) << "Call request: " << LOG_KV("blockNumber", blockNumber) + << LOG_KV("address", tx->to()); + + // set attribute before call + tx->setAttribute(m_isWasm ? bcos::protocol::Transaction::Attribute::LIQUID_SCALE_CODEC : + bcos::protocol::Transaction::Attribute::EVM_ABI_CODEC); + // Create temp block + auto block = m_blockFactory->createBlock(); + block->blockHeader()->setNumber(blockNumber); + block->appendTransaction(std::move(tx)); + + // Create temp executive + + // auto blockExecutive = std::make_shared(std::move(block), this, + // m_calledContextID.fetch_add(1), m_transactionSubmitResultFactory, true, m_blockFactory, + // m_txPool, m_gasLimit, false); + auto blockExecutive = + m_blockExecutiveFactory->build(std::move(block), this, m_calledContextID.fetch_add(1), + m_transactionSubmitResultFactory, true, m_blockFactory, m_txPool, m_gasLimit, false); + blockExecutive->setOnNeedSwitchEventHandler([this]() { triggerSwitch(); }); + + blockExecutive->asyncCall([callback = std::move(callback)](Error::UniquePtr&& error, + protocol::TransactionReceipt::Ptr&& receipt) { + if (error) + { + std::string errorMessage = "asyncCall error: " + error->errorMessage(); + SCHEDULER_LOG(DEBUG) << errorMessage; + callback(BCOS_ERROR_WITH_PREV_PTR(error->errorCode(), errorMessage, *error), nullptr); + return; + } + SCHEDULER_LOG(DEBUG) << "Call success"; + callback(nullptr, std::move(receipt)); + }); +} + +void SchedulerImpl::registerExecutor(std::string name, + bcos::executor::ParallelTransactionExecutorInterface::Ptr executor, + std::function callback) +{ + try + { + SCHEDULER_LOG(INFO) << "registerExecutor request: " << LOG_KV("name", name); + m_executorManager->addExecutor(name, executor); + } + catch (std::exception& e) + { + SCHEDULER_LOG(ERROR) << "registerExecutor error: " << boost::diagnostic_information(e); + callback(BCOS_ERROR_WITH_PREV_PTR(-1, "addExecutor error", e)); + return; + } + + SCHEDULER_LOG(INFO) << "registerExecutor success" << LOG_KV("name", name); + callback(nullptr); +} + +void SchedulerImpl::unregisterExecutor( + const std::string& name, std::function callback) +{ + (void)name; + (void)callback; +} + +void SchedulerImpl::reset(std::function callback) +{ + (void)callback; +} +// register a blockNumber receiver +void SchedulerImpl::registerBlockNumberReceiver( + std::function callback) +{ + m_blockNumberReceiver = [callback = std::move(callback)]( + protocol::BlockNumber blockNumber) { callback(blockNumber); }; +} + +void SchedulerImpl::getCode( + std::string_view contract, std::function callback) +{ + auto executor = m_executorManager->dispatchExecutor(contract); + executor->getCode(contract, [this, callback = std::move(callback)]( + Error::Ptr error, bcos::bytes code) { + if (error && error->errorCode() == bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + { + triggerSwitch(); + } + callback(std::move(error), std::move(code)); + }); +} + +void SchedulerImpl::getABI( + std::string_view contract, std::function callback) +{ + auto executor = m_executorManager->dispatchExecutor(contract); + executor->getABI(contract, [this, callback = std::move(callback)]( + Error::Ptr error, std::string abi) { + if (error && error->errorCode() == bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + { + triggerSwitch(); + } + callback(std::move(error), std::move(abi)); + }); +} + +void SchedulerImpl::registerTransactionNotifier(std::function)> + txNotifier) +{ + m_txNotifier = [callback = std::move(txNotifier)](bcos::protocol::BlockNumber blockNumber, + bcos::protocol::TransactionSubmitResultsPtr resultsPtr, + std::function _callback) { + callback(blockNumber, resultsPtr, std::move(_callback)); + }; +} + +BlockExecutive::Ptr SchedulerImpl::getPreparedBlock( + bcos::protocol::BlockNumber blockNumber, int64_t timestamp) +{ + bcos::ReadGuard readGuard(x_preparedBlockMutex); + + if (m_preparedBlocks.count(blockNumber) != 0 && + m_preparedBlocks[blockNumber].count(timestamp) != 0) + { + return m_preparedBlocks[blockNumber][timestamp]; + } + else + { + return nullptr; + } +} + +void SchedulerImpl::setPreparedBlock( + bcos::protocol::BlockNumber blockNumber, int64_t timestamp, BlockExecutive::Ptr blockExecutive) +{ + bcos::WriteGuard writeGuard(x_preparedBlockMutex); + + m_preparedBlocks[blockNumber][timestamp] = blockExecutive; +} + +void SchedulerImpl::removeAllOldPreparedBlock(bcos::protocol::BlockNumber oldBlockNumber) +{ + bcos::WriteGuard writeGuard(x_preparedBlockMutex); + + // erase all preparedBlock <= oldBlockNumber + for (auto itr = m_preparedBlocks.begin(); itr != m_preparedBlocks.end();) + { + if (itr->first <= oldBlockNumber) + { + SCHEDULER_LOG(DEBUG) << LOG_BADGE("prepareBlockExecutive") + << LOG_DESC("removeAllOldPreparedBlock") + << LOG_KV("blockNumber", itr->first); + itr = m_preparedBlocks.erase(itr); + } + else + { + itr++; + } + } +} + +void SchedulerImpl::removeAllPreparedBlock() +{ + bcos::WriteGuard writeGuard(x_preparedBlockMutex); + m_preparedBlocks.clear(); + + SCHEDULER_LOG(DEBUG) << LOG_BADGE("prepareBlockExecutive") + << LOG_DESC("removeAllPreparedBlock"); +} + +void SchedulerImpl::preExecuteBlock( + bcos::protocol::Block::Ptr block, bool verify, std::function _callback) +{ + auto startT = utcTime(); + SCHEDULER_LOG(INFO) << BLOCK_NUMBER(block->blockHeaderConst()->number()) + << "preExeBlock request" + << LOG_KV("tx count", + block->transactionsSize() + block->transactionsMetaDataSize()) + << LOG_KV("startT(ms)", startT); + + auto callback = [startT, _callback = std::move(_callback), + number = block->blockHeaderConst()->number()](bcos::Error::Ptr&& error) { + SCHEDULER_LOG(INFO) << BLOCK_NUMBER(number) << METRIC << "preExeBlock response" + << LOG_KV("message", error ? error->what() : "ok") + << LOG_KV("cost(ms)", utcTime() - startT); + _callback(error == nullptr ? nullptr : std::move(error)); + }; + + try + { + auto blockNumber = block->blockHeaderConst()->number(); + int64_t timestamp = block->blockHeaderConst()->timestamp(); + BlockExecutive::Ptr blockExecutive = getPreparedBlock(blockNumber, timestamp); + if (blockExecutive != nullptr) + { + SCHEDULER_LOG(DEBUG) << BLOCK_NUMBER(blockNumber) << LOG_BADGE("prepareBlockExecutive") + << "Duplicate block to prepare, dropped." + << LOG_KV("blockHeader.timestamp", timestamp); + callback(nullptr); // also success + return; + } + + // blockExecutive = std::make_shared(std::move(block), this, 0, + // m_transactionSubmitResultFactory, false, m_blockFactory, m_txPool, m_gasLimit, + // verify); + + blockExecutive = m_blockExecutiveFactory->build(std::move(block), this, 0, + m_transactionSubmitResultFactory, false, m_blockFactory, m_txPool, m_gasLimit, verify); + + blockExecutive->setOnNeedSwitchEventHandler([this]() { triggerSwitch(); }); + + blockExecutive->prepare(); + + setPreparedBlock(blockNumber, timestamp, blockExecutive); + + callback(nullptr); + } + catch (bcos::Error& e) + { + SCHEDULER_LOG(WARNING) << "preExeBlock exception: " << LOG_KV("code", e.errorCode()) + << LOG_KV("message", e.errorMessage()); + callback(std::make_shared(e.errorCode(), e.errorMessage())); + } +} + +template +struct overloaded : Ts... +{ + using Ts::operator()...; +}; +// explicit deduction guide (not needed as of C++20) +template +overloaded(Ts...) -> overloaded; + +void SchedulerImpl::asyncGetLedgerConfig( + std::function callback) +{ + auto ledgerConfig = std::make_shared(); + auto callbackPtr = std::make_shared(std::move(callback)); + auto summary = + std::make_shared>(8, 0, 0); + + auto collector = [this, summary = std::move(summary), ledgerConfig = std::move(ledgerConfig), + callback = std::move(callbackPtr)](Error::Ptr error, + std::variant, + std::tuple, + bcos::protocol::BlockNumber, bcos::crypto::HashType>&& + result) mutable { + if (!m_isRunning) + { + (*callback)(BCOS_ERROR_UNIQUE_PTR(SchedulerError::Stopped, "Scheduler is not running"), + nullptr); + return; + } + + auto& [total, success, failed] = *summary; + + if (error) + { + SCHEDULER_LOG(ERROR) << "Get ledger config with errors: " << error->errorMessage(); + ++failed; + } + else + { + std::visit( + overloaded{ + [&ledgerConfig](std::tuple& nodeList) { + auto& [isSealer, list] = nodeList; + + if (isSealer) + { + ledgerConfig->setConsensusNodeList(*list); + } + else + { + ledgerConfig->setObserverNodeList(*list); + } + }, + [&ledgerConfig](std::tuple config) { + auto& [type, value, blockNumber] = config; + switch (type) + { + case 0: + ledgerConfig->setBlockTxCountLimit( + boost::lexical_cast(value)); + break; + case 1: + ledgerConfig->setLeaderSwitchPeriod( + boost::lexical_cast(value)); + break; + case 2: + ledgerConfig->setGasLimit( + std::make_tuple(boost::lexical_cast(value), blockNumber)); + break; + case 3: + try + { + auto version = bcos::tool::toVersionNumber(value); + ledgerConfig->setCompatibilityVersion(version); + } + catch (std::exception const& e) + { + SCHEDULER_LOG(WARNING) << LOG_DESC("invalidVersionNumber") << value; + } + break; + default: + BOOST_THROW_EXCEPTION(BCOS_ERROR(SchedulerError::UnknownError, + "Unknown type: " + boost::lexical_cast(type))); + } + }, + [&ledgerConfig](bcos::protocol::BlockNumber number) { + ledgerConfig->setBlockNumber(number); + }, + [&ledgerConfig](bcos::crypto::HashType hash) { ledgerConfig->setHash(hash); }}, + result); + + ++success; + } + + // Collect done + if (success + failed == total) + { + if (failed > 0) + { + SCHEDULER_LOG(ERROR) << "Get ledger config with error: " << failed; + (*callback)( + BCOS_ERROR_PTR(SchedulerError::UnknownError, "Get ledger config with error"), + nullptr); + + return; + } + + (*callback)(nullptr, std::move(ledgerConfig)); + } + }; + + m_ledger->asyncGetNodeListByType(ledger::CONSENSUS_SEALER, + [collector](Error::Ptr error, consensus::ConsensusNodeListPtr list) mutable { + collector(std::move(error), std::tuple{true, std::move(list)}); + }); + m_ledger->asyncGetNodeListByType(ledger::CONSENSUS_OBSERVER, + [collector](Error::Ptr error, consensus::ConsensusNodeListPtr list) mutable { + collector(std::move(error), std::tuple{false, std::move(list)}); + }); + m_ledger->asyncGetSystemConfigByKey(ledger::SYSTEM_KEY_TX_COUNT_LIMIT, + [collector](Error::Ptr error, std::string config, protocol::BlockNumber _number) mutable { + collector(std::move(error), std::tuple{0, std::move(config), _number}); + }); + m_ledger->asyncGetSystemConfigByKey(ledger::SYSTEM_KEY_CONSENSUS_LEADER_PERIOD, + [collector](Error::Ptr error, std::string config, protocol::BlockNumber _number) mutable { + collector(std::move(error), std::tuple{1, std::move(config), _number}); + }); + m_ledger->asyncGetSystemConfigByKey(ledger::SYSTEM_KEY_TX_GAS_LIMIT, + [collector](Error::Ptr error, std::string config, protocol::BlockNumber _number) mutable { + collector(std::move(error), std::tuple{2, std::move(config), _number}); + }); + m_ledger->asyncGetBlockNumber( + [collector, ledger = m_ledger](Error::Ptr error, protocol::BlockNumber number) mutable { + ledger->asyncGetBlockHashByNumber( + number, [collector](Error::Ptr error, const crypto::HashType& hash) mutable { + collector(std::move(error), std::move(hash)); + }); + collector(std::move(error), std::move(number)); + }); + + // Note: The consensus module ensures serial execution and submit of system txs + m_ledger->asyncGetSystemConfigByKey(ledger::SYSTEM_KEY_COMPATIBILITY_VERSION, + [collector](Error::Ptr error, std::string config, protocol::BlockNumber _number) mutable { + collector(std::move(error), std::tuple{3, std::move(config), _number}); + }); +} + +bcos::protocol::BlockNumber SchedulerImpl::getBlockNumberFromStorage() +{ + std::promise blockNumberFuture; + m_ledger->asyncGetBlockNumber( + [&blockNumberFuture](Error::Ptr error, protocol::BlockNumber number) { + if (error) + { + SCHEDULER_LOG(ERROR) + << "Get blockNumber from storage failed" << LOG_KV("msg", error->errorMessage()) + << LOG_KV("code", error->errorCode()); + blockNumberFuture.set_value(-1); + } + else + { + blockNumberFuture.set_value(number); + } + }); + + return blockNumberFuture.get_future().get(); +} + +bcos::protocol::BlockNumber SchedulerImpl::getCurrentBlockNumber() +{ + std::unique_lock blocksLock(m_blocksMutex); + if (m_blocks->empty()) + { + blocksLock.unlock(); + return getBlockNumberFromStorage(); + } + else + { + return m_blocks->front()->number() - 1; + } +} diff --git "a/BFPL\345\243\271/bcos-scheduler/src/SchedulerImpl.h" "b/BFPL\345\243\271/bcos-scheduler/src/SchedulerImpl.h" new file mode 100644 index 00000000..4ca8162c --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/SchedulerImpl.h" @@ -0,0 +1,262 @@ +#pragma once + +#include "BlockExecutive.h" +#include "BlockExecutiveFactory.h" +#include "ExecutorManager.h" +#include "bcos-protocol/TransactionSubmitResultFactoryImpl.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace bcos::scheduler +{ +class SchedulerImpl : public SchedulerInterface, public std::enable_shared_from_this +{ +public: + friend class BlockExecutive; + using Ptr = std::shared_ptr; + SchedulerImpl(ExecutorManager::Ptr executorManager, bcos::ledger::LedgerInterface::Ptr ledger, + bcos::storage::TransactionalStorageInterface::Ptr storage, + bcos::protocol::ExecutionMessageFactory::Ptr executionMessageFactory, + bcos::protocol::BlockFactory::Ptr blockFactory, bcos::txpool::TxPoolInterface::Ptr txPool, + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory, + bcos::crypto::Hash::Ptr hashImpl, bool isAuthCheck, bool isWasm, int64_t schedulerTermId) + : SchedulerImpl(executorManager, ledger, storage, executionMessageFactory, blockFactory, + txPool, transactionSubmitResultFactory, hashImpl, isAuthCheck, isWasm, false, + schedulerTermId) + {} + + + SchedulerImpl(ExecutorManager::Ptr executorManager, bcos::ledger::LedgerInterface::Ptr ledger, + bcos::storage::TransactionalStorageInterface::Ptr storage, + bcos::protocol::ExecutionMessageFactory::Ptr executionMessageFactory, + bcos::protocol::BlockFactory::Ptr blockFactory, bcos::txpool::TxPoolInterface::Ptr txPool, + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory, + bcos::crypto::Hash::Ptr hashImpl, bool isAuthCheck, bool isWasm, bool isSerialExecute, + int64_t schedulerTermId) + : m_executorManager(std::move(executorManager)), + m_ledger(std::move(ledger)), + m_storage(std::move(storage)), + m_executionMessageFactory(std::move(executionMessageFactory)), + m_blockExecutiveFactory( + std::make_shared(isSerialExecute)), + m_blockFactory(std::move(blockFactory)), + m_txPool(txPool), + m_transactionSubmitResultFactory(std::move(transactionSubmitResultFactory)), + m_hashImpl(std::move(hashImpl)), + m_isAuthCheck(isAuthCheck), + m_isWasm(isWasm), + m_isSerialExecute(isSerialExecute), + m_schedulerTermId(schedulerTermId) + { + start(); + } + + SchedulerImpl(const SchedulerImpl&) = delete; + SchedulerImpl(SchedulerImpl&&) = delete; + SchedulerImpl& operator=(const SchedulerImpl&) = delete; + SchedulerImpl& operator=(SchedulerImpl&&) = delete; + + void executeBlock(bcos::protocol::Block::Ptr block, bool verify, + std::function + callback) override; + + void commitBlock(bcos::protocol::BlockHeader::Ptr header, + std::function callback) + override; + + void status( + std::function callback) override; + + void call(protocol::Transaction::Ptr tx, + std::function) override; + + void registerExecutor(std::string name, + bcos::executor::ParallelTransactionExecutorInterface::Ptr executor, + std::function callback) override; + + void unregisterExecutor( + const std::string& name, std::function callback) override; + + void reset(std::function callback) override; + // register a block number receiver + virtual void registerBlockNumberReceiver( + std::function callback); + + void getCode( + std::string_view contract, std::function callback) override; + + void getABI( + std::string_view contract, std::function callback) override; + + void registerTransactionNotifier(std::function)> + txNotifier); + + void preExecuteBlock(bcos::protocol::Block::Ptr block, bool verify, + std::function callback) override; + + ExecutorManager::Ptr executorManager() { return m_executorManager; } + + inline void fetchGasLimit(protocol::BlockNumber _number = -1) + { + SCHEDULER_LOG(INFO) << LOG_DESC("fetch gas limit from storage before execute block") + << LOG_KV("requestBlockNumber", _number); + if (_number == -1) + { + std::promise> numberPromise; + m_ledger->asyncGetBlockNumber( + [&numberPromise](Error::Ptr _error, protocol::BlockNumber _number) { + numberPromise.set_value(std::make_tuple(std::move(_error), _number)); + }); + Error::Ptr error; + std::tie(error, _number) = numberPromise.get_future().get(); + if (error) + { + return; + } + } + + std::promise> p; + m_ledger->asyncGetSystemConfigByKey(ledger::SYSTEM_KEY_TX_GAS_LIMIT, + [&p](Error::Ptr _e, std::string _value, protocol::BlockNumber) { + p.set_value(std::make_tuple(std::move(_e), std::move(_value))); + return; + }); + auto [e, value] = p.get_future().get(); + if (e) + { + SCHEDULER_LOG(WARNING) + << BLOCK_NUMBER(_number) << LOG_DESC("fetchGasLimit failed") + << LOG_KV("code", e->errorCode()) << LOG_KV("message", e->errorMessage()); + BOOST_THROW_EXCEPTION( + BCOS_ERROR(SchedulerError::fetchGasLimitError, e->errorMessage())); + } + + // cast must be success + m_gasLimit = boost::lexical_cast(value); + } + + int64_t getSchedulerTermId() { return m_schedulerTermId; } + + void start() + { + m_isRunning = true; + for (auto& blockExecutive : *m_blocks) + { + blockExecutive->start(); + } + + SCHEDULER_LOG(DEBUG) << LOG_BADGE("Switch") + << "Start with termId: " << getSchedulerTermId(); + } + void stop() override + { + SCHEDULER_LOG(INFO) << "Try to stop SchedulerImpl"; + m_isRunning = false; + std::unique_lock blocksLock(m_blocksMutex); + for (auto& blockExecutive : *m_blocks) + { + blockExecutive->stop(); + } + } + + void setBlockExecutiveFactory(bcos::scheduler::BlockExecutiveFactory::Ptr blockExecutiveFactory) + { + m_blockExecutiveFactory = blockExecutiveFactory; + } + + void setOnNeedSwitchEventHandler(std::function onNeedSwitchEvent) + { + f_onNeedSwitchEvent = std::move(onNeedSwitchEvent); + } + + void triggerSwitch() + { + if (f_onNeedSwitchEvent) + { + f_onNeedSwitchEvent(m_schedulerTermId); + } + } + + bcos::crypto::Hash::Ptr getHashImpl() { return m_hashImpl; } + +private: + void handleBlockQueue(bcos::protocol::BlockNumber requestBlockNumber, + std::function whenOlder, // whenOlder(frontNumber) + std::function whenQueueFront, std::function afterFront, + std::function whenNotFrontInQueue, + std::function beforeBack, std::function whenQueueBack, + std::function whenNewer, // whenNewer(backNumber) + std::function whenException); + + bcos::protocol::BlockNumber getCurrentBlockNumber(); + + void asyncGetLedgerConfig( + std::function callback); + + BlockExecutive::Ptr getPreparedBlock( + bcos::protocol::BlockNumber blockNumber, int64_t timestamp); + + void setPreparedBlock(bcos::protocol::BlockNumber blockNumber, int64_t timestamp, + BlockExecutive::Ptr blockExecutive); + + // remove prepared all block <= oldBlockNumber + void removeAllOldPreparedBlock(bcos::protocol::BlockNumber oldBlockNumber); + void removeAllPreparedBlock(); + + bcos::protocol::BlockNumber getBlockNumberFromStorage(); + + std::shared_ptr> m_blocks = + std::make_shared>(); + + std::shared_ptr> m_stoppedBlockExecutives; + + std::map> + m_preparedBlocks; // blockNumber -> BlockExecutive> + mutable SharedMutex x_preparedBlockMutex; + + std::mutex m_blocksMutex; + + std::mutex m_executeMutex; + std::mutex m_commitMutex; + + std::atomic_int64_t m_calledContextID = 1; + + uint64_t m_gasLimit = TRANSACTION_GAS; + + ExecutorManager::Ptr m_executorManager; + bcos::ledger::LedgerInterface::Ptr m_ledger; + bcos::storage::TransactionalStorageInterface::Ptr m_storage; + bcos::protocol::ExecutionMessageFactory::Ptr m_executionMessageFactory; + bcos::scheduler::BlockExecutiveFactory::Ptr m_blockExecutiveFactory; + bcos::protocol::BlockFactory::Ptr m_blockFactory; + bcos::txpool::TxPoolInterface::Ptr m_txPool; + bcos::protocol::TransactionSubmitResultFactory::Ptr m_transactionSubmitResultFactory; + bcos::crypto::Hash::Ptr m_hashImpl; + bool m_isAuthCheck = false; + bool m_isWasm = false; + bool m_isSerialExecute = false; + + std::function m_blockNumberReceiver; + std::function)> + m_txNotifier; + uint64_t m_lastExecuteFinishTime = 0; + + int64_t m_schedulerTermId; + + bool m_isRunning = false; + + std::function f_onNeedSwitchEvent; +}; +} // namespace bcos::scheduler \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/src/SchedulerManager.cpp" "b/BFPL\345\243\271/bcos-scheduler/src/SchedulerManager.cpp" new file mode 100644 index 00000000..07f94603 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/SchedulerManager.cpp" @@ -0,0 +1,451 @@ +#include "SchedulerManager.h" + +using namespace bcos::scheduler; + +// by pbft & sync +void SchedulerManager::executeBlock(bcos::protocol::Block::Ptr block, bool verify, + std::function + callback) +{ + auto [ok, message] = checkAndInit(); + + if (!ok) + { + SCHEDULER_LOG(DEBUG) << LOG_DESC("executeBlock: checkAndInit not ok") + << LOG_KV("message", message); + callback( + BCOS_ERROR_UNIQUE_PTR(SchedulerError::ExecutorNotEstablishedError, message), {}, false); + return; + } + + auto _holdSchedulerCallback = + [schedulerHolder = m_scheduler, callback = std::move(callback)](bcos::Error::Ptr&& error, + bcos::protocol::BlockHeader::Ptr&& blockHeader, bool _sysBlock) { + SCHEDULER_LOG(TRACE) << "Release scheduler holder" + << LOG_KV("ptr count", schedulerHolder.use_count()); + callback(std::move(error), std::move(blockHeader), _sysBlock); + }; + + m_scheduler->executeBlock(block, verify, std::move(_holdSchedulerCallback)); +} + +// by pbft & sync +void SchedulerManager::commitBlock(bcos::protocol::BlockHeader::Ptr header, + std::function callback) +{ + auto [ok, message] = checkAndInit(); + + if (!ok) + { + SCHEDULER_LOG(DEBUG) << LOG_DESC("commitBlock: checkAndInit not ok") + << LOG_KV("message", message); + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::ExecutorNotEstablishedError, message), {}); + return; + } + + auto _holdSchedulerCallback = [schedulerHolder = m_scheduler, callback = std::move(callback)]( + bcos::Error::Ptr&& error, + bcos::ledger::LedgerConfig::Ptr&& ledger) { + SCHEDULER_LOG(TRACE) << "Release scheduler holder" + << LOG_KV("ptr count", schedulerHolder.use_count()); + callback(std::move(error), std::move(ledger)); + }; + + m_scheduler->commitBlock(header, std::move(_holdSchedulerCallback)); +} + +// by console, query committed and committing executing +void SchedulerManager::status( + std::function callback) +{ + auto [ok, message] = checkAndInit(); + + if (!ok) + { + SCHEDULER_LOG(DEBUG) << LOG_DESC("status: checkAndInit not ok") + << LOG_KV("message", message); + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::ExecutorNotEstablishedError, message), {}); + return; + } + + auto _holdSchedulerCallback = [schedulerHolder = m_scheduler, callback = std::move(callback)]( + bcos::Error::Ptr&& error, + bcos::protocol::Session::ConstPtr&& session) { + SCHEDULER_LOG(TRACE) << "Release scheduler holder" + << LOG_KV("ptr count", schedulerHolder.use_count()); + callback(std::move(error), std::move(session)); + }; + m_scheduler->status(std::move(_holdSchedulerCallback)); +} + +// by rpc +void SchedulerManager::call(protocol::Transaction::Ptr tx, + std::function callback) +{ + auto [ok, message] = checkAndInit(); + + if (!ok) + { + SCHEDULER_LOG(DEBUG) << LOG_DESC("call: checkAndInit not ok") << LOG_KV("message", message); + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::ExecutorNotEstablishedError, message), {}); + return; + } + + auto _holdSchedulerCallback = [schedulerHolder = m_scheduler, callback = std::move(callback)]( + bcos::Error::Ptr&& error, + protocol::TransactionReceipt::Ptr&& receipt) { + SCHEDULER_LOG(TRACE) << "Release scheduler holder" + << LOG_KV("ptr count", schedulerHolder.use_count()); + callback(std::move(error), std::move(receipt)); + }; + + m_scheduler->call(tx, std::move(_holdSchedulerCallback)); +} + +// by executor +void SchedulerManager::registerExecutor(std::string name, + bcos::executor::ParallelTransactionExecutorInterface::Ptr executor, + std::function callback) +{ + initSchedulerIfNotExist(); + m_scheduler->registerExecutor(name, executor, std::move(callback)); +} + +void SchedulerManager::unregisterExecutor( + const std::string& name, std::function callback) +{ + initSchedulerIfNotExist(); + m_scheduler->unregisterExecutor(name, std::move(callback)); +} + +// clear all status +void SchedulerManager::reset(std::function callback) +{ + auto [ok, message] = checkAndInit(); + + if (!ok) + { + SCHEDULER_LOG(DEBUG) << LOG_DESC("reset: checkAndInit not ok") + << LOG_KV("message", message); + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::ExecutorNotEstablishedError, message)); + return; + } + + m_scheduler->reset(std::move(callback)); +} + +void SchedulerManager::getCode( + std::string_view contract, std::function callback) +{ + auto [ok, message] = checkAndInit(); + + if (!ok) + { + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::ExecutorNotEstablishedError, message), {}); + return; + } + + auto _holdSchedulerCallback = [schedulerHolder = m_scheduler, callback = std::move(callback)]( + bcos::Error::Ptr&& error, bcos::bytes bytes) { + SCHEDULER_LOG(TRACE) << "Release scheduler holder" + << LOG_KV("ptr count", schedulerHolder.use_count()); + callback(std::move(error), std::move(bytes)); + }; + + + m_scheduler->getCode(contract, std::move(_holdSchedulerCallback)); +} + +void SchedulerManager::getABI( + std::string_view contract, std::function callback) +{ + auto [ok, message] = checkAndInit(); + + if (!ok) + { + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::ExecutorNotEstablishedError, message), {}); + return; + } + + auto _holdSchedulerCallback = [schedulerHolder = m_scheduler, callback = std::move(callback)]( + bcos::Error::Ptr&& error, std::string str) { + SCHEDULER_LOG(TRACE) << "Release scheduler holder" + << LOG_KV("ptr count", schedulerHolder.use_count()); + callback(std::move(error), std::move(str)); + }; + + m_scheduler->getABI(contract, std::move(_holdSchedulerCallback)); +} + +void SchedulerManager::preExecuteBlock( + bcos::protocol::Block::Ptr block, bool verify, std::function callback) +{ + auto [ok, message] = checkAndInit(); + + if (!ok) + { + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::ExecutorNotEstablishedError, message)); + return; + } + + auto _holdSchedulerCallback = [schedulerHolder = m_scheduler, callback = std::move(callback)]( + bcos::Error::Ptr&& error) { + SCHEDULER_LOG(TRACE) << "Release scheduler holder" + << LOG_KV("ptr count", schedulerHolder.use_count()); + callback(std::move(error)); + }; + + m_scheduler->preExecuteBlock(block, verify, std::move(_holdSchedulerCallback)); +} + +std::pair SchedulerManager::checkAndInit() +{ + if (m_status == STOPPED) + { + return {false, "Scheduler has stopped"}; + } + + initSchedulerIfNotExist(); + + if (m_remoteExecutorManager->empty()) + { + return {false, "Waiting to connect some executors..."}; + } + + if (m_status == INITIALING) + { + return {false, "Scheduler is initialing, please wait and retry"}; + } + + if (m_status == SWITCHING) + { + return {false, "Scheduler is switching, please wait and retry"}; + } + + return {true, "ok"}; +} + +void SchedulerManager::asyncSwitchTerm( + int64_t schedulerSeq, std::function callback) +{ + if (m_status == STOPPED) + { + return; + } + + // Will update scheduler session, clear all scheduler & executor block pipeline cache and + // re-dispatch executor + m_pool.enqueue([this, callback = std::move(callback), schedulerSeq]() { + switchTerm(schedulerSeq); + callback(nullptr); + }); +} + + +void SchedulerManager::initSchedulerIfNotExist() +{ + try + { + if (!m_scheduler || m_status == INITIALING) + { + static bcos::SharedMutex mutex; + bcos::WriteGuard lock(mutex); + if (!m_scheduler || m_status == INITIALING) + { + updateScheduler(m_schedulerTerm.getSchedulerTermID()); + m_status.store(RUNNING); + } + } + } + catch (Exception const& _e) + { + SCHEDULER_LOG(FATAL) << "initSchedulerIfNotExist failed" << diagnostic_information(_e); + exit(-1); + } +} + +void SchedulerManager::registerOnSwitchTermHandler( + std::function onSwitchTerm) +{ + // onSwitchTerm(latest Uncommitted blockNumber) + m_onSwitchTermHandlers.push_back(std::move(onSwitchTerm)); +} + +void SchedulerManager::handleNeedSwitchEvent(int64_t oldSchedulerTermID) +{ + auto currentSchedulerTermID = m_schedulerTerm.getSchedulerTermID(); + if (m_status == SWITCHING) + { + SCHEDULER_LOG(DEBUG) << LOG_BADGE("Switch") + << "handleNeedSwitchEvent: SchedulerManager is switching. Ignore." + << LOG_KV("currentSchedulerTermID", currentSchedulerTermID) + << LOG_KV("oldSchedulerTermID", oldSchedulerTermID); + return; + } + else if (currentSchedulerTermID > oldSchedulerTermID) + { + SCHEDULER_LOG(DEBUG) << LOG_BADGE("Switch") + << "handleNeedSwitchEvent: Ignore outdated oldSchedulerTermID" + << LOG_KV("currentSchedulerTermID", currentSchedulerTermID) + << LOG_KV("oldSchedulerTermID", oldSchedulerTermID); + return; + } + else + { + SCHEDULER_LOG(DEBUG) << LOG_BADGE("Switch") << "handleNeedSwitchEvent: Trigger switch " + << LOG_KV("currentSchedulerTermID", currentSchedulerTermID) + << LOG_KV("oldSchedulerTermID", oldSchedulerTermID); + + + asyncSelfSwitchTerm(); + } +} + +void SchedulerManager::testTriggerSwitch() +{ + static std::set blockNumberHasSwitch; + if (utcTime() - m_scheduler->getSchedulerTermId() > 30000) + { + static bcos::SharedMutex mutex; + bcos::WriteGuard l(mutex); + + // Get current blockNumber + std::promise blockNumberFuture; + m_factory->getLedger()->asyncGetBlockNumber( + [&blockNumberFuture](Error::Ptr error, protocol::BlockNumber number) { + if (error) + { + SCHEDULER_LOG(ERROR) << "Scheduler get blockNumber from storage failed"; + blockNumberFuture.set_value(-1); + } + else + { + blockNumberFuture.set_value(number); + } + }); + auto blockNumber = blockNumberFuture.get_future().get(); + + // trigger switch + if (utcTime() - m_scheduler->getSchedulerTermId() > 30000 && + blockNumberHasSwitch.count(blockNumber) == 0) + { + selfSwitchTerm(); + blockNumberHasSwitch.insert(blockNumber); + } + } +} + + +void SchedulerManager::updateScheduler(int64_t schedulerTermId) +{ + if (m_scheduler) + { + if (m_scheduler->getSchedulerTermId() == schedulerTermId) + { + SCHEDULER_LOG(DEBUG) << LOG_BADGE("Switch") + << "SchedulerSwitch: scheduler has just switched, ignore trigger." + << m_scheduler->getSchedulerTermId() << " == " << schedulerTermId; + return; + } + } + auto newScheduler = m_factory->build(schedulerTermId); + + if (m_scheduler) + { + m_scheduler->stop(); + SCHEDULER_LOG(DEBUG) << LOG_BADGE("Switch") << "SchedulerSwitch: scheduler term switch " + << m_scheduler->getSchedulerTermId() << "->" << schedulerTermId; + } + + + m_scheduler = newScheduler; + m_scheduler->setOnNeedSwitchEventHandler( + [this](int64_t oldSchedulerTermID) { handleNeedSwitchEvent(oldSchedulerTermID); }); +} + +void SchedulerManager::switchTerm(int64_t schedulerSeq) +{ + if (m_status == STOPPED) + { + return; + } + + m_status.store(SWITCHING); + try + { + auto newTerm = SchedulerTerm(schedulerSeq); + updateScheduler(newTerm.getSchedulerTermID()); // may throw exception + m_schedulerTerm = newTerm; + + m_status.store(RUNNING); + onSwitchTermNotify(); + } + catch (Exception const& _e) + { + m_status.store(RUNNING); + SCHEDULER_LOG(ERROR) << "switchTerm failed. Re-push to task pool" + << diagnostic_information(_e); + asyncSwitchTerm(schedulerSeq, {}); + } +} + +void SchedulerManager::selfSwitchTerm() +{ + if (m_status == STOPPED) + { + return; + } + + if (m_status == SWITCHING) + { + // is self-switching, just return + return; + } + + m_status.store(SWITCHING); + try + { + auto newTerm = m_schedulerTerm.next(); + updateScheduler(newTerm.getSchedulerTermID()); // may throw exception + m_schedulerTerm = newTerm; + + + m_status.store(RUNNING); + onSwitchTermNotify(); + } + catch (Exception const& _e) + { + m_status.store(RUNNING); + SCHEDULER_LOG(ERROR) << "selfSwitchTerm failed. Re-push to task pool" + << diagnostic_information(_e); + asyncSelfSwitchTerm(); + } +} + +void SchedulerManager::asyncSelfSwitchTerm() +{ + m_pool.enqueue([this]() { selfSwitchTerm(); }); +} + +void SchedulerManager::onSwitchTermNotify() +{ + if (m_status == STOPPED) + { + return; + } + + m_factory->getLedger()->asyncGetBlockNumber( + [this](Error::Ptr error, protocol::BlockNumber blockNumber) { + if (error) + { + SCHEDULER_LOG(ERROR) + << "Could not get blockNumber from ledger on scheduler switch term"; + return; + } + + for (auto& onSwitchTerm : m_onSwitchTermHandlers) + { + onSwitchTerm(blockNumber + 1); + } + }); +} diff --git "a/BFPL\345\243\271/bcos-scheduler/src/SchedulerManager.h" "b/BFPL\345\243\271/bcos-scheduler/src/SchedulerManager.h" new file mode 100644 index 00000000..eec57119 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/SchedulerManager.h" @@ -0,0 +1,157 @@ +#pragma once +#include "TarsRemoteExecutorManager.h" +#include "bcos-scheduler/src/SchedulerFactory.h" +#include "bcos-scheduler/src/SchedulerImpl.h" +#include + +namespace bcos::scheduler +{ +class SchedulerManager : public SchedulerInterface +{ +public: + SchedulerManager(int64_t schedulerSeq, SchedulerFactory::Ptr factory, + TarsRemoteExecutorManager::Ptr remoteExecutorManager) + : m_factory(factory), + m_schedulerTerm(schedulerSeq), + m_remoteExecutorManager(remoteExecutorManager), + m_pool("SchedulerManager", 1), // Must set to 1 for serial execution + m_status(INITIALING) + { + remoteExecutorManager->setRemoteExecutorChangeHandler([this]() { + // trigger switch + asyncSelfSwitchTerm(); + }); + + // Notice: Not to initSchedulerIfNotExist here, because factory need to bind notifier after + // this constructor + } + + // by pbft & sync + void executeBlock(bcos::protocol::Block::Ptr block, bool verify, + std::function + callback) override; + // by pbft & sync + void commitBlock(bcos::protocol::BlockHeader::Ptr header, + std::function callback) + override; + void status( + std::function callback) override; + void call(protocol::Transaction::Ptr tx, + std::function callback) override; + void registerExecutor(std::string name, + bcos::executor::ParallelTransactionExecutorInterface::Ptr executor, + std::function callback) override; + void unregisterExecutor( + const std::string& name, std::function callback) override; + void reset(std::function callback) override; + void getCode( + std::string_view contract, std::function callback) override; + void getABI( + std::string_view contract, std::function callback) override; + + void preExecuteBlock(bcos::protocol::Block::Ptr block, bool verify, + std::function callback) override; + + void asyncSwitchTerm(int64_t schedulerSeq, std::function callback); + + void initSchedulerIfNotExist(); + + void registerOnSwitchTermHandler(std::function onSwitchTerm); + + void handleNeedSwitchEvent(int64_t oldSchedulerTermID); + + void testTriggerSwitch(); + + SchedulerFactory::Ptr getFactory() { return m_factory; } + + class SchedulerTerm + { + public: + SchedulerTerm(int64_t schedulerSeq) + : m_schedulerSeq(schedulerSeq), m_executorSeq(utcTime() / 1000) + {} + + SchedulerTerm next() { return SchedulerTerm(m_schedulerSeq); } + int64_t getSchedulerTermID() + { + int64_t id = (m_schedulerSeq << 32) + m_executorSeq; + if (id <= 0) + { + BCOS_LOG(FATAL) << "SchedulerTermID overflow!" + << LOG_KV("m_schedulerSeq", m_schedulerSeq) + << LOG_KV("m_executorSeq", m_executorSeq) + << LOG_KV("SchedulerTermID", id); + } + BCOS_LOG(DEBUG) << "Build SchedulerTermID" << LOG_KV("m_schedulerSeq", m_schedulerSeq) + << LOG_KV("m_executorSeq", m_executorSeq) + << LOG_KV("SchedulerTermID", id); + + return id; + } + + + private: + int64_t m_schedulerSeq; + int64_t m_executorSeq; + }; + + enum Status : uint8_t + { + INITIALING = 1, + RUNNING = 2, + SWITCHING = 3, + STOPPED = 4, + }; + + std::pair checkAndInit(); + void stop() override + { + if (m_status == STOPPED) + { + std::cout << "scheduler has just stopped." << std::endl; + return; + } + + SCHEDULER_LOG(INFO) << "Try to stop SchedulerManager"; + + m_status.store(STOPPED); + + if (m_scheduler) + { + m_scheduler->stop(); + } + m_remoteExecutorManager->stop(); + + // waiting for stopped + while (m_scheduler.use_count() > 1) + { + SCHEDULER_LOG(DEBUG) << "Scheduler is stopping.. " + << LOG_KV("unfinishedTaskNum", m_scheduler.use_count() - 1); + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + } + SCHEDULER_LOG(INFO) << "scheduler has stopped."; + m_scheduler = nullptr; + } + + void triggerSwitch() { asyncSelfSwitchTerm(); }; + +private: + void updateScheduler(int64_t schedulerTermId); + void switchTerm(int64_t schedulerSeq); + void selfSwitchTerm(); + void asyncSelfSwitchTerm(); + void onSwitchTermNotify(); + +private: + SchedulerImpl::Ptr m_scheduler; + SchedulerFactory::Ptr m_factory; + SchedulerTerm m_schedulerTerm; + TarsRemoteExecutorManager::Ptr m_remoteExecutorManager; + std::vector> m_onSwitchTermHandlers; + + bcos::ThreadPool m_pool; + + std::atomic m_status; +}; + +} // namespace bcos::scheduler diff --git "a/BFPL\345\243\271/bcos-scheduler/src/SerialBlockExecutive.cpp" "b/BFPL\345\243\271/bcos-scheduler/src/SerialBlockExecutive.cpp" new file mode 100644 index 00000000..7b3327c9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/SerialBlockExecutive.cpp" @@ -0,0 +1,272 @@ +#include "SerialBlockExecutive.h" +#include "DmcExecutor.h" +#include "SchedulerImpl.h" +#include "bcos-crypto/bcos-crypto/ChecksumAddress.h" +#include "bcos-framework/executor/ExecuteError.h" +#include "bcos-framework/executor/ExecutionMessage.h" + + +using namespace bcos::scheduler; + + +void SerialBlockExecutive::prepare() +{ + { + if (m_hasPrepared) + { + return; + } + WriteGuard lock(x_prepareLock); + if (m_hasPrepared) + { + return; + } + + uint64_t txSize = 0; + if (m_block->transactionsMetaDataSize() > 0) + { + txSize = m_block->transactionsMetaDataSize(); + } + else if (m_block->transactionsSize() > 0) + { + txSize = m_block->transactionsSize(); + } + else + { + SCHEDULER_LOG(DEBUG) << "BlockExecutive prepare: empty block" + << LOG_KV("block number", m_block->blockHeaderConst()->number()); + } + m_transactions.resize(txSize); + + if (m_executor == nullptr) + { + m_executor = m_scheduler->executorManager()->dispatchExecutor(SERIAL_EXECUTOR_NAME); + m_executorInfo = m_scheduler->executorManager()->getExecutorInfo(SERIAL_EXECUTOR_NAME); + } + } + + BlockExecutive::prepare(); +} + +void SerialBlockExecutive::saveMessage( + std::string, protocol::ExecutionMessage::UniquePtr message, bool) +{ + auto idx = message->contextID() - m_startContextID; + + if (m_transactions.size() <= idx) + { + m_transactions.resize(idx + 1); + } + m_transactions[idx] = std::move(message); +} + +void SerialBlockExecutive::asyncExecute( + std::function callback) +{ + SERIAL_EXECUTE_LOG(INFO) << BLOCK_NUMBER(number()) << LOG_DESC("serialExecute execute block"); + if (m_result) + { + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::InvalidStatus, "Invalid status"), nullptr, + m_isSysBlock); + return; + } + + if (m_scheduler->executorManager()->size() == 0) + { + callback(BCOS_ERROR_UNIQUE_PTR( + SchedulerError::ExecutorNotEstablishedError, "The executor has not started!"), + nullptr, m_isSysBlock); + } + m_currentTimePoint = std::chrono::system_clock::now(); + + auto startT = utcTime(); + prepare(); + + auto createMsgT = utcTime() - startT; + startT = utcTime(); + + if (!m_staticCall) + { + SERIAL_EXECUTE_LOG(INFO) << BLOCK_NUMBER(number()) + << LOG_DESC("serialExecute batch next block"); + // Execute nextBlock + batchNextBlock([this, startT, createMsgT, callback = std::move(callback)]( + Error::UniquePtr error) { + if (!m_isRunning) + { + callback( + BCOS_ERROR_UNIQUE_PTR(SchedulerError::Stopped, "BlockExecutive is stopped"), + nullptr, m_isSysBlock); + return; + } + + if (error) + { + SERIAL_EXECUTE_LOG(ERROR) << "Next block with error!" << error->errorMessage(); + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR( + SchedulerError::NextBlockError, "Next block error!", *error), + nullptr, m_isSysBlock); + + if (error->errorCode() == bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + { + triggerSwitch(); + } + return; + } + + SERIAL_EXECUTE_LOG(INFO) << BLOCK_NUMBER(number()) << LOG_BADGE("BlockTrace") + << LOG_DESC("serialExecute block begin"); + + serialExecute([this, createMsgT, startT, callback = std::move(callback)]( + bcos::Error::UniquePtr error, protocol::BlockHeader::Ptr header, + bool isSysBlock) { + if (error) + { + SERIAL_EXECUTE_LOG(ERROR) + << BLOCK_NUMBER(number()) << LOG_DESC("serialExecute block failed") + << LOG_KV("createMsgT", createMsgT) + << LOG_KV("executeT", (utcTime() - startT)) + << LOG_KV("errorCode", error->errorCode()) + << LOG_KV("errorMessage", error->errorMessage()); + callback(std::move(error), nullptr, isSysBlock); + return; + } + + SERIAL_EXECUTE_LOG(INFO) + << BLOCK_NUMBER(number()) << LOG_DESC("serialExecute success") + << LOG_KV("createMsgT", createMsgT) << LOG_KV("executeT", (utcTime() - startT)) + << LOG_KV("hash", header->hash().abridged()); + callback(nullptr, header, isSysBlock); + }); + }); + } + else + { + serialExecute(std::move(callback)); + } +} + +void SerialBlockExecutive::serialExecute( + std::function callback) +{ + // if is empty block just return + if (m_transactions.empty()) + { + SERIAL_EXECUTE_LOG(INFO) << BLOCK_NUMBER(number()) << "Empty block, just return."; + onExecuteFinish(std::move(callback)); + return; + } + + + SERIAL_EXECUTE_LOG(INFO) << "Send Transaction, size: " << m_transactions.size(); + // handle create message, to generate address + for (auto& tx : m_transactions) + { + if (tx->to().empty()) + { + auto newSeq = tx->seq(); + if (tx->createSalt()) + { + // TODO: Add sender in this process(consider compat with ethereum) + tx->setTo(bcos::newEVMAddress( + m_scheduler->getHashImpl(), tx->from(), tx->data(), *(tx->createSalt()))); + } + else + { + // TODO: Add sender in this process(consider compat with ethereum) + tx->setTo(bcos::newEVMAddress(m_scheduler->getHashImpl(), + m_block->blockHeaderConst()->number(), tx->contextID(), newSeq)); + } + } + + SERIAL_EXECUTE_LOG(DEBUG) << BLOCK_NUMBER(number()) << "0.Send:\t >>>> [" + << m_executorInfo->name << "]: " << tx->toString(); + } + + // send one tx + if (m_staticCall) + { + if (m_transactions.size() != 1) + { + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::CallError, + "The block of call should only has 1 request, but has " + + std::to_string(m_transactions.size())), + nullptr, m_syncBlock); + return; + } + + if (!m_executor) + { + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::CallError, "no executor found"), nullptr, + m_syncBlock); + return; + } + + m_executor->call(std::move(m_transactions[0]), + [this, callback = std::move(callback)]( + bcos::Error::UniquePtr error, bcos::protocol::ExecutionMessage::UniquePtr output) { + if (error) + { + SERIAL_EXECUTE_LOG(DEBUG) + << BLOCK_NUMBER(number()) + << "serialExecute:\t Error: " << error->errorMessage(); + callback(BCOS_ERROR_UNIQUE_PTR( + SchedulerError::SerialExecuteError, error->errorMessage()), + nullptr, m_syncBlock); + + if (error->errorCode() == bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + { + triggerSwitch(); + } + return; + } + + onTxFinish(std::move(output)); + onExecuteFinish(std::move(callback)); + }); + } + else + { + m_executor->executeTransactions(bcos::protocol::SERIAL_EXECUTIVE_FLOW_ADDRESS, + m_transactions, + [this, callback = std::move(callback)](bcos::Error::UniquePtr error, + std::vector outputs) { + if (error) + { + SERIAL_EXECUTE_LOG(DEBUG) + << BLOCK_NUMBER(number()) + << "serialExecute:\t Error: " << error->errorMessage(); + callback(BCOS_ERROR_UNIQUE_PTR( + SchedulerError::SerialExecuteError, error->errorMessage()), + nullptr, m_syncBlock); + + if (error->errorCode() == bcos::executor::ExecuteError::SCHEDULER_TERM_ID_ERROR) + { + triggerSwitch(); + } + return; + } + + for (auto& output : outputs) + { + SERIAL_EXECUTE_LOG(DEBUG) + << BLOCK_NUMBER(number()) << "1.Receive:\t <<<< [" << m_executorInfo->name + << "]: " << output->toString(); + onTxFinish(std::move(output)); + } + onExecuteFinish(std::move(callback)); + }); + } +} + +void SerialBlockExecutive::onExecuteFinish( + std::function callback) +{ + if (!m_staticCall) + { + SERIAL_EXECUTE_LOG(DEBUG) << BLOCK_NUMBER(number()) << "2.Receipt:\t [^^]" + << LOG_KV("receiptsSize", m_executiveResults.size()) + << LOG_KV("blockNumber", number()); + } + BlockExecutive::onExecuteFinish(std::move(callback)); +} diff --git "a/BFPL\345\243\271/bcos-scheduler/src/SerialBlockExecutive.h" "b/BFPL\345\243\271/bcos-scheduler/src/SerialBlockExecutive.h" new file mode 100644 index 00000000..11878d20 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/SerialBlockExecutive.h" @@ -0,0 +1,77 @@ +/* + * 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. + * + * @brief The block executive(context) for serial transaction execution + * @file SerialBlockExecutive.h + * @author: jimmyshi + * @date: 2022-07-12 + */ + + +#pragma once +#include "BlockExecutive.h" + +#define SERIAL_EXECUTE_LOG(LEVEL) SCHEDULER_LOG(LEVEL) << LOG_BADGE("serialExecute") + +namespace bcos::scheduler +{ + +static const std::string SERIAL_EXECUTOR_NAME = "serial_executor"; + +class SerialBlockExecutive : public BlockExecutive +{ +public: + SerialBlockExecutive(bcos::protocol::Block::Ptr block, SchedulerImpl* scheduler, + size_t startContextID, + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory, + bool staticCall, bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::txpool::TxPoolInterface::Ptr _txPool) + : BlockExecutive(block, scheduler, startContextID, transactionSubmitResultFactory, staticCall, + _blockFactory, _txPool){}; + + SerialBlockExecutive(bcos::protocol::Block::Ptr block, SchedulerImpl* scheduler, + size_t startContextID, + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory, + bool staticCall, bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::txpool::TxPoolInterface::Ptr _txPool, uint64_t _gasLimit, bool _syncBlock) + : BlockExecutive(block, scheduler, startContextID, transactionSubmitResultFactory, staticCall, + _blockFactory, _txPool, _gasLimit, _syncBlock) + {} + virtual ~SerialBlockExecutive(){}; + + + void prepare() override; + void asyncExecute( + std::function callback) override; + + void saveMessage( + std::string address, protocol::ExecutionMessage::UniquePtr message, bool withDAG) override; + +private: + void serialExecute( + std::function callback); + + void onExecuteFinish( + std::function callback) override; + + void serialPrepareExecutor() override{ + // do nothing + }; + + std::vector m_transactions; + bcos::executor::ParallelTransactionExecutorInterface::Ptr m_executor; + bcos::scheduler::ExecutorManager::ExecutorInfo::Ptr m_executorInfo; +}; +} // namespace bcos::scheduler diff --git "a/BFPL\345\243\271/bcos-scheduler/src/TarsRemoteExecutorManager.cpp" "b/BFPL\345\243\271/bcos-scheduler/src/TarsRemoteExecutorManager.cpp" new file mode 100644 index 00000000..e28a16c4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/TarsRemoteExecutorManager.cpp" @@ -0,0 +1,307 @@ +// +// Created by Jimmy Shi on 2022/5/25. +// + +#include "TarsRemoteExecutorManager.h" +#include "fisco-bcos-tars-service/Common/TarsUtils.h" +#include +#include +#include +#include +#include + +using namespace bcos::scheduler; +using namespace tars; + + +TarsRemoteExecutorManager::EndPointSet buildEndPointSet( + const std::vector& endPointInfos) +{ + TarsRemoteExecutorManager::EndPointSet endPointSet = + std::make_shared>>(); + + if (endPointInfos.empty()) + { + return endPointSet; + } + + for (const tars::EndpointF& endPointInfo : endPointInfos) + { + if (endPointInfo.host.empty()) + { + continue; // ignore error endpoint info + } + + endPointSet->insert({endPointInfo.host, endPointInfo.port}); + } + return endPointSet; +} + +std::string dumpEndPointsLog(const std::vector& activeEndPoints, + const std::vector& inactiveEndPoints) +{ + // dump logs + std::stringstream ss; + ss << "active:["; + for (const tars::EndpointF& endpointInfo : activeEndPoints) + { + ss << endpointInfo.host << ":" << endpointInfo.port << ", "; + } + ss << "], inactive:["; + for (const tars::EndpointF& endpointInfo : inactiveEndPoints) + { + ss << endpointInfo.host << ":" << endpointInfo.port << ", "; + } + ss << "]"; + return ss.str(); +} + +std::string dumpExecutor2Seq(std::map const& executor2Seq) +{ + // dump logs + std::stringstream ss; + ss << "seq:["; + if (!executor2Seq.empty()) + { + for (auto& it : executor2Seq) + { + ss << it.first << ":" << it.second << ", "; + } + } + ss << "]"; + return ss.str(); +} + + +// return success, activeEndPoints, inactiveEndPoints +static std::tuple, std::vector> +getActiveEndpoints(const std::string& executorServiceName) +{ + static std::string locator = tars::Application::getCommunicator()->getProperty("locator"); + + if (locator.find_first_not_of('@') == std::string::npos) + { + EXECUTOR_MANAGER_LOG(ERROR) << "Tars locator is not valid:" << LOG_KV("locator", locator); + + return {false, {}, {}}; + } + + // TODO: tars, how to impl without tars admin site + static tars::QueryFPrx locatorPrx = + tars::Application::getCommunicator()->stringToProxy(locator); + + std::vector activeEndPoints; + std::vector inactiveEndPoints; + int iRet = locatorPrx->findObjectById4Any( + executorServiceName, activeEndPoints, inactiveEndPoints, tars::ServerConfig::Context); + + if (iRet == 0) + { + return {true, std::move(activeEndPoints), std::move(inactiveEndPoints)}; + } + { + EXECUTOR_MANAGER_LOG(ERROR) << "Tars getActiveEndpoints error." + << LOG_KV("executorServiceName", executorServiceName); + return {false, {}, {}}; + } +} + +std::pair getExecutorStatus( + const std::string& name, bcos::executor::ParallelTransactionExecutorInterface::Ptr executor) +{ + // fetch status + EXECUTOR_MANAGER_LOG(TRACE) << "Query executor status" << LOG_KV("name", name); + std::promise statusPromise; + executor->status([&name, &statusPromise](bcos::Error::UniquePtr error, + bcos::protocol::ExecutorStatus::UniquePtr status) { + if (error) + { + EXECUTOR_MANAGER_LOG(ERROR) << "Could not get executor status" << LOG_KV("name", name) + << LOG_KV("errorCode", error->errorCode()) + << LOG_KV("errorMessage", error->errorMessage()); + statusPromise.set_value(nullptr); + } + else + { + statusPromise.set_value(std::move(status)); + } + }); + + bcos::protocol::ExecutorStatus::UniquePtr status = statusPromise.get_future().get(); + + if (status == nullptr) + { + return {false, nullptr}; + } + else + { + EXECUTOR_MANAGER_LOG(TRACE) << "Get executor status success" << LOG_KV("name", name) + << LOG_KV("statusSeq", status->seq()); + return {true, std::move(status)}; + } +} + +void TarsRemoteExecutorManager::refresh(bool needNotifyChange) +{ + try + { + auto [success, activeEndPoints, inactiveEndPoints] = + getActiveEndpoints(m_executorServiceName); + + if (!success) + { + EXECUTOR_MANAGER_LOG(DEBUG) << "getActiveEndpoints failed" + << LOG_KV("executorServiceName", m_executorServiceName); + return; + } + + EndPointSet currentEndPointMap = buildEndPointSet(activeEndPoints); + + if (*m_endPointSet != *currentEndPointMap) + { + EXECUTOR_MANAGER_LOG(DEBUG) << "Update current endpoint map(for map not the same): " + << dumpEndPointsLog(activeEndPoints, inactiveEndPoints); + update(currentEndPointMap, needNotifyChange); + } + else if (!checkAllExecutorSeq()) + { + EXECUTOR_MANAGER_LOG(DEBUG) + << "Update current endpoint map(for executor seq not the same): " + << dumpEndPointsLog(activeEndPoints, inactiveEndPoints); + update(currentEndPointMap, needNotifyChange); + } + + + EXECUTOR_MANAGER_LOG(DEBUG) + << "Executor endpoint map: " << dumpEndPointsLog(activeEndPoints, inactiveEndPoints) + << ", " << dumpExecutor2Seq(m_executor2Seq); + } + catch (std::exception const& e) + { + EXECUTOR_MANAGER_LOG(ERROR) << "Workloop exception: " << e.what(); + } + catch (...) + { + EXECUTOR_MANAGER_LOG(ERROR) << "WorkLoop exception"; + } +} + +void TarsRemoteExecutorManager::waitForExecutorConnection() +{ + int retryTimes = 1; + do + { + refresh(false); + + if (size() > 0) + { + break; + } + std::string message = + "Waiting for connecting some executors, try times: " + std::to_string(retryTimes) + + ", max retry times: " + std::to_string(m_waitingExecutorMaxRetryTimes); + + std::cout << message << std::endl; + EXECUTOR_MANAGER_LOG(INFO) << message; + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // wait for 1s + + } while (size() == 0 && ++retryTimes <= m_waitingExecutorMaxRetryTimes); + + + if (retryTimes > m_waitingExecutorMaxRetryTimes) + { + // throw error + throw std::runtime_error("Error: Could not connect any executor after " + + std::to_string(m_waitingExecutorMaxRetryTimes) + " times retry"); + } +} + +void TarsRemoteExecutorManager::executeWorker() +{ + refresh(); +} + +void TarsRemoteExecutorManager::update(EndPointSet endPointSet, bool needNotifyChange) +{ + // update + clear(); + m_executor2Seq.clear(); + m_endPointSet = endPointSet; + for (auto hostAndPort : *m_endPointSet) + { + auto host = hostAndPort.first; + auto port = hostAndPort.second; + + string endPointUrl = bcostars::endPointToString(m_executorServiceName, host, port); + auto executorServicePrx = bcostars::createServantProxy( + m_executorServiceName, host, port); + + auto executor = std::make_shared(executorServicePrx); + auto executorName = "executor-" + host + "-" + std::to_string(port); + + // get seq + auto [success, status] = getExecutorStatus(executorName, executor); + if (success) + { + EXECUTOR_MANAGER_LOG(DEBUG) + << "Build new executor connection: " << LOG_KV("endPointUrl", endPointUrl) + << LOG_KV("seq", status->seq()); + m_executor2Seq[executorName] = status->seq(); + addExecutor(executorName, executor); + } + else + { + EXECUTOR_MANAGER_LOG(ERROR) + << "Could not make executor connection, ignore: " << endPointUrl; + } + } + + // trigger handler to notify scheduler + if (needNotifyChange && m_onRemoteExecutorChange != nullptr) + { + m_onRemoteExecutorChange(); + } +} + +bool TarsRemoteExecutorManager::checkAllExecutorSeq() +{ + if (m_executor2Seq.empty()) + { + return true; // is ok, no need to update + } + + bool isSame = true; + + for (auto& it : m_executor2Seq) + { + auto name = it.first; + auto seq = it.second; + auto executorInfo = getExecutorInfoByName(name); + + if (!executorInfo) + { + EXECUTOR_MANAGER_LOG(ERROR) + << "Could not get executor info in checkAllExecutorSeq()" << LOG_KV("name", name); + continue; + } + + auto [success, status] = getExecutorStatus(name, executorInfo->executor); + if (!success) + { + EXECUTOR_MANAGER_LOG(ERROR) + << "Could not query executor status" << LOG_KV("name", name); + continue; + } + + if (status->seq() != seq) + { + EXECUTOR_MANAGER_LOG(DEBUG) + << "Executor seq not the same, need to switch" << LOG_KV("name", name) + << LOG_KV("oldSeq", seq) << LOG_KV("queriedSeq", status->seq()); + + isSame = false; + } + } + + return isSame; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/src/TarsRemoteExecutorManager.h" "b/BFPL\345\243\271/bcos-scheduler/src/TarsRemoteExecutorManager.h" new file mode 100644 index 00000000..4098c733 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/src/TarsRemoteExecutorManager.h" @@ -0,0 +1,98 @@ +/* + * 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. + * + * @brief Manager remote executor + * @file TarsRemoteExecutorManager.h + * @author: jimmyshi + * @date: 2022-05-25 + */ +#pragma once +#include "bcos-scheduler/src/ExecutorManager.h" +#include +#include +#include + + +namespace bcos::scheduler +{ +class TarsRemoteExecutorManager : public ExecutorManager, Worker +{ +public: + using Ptr = std::shared_ptr; + using EndPointSet = std::shared_ptr>>; + + TarsRemoteExecutorManager(std::string executorServiceName) + : Worker("TarsRemoteExecutorManager", 5000) // ms + { + if (executorServiceName.empty()) + { + return; + } + + m_executorServiceName = executorServiceName + "." + bcos::protocol::EXECUTOR_SERVANT_NAME; + + EXECUTOR_MANAGER_LOG(INFO) << "Initialize " << threadName() << " " + << LOG_KV("executorServiceName", m_executorServiceName); + } + + virtual ~TarsRemoteExecutorManager() { stopWorking(); }; + + void start() + { + EXECUTOR_MANAGER_LOG(INFO) << "Start" << threadName() << " " + << LOG_KV("executorServiceName", m_executorServiceName); + waitForExecutorConnection(); + startWorking(); + } + + void waitForExecutorConnection(); + + void setRemoteExecutorChangeHandler(std::function handler) + { + m_onRemoteExecutorChange = std::move(handler); + } + + void executeWorker() override; + + void refresh(bool needNotifyChange = true); + + void update(EndPointSet endPointMap, bool needNotifyChange = true); + + bool empty() { return size() == 0; } + + bool checkAllExecutorSeq(); + + void stop() override + { + EXECUTOR_MANAGER_LOG(INFO) << "Try to stop TarsRemoteExecutorManager"; + if (isWorking()) + { + stopWorking(); + } + ExecutorManager::stop(); + } + +private: + std::function m_onRemoteExecutorChange; + std::string m_executorServiceName; + + boost::condition_variable m_signalled; + boost::mutex x_signalled; + uint8_t m_waitingExecutorMaxRetryTimes = 20; + + EndPointSet m_endPointSet = std::make_shared>>(); + std::map m_executor2Seq; +}; +} // namespace bcos::scheduler diff --git "a/BFPL\345\243\271/bcos-scheduler/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-scheduler/test/CMakeLists.txt" new file mode 100644 index 00000000..f8382a3d --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/CMakeLists.txt" @@ -0,0 +1,15 @@ +file(GLOB_RECURSE SOURCES main.cpp testExecutorManager.cpp testKeyLocks.cpp testScheduler.cpp testChecksumAddress.cpp testDmcStepRecorder.cpp testExecutivePool.cpp testDmcExecutor.cpp testSchedulerImpl.cpp testBlockExecutive.cpp) + +# cmake settings +set(TEST_BINARY_NAME bcos-dispatcher-test) + +add_executable(${TEST_BINARY_NAME} ${SOURCES}) +target_include_directories(${TEST_BINARY_NAME} PRIVATE . ${CMAKE_SOURCE_DIR}/bcos-scheduler/src) +target_compile_options(${TEST_BINARY_NAME} PRIVATE -Wno-unused-variable) + +find_package(Boost REQUIRED unit_test_framework serialization) + +target_link_libraries(${TEST_BINARY_NAME} ${SCHEDULER_TARGET} ${CRYPTO_TARGET} +Boost::unit_test_framework ${TARS_PROTOCOL_TARGET} Boost::serialization ${STORAGE_TARGET} ${SECURITY_TARGET}) + +add_test(NAME test-scheduler WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) diff --git "a/BFPL\345\243\271/bcos-scheduler/test/main.cpp" "b/BFPL\345\243\271/bcos-scheduler/test/main.cpp" new file mode 100644 index 00000000..0e27edc0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/main.cpp" @@ -0,0 +1,3 @@ +#define BOOST_TEST_MAIN + +#include \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/mock/MockBlockExecutive.h" "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockBlockExecutive.h" new file mode 100644 index 00000000..8db31de4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockBlockExecutive.h" @@ -0,0 +1,167 @@ +#pragma once +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/executor/ParallelTransactionExecutorInterface.h" +#include "bcos-framework/protocol/Block.h" +#include "bcos-framework/protocol/BlockHeader.h" +#include "bcos-framework/protocol/BlockHeaderFactory.h" +#include "bcos-framework/protocol/ProtocolTypeDef.h" +#include "bcos-framework/protocol/TransactionMetaData.h" +#include "bcos-framework/protocol/TransactionReceiptFactory.h" +#include "bcos-protocol/TransactionSubmitResultFactoryImpl.h" +#include "bcos-scheduler/src/BlockExecutive.h" +#include "bcos-scheduler/test/mock/MockLedger3.h" +#include "bcos-tars-protocol/bcos-tars-protocol/protocol/BlockFactoryImpl.h" +#include "bcos-tars-protocol/bcos-tars-protocol/protocol/BlockHeaderFactoryImpl.h" +#include "mock/MockLedger.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace bcos; +using namespace bcostars::protocol; +using namespace bcos::crypto; +using namespace bcos::scheduler; + +namespace bcos::test +{ +class MockBlockExecutive : public bcos::scheduler::BlockExecutive +{ +public: + using Ptr = std::shared_ptr; + MockBlockExecutive(bcos::protocol::Block::Ptr block, SchedulerImpl* scheduler, + size_t startContextID, + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory, + bool staticCall, bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::txpool::TxPoolInterface::Ptr _txPool) + : BlockExecutive(block, scheduler, startContextID, transactionSubmitResultFactory, staticCall, + _blockFactory, _txPool) + {} + + MockBlockExecutive(bcos::protocol::Block::Ptr block, SchedulerImpl* scheduler, + size_t startContextID, + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory, + bool staticCall, bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::txpool::TxPoolInterface::Ptr _txPool, uint64_t _gasLimit, bool _syncBlock) + : BlockExecutive(block, scheduler, startContextID, transactionSubmitResultFactory, staticCall, + _blockFactory, _txPool) + { + m_blockNumber = block->blockHeader()->number(); + m_syncBlock = _syncBlock; + m_gasLimit = _gasLimit; + } + virtual ~MockBlockExecutive(){}; + + void prepare() override + { + // do nothing + } + void asyncCall( + std::function callback) + override + { + asyncExecute([executive = shared_from_this(), callback]( + Error::UniquePtr&& _error, protocol::BlockHeader::Ptr, bool) { + if (!executive) + { + callback(BCOS_ERROR_UNIQUE_PTR( + SchedulerError::UnknownError, "get block executive failed"), + nullptr); + return; + } + auto receipt = std::const_pointer_cast( + executive->block()->receipt(0)); + callback(std::move(_error), std::move(receipt)); + }); + } + void asyncNotify( + std::function)>&, + std::function _callback) override + { + // do nothing + _callback(nullptr); + } + void asyncExecute( + std::function callback) override + { + if (m_result) + { + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::InvalidStatus, "Invalid status"), + nullptr, m_isSysBlock); + SCHEDULER_LOG(DEBUG) << "i am asyncExecute error"; + return; + } + else + { + SCHEDULER_LOG(DEBUG) << "MockBlockExecutive asyncExecute begin"; + bcos::crypto::Hash::Ptr hashImpl = std::make_shared(); + bcos::crypto::SignatureCrypto::Ptr signature = std::make_shared(); + bcos::crypto::CryptoSuite::Ptr suite = + std::make_shared(hashImpl, signature, nullptr); + m_blockHeaderFactory = + std::make_shared(suite); + auto blockHeader = m_blockHeaderFactory->createBlockHeader(); + SCHEDULER_LOG(DEBUG) << LOG_KV("BlockNumber", m_blockNumber) + << LOG_KV("blockHeader", blockHeader); + blockHeader->setNumber(m_blockNumber); + m_result = blockHeader; + callback(nullptr, blockHeader, false); + return; + } + } + void asyncCommit(std::function callback) override + { + if (m_blockNumber <= 5) + { + // m_ledger->commitSuccess(false); + callback(BCOS_ERROR_UNIQUE_PTR(SchedulerError::CommitError, "asyncCommit errors!")); + } + else + { + // m_ledger->commitSuccess(true); + callback(nullptr); + } + } + bcos::protocol::BlockNumber number() { return m_block->blockHeaderConst()->number(); } + bcos::protocol::Block::Ptr block() { return m_block; } + bool sysBlock() const { return m_isSysBlock; } + + void triggerSwitch() + { + if (f_onNeedSwitchEvent) + { + f_onNeedSwitchEvent(); + } + } + void setOnNeedSwitchEventHandler(std::function onNeedSwitchEvent) + { + f_onNeedSwitchEvent = std::move(onNeedSwitchEvent); + } + +private: + bcos::protocol::Block::Ptr m_block; + bcostars::protocol::BlockHeaderFactoryImpl::Ptr m_blockHeaderFactory; + bcos::protocol::BlockNumber m_blockNumber; + bcos::test::MockLedger3::Ptr m_ledger; + size_t m_gasLimit = TRANSACTION_GAS; + bool m_isSysBlock; + bool m_syncBlock = false; + +public: + struct CommitStatus + { + std::atomic_size_t total; + std::atomic_size_t success = 0; + std::atomic_size_t failed = 0; + std::function checkAndCommit; + mutable SharedMutex x_lock; + }; +}; +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/mock/MockBlockExecutiveFactory.h" "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockBlockExecutiveFactory.h" new file mode 100644 index 00000000..5684bf29 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockBlockExecutiveFactory.h" @@ -0,0 +1,78 @@ +#pragma once +#include "bcos-framework/protocol/Block.h" +#include "bcos-framework/protocol/TransactionReceiptFactory.h" +#include "bcos-protocol/TransactionSubmitResultFactoryImpl.h" +#include "bcos-scheduler/src/BlockExecutive.h" +#include "bcos-scheduler/src/BlockExecutiveFactory.h" +#include "bcos-scheduler/src/Common.h" +#include "bcos-scheduler/src/SerialBlockExecutive.h" +#include "bcos-scheduler/test/mock/MockBlockExecutive.h" +#include +#include +#include + +using namespace bcos; +using namespace bcos::scheduler; +using namespace bcos::protocol; + +namespace bcos::test +{ +class MockBlockExecutiveFactory : public bcos::scheduler::BlockExecutiveFactory +{ +public: + using Ptr = std::shared_ptr; + MockBlockExecutiveFactory(bool isSerialExecute) : BlockExecutiveFactory(isSerialExecute) {} + virtual ~MockBlockExecutiveFactory() {} + + std::shared_ptr build(bcos::protocol::Block::Ptr block, + SchedulerImpl* scheduler, size_t startContextID, + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory, + bool staticCall, bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::txpool::TxPoolInterface::Ptr _txPool) override + { + if (m_isSerialExecute) + { + SCHEDULER_LOG(DEBUG) << "-----building MockSerialBlockExecutive-----"; + auto serialBlockExecutive = std::make_shared(block, scheduler, + startContextID, transactionSubmitResultFactory, staticCall, _blockFactory, _txPool); + return serialBlockExecutive; + } + else + { + SCHEDULER_LOG(DEBUG) << "-----building MockBlockExecutive-----"; + auto blockExecutive = std::make_shared(block, scheduler, + startContextID, transactionSubmitResultFactory, staticCall, _blockFactory, _txPool); + return blockExecutive; + } + } + + std::shared_ptr build(bcos::protocol::Block::Ptr block, + SchedulerImpl* scheduler, size_t startContextID, + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory, + bool staticCall, bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::txpool::TxPoolInterface::Ptr _txPool, uint64_t _gasLimit, bool _syncBlock) override + { + if (m_isSerialExecute) + { + SCHEDULER_LOG(DEBUG) << "-----building MockSerialBlockExecutive-----" + << LOG_KV("serialExecuteFlag", m_isSerialExecute); + auto serialBlockExecutive = std::make_shared(block, scheduler, + startContextID, transactionSubmitResultFactory, staticCall, _blockFactory, _txPool, + _gasLimit, _syncBlock); + return serialBlockExecutive; + } + else + { + SCHEDULER_LOG(DEBUG) << "-----building MockBlockExecutive-----" + << LOG_KV("serialExecuteFlag", m_isSerialExecute); + auto blockExecutive = std::make_shared(block, scheduler, + startContextID, transactionSubmitResultFactory, staticCall, _blockFactory, _txPool, + _gasLimit, _syncBlock); + return blockExecutive; + } + } + +private: + bool m_isSerialExecute = false; +}; +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/mock/MockDeadLockExecutor.h" "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockDeadLockExecutor.h" new file mode 100644 index 00000000..7e68962b --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockDeadLockExecutor.h" @@ -0,0 +1,117 @@ +#pragma once +#include "MockExecutor.h" +#include "bcos-framework/executor/ExecutionMessage.h" + +namespace bcos::test +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +class MockDeadLockParallelExecutor : public MockParallelExecutor +{ +public: + using MockParallelExecutor::MockParallelExecutor; + + ~MockDeadLockParallelExecutor() override {} + + void executeTransaction(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + { + std::set> contracts = {"contract1", "contract2"}; + BOOST_CHECK(contracts.count(input->to()) == 1); + BOOST_CHECK(input->contextID() == 0 || input->contextID() == 1); + + if (input->type() == protocol::ExecutionMessage::REVERT) + { + if (input->contextID() == 1) + { + if (input->seq() == 1) + { + BOOST_CHECK_EQUAL(input->to(), "contract1"); + BOOST_CHECK_EQUAL(input->from(), "contract1"); + } + else if (input->seq() == 0) + { + BOOST_CHECK_EQUAL(input->to(), "contract1"); + BOOST_CHECK_EQUAL(input->from(), "contract1"); + } + else + { + BOOST_FAIL("Unexecuted seq"); + } + } + else + { + if (input->seq() == 1) + { + BOOST_CHECK_EQUAL(input->to(), "contract2"); + BOOST_CHECK_EQUAL(input->from(), "contract2"); + } + else if (input->seq() == 0) + { + BOOST_CHECK_EQUAL(input->to(), "contract2"); + BOOST_CHECK_EQUAL(input->from(), "contract2"); + } + else + { + BOOST_FAIL("Unexecuted seq"); + } + } + } + else if (input->type() == protocol::ExecutionMessage::TXHASH) + { + BOOST_CHECK_EQUAL(input->seq(), 0); + input->setType(protocol::ExecutionMessage::MESSAGE); + if (input->to() == "contract1") + { + input->setFrom("contract1"); + input->setTo("contract2"); + input->setKeyLocks({"key1"}); + } + else + { + input->setFrom("contract2"); + input->setTo("contract1"); + input->setKeyLocks({"key2"}); + } + } + else if (input->type() == protocol::ExecutionMessage::MESSAGE) + { + BOOST_CHECK_GT(input->seq(), 0); + input->setType(protocol::ExecutionMessage::KEY_LOCK); + + if (input->contextID() == 1) + { + BOOST_CHECK_EQUAL(input->keyLocks()[0], "key1"); + input->setFrom(std::string(input->to())); + input->setKeyLocks({}); + input->setKeyLockAcquired("key1"); + } + else + { + BOOST_CHECK_EQUAL(input->keyLocks()[0], "key2"); + input->setFrom(std::string(input->to())); + input->setKeyLocks({}); + input->setKeyLockAcquired("key2"); + } + } + else if (input->type() == protocol::ExecutionMessage::KEY_LOCK) + { + std::set> contracts = {"contract1", "contract2"}; + BOOST_CHECK_EQUAL(contracts.count(input->to()), 1); + input->setType(protocol::ExecutionMessage::FINISHED); + } + else if (input->type() == protocol::ExecutionMessage::FINISHED) + { + std::set> contracts = {"contract1", "contract2"}; + BOOST_CHECK_EQUAL(contracts.count(input->to()), 1); + } + + callback(nullptr, std::move(input)); + } + + std::string m_name; + bcos::protocol::BlockNumber m_blockNumber = 0; +}; +#pragma GCC diagnostic pop +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/mock/MockDmcExecutor.h" "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockDmcExecutor.h" new file mode 100644 index 00000000..e8abee4e --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockDmcExecutor.h" @@ -0,0 +1,239 @@ +#pragma once +#include "Common.h" +#include "bcos-framework/executor/ExecuteError.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/executor/NativeExecutionMessage.h" +#include "bcos-framework/executor/ParallelTransactionExecutorInterface.h" +#include "bcos-scheduler/src/DmcExecutor.h" +#include +#include + + +using namespace std; +using namespace bcos; +using namespace bcos::executor; +using namespace bcos::scheduler; +namespace bcos::test +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +class MockDmcExecutor : public bcos::executor::ParallelTransactionExecutorInterface +{ +public: + using executorStatus = bcos::scheduler::DmcExecutor::Status; + using Ptr = std::shared_ptr(); + MockDmcExecutor(const std::string& name) : m_name(name) {} + + virtual ~MockDmcExecutor() {} + // const std::string& name() const { return m_name; } + + void status( + std::function) + override + {} + + void dmcCall(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + { + // auto output = std::make_unique(); + // output = std::move(input); + SCHEDULER_LOG(DEBUG) << LOG_KV(",input type ", input->type()); + if (input->type() == bcos::protocol::ExecutionMessage::TXHASH) + { + std::string str = "Call error, Need Switch!"; + input->setData(bcos::bytes(str.begin(), str.end())); + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::SCHEDULER_TERM_ID_ERROR, "Call is error"), + std::move(input)); + SCHEDULER_LOG(DEBUG) << LOG_KV("call error, input type is ", input->type()); + return; + } + else + { + if (input->to() == "0xaabbccdd") + { + std::string str = "Call Finished!"; + input->setType(protocol::ExecutionMessage::FINISHED); + input->setData(bcos::bytes(str.begin(), str.end())); + input->setGasAvailable(123456); + SCHEDULER_LOG(DEBUG) << LOG_KV("call finished, input type is ", input->type()); + callback(nullptr, std::move(input)); + } + else + { + DMC_LOG(FATAL) << "Error! Need schedulerOut, But perform Call!"; + assert(false); + callback( + BCOS_ERROR_UNIQUE_PTR(ExecuteError::SCHEDULER_TERM_ID_ERROR, "Call is error"), + std::move(input)); + return; + } + } + } + + + void dmcExecuteTransactions(std::string contractAddress, + gsl::span inputs, + std::function)> + callback) override + { + SCHEDULER_LOG(DEBUG) << LOG_KV("inputs size ", inputs.size()); + std::vector results(inputs.size()); + for (auto i = 0u; i < inputs.size(); i++) + { + SCHEDULER_LOG(DEBUG) << "begin dmcExecute" << LOG_KV("input type ", inputs[i]->type()); + results.at(i) = std::move(inputs[i]); + if (results.at(i)->to() == "aabbccdd") + { + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::EXECUTE_ERROR, "execute is error"), + std::move(results)); + return; + } + + if (results[i]->type() == bcos::protocol::ExecutionMessage::KEY_LOCK) + { + SCHEDULER_LOG(DEBUG) << "setData, " + << "type is keyLocks"; + std::string str = "DMCExecuteTransaction Finish, I am keyLock!"; + results[i]->setData(bcos::bytes(str.begin(), str.end())); + } + else + { + SCHEDULER_LOG(DEBUG) << "setData, " + << "type is not keyLocks"; + results[i]->setType(bcos::protocol::ExecutionMessage::FINISHED); + std::string str = "DMCExecuteTransaction Finish!"; + results[i]->setData(bcos::bytes(str.begin(), str.end())); + } + } + SCHEDULER_LOG(DEBUG) << LOG_KV("results size ", results.size()); + callback(nullptr, std::move(results)); + }; + + void nextBlockHeader(int64_t, const bcos::protocol::BlockHeader::ConstPtr& blockHeader, + std::function callback) override + { + SCHEDULER_LOG(TRACE) << "Receiving nextBlock: " << blockHeader->number(); + m_blockNumber = blockHeader->number(); + callback(nullptr); // always success + } + + + void executeTransaction(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + { + if (input->to() == "0xaabbccdd") + { + callback(BCOS_ERROR_UNIQUE_PTR(-1, "i am an error!!!!"), nullptr); + return; + } + + // Always success + BOOST_CHECK(input); + if (input->type() == bcos::protocol::ExecutionMessage::TXHASH) + { + BOOST_CHECK_NE(input->transactionHash(), bcos::crypto::HashType()); + } + + input->setStatus(0); + input->setMessage(""); + + std::string data = "Hello world!"; + input->setData(bcos::bytes(data.begin(), data.end())); + input->setType(bcos::protocol::ExecutionMessage::FINISHED); + + callback(nullptr, std::move(input)); + } + + void call(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + {} + + void executeTransactions(std::string, gsl::span, + std::function)>) override + {} + + void dagExecuteTransactions(gsl::span inputs, + std::function)> + callback) override + { + std::vector messages(inputs.size()); + for (auto i = 0u; i < inputs.size(); ++i) + { + auto [it, inserted] = m_dagHashes.emplace(inputs[i]->transactionHash()); + boost::ignore_unused(it); + BOOST_TEST(inserted); + if (inputs[i]->to() == "aabbccdd") + { + callback(BCOS_ERROR_UNIQUE_PTR(ExecuteError::EXECUTE_ERROR, + "Execute failed with empty blockContext!"), + {}); + return; + } + + // SCHEDULER_LOG(TRACE) << "Executing: " << inputs[i].get(); + BOOST_TEST(inputs[i].get()); + messages.at(i) = std::move(inputs[i]); + if (i < 5) + { + messages[i]->setType(protocol::ExecutionMessage::SEND_BACK); + } + else + { + messages[i]->setType(protocol::ExecutionMessage::FINISHED); + std::string result = "OK!"; + messages[i]->setData(bcos::bytes(result.begin(), result.end())); + } + } + + callback(nullptr, std::move(messages)); + } + + void getHash(bcos::protocol::BlockNumber number, + std::function callback) override + { + callback(nullptr, h256(12345)); + } + + void prepare(const bcos::protocol::TwoPCParams& params, + std::function callback) override + { + callback(nullptr); + } + + void commit(const bcos::protocol::TwoPCParams& params, + std::function callback) override + { + callback(nullptr); + } + + void rollback(const bcos::protocol::TwoPCParams& params, + std::function callback) override + { + callback(nullptr); + } + + void getCode(std::string_view contract, + std::function callback) override + { + callback(nullptr, {}); + } + void getABI(std::string_view contract, + std::function callback) override + { + callback(nullptr, {}); + } + void reset(std::function callback) override { callback(nullptr); } + + + std::string m_name; + bcos::protocol::BlockNumber m_blockNumber = 0; + std::set m_dagHashes; +}; +#pragma GCC diagnostic pop +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutor.h" "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutor.h" new file mode 100644 index 00000000..de33669c --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutor.h" @@ -0,0 +1,185 @@ +#pragma once +#include "../../src/Common.h" +#include "Common.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/executor/ParallelTransactionExecutorInterface.h" +#include "bcos-framework/protocol/ProtocolTypeDef.h" +#include +#include + +using namespace bcos; +using namespace bcos::executor; +namespace bcos::test +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +class MockParallelExecutor : public bcos::executor::ParallelTransactionExecutorInterface +{ +public: + MockParallelExecutor(const std::string& name) : m_name(name) {} + + ~MockParallelExecutor() override {} + + const std::string& name() const { return m_name; } + + void nextBlockHeader(int64_t schedulerTermId, + const bcos::protocol::BlockHeader::ConstPtr& blockHeader, + std::function callback) override + { + SCHEDULER_LOG(TRACE) << "Receiving nextBlock: " << blockHeader->number(); + m_blockNumber = blockHeader->number(); + callback(nullptr); // always success + } + + void executeTransaction(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + { + if (input->transactionHash() == h256(10086)) + { + callback(BCOS_ERROR_UNIQUE_PTR(-1, "i am an error!!!!"), nullptr); + return; + } + + // Always success + BOOST_CHECK(input); + if (input->type() == bcos::protocol::ExecutionMessage::TXHASH) + { + BOOST_CHECK_NE(input->transactionHash(), bcos::crypto::HashType()); + } + + input->setStatus(0); + input->setMessage(""); + + std::string data = "Hello world!"; + input->setData(bcos::bytes(data.begin(), data.end())); + input->setType(bcos::protocol::ExecutionMessage::FINISHED); + + callback(nullptr, std::move(input)); + } + + void call(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + {} + + + void executeTransactions(std::string contractAddress, + gsl::span inputs, + + std::function)> + callback) override + { + std::vector results(inputs.size()); + for (auto i = 0u; i < inputs.size(); i++) + { + executeTransaction(std::move(inputs[i]), + [&](bcos::Error::UniquePtr, bcos::protocol::ExecutionMessage::UniquePtr result) { + results[i] = std::move(result); + }); + } + callback(nullptr, std::move(results)); + }; + + void dagExecuteTransactions(gsl::span inputs, + std::function)> + callback) override + { + BOOST_CHECK_EQUAL(inputs.size(), 100); + + std::vector messages(inputs.size()); + for (auto i = 0u; i < inputs.size(); ++i) + { + auto [it, inserted] = m_dagHashes.emplace(inputs[i]->transactionHash()); + boost::ignore_unused(it); + BOOST_TEST(inserted); + + // SCHEDULER_LOG(TRACE) << "Executing: " << inputs[i].get(); + BOOST_TEST(inputs[i].get()); + BOOST_CHECK_EQUAL(inputs[i]->type(), protocol::ExecutionMessage::TXHASH); + messages.at(i) = std::move(inputs[i]); + if (i < 50) + { + messages[i]->setType(protocol::ExecutionMessage::SEND_BACK); + } + else + { + messages[i]->setType(protocol::ExecutionMessage::FINISHED); + + std::string result = "OK!"; + messages[i]->setData(bcos::bytes(result.begin(), result.end())); + } + } + + callback(nullptr, std::move(messages)); + } + + void dmcExecuteTransactions(std::string contractAddress, + gsl::span inputs, + + std::function)> + callback) override + { + std::vector results(inputs.size()); + for (auto i = 0u; i < inputs.size(); i++) + { + executeTransaction(std::move(inputs[i]), + [&](bcos::Error::UniquePtr, bcos::protocol::ExecutionMessage::UniquePtr result) { + results[i] = std::move(result); + }); + } + callback(nullptr, std::move(results)); + }; + + void dmcCall(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + {} + + void getHash(bcos::protocol::BlockNumber number, + std::function callback) override + { + callback(nullptr, h256(12345)); + } + + void prepare(const bcos::protocol::TwoPCParams& params, + std::function callback) override + { + callback(nullptr); + } + + void commit(const bcos::protocol::TwoPCParams& params, + std::function callback) override + { + callback(nullptr); + } + + void rollback(const bcos::protocol::TwoPCParams& params, + std::function callback) override + { + callback(nullptr); + } + + void getCode(std::string_view contract, + std::function callback) override + { + callback(nullptr, {}); + } + void getABI(std::string_view contract, + std::function callback) override + { + callback(nullptr, {}); + } + void reset(std::function callback) override {} + + void clear() { m_dagHashes.clear(); } + + std::string m_name; + bcos::protocol::BlockNumber m_blockNumber = 0; + std::set m_dagHashes; +}; +#pragma GCC diagnostic pop +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutor3.h" "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutor3.h" new file mode 100644 index 00000000..cc61fb86 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutor3.h" @@ -0,0 +1,119 @@ +#pragma once +#include "Common.h" +#include "MockExecutor.h" +#include +#include + +namespace bcos::test +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +class MockParallelExecutor3 : public MockParallelExecutor +{ +public: + MockParallelExecutor3(const std::string& name) : MockParallelExecutor(name) {} + + ~MockParallelExecutor3() override {} + + void executeTransaction(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + { + BOOST_CHECK_EQUAL(input->type(), protocol::ExecutionMessage::TXHASH); + BOOST_CHECK_GE(input->contextID(), 0); + + // if (input->transactionHash() == h256(9)) + // { + // BOOST_CHECK(input->to().find("contract") == std::string::npos); + // } + + // Always success + SCHEDULER_LOG(TRACE) << "Input, hash:" << input->transactionHash().hex() + << " contract:" << input.get() << " to:" << input->to(); + BOOST_CHECK(input); + BOOST_CHECK(!input->to().empty()); + BOOST_CHECK_EQUAL(input->depth(), 0); + BOOST_CHECK_EQUAL(input->gasAvailable(), gasLimit); + + input->setType(bcos::protocol::ExecutionMessage::FINISHED); + input->setStatus(0); + input->setMessage(""); + + std::string data = "Hello world!"; + input->setData(bcos::bytes(data.begin(), data.end())); + + auto [it, inserted] = txHashes.emplace(input->transactionHash()); + (void)it; + if (!inserted) + { + BOOST_FAIL("Duplicate hash: " + input->transactionHash().hex()); + } + + auto [it3, inserted3] = contextIDs.emplace(input->contextID()); + (void)it3; + if (!inserted3) + { + BOOST_FAIL( + "Duplicate contextID: " + boost::lexical_cast(input->contextID())); + } + + auto inputShared = + std::make_shared(std::move(input)); + + auto [it2, inserted2] = batchContracts.emplace(std::string((*inputShared)->to()), + [callback, inputShared]() { callback(nullptr, std::move(*inputShared)); }); + + (void)it2; + if (!inserted2) + { + BOOST_FAIL("Duplicate contract: " + std::string((*inputShared)->to())); + } + + if (batchContracts.size() == 3) + { + // current batch is finished + std::map> results; + results.swap(batchContracts); + + for (auto& it : results) + { + (it.second)(); + } + } + } + + void prepare(const bcos::protocol::TwoPCParams& params, + std::function callback) override + { + BOOST_CHECK_EQUAL(params.number, 100); + callback(nullptr); + } + + void commit(const bcos::protocol::TwoPCParams& params, + std::function callback) override + { + BOOST_CHECK_EQUAL(params.number, 100); + callback(nullptr); + } + + void rollback(const bcos::protocol::TwoPCParams& params, + std::function callback) override + { + BOOST_CHECK_EQUAL(params.number, 100); + callback(nullptr); + } + + void getHash(bcos::protocol::BlockNumber number, + std::function callback) override + { + BOOST_CHECK_EQUAL(number, 100); + callback(nullptr, h256(255)); + } + + std::map> batchContracts; + std::set txHashes; + std::set contextIDs; + size_t gasLimit = 300000000; +}; +#pragma GCC diagnostic pop +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutorForCall.h" "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutorForCall.h" new file mode 100644 index 00000000..a5c4977b --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutorForCall.h" @@ -0,0 +1,87 @@ +#pragma once + +#include "Common.h" +#include "MockExecutor.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include +#include +#include + +namespace bcos::test +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +class MockParallelExecutorForCall : public MockParallelExecutor +{ +public: + using Ptr = std::shared_ptr(); + MockParallelExecutorForCall(const std::string& name) : MockParallelExecutor(name) {} + + ~MockParallelExecutorForCall() override {} + + void nextBlockHeader(int64_t schedulerTermId, + const bcos::protocol::BlockHeader::ConstPtr& blockHeader, + std::function callback) override + { + BOOST_FAIL("Unexpected nextBlockHeader!"); + } + + void executeTransaction(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + { + BOOST_FAIL("Unexpected execute!"); + } + + void dmcCall(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + { + if (input->to() == precompiled::AUTH_COMMITTEE_ADDRESS) + { + if (input->create()) + { + callback(BCOS_ERROR_UNIQUE_PTR(-1, "deploy sys contract!"), nullptr); + return; + } + input->setType(protocol::ExecutionMessage::FINISHED); + std::string data = "Hello world! response"; + input->setData(bcos::bytes(data.begin(), data.end())); + input->setStatus(0); + input->setGasAvailable(123456); + callback(nullptr, std::move(input)); + return; + } + + BOOST_CHECK_EQUAL(input->type(), protocol::ExecutionMessage::MESSAGE); + BOOST_CHECK_EQUAL(input->to(), "address_to"); + // BOOST_CHECK_EQUAL(input->from().size(), 40); + + auto inputBytes = input->data(); + std::string inputStr((char*)inputBytes.data(), inputBytes.size()); + + BOOST_CHECK_EQUAL(inputStr, "Hello world! request"); + + input->setType(protocol::ExecutionMessage::FINISHED); + + std::string data = "Hello world! response"; + input->setData(bcos::bytes(data.begin(), data.end())); + input->setStatus(0); + input->setGasAvailable(123456); + callback(nullptr, std::move(input)); + } + + void getHash(bcos::protocol::BlockNumber number, + std::function callback) override + { + BOOST_CHECK_GT(number, 0); + callback(nullptr, h256(255)); + } + + std::map> batchContracts; + std::set txHashes; + std::set contextIDs; + int count = 0; +}; +#pragma GCC diagnostic pop +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutorForCreate.h" "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutorForCreate.h" new file mode 100644 index 00000000..c0c8e9ae --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutorForCreate.h" @@ -0,0 +1,90 @@ +#pragma once + +#include "Common.h" +#include "MockExecutor.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include +#include + +namespace bcos::test +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +class MockParallelExecutorForCreate : public MockParallelExecutor +{ +public: + using Ptr = std::shared_ptr(); + MockParallelExecutorForCreate(const std::string& name) : MockParallelExecutor(name) {} + + ~MockParallelExecutorForCreate() override {} + + void executeTransaction(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + { + SCHEDULER_LOG(TRACE) << "Execute, type: " << input->type() + << " contextID: " << input->contextID() << " seq: " << input->seq(); + + if (count == 0) + { + BOOST_CHECK_EQUAL(input->type(), protocol::ExecutionMessage::TXHASH); + } + else if (count == 1) + { + BOOST_CHECK_EQUAL(input->type(), protocol::ExecutionMessage::MESSAGE); + } + else + { + BOOST_CHECK_EQUAL(input->type(), protocol::ExecutionMessage::FINISHED); + } + BOOST_CHECK_GE(input->contextID(), 0); + + SCHEDULER_LOG(TRACE) << "contract address: " << input->to(); + BOOST_CHECK(input->to().find("contract") == std::string::npos); + BOOST_CHECK_EQUAL(input->to().size(), 40); + + // Always success + SCHEDULER_LOG(TRACE) << "Input, hash:" << input->transactionHash().hex() + << " contract:" << input.get() << " to:" << input->to(); + BOOST_CHECK(input); + BOOST_CHECK(!input->to().empty()); + BOOST_CHECK_EQUAL(input->depth(), 0); + BOOST_CHECK_EQUAL(input->gasAvailable(), gasLimit); + + if (count == 0) + { + input->setType(bcos::protocol::ExecutionMessage::MESSAGE); + input->setTo(""); + } + else + { + input->setType(bcos::protocol::ExecutionMessage::FINISHED); + } + input->setStatus(0); + input->setMessage(""); + + std::string data = "Hello world!"; + + input->setData(bcos::bytes(data.begin(), data.end())); + + ++count; + + callback(nullptr, std::move(input)); + } + + + void getHash(bcos::protocol::BlockNumber number, + std::function callback) override + { + BOOST_CHECK_GT(number, 0); + callback(nullptr, h256(255)); + } + + std::map> batchContracts; + std::set txHashes; + std::set contextIDs; + int count = 0; + size_t gasLimit = 300000000; +}; +#pragma GCC diagnostic pop +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutorForMessageDAG.h" "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutorForMessageDAG.h" new file mode 100644 index 00000000..9e809560 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockExecutorForMessageDAG.h" @@ -0,0 +1,163 @@ +#pragma once +#include "Common.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/executor/ParallelTransactionExecutorInterface.h" +#include "bcos-framework/protocol/ProtocolTypeDef.h" +#include +#include + +namespace bcos::test +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +class MockParallelExecutorByMessage : public bcos::executor::ParallelTransactionExecutorInterface +{ +public: + MockParallelExecutorByMessage(const std::string& name) : m_name(name) {} + + ~MockParallelExecutorByMessage() override {} + + const std::string& name() const { return m_name; } + + void nextBlockHeader(int64_t schedulerTermId, + const bcos::protocol::BlockHeader::ConstPtr& blockHeader, + std::function callback) override + { + SCHEDULER_LOG(TRACE) << "Receiving nextBlock: " << blockHeader->number(); + m_blockNumber = blockHeader->number(); + callback(nullptr); // always success + } + + void executeTransaction(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + { + // Always success + BOOST_CHECK(input); + if (input->type() == bcos::protocol::ExecutionMessage::MESSAGE) + { + BOOST_CHECK_NE(input->transactionHash(), bcos::crypto::HashType()); + } + + input->setStatus(0); + input->setMessage(""); + + std::string data = "Hello world!"; + input->setData(bcos::bytes(data.begin(), data.end())); + input->setType(bcos::protocol::ExecutionMessage::FINISHED); + + callback(nullptr, std::move(input)); + } + + void call(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + {} + + void executeTransactions(std::string contractAddress, + gsl::span inputs, + + std::function)> + callback) override + { + std::vector results(inputs.size()); + for (auto i = 0u; i < inputs.size(); i++) + { + executeTransaction(std::move(inputs[i]), + [&](bcos::Error::UniquePtr, bcos::protocol::ExecutionMessage::UniquePtr result) { + results[i] = std::move(result); + }); + } + callback(nullptr, std::move(results)); + }; + + void dagExecuteTransactions(gsl::span inputs, + std::function)> + callback) override + { + BOOST_CHECK_EQUAL(inputs.size(), 100); + + std::vector messages(inputs.size()); + for (auto i = 0u; i < inputs.size(); ++i) + { + BOOST_TEST(inputs[i].get()); + BOOST_CHECK_EQUAL(inputs[i]->type(), protocol::ExecutionMessage::MESSAGE); + messages.at(i) = std::move(inputs[i]); + messages[i]->setType(protocol::ExecutionMessage::FINISHED); + + std::string result = "OK!"; + messages[i]->setData(bcos::bytes(result.begin(), result.end())); + } + + callback(nullptr, std::move(messages)); + } + + void dmcExecuteTransactions(std::string contractAddress, + gsl::span inputs, + + std::function)> + callback) override + { + std::vector results(inputs.size()); + for (auto i = 0u; i < inputs.size(); i++) + { + executeTransaction(std::move(inputs[i]), + [&](bcos::Error::UniquePtr, bcos::protocol::ExecutionMessage::UniquePtr result) { + results[i] = std::move(result); + }); + } + callback(nullptr, std::move(results)); + }; + + void dmcCall(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + {} + + void getHash(bcos::protocol::BlockNumber number, + std::function callback) override + { + callback(nullptr, h256(12345)); + } + + void prepare(const bcos::protocol::TwoPCParams& params, + std::function callback) override + { + callback(nullptr); + } + + void commit(const bcos::protocol::TwoPCParams& params, + std::function callback) override + { + callback(nullptr); + } + + void rollback(const bcos::protocol::TwoPCParams& params, + std::function callback) override + { + callback(nullptr); + } + + void reset(std::function callback) override {} + + void getCode(std::string_view contract, + std::function callback) override + { + callback(nullptr, {}); + } + void getABI(std::string_view contract, + std::function callback) override + { + callback(nullptr, {}); + } + void clear() { m_dagHashes.clear(); } + + std::string m_name; + bcos::protocol::BlockNumber m_blockNumber = 0; + std::set m_dagHashes; +}; +#pragma GCC diagnostic pop +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-scheduler/test/mock/MockLedger.h" "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockLedger.h" new file mode 100644 index 00000000..9e00492f --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockLedger.h" @@ -0,0 +1,121 @@ +#pragma once + +#include "bcos-framework/ledger/LedgerInterface.h" +#include "bcos-framework/protocol/Protocol.h" +#include + +namespace bcos::test +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +class MockLedger : public bcos::ledger::LedgerInterface +{ +public: + void asyncPrewriteBlock(bcos::storage::StorageInterface::Ptr storage, + bcos::protocol::TransactionsPtr _blockTxs, bcos::protocol::Block::ConstPtr block, + std::function callback) override + { + BOOST_CHECK_EQUAL(block->blockHeaderConst()->number(), 100); + callback(nullptr); + } + void asyncPreStoreBlockTxs(bcos::protocol::TransactionsPtr, bcos::protocol::Block::ConstPtr, + std::function _callback) override + { + if (!_callback) + { + return; + } + _callback(nullptr); + } + void asyncStoreTransactions(std::shared_ptr> _txToStore, + crypto::HashListPtr _txHashList, std::function _onTxStored) override + {} + + void asyncGetBlockDataByNumber(protocol::BlockNumber _blockNumber, int32_t _blockFlag, + std::function _onGetBlock) override + {} + + void asyncGetBlockNumber( + std::function _onGetBlock) override + { + _onGetBlock(nullptr, 100); + } + + void asyncGetBlockHashByNumber(protocol::BlockNumber _blockNumber, + std::function _onGetBlock) override + { + BOOST_CHECK_EQUAL(_blockNumber, 100); + _onGetBlock(nullptr, h256(110)); + } + + void asyncGetBlockNumberByHash(crypto::HashType const& _blockHash, + std::function _onGetBlock) override + {} + + void asyncGetBatchTxsByHashList(crypto::HashListPtr _txHashList, bool _withProof, + std::function>)> + _onGetTx) override + {} + + void asyncGetTransactionReceiptByHash(crypto::HashType const& _txHash, bool _withProof, + std::function + _onGetTx) override + {} + + void asyncGetTotalTransactionCount(std::function + _callback) override + {} + + void asyncGetSystemConfigByKey(std::string_view const& _key, + std::function _onGetConfig) override + { + if (_key == ledger::SYSTEM_KEY_TX_COUNT_LIMIT) + { + _onGetConfig(nullptr, "100", 100); + } + else if (_key == ledger::SYSTEM_KEY_CONSENSUS_LEADER_PERIOD) + { + _onGetConfig(nullptr, "300", 100); + } + else if (_key == ledger::SYSTEM_KEY_TX_GAS_LIMIT) + { + _onGetConfig(nullptr, "300000000", 100); + } + else if (_key == ledger::SYSTEM_KEY_COMPATIBILITY_VERSION) + { + _onGetConfig(nullptr, bcos::protocol::RC4_VERSION_STR, 100); + } + else + { + BOOST_FAIL("Unknown query key"); + } + } + + void asyncGetNodeListByType(std::string_view const& _type, + std::function _onGetConfig) override + { + if (_type == ledger::CONSENSUS_SEALER) + { + _onGetConfig(nullptr, std::make_shared(1)); + } + else if (_type == ledger::CONSENSUS_OBSERVER) + { + _onGetConfig(nullptr, std::make_shared(2)); + } + else + { + BOOST_FAIL("Unknown query type"); + } + } + + void asyncGetNonceList(protocol::BlockNumber _startNumber, int64_t _offset, + std::function>)> + _onGetList) override + {} +}; +#pragma GCC diagnostic pop +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/mock/MockLedger2.h" "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockLedger2.h" new file mode 100644 index 00000000..e48e49e3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockLedger2.h" @@ -0,0 +1,91 @@ +#pragma once + +#include "bcos-framework/ledger/LedgerInterface.h" + +namespace bcos::test +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +class MockLedger2 : public bcos::ledger::LedgerInterface +{ +public: + void asyncPrewriteBlock(bcos::storage::StorageInterface::Ptr storage, + bcos::protocol::TransactionsPtr, bcos::protocol::Block::ConstPtr block, + std::function callback) + { + auto mutableBlock = std::const_pointer_cast(block); + auto header = mutableBlock->blockHeader(); + auto blockNumberStr = boost::lexical_cast(header->number()); + storage::Entry numberEntry; + numberEntry.importFields({blockNumberStr}); + storage->asyncSetRow(ledger::SYS_CURRENT_STATE, ledger::SYS_KEY_CURRENT_NUMBER, + std::move(numberEntry), [callback = std::move(callback)](auto&& error) { + if (error) + { + BOOST_FAIL("asyncSetRow failed" + error->errorMessage()); + } + callback(nullptr); + }); + // TODO: write receipts and tx count + } + + void asyncPreStoreBlockTxs(bcos::protocol::TransactionsPtr, bcos::protocol::Block::ConstPtr, + std::function _callback) override + { + if (!_callback) + { + return; + } + _callback(nullptr); + } + void asyncStoreTransactions(std::shared_ptr> _txToStore, + crypto::HashListPtr _txHashList, std::function _onTxStored) + {} + + void asyncGetBlockDataByNumber(protocol::BlockNumber _blockNumber, int32_t _blockFlag, + std::function _onGetBlock) + {} + + void asyncGetBlockNumber(std::function _onGetBlock) {} + + void asyncGetBlockHashByNumber(protocol::BlockNumber _blockNumber, + std::function _onGetBlock) + {} + + void asyncGetBlockNumberByHash(crypto::HashType const& _blockHash, + std::function _onGetBlock) + {} + + void asyncGetBatchTxsByHashList(crypto::HashListPtr _txHashList, bool _withProof, + std::function>)> + _onGetTx) + {} + + void asyncGetTransactionReceiptByHash(crypto::HashType const& _txHash, bool _withProof, + std::function + _onGetTx) + {} + + void asyncGetTotalTransactionCount(std::function + _callback) + {} + + void asyncGetSystemConfigByKey(std::string const& _key, + std::function _onGetConfig) + {} + + void asyncGetNodeListByType(std::string const& _type, + std::function _onGetConfig) + {} + + void asyncGetNonceList(protocol::BlockNumber _startNumber, int64_t _offset, + std::function>)> + _onGetList) + {} +}; +#pragma GCC diagnostic pop +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/mock/MockLedger3.h" "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockLedger3.h" new file mode 100644 index 00000000..44818f97 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockLedger3.h" @@ -0,0 +1,160 @@ +#pragma once + +#include "bcos-framework/bcos-framework/ledger/LedgerConfig.h" +#include "bcos-framework/bcos-framework/ledger/LedgerInterface.h" +#include "bcos-framework/bcos-framework/ledger/LedgerTypeDef.h" +#include "bcos-framework/bcos-framework/protocol/Block.h" +#include "bcos-framework/bcos-framework/protocol/Protocol.h" +#include "bcos-framework/bcos-framework/protocol/Transaction.h" +#include "bcos-framework/bcos-framework/protocol/TransactionReceipt.h" +#include "bcos-framework/bcos-framework/storage/StorageInterface.h" +#include "bcos-ledger/src/libledger/utilities/Common.h" +#include +#include +#include +#include +#include + +using namespace bcos::ledger; + + +namespace bcos::test +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +class MockLedger3 : public bcos::ledger::LedgerInterface +{ +public: + MockLedger3() : LedgerInterface() {} + using Ptr = std::shared_ptr; + void asyncPrewriteBlock(bcos::storage::StorageInterface::Ptr storage, + bcos::protocol::TransactionsPtr _blockTxs, bcos::protocol::Block::ConstPtr block, + std::function callback) override + { + auto blockNumber = block->blockHeaderConst()->number(); + SCHEDULER_LOG(DEBUG) << LOG_KV("blockNumber", blockNumber); + if (blockNumber == 1024) + { + callback(BCOS_ERROR_PTR(LedgerError::CollectAsyncCallbackError, "PrewriteBlock error")); + return; + } + callback(nullptr); + } + + void asyncStoreTransactions(std::shared_ptr> _txToStore, + crypto::HashListPtr _txHashList, std::function _onTxStored) override + {} + + void asyncGetBlockDataByNumber(protocol::BlockNumber _blockNumber, int32_t _blockFlag, + std::function _onGetBlock) override + {} + + void asyncGetBlockNumber( + std::function _onGetBlock) override + { + _onGetBlock(nullptr, commitBlockNumber); + } + + void asyncGetBlockHashByNumber(protocol::BlockNumber _blockNumber, + std::function _onGetBlock) override + { + BOOST_CHECK_EQUAL(_blockNumber, commitBlockNumber); + _onGetBlock(nullptr, h256(commitBlockNumber)); + } + + void asyncGetBlockNumberByHash(crypto::HashType const& _blockHash, + std::function _onGetBlock) override + {} + + void asyncGetBatchTxsByHashList(crypto::HashListPtr _txHashList, bool _withProof, + std::function>)> + _onGetTx) override + {} + + void asyncGetTransactionReceiptByHash(crypto::HashType const& _txHash, bool _withProof, + std::function + _onGetTx) override + {} + + void asyncGetTotalTransactionCount(std::function + _callback) override + {} + + void asyncGetSystemConfigByKey(std::string_view const& _key, + std::function _onGetConfig) override + { + if (_key == ledger::SYSTEM_KEY_TX_COUNT_LIMIT) + { + _onGetConfig(nullptr, "100", commitBlockNumber); + } + else if (_key == ledger::SYSTEM_KEY_CONSENSUS_LEADER_PERIOD) + { + _onGetConfig(nullptr, "300", commitBlockNumber); + } + else if (_key == ledger::SYSTEM_KEY_TX_GAS_LIMIT) + { + _onGetConfig(nullptr, "300000000", commitBlockNumber); + } + else if (_key == ledger::SYSTEM_KEY_COMPATIBILITY_VERSION) + { + _onGetConfig(nullptr, bcos::protocol::RC4_VERSION_STR, commitBlockNumber); + } + else + { + BOOST_FAIL("Unknown query key"); + } + } + + void asyncGetNodeListByType(std::string_view const& _type, + std::function _onGetConfig) override + { + if (_type == ledger::CONSENSUS_SEALER) + { + _onGetConfig(nullptr, std::make_shared(1)); + } + else if (_type == ledger::CONSENSUS_OBSERVER) + { + _onGetConfig(nullptr, std::make_shared(2)); + } + else + { + BOOST_FAIL("Unknown query type"); + } + } + + void asyncGetNonceList(protocol::BlockNumber _startNumber, int64_t _offset, + std::function>)> + _onGetList) override + {} + + void asyncPreStoreBlockTxs(bcos::protocol::TransactionsPtr _blockTxs, + bcos::protocol::Block::ConstPtr block, + std::function _callback) override + {} + + void commitSuccess(bool _success) + { + if (_success) + { + ++commitBlockNumber; + SCHEDULER_LOG(DEBUG) << "---- mockLedger -----" + << LOG_KV("CommitBlock success, commitBlockNumber", + commitBlockNumber); + } + else + { + SCHEDULER_LOG(DEBUG) << "---- mockLedger -----" + << LOG_KV( + "CommitBlock failed, commitBlockNumber", commitBlockNumber); + } + } + +private: + bcos::protocol::BlockNumber commitBlockNumber = 5; +}; + +#pragma GCC diagnostic pop +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/mock/MockMultiParallelExecutor.h" "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockMultiParallelExecutor.h" new file mode 100644 index 00000000..3e8d2f45 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockMultiParallelExecutor.h" @@ -0,0 +1,102 @@ +#pragma once + +#include "MockExecutor.h" +#include +#include + +namespace bcos::test +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +class MockMultiParallelExecutor : public MockParallelExecutor +{ +public: + MockMultiParallelExecutor(const std::string& name) : MockParallelExecutor(name) {} + ~MockMultiParallelExecutor() noexcept override { m_taskGroup.wait(); } + + void nextBlockHeader(int64_t schedulerTermId, + const bcos::protocol::BlockHeader::ConstPtr& blockHeader, + std::function callback) override + { + m_taskGroup.run([this, blockHeader = blockHeader, callback = std::move(callback)]() { + MockParallelExecutor::nextBlockHeader(0, blockHeader, std::move(callback)); + }); + } + + void executeTransaction(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + { + m_taskGroup.run([this, inputRaw = input.release(), callback = std::move(callback)]() { + MockParallelExecutor::executeTransaction( + bcos::protocol::ExecutionMessage::UniquePtr(inputRaw), std::move(callback)); + }); + } + + void call(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + {} + + void dagExecuteTransactions(gsl::span inputs, + std::function)> + callback) override + { + m_taskGroup.run([this, inputs = std::move(inputs), callback = std::move(callback)]() { + MockParallelExecutor::dagExecuteTransactions(std::move(inputs), std::move(callback)); + }); + } + + void dmcCall(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override + {} + + void getHash(bcos::protocol::BlockNumber number, + std::function callback) override + { + m_taskGroup.run([this, number = number, callback = std::move(callback)]() { + MockParallelExecutor::getHash(number, std::move(callback)); + }); + } + + /* ----- XA Transaction interface Start ----- */ + + // Write data to storage uncommitted + void prepare(const bcos::protocol::TwoPCParams& params, + std::function callback) override + { + m_taskGroup.run([this, params = params, callback = std::move(callback)]() { + MockParallelExecutor::prepare(params, std::move(callback)); + }); + } + + // Commit uncommitted data + void commit(const bcos::protocol::TwoPCParams& params, + std::function callback) override + { + m_taskGroup.run([this, params = params, callback = std::move(callback)]() { + MockParallelExecutor::commit(params, std::move(callback)); + }); + } + + // Rollback the changes + void rollback(const bcos::protocol::TwoPCParams& params, + std::function callback) override + { + m_taskGroup.run([this, params = params, callback = std::move(callback)]() { + MockParallelExecutor::rollback(params, std::move(callback)); + }); + } + + /* ----- XA Transaction interface End ----- */ + + // drop all status + void reset(std::function callback) override {} + +private: + tbb::task_group m_taskGroup; +}; +#pragma GCC diagnostic pop +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/mock/MockRPC.h" "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockRPC.h" new file mode 100644 index 00000000..612774ad --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockRPC.h" @@ -0,0 +1,33 @@ +#pragma once + +#include "Common.h" +#include +#include +#include + +namespace bcos::test +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" + +class MockRPC : public bcos::rpc::RPCInterface +{ +public: + void start() override {} + void stop() override {} + + void asyncNotifyBlockNumber(std::string const& _groupID, std::string const& _nodeName, + bcos::protocol::BlockNumber _blockNumber, + std::function _callback) override + {} + + void asyncNotifyGroupInfo( + bcos::group::GroupInfo::Ptr _groupInfo, std::function) override + {} + + boost::latch* latch = nullptr; +}; + +#pragma GCC diagnostic pop + +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/mock/MockTransactionalStorage.h" "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockTransactionalStorage.h" new file mode 100644 index 00000000..162c768f --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockTransactionalStorage.h" @@ -0,0 +1,90 @@ +#pragma once + +#include "bcos-framework/ledger/LedgerInterface.h" +#include "bcos-framework/protocol/ProtocolTypeDef.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-table/src/StateStorage.h" + +using namespace bcos::protocol; +namespace bcos::test +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +class MockTransactionalStorage : public bcos::storage::TransactionalStorageInterface +{ +public: + ~MockTransactionalStorage() override = default; + + void asyncGetPrimaryKeys(std::string_view table, + const std::optional& _condition, + std::function)> _callback) noexcept override + { + m_storage->asyncGetPrimaryKeys(table, _condition, std::move(_callback)); + } + + void asyncGetRow(std::string_view table, std::string_view _key, + std::function)> _callback) noexcept + override + { + m_storage->asyncGetRow(table, _key, std::move(_callback)); + } + + void asyncGetRows(std::string_view table, + const std::variant, + const gsl::span>& _keys, + std::function>)> + _callback) noexcept override + { + // if(table == ledge) + if (table == ledger::SYS_CONFIG) + { + // Return 3 dataes + std::vector> result; + storage::Entry entry1; + entry1.importFields({"100"}); + result.emplace_back(std::move(entry1)); + + storage::Entry entry2; + entry2.importFields({"200"}); + result.emplace_back(std::move(entry2)); + + storage::Entry entry3; + entry3.importFields({"300"}); + result.emplace_back(std::move(entry3)); + + _callback(nullptr, std::move(result)); + return; + } + + m_storage->asyncGetRows(table, _keys, std::move(_callback)); + } + + void asyncSetRow(std::string_view table, std::string_view key, storage::Entry entry, + std::function callback) noexcept override + { + m_storage->asyncSetRow(table, key, std::move(entry), std::move(callback)); + } + + void asyncPrepare(const bcos::protocol::TwoPCParams& params, + const storage::TraverseStorageInterface& storage, + std::function callback) noexcept override + { + callback(nullptr, 0, ""); + } + + void asyncCommit(const bcos::protocol::TwoPCParams& params, + std::function callback) noexcept override + { + callback(nullptr, 0); + } + + void asyncRollback(const bcos::protocol::TwoPCParams& params, + std::function callback) noexcept override + { + callback(nullptr); + } + + bcos::storage::StateStorage::Ptr m_storage; +}; +#pragma GCC diagnostic pop +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/mock/MockTxPool1.h" "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockTxPool1.h" new file mode 100644 index 00000000..0ec8d7ef --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/mock/MockTxPool1.h" @@ -0,0 +1,110 @@ +#pragma once + +#include "bcos-scheduler/src/Common.h" +#include "bcos-tars-protocol/testutil/FakeBlockHeader.h" +#include +#include +#include +#include +#include +#include +#include + + +using namespace std; +using namespace bcos::crypto; +namespace bcos::test +{ +class MockTxPool1 : public txpool::TxPoolInterface +{ +public: + using Ptr = std::shared_ptr(); + void start() override {} + void stop() override {} + task::Task submitTransaction( + protocol::Transaction::Ptr transaction) override + { + co_return nullptr; + } + + void asyncSealTxs(uint64_t, bcos::txpool::TxsHashSetPtr, + std::function) + override + {} + void asyncMarkTxs(bcos::crypto::HashListPtr, bool, bcos::protocol::BlockNumber, + bcos::crypto::HashType const&, std::function) override + {} + void asyncVerifyBlock(bcos::crypto::PublicPtr, bytesConstRef const&, + std::function) override + {} + void asyncNotifyBlockResult(bcos::protocol::BlockNumber, + bcos::protocol::TransactionSubmitResultsPtr, std::function) override + {} + void asyncNotifyTxsSyncMessage(bcos::Error::Ptr, std::string const&, bcos::crypto::NodeIDPtr, + bytesConstRef, std::function) override + {} + void notifyConsensusNodeList( + bcos::consensus::ConsensusNodeList const&, std::function) override + {} + void notifyObserverNodeList( + bcos::consensus::ConsensusNodeList const&, std::function) override + {} + void asyncGetPendingTransactionSize(std::function) override {} + void asyncResetTxPool(std::function) override {} + + void asyncFillBlock(bcos::crypto::HashListPtr _txsHash, + std::function _onBlockFilled) override + { + BOOST_CHECK_GT(_txsHash->size(), 0); + SCHEDULER_LOG(DEBUG) << LOG_KV("txHashes size", _txsHash->size()) + << LOG_KV("map Size", hash2Transaction.size()); + auto transactions = std::make_shared(); + for (auto& hash : *_txsHash) + { + auto it = hash2Transaction.find(hash); + if (it != hash2Transaction.end()) + { + transactions->push_back(it->second); + SCHEDULER_LOG(DEBUG) << LOG_KV("hash", hash) << LOG_KV("tx", it->second); + } + else + { + transactions->push_back(nullptr); + } + } + _onBlockFilled(nullptr, std::move(transactions)); + } + + void notifyConnectedNodes( + const bcos::crypto::NodeIDSet&, std::function)>) override + {} + + // protocol::HashList generateTransaction(std::size_t number) + // { + // auto txHashes = std::make_shared(); + // for (size_t i = 0; i < number; ++i) + // { + // hashImpl = std::make_shared(); + // assert(hashImpl); + // signatureImpl = std::make_shared(); + // assert(signatureImpl); + // cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + // keyPair = cryptoSuite->signatureImpl()->generateKeyPair(); + + // // Generate fakeTransaction + // auto tx = fakeTransaction(cryptoSuite, keyPair, "", "", 101, 100001, "1", "1"); + // auto hash = tx->hash(); + // hash2Transaction.emplace(hash, tx); + // txHashes.emplace_back(hash); + // } + // return txHashes; + // } + +public: + std::map hash2Transaction; + bcos::crypto::Hash::Ptr hashImpl; + bcos::crypto::SignatureCrypto::Ptr signatureImpl; + bcos::crypto::CryptoSuite::Ptr cryptoSuite; + bcos::crypto::KeyPairInterface::Ptr keyPair; +}; +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/testBlockExecutive.cpp" "b/BFPL\345\243\271/bcos-scheduler/test/testBlockExecutive.cpp" new file mode 100644 index 00000000..4b3ba2f3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/testBlockExecutive.cpp" @@ -0,0 +1,517 @@ +#include "bcos-crypto/interfaces/crypto/KeyPairInterface.h" +#include "bcos-executor/test/unittest/mock/MockTxPool.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/ledger/LedgerInterface.h" +#include "bcos-framework/protocol/BlockHeaderFactory.h" +#include "bcos-framework/protocol/TransactionReceiptFactory.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-protocol/bcos-protocol/TransactionSubmitResultFactoryImpl.h" +#include "bcos-scheduler/src/BlockExecutiveFactory.h" +#include "bcos-scheduler/src/SchedulerImpl.h" +#include "bcos-storage/RocksDBStorage.h" +#include "bcos-table/src/KeyPageStorage.h" +#include "bcos-table/src/StateStorage.h" +#include "bcos-table/src/StateStorageInterface.h" +#include "mock/MockBlockExecutive.h" +#include "mock/MockBlockExecutiveFactory.h" +#include "mock/MockDmcExecutor.h" +#include "mock/MockExecutor.h" +#include "mock/MockExecutorForCall.h" +#include "mock/MockExecutorForCreate.h" +#include "mock/MockLedger3.h" +#include "mock/MockTxPool1.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace std; +using namespace bcos; +using namespace rocksdb; +using namespace bcos::ledger; +using namespace bcos::storage; +using namespace bcos::scheduler; +using namespace bcos::crypto; +using namespace bcos::protocol; +using namespace bcos::txpool; +using namespace bcos::test; +using namespace bcostars::protocol; +namespace bcos::test +{ +struct BlockExecutiveFixture +{ + BlockExecutiveFixture() + { + hashImpl = std::make_shared(); + signature = std::make_shared(); + suite = std::make_shared(hashImpl, signature, nullptr); + ledger = std::make_shared(); + executorManager = std::make_shared(); + + // create RocksDBStorage + rocksdb::DB* db; + rocksdb::Options options; + options.create_if_missing = true; + std::string path = "./unittestdb"; + // open DB + rocksdb::Status s = rocksdb::DB::Open(options, path, &db); + storage = std::make_shared(std::unique_ptr(db), nullptr); + + transactionFactory = std::make_shared(suite); + transactionReceiptFactory = + std::make_shared(suite); + blockHeaderFactory = std::make_shared(suite); + executionMessageFactory = std::make_shared(); + + blockFactory = std::make_shared( + suite, blockHeaderFactory, transactionFactory, transactionReceiptFactory); + keyPair = blockFactory->cryptoSuite()->signatureImpl()->generateKeyPair(); + txPool = std::make_shared(); + transactionSubmitResultFactory = + std::make_shared(); + scheduler = std::make_shared(executorManager, ledger, + storage, executionMessageFactory, blockFactory, txPool, transactionSubmitResultFactory, + hashImpl, false, false, false, 0); + } + + ~BlockExecutiveFixture() {} + + bcos::ledger::LedgerInterface::Ptr ledger; + bcos::scheduler::ExecutorManager::Ptr executorManager; + bcos::protocol::ExecutionMessageFactory::Ptr executionMessageFactory; + bcos::protocol::TransactionReceiptFactory::Ptr transactionReceiptFactory; + bcos::protocol::BlockHeaderFactory::Ptr blockHeaderFactory; + bcos::crypto::Hash::Ptr hashImpl; + bcos::scheduler::SchedulerImpl::Ptr scheduler; + bcostars::protocol::TransactionFactoryImpl::Ptr transactionFactory; + bcos::protocol::Block::Ptr block; + bcos::crypto::SignatureCrypto::Ptr signature; + bcos::crypto::CryptoSuite::Ptr suite; + bcos::protocol::BlockFactory::Ptr blockFactory; + std::shared_ptr txPool; + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory; + bcos::scheduler::BlockExecutiveFactory::Ptr blockExecutiveFactory; + bcos::scheduler::BlockExecutive::Ptr blockExecutive; + bcos::crypto::KeyPairInterface::Ptr keyPair; + bcos::storage::RocksDBStorage::Ptr storage = nullptr; +}; + +BOOST_FIXTURE_TEST_SUITE(testBlockExecutive, BlockExecutiveFixture) +BOOST_AUTO_TEST_CASE(prepareTest) +{ + SCHEDULER_LOG(DEBUG) << "----------prepareTest----------------"; + // Generate Block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(9999); + + // Add Executor + for (size_t i = 1; i <= 10; ++i) + { + auto executor = + std::make_shared("executor" + boost::lexical_cast(i)); + executorManager->addExecutor("executor" + boost::lexical_cast(i), executor); + } + + // Generate MetaTx + for (size_t j = 0; j < 50; ++j) + { + // Fill txPool + std::string inputStr = "Hello world! request"; + auto tx = blockFactory->transactionFactory()->createTransaction(0, + "contract" + boost::lexical_cast((j + 1) % 10), + bytes(inputStr.begin(), inputStr.end()), j, 300, "chain", "group", 500, keyPair); + auto hash = tx->hash(); + SCHEDULER_LOG(DEBUG) << LOG_KV("hash", hash); + txPool->hash2Transaction.emplace(hash, tx); + block->appendTransaction(std::move(tx)); + auto metaTx = std::make_shared( + hash, "contract" + boost::lexical_cast((j + 1) % 10)); + block->appendTransactionMetaData(std::move(metaTx)); + } + SCHEDULER_LOG(DEBUG) << LOG_KV("metaTx size:", block->transactionsMetaDataSize()) + << LOG_KV("transaction size", block->transactionsSize()); + + auto blockExecutive = std::make_shared(block, scheduler.get(), + 0, transactionSubmitResultFactory, false, blockFactory, txPool, 3000000000, false); + blockExecutive->prepare(); + // BOOST_CHECK(); +} + +BOOST_AUTO_TEST_CASE(asyncExecuteTest1) +{ + SCHEDULER_LOG(DEBUG) << "----------asyncExecuteTest----------------"; + // Generate Block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(99); + // Add Executor + auto executor1 = std::make_shared("executor1"); + executorManager->addExecutor("executor1", executor1); + + // Fill normalTx + for (size_t i = 0; i < 10; i++) + { + std::string inputStr = "hello world!"; + bytes input(inputStr.begin(), inputStr.end()); + auto tx = transactionFactory->createTransaction(20, + "contract" + boost::lexical_cast((i + 1) % 10), input, i, 200, "chainID", + "groupID", 400, keyPair); + // tx->setAttribute(bcos::protocol::Transaction::Attribute::DAG); + block->appendTransaction(tx); + } + auto blockExecutive = std::make_shared( + block, scheduler.get(), 0, transactionSubmitResultFactory, false, blockFactory, txPool); + SCHEDULER_LOG(DEBUG) << LOG_KV("blockExecutive", blockExecutive); + blockExecutive->stop(); + blockExecutive->asyncExecute([&](Error::UniquePtr error, protocol::BlockHeader::Ptr header, + bool) { BOOST_CHECK(error); }); + blockExecutive->start(); + blockExecutive->asyncExecute([&](Error::UniquePtr error, protocol::BlockHeader::Ptr header, + bool) { BOOST_CHECK(!error); }); +} +BOOST_AUTO_TEST_CASE(asyncExecuteTest2) +{ + SCHEDULER_LOG(DEBUG) << "----------asyncExecuteTest----------------"; + // Generate Block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(1024); + + // Add Executor + auto executor1 = std::make_shared("executor1"); + executorManager->addExecutor("executor1", executor1); + + // Fill metaTx + for (size_t j = 0; j < 10; j++) + { + std::string inputStr = "Hello world! request"; + + auto tx = blockFactory->transactionFactory()->createTransaction(0, "0xaabbccdd", + bytes(inputStr.begin(), inputStr.end()), j, 300, "chain", "group", 500, keyPair); + auto hash = tx->hash(); + txPool->hash2Transaction.emplace(hash, tx); + block->appendTransaction(std::move(tx)); + auto metaTx = std::make_shared( + hash, "contract" + boost::lexical_cast((j + 1) % 10)); + block->appendTransactionMetaData(std::move(metaTx)); + } + auto blockExecutive = std::make_shared( + block, scheduler.get(), 0, transactionSubmitResultFactory, false, blockFactory, txPool); + SCHEDULER_LOG(DEBUG) << LOG_KV("blockExecutive", blockExecutive); + blockExecutive->stop(); + blockExecutive->asyncExecute([&](Error::UniquePtr error, protocol::BlockHeader::Ptr header, + bool) { BOOST_CHECK(error); }); + blockExecutive->start(); + blockExecutive->asyncExecute([&](Error::UniquePtr error, protocol::BlockHeader::Ptr header, + bool) { BOOST_CHECK(error); }); +} + +BOOST_AUTO_TEST_CASE(asyncCommitTest1) +{ + SCHEDULER_LOG(DEBUG) << "----------asyncCommitTest1----------------"; + // Generate Block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(999); + // Add Executor + auto executor1 = std::make_shared("executor1"); + executorManager->addExecutor("executor1", executor1); + // Fill MetaTx + for (size_t j = 0; j < 10; j++) + { + std::string inputStr = "Hello world! request"; + auto tx = blockFactory->transactionFactory()->createTransaction(0, + "contract" + boost::lexical_cast((j + 1) % 10), + bytes(inputStr.begin(), inputStr.end()), j, 300, "chain", "group", 500, keyPair); + auto hash = tx->hash(); + txPool->hash2Transaction.emplace(hash, tx); + block->appendTransaction(std::move(tx)); + auto metaTx = std::make_shared( + hash, "contract" + boost::lexical_cast((j + 1) % 10)); + block->appendTransactionMetaData(std::move(metaTx)); + } + auto blockExecutive = std::make_shared( + block, scheduler.get(), 0, transactionSubmitResultFactory, false, blockFactory, txPool); + SCHEDULER_LOG(DEBUG) << LOG_KV("blockExecutive", blockExecutive); + blockExecutive->stop(); + blockExecutive->asyncCommit([&](Error::UniquePtr error) { BOOST_CHECK(error); }); + blockExecutive->start(); + blockExecutive->asyncCommit([&](Error::UniquePtr error) { BOOST_CHECK(!error); }); +} + +BOOST_AUTO_TEST_CASE(asyncCommitTest2) +{ + SCHEDULER_LOG(DEBUG) << "----------asyncCommitTest2----------------"; + // Generate Block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(1024); + + // Add Executor + for (size_t i = 1; i <= 10; ++i) + { + auto executor = + std::make_shared("executor" + boost::lexical_cast(i)); + executorManager->addExecutor("executor" + boost::lexical_cast(i), executor); + } + // Fill normalTransaction + for (size_t j = 0; j < 100; j++) + { + std::string inputStr = "Hello world! request"; + auto tx = blockFactory->transactionFactory()->createTransaction(0, + "contract" + boost::lexical_cast((j + 1) % 10), + bytes(inputStr.begin(), inputStr.end()), j, 300, "chain", "group", 500, keyPair); + auto hash = tx->hash(); + txPool->hash2Transaction.emplace(hash, tx); + block->appendTransaction(std::move(tx)); + } + auto blockExecutive = std::make_shared( + block, scheduler.get(), 0, transactionSubmitResultFactory, false, blockFactory, txPool); + SCHEDULER_LOG(DEBUG) << LOG_KV("blockExecutive", blockExecutive); + blockExecutive->stop(); + blockExecutive->asyncCommit([&](Error::UniquePtr error) { + BOOST_CHECK(error); + SCHEDULER_LOG(DEBUG) << "----------asyncCommitTest2 END----------------"; + }); + blockExecutive->start(); + blockExecutive->asyncCommit([&](Error::UniquePtr error) { + BOOST_CHECK(error); + SCHEDULER_LOG(DEBUG) << "----------asyncCommitTest2 END----------------"; + }); +} + +BOOST_AUTO_TEST_CASE(asyncNotify) +{ + // Generate block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(999); + // Add Executor + auto executor1 = std::make_shared("executor1"); + executorManager->addExecutor("executor1", executor1); + + // Fill metaTX + for (size_t j = 0; j < 20; ++j) + { + std::string inputStr = "Hello world! request"; + auto tx = blockFactory->transactionFactory()->createTransaction(0, + "contract" + boost::lexical_cast((j + 1) % 10), + bytes(inputStr.begin(), inputStr.end()), j, 300, "chain", "group", 500, keyPair); + // auto hash = tx->hash(); + // txPool->hash2Transaction.emplace(hash, tx); + // auto metaTx = std::make_shared( + // hash, "contract" + boost::lexical_cast((j + 1) % 10)); + block->appendTransaction(std::move(tx)); + } + auto blockExecutive = std::make_shared( + block, scheduler.get(), 0, transactionSubmitResultFactory, false, blockFactory, txPool); + + std::function)> + m_txNotifier; + + bcos::protocol::BlockNumber blockNumber = 999; + m_txNotifier = [&](bcos::protocol::BlockNumber blockNumber, + bcos::protocol::TransactionSubmitResultsPtr, + std::function _callback) { + + }; + blockExecutive->asyncNotify( + m_txNotifier, [&](Error::Ptr _error) mutable { BOOST_CHECK(!_error); }); +} + +BOOST_AUTO_TEST_CASE(dagTest) +{ + // Add executor + executorManager->addExecutor("executor1", std::make_shared("executor1")); + + // Generate a test block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(100); + + for (size_t j = 0; j < 10; ++j) + { + std::string inputStr = "Hello world! request"; + auto tx = blockFactory->transactionFactory()->createTransaction(0, + "contract" + boost::lexical_cast((j + 1) % 10), + bytes(inputStr.begin(), inputStr.end()), j, 300, "chain", "group", 500, keyPair); + auto hash = tx->hash(); + txPool->hash2Transaction.emplace(hash, tx); + block->appendTransaction(std::move(tx)); + auto metaTx = std::make_shared( + hash, "contract" + boost::lexical_cast((j + 1) % 10)); + metaTx->setAttribute(bcos::protocol::Transaction::Attribute::DAG); + block->appendTransactionMetaData(std::move(metaTx)); + } + auto blockExecutive = std::make_shared( + block, scheduler.get(), 0, transactionSubmitResultFactory, false, blockFactory, txPool); + blockExecutive->stop(); + blockExecutive->asyncExecute( + [](Error::UniquePtr error, protocol::BlockHeader::Ptr header, bool) { + BOOST_CHECK(error); + SCHEDULER_LOG(DEBUG) << "----------dagTest END----------------"; + }); + blockExecutive->start(); + blockExecutive->asyncExecute( + [](Error::UniquePtr error, protocol::BlockHeader::Ptr header, bool) { + BOOST_CHECK(!error); + SCHEDULER_LOG(DEBUG) << "----------dagTest END----------------"; + }); +} + +BOOST_AUTO_TEST_CASE(dagTest2) +{ + // Add executor + executorManager->addExecutor("executor1", std::make_shared("executor1")); + + // Generate a test block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(1024); + + for (size_t j = 0; j < 10; ++j) + { + std::string inputStr = "Hello world! request"; + auto tx = blockFactory->transactionFactory()->createTransaction(0, "0xaabbccdd", + bytes(inputStr.begin(), inputStr.end()), j, 300, "chain", "group", 500, keyPair); + auto hash = tx->hash(); + txPool->hash2Transaction.emplace(hash, tx); + block->appendTransaction(std::move(tx)); + auto metaTx = std::make_shared( + hash, "contract" + boost::lexical_cast((j + 1) % 10)); + metaTx->setAttribute(bcos::protocol::Transaction::Attribute::DAG); + block->appendTransactionMetaData(std::move(metaTx)); + } + auto blockExecutive = std::make_shared( + block, scheduler.get(), 0, transactionSubmitResultFactory, false, blockFactory, txPool); + blockExecutive->stop(); + blockExecutive->asyncExecute( + [](Error::UniquePtr error, protocol::BlockHeader::Ptr header, bool) { + BOOST_CHECK(error); + SCHEDULER_LOG(DEBUG) << "----------dagTest END----------------"; + }); + blockExecutive->start(); + blockExecutive->asyncExecute( + [](Error::UniquePtr error, protocol::BlockHeader::Ptr header, bool) { + BOOST_CHECK(error); + SCHEDULER_LOG(DEBUG) << "----------dagTest END----------------"; + }); +} + +BOOST_AUTO_TEST_CASE(dagByMessage) +{ + SCHEDULER_LOG(DEBUG) << "----------dagByMessageTest----------------"; + // Add executor + executorManager->addExecutor("executor1", std::make_shared("executor1")); + + // Generate a test block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(100); + + for (size_t i = 0; i < 10; ++i) + { + std::string inputStr = "hello world!"; + bytes input(inputStr.begin(), inputStr.end()); + auto tx = transactionFactory->createTransaction(20, + "contract" + boost::lexical_cast((i + 1) % 10), input, i, 200, "chainID", + "groupID", 400, keyPair); + tx->setAttribute(bcos::protocol::Transaction::Attribute::DAG); + block->appendTransaction(tx); + } + auto blockExecutive = std::make_shared( + block, scheduler.get(), 0, transactionSubmitResultFactory, false, blockFactory, txPool); + blockExecutive->stop(); + blockExecutive->asyncExecute([](Error::UniquePtr error, protocol::BlockHeader::Ptr header, + bool) { BOOST_CHECK(error); }); + blockExecutive->start(); + blockExecutive->asyncExecute([](Error::UniquePtr error, protocol::BlockHeader::Ptr header, + bool) { BOOST_CHECK(!error); }); +} + +BOOST_AUTO_TEST_CASE(callTest) +{ + SCHEDULER_LOG(DEBUG) << "----------callTest----------------"; + // Generate block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(999); + + // Generate call transaction + std::string inputStr = "Hello world! request"; + bcos::crypto::KeyPairInterface::Ptr keyPair = + blockFactory->cryptoSuite()->signatureImpl()->generateKeyPair(); + auto tx = blockFactory->transactionFactory()->createTransaction(0, "address_to", + bytes(inputStr.begin(), inputStr.end()), 200, 300, "chain", "group", 500, keyPair); + block->appendTransaction(std::move(tx)); + // Add executor + auto executor1 = std::make_shared("executor1"); + executorManager->addExecutor("executor1", executor1); + + // Build blockExecutive + auto blockExecutive = std::make_shared(block, scheduler.get(), + 0, transactionSubmitResultFactory, false, blockFactory, txPool, 3000000000, false); + // call + { + bcos::protocol::TransactionReceipt::Ptr receipt; + blockExecutive->asyncCall([&](bcos::Error::Ptr&& error, + bcos::protocol::TransactionReceipt::Ptr&& receiptResponse) { + if (error) + { + BOOST_CHECK(error); + return; + } + BOOST_CHECK(!error); + BOOST_CHECK(receiptResponse); + receipt = std::move(receiptResponse); + }); + } +} +BOOST_AUTO_TEST_CASE(executeWithSystemError) +{ + SCHEDULER_LOG(DEBUG) << "----------executeWithSystemError----------------"; + // Generate block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(100); + + auto tx = blockFactory->transactionFactory()->createTransaction( + 3, "0xaabbccdd", {}, u256(1), 500, "chainId", "groupId", utcTime()); + block->appendTransaction(std::move(tx)); + + // Add Executor + auto executor1 = std::make_shared("executor1"); + executorManager->addExecutor("executor1", executor1); + + auto blockExecutive = std::make_shared(block, scheduler.get(), + 0, transactionSubmitResultFactory, false, blockFactory, txPool, 3000000000, false); + + bool errorFlag = false; + bcos::protocol::BlockHeader::Ptr executedHeader; + blockExecutive->asyncExecute( + [&](Error::UniquePtr error, protocol::BlockHeader::Ptr header, bool) { + if (error) + { + SCHEDULER_LOG(DEBUG) << "I am executeWithSystemError"; + BOOST_CHECK(error); + errorFlag = true; + return; + } + else + { + executedHeader = std::move(header); + } + }); + BOOST_CHECK(errorFlag); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/testChecksumAddress.cpp" "b/BFPL\345\243\271/bcos-scheduler/test/testChecksumAddress.cpp" new file mode 100644 index 00000000..3fe2cddc --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/testChecksumAddress.cpp" @@ -0,0 +1,73 @@ +#include "ExecutorManager.h" +#include "SchedulerImpl.h" +#include "bcos-crypto/bcos-crypto/ChecksumAddress.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/ledger/LedgerInterface.h" +#include "bcos-framework/protocol/BlockHeaderFactory.h" +#include "bcos-framework/protocol/ProtocolTypeDef.h" +#include "bcos-framework/protocol/Transaction.h" +#include "bcos-framework/protocol/TransactionReceipt.h" +#include "bcos-framework/protocol/TransactionReceiptFactory.h" +#include "bcos-framework/protocol/TransactionSubmitResult.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-protocol/TransactionSubmitResultFactoryImpl.h" +#include "mock/MockExecutor.h" +#include "mock/MockExecutor3.h" +#include "mock/MockExecutorForCall.h" +#include "mock/MockExecutorForCreate.h" +#include "mock/MockExecutorForMessageDAG.h" +#include "mock/MockLedger.h" +#include "mock/MockMultiParallelExecutor.h" +#include "mock/MockRPC.h" +#include "mock/MockTransactionalStorage.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::crypto; + +namespace bcos::test +{ +struct ChecksumFixture +{ + ChecksumFixture() { hashImpl = std::make_shared(); } + + bcos::crypto::Hash::Ptr hashImpl; + + void check(const std::string& addr) + { + auto ret = addr; + toCheckSumAddress(ret, hashImpl); + BOOST_CHECK_EQUAL(addr, ret); + } +}; + +BOOST_FIXTURE_TEST_SUITE(utils, ChecksumFixture) + +BOOST_AUTO_TEST_CASE(checksumAddress) +{ + check("5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"); + check("fB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"); + check("dbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB"); + check("D1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb"); + check("fB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"); + check("3C589CB0Be25f651b0563e052DEa63d3844C33e6"); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/testCommitBlock.cpp" "b/BFPL\345\243\271/bcos-scheduler/test/testCommitBlock.cpp" new file mode 100644 index 00000000..d93e8624 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/testCommitBlock.cpp" @@ -0,0 +1,167 @@ +#include "BlockExecutive.h" +#include "ExecutorManager.h" +#include "SchedulerImpl.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/ledger/LedgerInterface.h" +#include "bcos-framework/protocol/BlockHeaderFactory.h" +#include "bcos-framework/protocol/TransactionReceiptFactory.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "mock/MockExecutor.h" +#include "mock/MockExecutor3.h" +#include "mock/MockLedger2.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos::storage; +using namespace bcos::ledger; +using namespace std; + +namespace bcos::test +{ +struct BlockExecutiveFixture +{ + BlockExecutiveFixture() + { + hashImpl = std::make_shared(); + signature = std::make_shared(); + suite = std::make_shared(hashImpl, signature, nullptr); + + ledger = std::make_shared(); + executorManager = std::make_shared(); + + // create RocksDBStorage + rocksdb::DB* db; + rocksdb::Options options; + options.create_if_missing = true; + rocksdb::Status s = rocksdb::DB::Open(options, path, &db); + BOOST_CHECK_EQUAL(s.ok(), true); + storage = std::make_shared(std::unique_ptr(db)); + + transactionFactory = std::make_shared(suite); + transactionReceiptFactory = + std::make_shared(suite); + blockHeaderFactory = std::make_shared(suite); + executionMessageFactory = std::make_shared(); + + blockFactory = std::make_shared( + suite, blockHeaderFactory, transactionFactory, transactionReceiptFactory); + + scheduler = std::make_shared( + executorManager, ledger, storage, executionMessageFactory, blockFactory, hashImpl); + + std::promise> createTablePromise; + storage->asyncCreateTable(SYS_CURRENT_STATE, "value", + [&createTablePromise](auto&& error, std::optional
&& table) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + createTablePromise.set_value(table); + }); + auto createTableResult = createTablePromise.get_future().get(); + BOOST_CHECK_EQUAL(createTableResult.has_value(), true); + } + + ~BlockExecutiveFixture() + { + filesystem::path p(path); + + if (filesystem::exists(p)) + { + filesystem::remove_all(p); + } + } + + ledger::LedgerInterface::Ptr ledger; + scheduler::ExecutorManager::Ptr executorManager; + protocol::ExecutionMessageFactory::Ptr executionMessageFactory; + protocol::TransactionReceiptFactory::Ptr transactionReceiptFactory; + protocol::BlockHeaderFactory::Ptr blockHeaderFactory; + bcos::crypto::Hash::Ptr hashImpl; + scheduler::SchedulerImpl::Ptr scheduler; + + bcostars::protocol::TransactionFactoryImpl::Ptr transactionFactory; + bcos::crypto::SignatureCrypto::Ptr signature; + bcos::crypto::CryptoSuite::Ptr suite; + bcostars::protocol::BlockFactoryImpl::Ptr blockFactory; + + std::string path = "./unittestdb"; + RocksDBStorage::Ptr storage = nullptr; +}; + +BOOST_FIXTURE_TEST_SUITE(BlockExecutive, BlockExecutiveFixture) + +BOOST_AUTO_TEST_CASE(commitBlock) +{ + // Add executor + executorManager->addExecutor("executor1", std::make_shared("executor1")); + + // Generate a test block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(100); + + for (size_t i = 0; i < 10; ++i) + { + auto metaTx = + std::make_shared(h256(i), "contract1"); + block->appendTransactionMetaData(std::move(metaTx)); + } + + for (size_t i = 10; i < 20; ++i) + { + auto metaTx = + std::make_shared(h256(i), "contract2"); + block->appendTransactionMetaData(std::move(metaTx)); + } + + for (size_t i = 20; i < 30; ++i) + { + auto metaTx = + std::make_shared(h256(i), "contract3"); + block->appendTransactionMetaData(std::move(metaTx)); + } + + bcos::protocol::BlockHeader::Ptr executedHeader; + + scheduler->executeBlock(block, false, + [&](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr&& header, bool) { + BOOST_CHECK(!error); + BOOST_CHECK(header); + + executedHeader = std::move(header); + }); + + scheduler->commitBlock( + executedHeader, [&](bcos::Error::Ptr&& error, bcos::ledger::LedgerConfig::Ptr&& config) { + BOOST_CHECK(!error); + // BOOST_CHECK(config); + (void)config; + }); + promise
prom; + storage->asyncOpenTable(SYS_CURRENT_STATE, [&](auto&& error, auto&& table) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + if (table) + { + prom.set_value(table.value()); + } + }); + auto table = prom.get_future().get(); + auto entry = table.getRow(SYS_KEY_CURRENT_NUMBER); + BOOST_CHECK_EQUAL(entry.has_value(), true); + auto blockNumber = entry->getField("value"); + BOOST_CHECK_EQUAL(blockNumber, "100"); +} + +BOOST_AUTO_TEST_CASE(rollback) {} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/testDmcExecutor.cpp" "b/BFPL\345\243\271/bcos-scheduler/test/testDmcExecutor.cpp" new file mode 100644 index 00000000..0e7dd7cf --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/testDmcExecutor.cpp" @@ -0,0 +1,369 @@ +#include "../src/Executive.h" +#include "bcos-executor/src/CallParameters.h" +#include "bcos-executor/src/executive/BlockContext.h" +#include "bcos-executor/src/executive/ExecutiveState.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/executor/NativeExecutionMessage.h" +#include "bcos-scheduler/src/DmcExecutor.h" +#include "bcos-scheduler/src/DmcStepRecorder.h" +#include "bcos-scheduler/src/GraphKeyLocks.h" +#include "bcos-tars-protocol/testutil/FakeBlock.h" +#include "bcos-tars-protocol/testutil/FakeBlockHeader.h" +#include "mock/MockDmcExecutor.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::scheduler; +using namespace bcos::crypto; + +namespace bcos::test +{ +struct DmcExecutorFixture +{ + DmcExecutorFixture() + { + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + blockFactory = createBlockFactory(cryptoSuite); + executor1 = std::make_shared("executor1"); + keyLocks = std::make_shared(); + dmcRecorder = std::make_shared(); + } + bcos::scheduler::DmcExecutor::Ptr dmcExecutor; + std::shared_ptr executor1; + bcos::scheduler::GraphKeyLocks::Ptr keyLocks; + bcos::scheduler::DmcStepRecorder::Ptr dmcRecorder; + CryptoSuite::Ptr cryptoSuite = nullptr; + bcos::protocol::BlockFactory::Ptr blockFactory; +}; + +BOOST_FIXTURE_TEST_SUITE(TestDmcExecutor, DmcExecutorFixture) + +struct DmcFlagStruct +{ + using Ptr = std::shared_ptr; + bool schedulerOutFlag = false; + bool callFlag = false; + bool DmcFlag = false; + bool switchFlag = false; + bool finishFlag = false; + bool lockedFlag = false; + std::atomic_size_t round = 0; + std::atomic_size_t paused = 0; + std::atomic_size_t finished = 0; + std::atomic_size_t error = 0; +}; + +bcos::protocol::ExecutionMessage::UniquePtr createMessage( + int contextID, int seq, int type, std::string toAddress, bool staticCall) +{ + auto message = std::make_unique(); + message->setStaticCall(staticCall); + message->setType(bcos::protocol::ExecutionMessage::Type(type)); + message->setContextID(contextID); + message->setSeq(seq); + message->setFrom("0xeeffaabb"); + message->setTo(toAddress); + return message; +} + +BOOST_AUTO_TEST_CASE(stateSwitchTest) +{ + DmcFlagStruct dmcFlagStruct; + + auto hashImpl = std::make_shared(); + auto block = blockFactory->createBlock(); + auto blockHeader = blockFactory->blockHeaderFactory()->createBlockHeader(); + blockHeader->setNumber(1); + block->setBlockHeader(blockHeader); + // block = fakeBlock(cryptoSuite, blockFactory, 1, 1, 1); + auto dmcExecutor = std::make_shared( + "DmcExecutor1", "0xaabbccdd", block, executor1, keyLocks, hashImpl, dmcRecorder); + + dmcExecutor->setSchedulerOutHandler( + [this, &dmcFlagStruct](bcos::scheduler::ExecutiveState::Ptr executiveState) { + dmcFlagStruct.schedulerOutFlag = true; + auto to = std::string(executiveState->message->to()); + auto hashImpl = std::make_shared(); + auto block = blockFactory->createBlock(); + auto blockHeader = blockFactory->blockHeaderFactory()->createBlockHeader(); + blockHeader->setNumber(2); + block->setBlockHeader(blockHeader); + auto dmcExecutor2 = std::make_shared( + "DmcExecutor2", to, block, executor1, keyLocks, hashImpl, dmcRecorder); + dmcExecutor2->scheduleIn(executiveState); + }); + + dmcExecutor->setOnTxFinishedHandler( + [&dmcFlagStruct](bcos::protocol::ExecutionMessage::UniquePtr output) { + auto outputBytes = output->data(); + std::string outputStr((char*)outputBytes.data(), outputBytes.size()); + SCHEDULER_LOG(DEBUG) << LOG_KV("output data is ", outputStr); + if (outputStr == "Call Finished!") + { + dmcFlagStruct.callFlag = true; + dmcFlagStruct.finishFlag = true; + } + else if (outputStr == "DMCExecuteTransaction Finish!") + { + dmcFlagStruct.DmcFlag = true; + dmcFlagStruct.finishFlag = true; + } + else if (outputStr == "DMCExecuteTransaction Finish, I am keyLock!") + { + dmcFlagStruct.lockedFlag = true; + } + else + { + dmcFlagStruct.finishFlag = true; + } + }); + + dmcExecutor->setOnNeedSwitchEventHandler([&dmcFlagStruct]() { + SCHEDULER_LOG(DEBUG) << "Transaction Perform Error , Need Switch."; + dmcFlagStruct.switchFlag = true; + }); + + + auto executorCallback = [&dmcFlagStruct]( + bcos::Error::UniquePtr error, DmcExecutor::Status status) { + if (error || status == DmcExecutor::Status::ERROR) + { + ++dmcFlagStruct.error; + ++dmcFlagStruct.round; + SCHEDULER_LOG(DEBUG) << LOG_BADGE("DmcExecutor") + << LOG_KV("dmcExecutor go error", dmcFlagStruct.error) + << LOG_KV("total is ", dmcFlagStruct.round); + } + if (status == DmcExecutor::Status::PAUSED || status == DmcExecutor::Status::NEED_PREPARE) + { + ++dmcFlagStruct.paused; + ++dmcFlagStruct.round; + SCHEDULER_LOG(DEBUG) << LOG_BADGE("DmcExecutor") + << LOG_KV("dmcExecutor go paused or need prepare", + dmcFlagStruct.paused) + << LOG_KV("total is ", dmcFlagStruct.round); + } + if (status == DmcExecutor::Status::FINISHED) + { + ++dmcFlagStruct.finished; + ++dmcFlagStruct.round; + SCHEDULER_LOG(DEBUG) << LOG_BADGE("DmcExecutor") + << LOG_KV("dmcExecutor go Finished", dmcFlagStruct.finished) + << LOG_KV("total is ", dmcFlagStruct.round); + } + }; + + + // TXHASH = 0, // Received an new transaction from scheduler + // MESSAGE, // Send/Receive an external call to/from another contract + // FINISHED, // Send a finish to another contract + // KEY_LOCK, // Send a wait key lock to scheduler, or release key lock + // SEND_BACK, // Send a dag refuse to scheduler + // REVERT, // Send/Receive a revert to/from previous external call + + // TXHASH DMCEXECUTE + auto message = createMessage(0, 0, 0, "0xaabbccdd", false); + dmcExecutor->submit(std::move(message), false); + SCHEDULER_LOG(DEBUG) << "prepare begin"; + + + // MESSAGE + auto message1 = createMessage(1, 0, 1, "0xaabbccdd", false); + dmcExecutor->submit(std::move(message1), false); + + // SEND_BACK DMC_EXECUTE (TXHASH) + auto message2 = createMessage(2, 0, 4, "0xaabbccdd", false); + dmcExecutor->submit(std::move(message2), false); + + + // SEND_BACK (MESSAGE) + auto message3 = createMessage(3, 0, 4, "", false); + message3->setTransactionHash(h256(123)); + bcos::u256 salt(787667543453); + message3->setCreateSalt(salt); + dmcExecutor->submit(std::move(message3), false); + + // NEED_SCHEDULE_OUT + auto message4 = createMessage(3, 0, 2, "0xccddeeff", false); + dmcExecutor->submit(std::move(message4), false); + + auto needScheduler_out = dmcExecutor->prepare(); + BOOST_CHECK(needScheduler_out); + dmcExecutor->go(executorCallback); + SCHEDULER_LOG(DEBUG) << LOG_BADGE("DmcExecutor") << LOG_KV("round is ", dmcFlagStruct.round) + << LOG_KV("finished is ", dmcFlagStruct.finished) + << LOG_KV("paused is ", dmcFlagStruct.paused) + << LOG_KV("error is ", dmcFlagStruct.error); + dmcExecutor->prepare(); + // dmcExecutor->go(executorCallback); + SCHEDULER_LOG(DEBUG) << LOG_BADGE("DmcExecutor") << LOG_KV("round is ", dmcFlagStruct.round) + << LOG_KV("finished is ", dmcFlagStruct.finished) + << LOG_KV("paused is ", dmcFlagStruct.paused) + << LOG_KV("error is ", dmcFlagStruct.error); + + BOOST_CHECK(dmcFlagStruct.DmcFlag && dmcFlagStruct.finishFlag); + BOOST_CHECK(dmcFlagStruct.schedulerOutFlag); + BOOST_CHECK_EQUAL(dmcFlagStruct.paused, 1); + BOOST_CHECK_EQUAL(dmcFlagStruct.round, 1); + BOOST_CHECK(!dmcFlagStruct.callFlag); + dmcFlagStruct.DmcFlag = false; + dmcFlagStruct.finishFlag = false; + dmcFlagStruct.schedulerOutFlag = false; + + + // call + auto callMessage = createMessage(4, 0, 1, "0xaabbccdd", true); + dmcExecutor->submit(std::move(callMessage), false); + dmcExecutor->prepare(); + dmcExecutor->go(executorCallback); + dmcExecutor->prepare(); + + SCHEDULER_LOG(DEBUG) << LOG_BADGE("DmcExecutor") << LOG_KV("total is ", dmcFlagStruct.round) + << LOG_KV("finished is ", dmcFlagStruct.finished) + << LOG_KV("paused is ", dmcFlagStruct.paused) + << LOG_KV("error is ", dmcFlagStruct.error); + BOOST_CHECK(dmcFlagStruct.callFlag && dmcFlagStruct.finishFlag); + BOOST_CHECK(!dmcFlagStruct.DmcFlag && !dmcFlagStruct.schedulerOutFlag); + BOOST_CHECK(!dmcFlagStruct.lockedFlag && !dmcFlagStruct.switchFlag); +} + +BOOST_AUTO_TEST_CASE(keyLocksTest) +{ + DmcFlagStruct dmcFlagStruct; + + auto hashImpl = std::make_shared(); + auto block = blockFactory->createBlock(); + auto blockHeader = blockFactory->blockHeaderFactory()->createBlockHeader(); + blockHeader->setNumber(1); + block->setBlockHeader(blockHeader); + // block = fakeBlock(cryptoSuite, blockFactory, 1, 1, 1); + auto dmcExecutor = std::make_shared( + "DmcExecutor1", "0xaabbccdd", block, executor1, keyLocks, hashImpl, dmcRecorder); + + dmcExecutor->setSchedulerOutHandler( + [this, &dmcFlagStruct](bcos::scheduler::ExecutiveState::Ptr executiveState) { + dmcFlagStruct.schedulerOutFlag = true; + auto to = std::string(executiveState->message->to()); + auto hashImpl = std::make_shared(); + auto block = blockFactory->createBlock(); + auto blockHeader = blockFactory->blockHeaderFactory()->createBlockHeader(); + blockHeader->setNumber(2); + block->setBlockHeader(blockHeader); + auto dmcExecutor2 = std::make_shared( + "DmcExecutor2", to, block, executor1, keyLocks, hashImpl, dmcRecorder); + dmcExecutor2->scheduleIn(executiveState); + }); + + dmcExecutor->setOnTxFinishedHandler( + [&dmcFlagStruct](bcos::protocol::ExecutionMessage::UniquePtr output) { + auto outputBytes = output->data(); + std::string outputStr((char*)outputBytes.data(), outputBytes.size()); + SCHEDULER_LOG(DEBUG) << LOG_KV("output data is ", outputStr); + if (outputStr == "Call Finished!") + { + dmcFlagStruct.callFlag = true; + dmcFlagStruct.finishFlag = true; + } + else if (outputStr == "DMCExecuteTransaction Finish!") + { + dmcFlagStruct.DmcFlag = true; + dmcFlagStruct.finishFlag = true; + } + else if (outputStr == "DMCExecuteTransaction Finish, I am keyLock!") + { + dmcFlagStruct.lockedFlag = true; + } + else + { + dmcFlagStruct.finishFlag = true; + } + }); + + dmcExecutor->setOnNeedSwitchEventHandler([&dmcFlagStruct]() { + SCHEDULER_LOG(DEBUG) << "Transaction Perform Error , Need Switch."; + dmcFlagStruct.switchFlag = true; + }); + + auto executorCallback = [&dmcFlagStruct]( + bcos::Error::UniquePtr error, DmcExecutor::Status status) { + if (error || status == DmcExecutor::Status::ERROR) + { + ++dmcFlagStruct.error; + ++dmcFlagStruct.round; + SCHEDULER_LOG(DEBUG) << LOG_BADGE("DmcExecutor") + << LOG_KV("dmcExecutor go error", dmcFlagStruct.error) + << LOG_KV("total is ", dmcFlagStruct.round); + } + if (status == DmcExecutor::Status::PAUSED || status == DmcExecutor::Status::NEED_PREPARE) + { + ++dmcFlagStruct.paused; + ++dmcFlagStruct.round; + SCHEDULER_LOG(DEBUG) << LOG_BADGE("DmcExecutor") + << LOG_KV("dmcExecutor go paused or need prepare", + dmcFlagStruct.paused) + << LOG_KV("total is ", dmcFlagStruct.round); + } + if (status == DmcExecutor::Status::FINISHED) + { + ++dmcFlagStruct.finished; + ++dmcFlagStruct.round; + SCHEDULER_LOG(DEBUG) << LOG_BADGE("DmcExecutor") + << LOG_KV("dmcExecutor go Finished", dmcFlagStruct.finished) + << LOG_KV("total is ", dmcFlagStruct.round); + } + }; + + + // TXHASH = 0, // Received an new transaction from scheduler + // MESSAGE, // Send/Receive an external call to/from another contract + // FINISHED, // Send a finish to another contract + // KEY_LOCK, // Send a wait key lock to scheduler, or release key lock + // SEND_BACK, // Send a dag refuse to scheduler + // REVERT, // Send/Receive a revert to/from previous external call + + // TXHASH DMCEXECUTE + auto lockMessage1 = createMessage(0, 0, 3, "0xaabbccdd", false); + lockMessage1->setKeyLocks({"key1"}); + lockMessage1->setKeyLockAcquired("key2"); + dmcExecutor->submit(std::move(lockMessage1), false); + auto lockMessage2 = createMessage(1, 0, 3, "0xaabbccdd", false); + lockMessage2->setKeyLocks({"key2"}); + lockMessage2->setKeyLockAcquired("key3"); + dmcExecutor->submit(std::move(lockMessage2), false); + auto lockMessage3 = createMessage(2, 0, 3, "0xaabbccdd", false); + lockMessage3->setKeyLocks({"key3"}); + lockMessage3->setKeyLockAcquired("key1"); + dmcExecutor->submit(std::move(lockMessage3), false); + + dmcExecutor->prepare(); + auto locked = dmcExecutor->unlockPrepare(); + auto found = dmcExecutor->detectLockAndRevert(); + BOOST_CHECK(found && locked); + SCHEDULER_LOG(DEBUG) << "no need_prepare, found deadlock and revert"; + dmcExecutor->releaseOutdatedLock(); + dmcExecutor->go(executorCallback); + // dmcExecutor->prepare(); + SCHEDULER_LOG(DEBUG) << LOG_BADGE("DmcExecutor") << LOG_KV("round is ", dmcFlagStruct.round) + << LOG_KV("finished is ", dmcFlagStruct.finished) + << LOG_KV("paused is ", dmcFlagStruct.paused) + << LOG_KV("error is ", dmcFlagStruct.error); + // BOOST_CHECK(dmcFlagStruct.DmcFlag && dmcFlagStruct.finishFlag); + BOOST_CHECK_EQUAL(dmcFlagStruct.paused, 1); + BOOST_CHECK_EQUAL(dmcFlagStruct.round, 1); + // dmcExecutor->go(executorCallback); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-scheduler/test/testDmcStepRecorder.cpp" "b/BFPL\345\243\271/bcos-scheduler/test/testDmcStepRecorder.cpp" new file mode 100644 index 00000000..1f249d66 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/testDmcStepRecorder.cpp" @@ -0,0 +1,91 @@ +#include "DmcStepRecorder.h" +#include +#include +#include + + +using namespace bcos::scheduler; + +namespace bcos::test +{ +struct DmcStepRecorderFixture +{ + DmcStepRecorderFixture() + { + auto start = utcTime(); + for (size_t id = 0; utcTime() - start < m_generateCntLimit; id++) + { + auto message = std::make_unique(); + + message->setStaticCall(bool(id % 2)); + message->setType(protocol::ExecutionMessage::Type(id % 6)); + message->setContextID(id); + message->setSeq(id * id * ~id % (id + 1)); + message->setOrigin("aabbccdd"); + message->setFrom("eeffaabb"); + message->setTo("ccddeeff"); + m_sendMessages.push_back(std::move(message)); + } + + start = utcTime(); + for (size_t id = 0; utcTime() - start < m_generateCntLimit; id++) + { + auto message = std::make_unique(); + + message->setStaticCall(bool(id % 2)); + message->setType(protocol::ExecutionMessage::Type(id % 6)); + message->setContextID(id); + message->setSeq(id * id * ~id % (id + 1)); + message->setOrigin("aabbccdd"); + message->setFrom("eeffaabb"); + message->setTo("ccddeeff"); + m_rcvMessages.emplace_back(std::move(message)); + } + } + + uint64_t m_generateCntLimit = 500; // 500ms + std::vector m_sendMessages; + std::vector m_rcvMessages; + std::string m_address = "aacc0011"; +}; + +BOOST_FIXTURE_TEST_SUITE(TestDmcStepRecorder, DmcStepRecorderFixture) + +BOOST_AUTO_TEST_CASE(Test) +{ + size_t totalLoop = 10; + std::cout << "[0] Dmc recorder test start for:" << m_sendMessages.size() << "-" + << m_rcvMessages.size() << std::endl; + auto start = utcTime(); + DmcStepRecorder::Ptr recorder = std::make_shared(); + for (size_t i = 0; i < totalLoop; i++) + { + recorder->recordSends(m_address, m_sendMessages); + recorder->recordReceives(m_address, m_rcvMessages); + recorder->nextDmcRound(); + } + std::string res1 = recorder->dumpAndClearChecksum(); + std::cout << "[1]:" << res1 << " Use " << (utcTime() - start) << "ms from start" << std::endl; + + start = utcTime(); + for (size_t i = 0; i < totalLoop; i++) + { + for (size_t id = 0; id < m_sendMessages.size(); id++) + { + recorder->recordSend(m_address, id, m_sendMessages[id]); + } + + for (size_t id = 0; id < m_rcvMessages.size(); id++) + { + recorder->recordReceive(m_address, id, m_rcvMessages[id]); + } + recorder->nextDmcRound(); + } + std::string res2 = recorder->dumpAndClearChecksum(); + + std::cout << "[2]:" << res2 << " Use " << (utcTime() - start) << "ms from last" << std::endl; + + BOOST_CHECK(res1 == res2); +} +} // namespace bcos::test +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/testExecutivePool.cpp" "b/BFPL\345\243\271/bcos-scheduler/test/testExecutivePool.cpp" new file mode 100644 index 00000000..1f4a6a87 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/testExecutivePool.cpp" @@ -0,0 +1,249 @@ +#include "Executive.h" +#include "ExecutivePool.h" +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace bcos::scheduler; + +namespace bcos::test +{ +struct ExecutivePoolFixture +{ + ExecutivePoolFixture(){}; + + scheduler::ExecutivePool m_executivePool; + scheduler::ExecutivePool::MessageHint m_messageHint; +}; +BOOST_FIXTURE_TEST_SUITE(TestExecutivePool, ExecutivePoolFixture) + +BOOST_AUTO_TEST_CASE(addAndgetTest1) +{ + ExecutivePool::Ptr executivePool = std::make_shared(); + BOOST_CHECK(executivePool->empty(ExecutivePool::MessageHint::NEED_PREPARE)); + BOOST_CHECK(executivePool->empty()); + for (int64_t i = 0; i < 50; ++i) + { + auto executiveState = std::make_shared(i, nullptr, false); + executivePool->add(i, executiveState); + } + int64_t count = 0; + while (executivePool->get(count) != nullptr) + { + ++count; + } + BOOST_CHECK(count == 50); + BOOST_CHECK(!executivePool->empty(ExecutivePool::MessageHint::NEED_PREPARE)); +} + +BOOST_AUTO_TEST_CASE(addAndgetTest2) +{ + ExecutivePool::Ptr executivePool = std::make_shared(); + BOOST_CHECK(executivePool->empty(ExecutivePool::MessageHint::NEED_PREPARE)); + BOOST_CHECK(executivePool->empty()); + for (int64_t i = 0; i < 10; ++i) + { + auto message = std::make_unique(); + message->setStaticCall(bool(i % 2)); + message->setType(protocol::ExecutionMessage::Type(i % 6)); + message->setContextID(i); + message->setSeq(i * i * ~i % (i + 1)); + message->setOrigin("aabbccdd"); + message->setFrom("eeffaabb"); + message->setTo("ccddeeff"); + ExecutiveState::Ptr executiveState = + std::make_shared(i, std::move(message), false); + executivePool->add(i, executiveState); + } + + + auto message = std::make_unique(); + message->setStaticCall(true); + message->setType(protocol::ExecutionMessage::Type(6)); + message->setContextID(9); + message->setSeq(1000); + message->setOrigin("aabbccdd"); + message->setFrom("cccccccc"); + message->setTo("dddd"); + auto executiveState = + std::make_shared(9, std::move(message), true); + bool success = executivePool->add(9, executiveState); + BOOST_CHECK(!success); + auto state = executivePool->get(9); + SCHEDULER_LOG(DEBUG) << state->message->to(); + BOOST_CHECK(std::string(state->message->to()) == "ccddeeff"); +} + +BOOST_AUTO_TEST_CASE(refreshTest) +{ + ExecutivePool::Ptr executivePool = std::make_shared(); + BOOST_CHECK(executivePool->empty()); + for (int64_t i = 1; i <= 30; ++i) + { + executivePool->markAs(i, ExecutivePool::MessageHint::ALL); + // executivePool->markAs(ExecutivePool::MessageHint::NEED_PREPARE, i); + if (i % 3 == 0) + { + executivePool->markAs(i, ExecutivePool::MessageHint::NEED_SCHEDULE_OUT); + } + if (i % 3 == 1) + { + executivePool->markAs(i, ExecutivePool::MessageHint::LOCKED); + executivePool->markAs(i, ExecutivePool::MessageHint::NEED_SEND); + } + if (i % 3 == 2) + { + executivePool->markAs(i, ExecutivePool::MessageHint::END); + } + } + BOOST_CHECK(!executivePool->empty(ExecutivePool::MessageHint::NEED_SCHEDULE_OUT)); + BOOST_CHECK(!executivePool->empty(ExecutivePool::MessageHint::END)); + BOOST_CHECK(!executivePool->empty(ExecutivePool::MessageHint::LOCKED)); + BOOST_CHECK(!executivePool->empty(ExecutivePool::MessageHint::NEED_SEND)); + executivePool->refresh(); + BOOST_CHECK(executivePool->empty(ExecutivePool::MessageHint::NEED_SCHEDULE_OUT)); + BOOST_CHECK(executivePool->empty(ExecutivePool::MessageHint::END)); + BOOST_CHECK(executivePool->empty(ExecutivePool::MessageHint::LOCKED) && + !executivePool->empty(ExecutivePool::MessageHint::NEED_SEND)); +} + +BOOST_AUTO_TEST_CASE(forEachTest) +{ + ExecutivePool::Ptr executivePool = std::make_shared(); + std::set needPrepare; + std::set needSchedule; + std::set needRemove; + for (int64_t i = 1; i <= 10; ++i) + { + // generate between random number + auto id = (rand() % 10000) + 1; + BCOS_LOG(DEBUG) << LOG_BADGE("scheduel_test") << LOG_KV("needPrepare", needPrepare.size()) + << LOG_KV("ID", id); + needPrepare.insert(id); + + needPrepare.insert(id + 5); + needSchedule.insert(id + 5); + + needPrepare.insert(id + 10); + needRemove.insert(id + 10); + } + + for (auto i : needPrepare) + { + BCOS_LOG(DEBUG) << LOG_BADGE("scheduel_test") << LOG_KV("needPrepare", needPrepare.size()) + << LOG_KV("ID", i); + auto executiveState = std::make_shared(i, nullptr, false); + executivePool->add(i, executiveState); + executivePool->markAs(i, ExecutivePool::MessageHint::NEED_PREPARE); + executivePool->markAs(i, ExecutivePool::MessageHint::ALL); + } + for (auto i : needSchedule) + { + BCOS_LOG(DEBUG) << LOG_BADGE("scheduel_test") << LOG_KV("needSchedule", needSchedule.size()) + << LOG_KV("ID", i); + auto executiveState = std::make_shared(i, nullptr, false); + executivePool->add(i, executiveState); + executivePool->markAs(i, ExecutivePool::MessageHint::NEED_SCHEDULE_OUT); + } + for (auto i : needRemove) + { + BCOS_LOG(DEBUG) << LOG_BADGE("scheduel_test") << LOG_KV("needRemove", needRemove.size()) + << LOG_KV("ID", i); + auto executiveState = std::make_shared(i, nullptr, false); + executivePool->add(i, executiveState); + executivePool->markAs(i, ExecutivePool::MessageHint::END); + } + + BOOST_CHECK(!executivePool->empty(ExecutivePool::MessageHint::ALL)); + BOOST_CHECK(!executivePool->empty(ExecutivePool::MessageHint::NEED_PREPARE)); + BOOST_CHECK(!executivePool->empty(ExecutivePool::MessageHint::END)); + BOOST_CHECK(!executivePool->empty(ExecutivePool::MessageHint::NEED_SCHEDULE_OUT)); + + executivePool->forEach(ExecutivePool::MessageHint::NEED_PREPARE, + [&needPrepare](int64_t contextID, ExecutiveState::Ptr) { + // BCOS_LOG(DEBUG) << LOG_BADGE("scheduel_test") << LOG_KV("setlen", needPrepare.size()) + // << LOG_KV("contextID", contextID); + auto iter = needPrepare.find(contextID); + needPrepare.erase(iter); + return true; + }); + + executivePool->forEach(ExecutivePool::MessageHint::NEED_SCHEDULE_OUT, + [&needSchedule](int64_t contextID, ExecutiveState::Ptr) { + auto iter = needSchedule.find(contextID); + needSchedule.erase(iter); + return true; + }); + + executivePool->forEach( + ExecutivePool::MessageHint::END, [&needRemove](int64_t contextID, ExecutiveState::Ptr) { + auto iter = needRemove.find(contextID); + needRemove.erase(iter); + return true; + }); + + executivePool->forEach( + ExecutivePool::MessageHint::ALL, [](int64_t, ExecutiveState::Ptr executiveState) { + // do nothing + BCOS_LOG(DEBUG) << " 1.PendingMsg: \t\t [--] " << executiveState->toString(); + return true; + }); + BOOST_CHECK(needPrepare.empty()); + BOOST_CHECK(needSchedule.empty()); + BOOST_CHECK(needRemove.empty()); +} + +BOOST_AUTO_TEST_CASE(forEachAndClearTest) +{ + ExecutivePool::Ptr executivePool = std::make_shared(); + std::set needSend; + std::set locked; + for (int64_t i = 1; i <= 10; ++i) + { + auto id = (rand() % 10000) + 1; + needSend.insert(id); + locked.insert(id + 1); + } + for (auto i : needSend) + { + auto executiveState = std::make_shared(i, nullptr, false); + executivePool->add(i, executiveState); + executivePool->markAs(i, ExecutivePool::MessageHint::NEED_SEND); + } + for (auto i : locked) + { + auto executiveState = std::make_shared(i, nullptr, false); + executivePool->add(i, executiveState); + executivePool->markAs(i, ExecutivePool::MessageHint::LOCKED); + } + + BOOST_CHECK(!executivePool->empty(ExecutivePool::MessageHint::NEED_SEND)); + BOOST_CHECK(!executivePool->empty(ExecutivePool::MessageHint::LOCKED)); + + executivePool->forEachAndClear( + ExecutivePool::MessageHint::NEED_SEND, [&needSend](int64_t contextID, ExecutiveState::Ptr) { + auto iter = needSend.find(contextID); + needSend.erase(iter); + // BCOS_LOG(DEBUG) << LOG_BADGE("SCHEDULE") << LOG_DESC("set length is") + // << LOG_KV("needSend", needSend.size()); + + return true; + }); + executivePool->forEachAndClear( + ExecutivePool::MessageHint::LOCKED, [&locked](int64_t contextID, ExecutiveState::Ptr) { + auto iter = locked.find(contextID); + locked.erase(iter); + return true; + }); + BOOST_CHECK(needSend.empty()); + BOOST_CHECK(locked.empty()); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-scheduler/test/testExecutorManager.cpp" "b/BFPL\345\243\271/bcos-scheduler/test/testExecutorManager.cpp" new file mode 100644 index 00000000..0a10c245 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/testExecutorManager.cpp" @@ -0,0 +1,178 @@ +#include "ExecutorManager.h" +#include "bcos-framework/executor/ParallelTransactionExecutorInterface.h" +#include "mock/MockExecutor.h" +#include +#include +#include + +namespace bcos::test +{ +struct ExecutorManagerFixture +{ + ExecutorManagerFixture() { executorManager = std::make_shared(); } + + scheduler::ExecutorManager::Ptr executorManager; +}; + +BOOST_FIXTURE_TEST_SUITE(TestExecutorManager, ExecutorManagerFixture) + +BOOST_AUTO_TEST_CASE(addExecutor) +{ + BOOST_CHECK_NO_THROW( + executorManager->addExecutor("1", std::make_shared("1"))); + BOOST_CHECK_NO_THROW( + executorManager->addExecutor("2", std::make_shared("2"))); + BOOST_CHECK_NO_THROW( + executorManager->addExecutor("3", std::make_shared("3"))); + BOOST_CHECK_NO_THROW( + executorManager->addExecutor("3", std::make_shared("3"))); +} + +BOOST_AUTO_TEST_CASE(dispatch) +{ + BOOST_CHECK_NO_THROW( + executorManager->addExecutor("1", std::make_shared("1"))); + BOOST_CHECK_NO_THROW( + executorManager->addExecutor("2", std::make_shared("2"))); + BOOST_CHECK_NO_THROW( + executorManager->addExecutor("3", std::make_shared("3"))); + BOOST_CHECK_NO_THROW( + executorManager->addExecutor("4", std::make_shared("4"))); + + std::vector contracts; + for (int i = 0; i < 100; ++i) + { + contracts.push_back(boost::lexical_cast(i)); + } + + std::vector executors; + for (auto& it : contracts) + { + executors.push_back(executorManager->dispatchExecutor(it)); + } + BOOST_CHECK_EQUAL(executors.size(), 100); + + std::map executor2count{ + std::pair("1", 0), std::pair("2", 0), std::pair("3", 0), std::pair("4", 0)}; + for (auto it = executors.begin(); it != executors.end(); ++it) + { + ++executor2count[std::dynamic_pointer_cast(*it)->name()]; + } + + BOOST_CHECK_EQUAL(executor2count["1"], 25); + BOOST_CHECK_EQUAL(executor2count["2"], 25); + BOOST_CHECK_EQUAL(executor2count["3"], 25); + BOOST_CHECK_EQUAL(executor2count["4"], 25); + + std::vector executors2; + for (auto& it : contracts) + { + executors2.push_back(executorManager->dispatchExecutor(it)); + } + BOOST_CHECK_EQUAL_COLLECTIONS( + executors.begin(), executors.end(), executors2.begin(), executors2.end()); + + std::vector contracts2; + for (int i = 0; i < 40; ++i) + { + contracts2.push_back(boost::lexical_cast(i + 1000)); + } + + contracts2.insert(contracts2.end(), contracts.begin(), contracts.end()); + + std::vector executors3; + for (auto& it : contracts2) + { + executors3.push_back(executorManager->dispatchExecutor(it)); + } + std::map executor2count2{ + std::pair("1", 0), std::pair("2", 0), std::pair("3", 0), std::pair("4", 0)}; + for (auto it = executors3.begin(); it != executors3.end(); ++it) + { + ++executor2count2[std::dynamic_pointer_cast(*it)->name()]; + } + + BOOST_CHECK_EQUAL(executor2count2["1"], 35); + BOOST_CHECK_EQUAL(executor2count2["2"], 35); + BOOST_CHECK_EQUAL(executor2count2["3"], 35); + BOOST_CHECK_EQUAL(executor2count2["4"], 35); + + std::vector executors4; + for (auto& it : contracts2) + { + executors4.push_back(executorManager->dispatchExecutor(it)); + } + + std::map contract2executor; + for (size_t i = 0; i < contracts2.size(); ++i) + { + contract2executor.insert({contracts2[i], executors4[i]}); + } + + BOOST_CHECK_EQUAL_COLLECTIONS( + executors3.begin(), executors3.end(), executors4.begin(), executors4.end()); + + // record executor3's contract + std::set contractsInExecutor3; + for (size_t i = 0; i < executors4.size(); ++i) + { + auto executor = executors4[i]; + if (std::dynamic_pointer_cast(executor)->name() == "3") + { + contractsInExecutor3.insert(contracts2[i]); + } + } + + BOOST_CHECK_EQUAL(contractsInExecutor3.size(), 35); + + BOOST_CHECK_NO_THROW(executorManager->removeExecutor("3")); + + std::vector contracts3; + for (int i = 0; i < 10; ++i) + { + contracts2.push_back(boost::lexical_cast(i + 2000)); + } + + contracts2.insert(contracts2.end(), contracts3.begin(), contracts3.end()); + + std::vector executors5; + for (auto& it : contracts2) + { + executors5.push_back(executorManager->dispatchExecutor(it)); + } + BOOST_CHECK_EQUAL(executors5.size(), 150); + + size_t oldContract = 0; + for (size_t i = 0; i < contracts2.size(); ++i) + { + auto contract = contracts2[i]; + if (boost::lexical_cast(contract) < 2000 && + contractsInExecutor3.find(contract) == contractsInExecutor3.end()) + { + ++oldContract; + + BOOST_CHECK_EQUAL(executors5[i], contract2executor[contract]); + } + } + + BOOST_CHECK_EQUAL(oldContract, 150 - 35 - 10); // exclude new contract and executor3's contract +} + +BOOST_AUTO_TEST_CASE(remove) +{ + BOOST_CHECK_NO_THROW( + executorManager->addExecutor("1", std::make_shared("1"))); + BOOST_CHECK_NO_THROW( + executorManager->addExecutor("2", std::make_shared("2"))); + BOOST_CHECK_NO_THROW( + executorManager->addExecutor("3", std::make_shared("3"))); + BOOST_CHECK_NO_THROW( + executorManager->addExecutor("3", std::make_shared("3"))); + + BOOST_CHECK_NO_THROW(executorManager->removeExecutor("2")); + BOOST_CHECK_THROW(executorManager->removeExecutor("10"), bcos::Exception); + BOOST_CHECK_THROW(executorManager->removeExecutor("2"), bcos::Exception); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/testKeyLocks.cpp" "b/BFPL\345\243\271/bcos-scheduler/test/testKeyLocks.cpp" new file mode 100644 index 00000000..86c4404b --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/testKeyLocks.cpp" @@ -0,0 +1,114 @@ +#include "GraphKeyLocks.h" +#include "mock/MockExecutor.h" +#include +#include +#include +#include +#include + +namespace bcos::test +{ +struct KeyLocksFixture +{ + KeyLocksFixture() {} + + scheduler::GraphKeyLocks keyLocks; +}; + +BOOST_FIXTURE_TEST_SUITE(TestKeyLocks, KeyLocksFixture) + +BOOST_AUTO_TEST_CASE(acquireKeyLock) +{ + // Test same contextID + std::string to = "contract1"; + std::string key = "key100"; + int64_t contextID = 1000; + + for (int64_t seq = 0; seq < 10; ++seq) + { + BOOST_CHECK(keyLocks.acquireKeyLock(to, key, contextID, seq)); + } + + // Test another contextID + BOOST_CHECK(!keyLocks.acquireKeyLock(to, key, 1001, 0)); + + // Release 5 times + for (int64_t seq = 0; seq < 5; ++seq) + { + keyLocks.releaseKeyLocks(contextID, seq); + } + + // Test another contextID + BOOST_CHECK(!keyLocks.acquireKeyLock(to, key, 1001, 0)); + + // Release all + for (int64_t seq = 5; seq < 10; ++seq) + { + keyLocks.releaseKeyLocks(contextID, seq); + } + + BOOST_CHECK(keyLocks.acquireKeyLock(to, key, 1001, 0)); +} + +BOOST_AUTO_TEST_CASE(getKeyLocksNotHoldingByContext) +{ + std::string to = "contract1"; + std::string keyPrefix = "key"; + + for (size_t i = 0; i < 100; ++i) + { + keyLocks.acquireKeyLock(to, keyPrefix + boost::lexical_cast(i), 100, 10); + } + + for (size_t i = 100; i < 200; ++i) + { + keyLocks.acquireKeyLock(to, keyPrefix + boost::lexical_cast(i), 101, 20); + } + + auto keys = keyLocks.getKeyLocksNotHoldingByContext(to, 101); + + BOOST_CHECK_EQUAL(keys.size(), 100); + std::vector matchKeys; + for (size_t i = 0; i < 100; ++i) + { + std::string matchKey = keyPrefix + boost::lexical_cast(i); + matchKeys.emplace_back(std::move(matchKey)); + } + std::sort(matchKeys.begin(), matchKeys.end()); + + BOOST_CHECK_EQUAL_COLLECTIONS(keys.begin(), keys.end(), matchKeys.begin(), matchKeys.end()); +} + +BOOST_AUTO_TEST_CASE(deadLock) +{ + std::string to = "contract1"; + std::string key1 = "key1"; + std::string key2 = "key2"; + + // No dead lock + BOOST_CHECK(keyLocks.acquireKeyLock(to, key1, 1000, 1)); + BOOST_CHECK(keyLocks.acquireKeyLock(to, key2, 1001, 1)); + + BOOST_CHECK(!keyLocks.acquireKeyLock(to, key2, 1000, 2)); + BOOST_CHECK(!keyLocks.acquireKeyLock(to, key2, 1000, 4)); + + BOOST_CHECK(!keyLocks.detectDeadLock(1000)); + BOOST_CHECK(!keyLocks.detectDeadLock(1000)); + BOOST_CHECK(!keyLocks.detectDeadLock(1001)); + + // Add more duplicate edge + BOOST_CHECK(keyLocks.acquireKeyLock(to, key1, 1000, 3)); + BOOST_CHECK(keyLocks.acquireKeyLock(to, key2, 1001, 3)); + + BOOST_CHECK(!keyLocks.detectDeadLock(1000)); + BOOST_CHECK(!keyLocks.detectDeadLock(1001)); + + // Add a dead lock + BOOST_CHECK(!keyLocks.acquireKeyLock(to, key1, 1001, 2)); + + BOOST_CHECK(keyLocks.detectDeadLock(1000)); + BOOST_CHECK(keyLocks.detectDeadLock(1001)); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/testScheduler.cpp" "b/BFPL\345\243\271/bcos-scheduler/test/testScheduler.cpp" new file mode 100644 index 00000000..48d41908 --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/testScheduler.cpp" @@ -0,0 +1,592 @@ +#include "ExecutorManager.h" +#include "SchedulerImpl.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/ledger/LedgerInterface.h" +#include "bcos-framework/protocol/BlockHeaderFactory.h" +#include "bcos-framework/protocol/ProtocolTypeDef.h" +#include "bcos-framework/protocol/Transaction.h" +#include "bcos-framework/protocol/TransactionReceipt.h" +#include "bcos-framework/protocol/TransactionReceiptFactory.h" +#include "bcos-framework/protocol/TransactionSubmitResult.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-protocol/TransactionSubmitResultFactoryImpl.h" +#include "mock/MockDeadLockExecutor.h" +#include "mock/MockExecutor.h" +#include "mock/MockExecutor3.h" +#include "mock/MockExecutorForCall.h" +#include "mock/MockExecutorForCreate.h" +#include "mock/MockExecutorForMessageDAG.h" +#include "mock/MockLedger.h" +#include "mock/MockMultiParallelExecutor.h" +#include "mock/MockRPC.h" +#include "mock/MockTransactionalStorage.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace bcos; +using namespace bcos::crypto; + +namespace bcos::test +{ +struct SchedulerFixture +{ + SchedulerFixture() {} +}; + +/* // TODO: update this unittest to support batch txs sending logic +BOOST_FIXTURE_TEST_SUITE(Scheduler, SchedulerFixture) + +BOOST_AUTO_TEST_CASE(executeBlock) +{ + // Add executor + executorManager->addExecutor("executor1", std::make_shared("executor1")); + + // Generate a test block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(100); + + for (size_t i = 0; i < 10; ++i) + { + auto metaTx = + std::make_shared(h256(i), "contract1"); + block->appendTransactionMetaData(std::move(metaTx)); + } + + for (size_t i = 10; i < 20; ++i) + { + auto metaTx = + std::make_shared(h256(i), "contract2"); + block->appendTransactionMetaData(std::move(metaTx)); + } + + for (size_t i = 20; i < 30; ++i) + { + auto metaTx = + std::make_shared(h256(i), "contract3"); + block->appendTransactionMetaData(std::move(metaTx)); + } + + std::promise executedHeaderPromise; + + scheduler->executeBlock(block, false, + [&](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr&& header, bool) { + BOOST_CHECK(!error); + BOOST_CHECK(header); + + executedHeaderPromise.set_value(std::move(header)); + }); + + bcos::protocol::BlockHeader::Ptr executedHeader = executedHeaderPromise.get_future().get(); + + BOOST_CHECK(executedHeader); + BOOST_CHECK_NE(executedHeader->stateRoot(), h256()); + + bcos::protocol::BlockNumber notifyBlockNumber = 0; + scheduler->registerBlockNumberReceiver( + [&](protocol::BlockNumber number) { notifyBlockNumber = number; }); + + std::promise committedPromise; + scheduler->commitBlock( + executedHeader, [&](bcos::Error::Ptr&& error, bcos::ledger::LedgerConfig::Ptr&& config) { + BOOST_CHECK(!error); + BOOST_CHECK(config); + BOOST_CHECK_EQUAL(config->blockTxCountLimit(), 100); + BOOST_CHECK_EQUAL(config->leaderSwitchPeriod(), 300); + BOOST_CHECK_EQUAL(config->consensusNodeList().size(), 1); + BOOST_CHECK_EQUAL(config->observerNodeList().size(), 2); + BOOST_CHECK_EQUAL(config->hash().hex(), h256(110).hex()); + committedPromise.set_value(true); + }); + + bool committed = committedPromise.get_future().get(); + BOOST_CHECK(committed); + BOOST_CHECK_EQUAL(notifyBlockNumber, 100); +} + +BOOST_AUTO_TEST_CASE(parallelExecuteBlock) +{ + // Add executor + executorManager->addExecutor( + "executor1", std::make_shared("executor1")); + + // Generate a test block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(100); + + for (size_t i = 0; i < 100; ++i) + { + for (size_t j = 0; j < 8; ++j) + { + auto metaTx = std::make_shared( + h256((i + 1) * (j + 1)), "contract" + boost::lexical_cast(j)); + // metaTx->setSource("i am a source!"); + block->appendTransactionMetaData(std::move(metaTx)); + } + } + + std::promise executedHeader; + + scheduler->executeBlock( + block, false, [&](bcos::Error::Ptr error, bcos::protocol::BlockHeader::Ptr header, bool) { + BOOST_CHECK(!error); + BOOST_CHECK(header); + + executedHeader.set_value(std::move(header)); + }); + + auto header = executedHeader.get_future().get(); + + BOOST_CHECK(header); + BOOST_CHECK_NE(header->stateRoot(), h256()); + + bcos::protocol::BlockNumber notifyBlockNumber = 0; + scheduler->registerBlockNumberReceiver( + [&](protocol::BlockNumber number) { notifyBlockNumber = number; }); + std::promise commitPromise; + scheduler->commitBlock( + header, [&](bcos::Error::Ptr&& error, bcos::ledger::LedgerConfig::Ptr&& config) { + BOOST_CHECK(!error); + BOOST_CHECK(config); + BOOST_CHECK_EQUAL(config->blockTxCountLimit(), 100); + BOOST_CHECK_EQUAL(config->leaderSwitchPeriod(), 300); + BOOST_CHECK_EQUAL(config->consensusNodeList().size(), 1); + BOOST_CHECK_EQUAL(config->observerNodeList().size(), 2); + BOOST_CHECK_EQUAL(config->hash().hex(), h256(110).hex()); + + commitPromise.set_value(); + }); + + commitPromise.get_future().get(); + + BOOST_CHECK_EQUAL(notifyBlockNumber, 100); +} + +BOOST_AUTO_TEST_CASE(keyLocks) +{ + // Add executor + executorManager->addExecutor( + "executor1", std::make_shared("executor1")); + + // Generate a test block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(100); + + for (size_t i = 0; i < 1000; ++i) + { + for (size_t j = 0; j < 8; ++j) + { + auto metaTx = std::make_shared( + h256(i * j), "contract" + boost::lexical_cast(j)); + block->appendTransactionMetaData(std::move(metaTx)); + } + } +} + +BOOST_AUTO_TEST_CASE(call) +{ + // Add executor + executorManager->addExecutor( + "executor1", std::make_shared("executor1")); + + std::string inputStr = "Hello world! request"; + auto tx = blockFactory->transactionFactory()->createTransaction(0, "address_to", + bytes(inputStr.begin(), inputStr.end()), 200, 300, "chain", "group", 500, keyPair); + + auto empty_to = blockFactory->transactionFactory()->createTransaction( + 0, "", bytes(inputStr.begin(), inputStr.end()), 200, 300, "chain", "group", 500, keyPair); + + // call + { + bcos::protocol::TransactionReceipt::Ptr receipt; + + scheduler->call(tx, + [&](bcos::Error::Ptr error, bcos::protocol::TransactionReceipt::Ptr receiptResponse) { + BOOST_CHECK(!error); + BOOST_CHECK(receiptResponse); + + receipt = std::move(receiptResponse); + }); + + BOOST_CHECK_EQUAL(receipt->blockNumber(), 0); + BOOST_CHECK_EQUAL(receipt->status(), 0); + BOOST_CHECK_GT(receipt->gasUsed(), 0); + auto output = receipt->output(); + + std::string outputStr((char*)output.data(), output.size()); + BOOST_CHECK_EQUAL(outputStr, "Hello world! response"); + } + + // call empty to + { + scheduler->call(empty_to, + [&](bcos::Error::Ptr error, bcos::protocol::TransactionReceipt::Ptr receiptResponse) { + BOOST_CHECK(error); + BOOST_CHECK(error->errorMessage() == "Call address is empty"); + BOOST_CHECK(receiptResponse == nullptr); + }); + } +} + +BOOST_AUTO_TEST_CASE(registerExecutor) +{ + auto executor = std::make_shared("executor1"); + auto executor2 = std::make_shared("executor2"); + + scheduler->registerExecutor( + "executor1", executor, [&](Error::Ptr&& error) { BOOST_CHECK(!error); }); + scheduler->registerExecutor( + "executor2", executor2, [&](Error::Ptr&& error) { BOOST_CHECK(!error); }); +} + +BOOST_AUTO_TEST_CASE(createContract) +{ + // Add executor + executorManager->addExecutor( + "executor1", std::make_shared("executor1")); + + // Generate a test block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(100); + + auto metaTx = std::make_shared( + [inner = bcostars::TransactionMetaData()]() mutable { return &inner; }); + metaTx->setHash(h256(1)); + metaTx->setTo(""); + block->appendTransactionMetaData(std::move(metaTx)); + + bcos::protocol::BlockHeader::Ptr executedHeader; + scheduler->executeBlock(block, false, + [&](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr&& header, bool) { + BOOST_CHECK(!error); + BOOST_CHECK(header); + + executedHeader = std::move(header); + }); + + BOOST_CHECK(executedHeader); + BOOST_CHECK_NE(executedHeader->stateRoot(), h256()); +} + +BOOST_AUTO_TEST_CASE(dag) +{ + // Add executor + executorManager->addExecutor("executor1", std::make_shared("executor1")); + + // Generate a test block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(100); + + for (size_t i = 0; i < 1000; ++i) + { + auto metaTx = std::make_shared( + h256(i + 1), "contract" + boost::lexical_cast((i + 1) % 10)); + metaTx->setAttribute(metaTx->attribute() | bcos::protocol::Transaction::Attribute::DAG); + block->appendTransactionMetaData(std::move(metaTx)); + } + + std::promise executedHeader; + scheduler->executeBlock(block, false, + [&](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr&& header, bool) { + BOOST_CHECK(!error); + BOOST_CHECK(header); + + executedHeader.set_value(std::move(header)); + }); + + auto header = executedHeader.get_future().get(); + + BOOST_CHECK(header); + BOOST_CHECK_NE(header->stateRoot(), h256()); +} + +BOOST_AUTO_TEST_CASE(dagByMessage) +{ + // Add executor + executorManager->addExecutor( + "executor1", std::make_shared("executor1")); + + // Generate a test block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(100); + + bcos::crypto::KeyPairInterface::Ptr keyPair = + blockFactory->cryptoSuite()->signatureImpl()->generateKeyPair(); + for (size_t i = 0; i < 1000; ++i) + { + bytes input; + auto tx = transactionFactory->createTransaction(20, + "contract" + boost::lexical_cast((i + 1) % 10), input, 100, 200, "chainID", + "groupID", 400, keyPair); + tx->setAttribute(bcos::protocol::Transaction::Attribute::DAG); + block->appendTransaction(tx); + } + + std::promise executedHeader; + scheduler->executeBlock(block, false, + [&](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr&& header, bool) { + BOOST_CHECK(!error); + BOOST_CHECK(header); + + executedHeader.set_value(std::move(header)); + }); + + auto header = executedHeader.get_future().get(); + + BOOST_CHECK(header); + BOOST_CHECK_NE(header->stateRoot(), h256()); +} + +BOOST_AUTO_TEST_CASE(executedBlock) +{ + // Add executor + auto executor = std::make_shared("executor1"); + executorManager->addExecutor("executor1", executor); + + size_t count = 10; + std::vector hashes; + for (size_t blockNumber = 0; blockNumber < count; ++blockNumber) + { + // Generate a test block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(blockNumber); + + for (size_t i = 0; i < 1000; ++i) + { + auto metaTx = std::make_shared( + h256(i + 1), "contract" + boost::lexical_cast((i + 1) % 10)); + metaTx->setAttribute(metaTx->attribute() | bcos::protocol::Transaction::Attribute::DAG); + block->appendTransactionMetaData(std::move(metaTx)); + } + + std::promise executedHeader; + scheduler->executeBlock(block, false, + [&](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr&& header, bool) { + BOOST_CHECK(!error); + BOOST_CHECK(header); + + executedHeader.set_value(std::move(header)); + }); + + auto header = executedHeader.get_future().get(); + + BOOST_CHECK(header); + BOOST_CHECK_NE(header->stateRoot(), h256()); + + SCHEDULER_LOG(TRACE) << "StateRoot: " << header->stateRoot(); + + hashes.emplace_back(header->stateRoot()); + + executor->clear(); + } + + for (size_t blockNumber = 0; blockNumber < count; ++blockNumber) + { + // Get the executed block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(blockNumber); + + scheduler->executeBlock(block, false, + [&](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr&& header, bool) { + BOOST_CHECK(!error); + BOOST_CHECK_EQUAL(header->stateRoot().hex(), hashes[blockNumber].hex()); + }); + } +} + +BOOST_AUTO_TEST_CASE(testDeploySysContract) +{ + // Add executor + auto executor = std::make_shared("executor1"); + executorManager->addExecutor("executor1", executor); + + protocol::BlockNumber blockNumber = 0; + + // Generate a test block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(blockNumber); + + auto tx = blockFactory->transactionFactory()->createTransaction( + 3, precompiled::AUTH_COMMITTEE_ADDRESS, {}, u256(1), 500, "chainId", "groupId", utcTime()); + block->appendTransaction(std::move(tx)); + + std::promise executedHeader; + scheduler->executeBlock(block, false, + [&](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr&& header, bool) { + // callback(BCOS_ERROR_UNIQUE_PTR(-1, "deploy sys contract!"), nullptr); + BOOST_CHECK(error == nullptr); + executedHeader.set_value(std::move(header)); + }); + + auto header = executedHeader.get_future().get(); + + BOOST_CHECK(header); + BOOST_CHECK_NE(header->stateRoot(), h256()); +} + +BOOST_AUTO_TEST_CASE(testCallSysContract) +{ + // Add executor + executorManager->addExecutor( + "executor1", std::make_shared("executor1")); + + auto tx = blockFactory->transactionFactory()->createTransaction( + 3, precompiled::AUTH_COMMITTEE_ADDRESS, {}, u256(1), 500, "chainId", "groupId", utcTime()); + + bcos::protocol::TransactionReceipt::Ptr receipt; + + scheduler->call( + tx, [&](bcos::Error::Ptr error, bcos::protocol::TransactionReceipt::Ptr receiptResponse) { + BOOST_CHECK(!error); + BOOST_CHECK(receiptResponse); + + receipt = std::move(receiptResponse); + }); + BOOST_CHECK_EQUAL(receipt->blockNumber(), 0); + BOOST_CHECK_EQUAL(receipt->status(), 0); + BOOST_CHECK_GT(receipt->gasUsed(), 0); +} + +BOOST_AUTO_TEST_CASE(checkCommittedBlock) +{ + // Add executor + auto executor = std::make_shared("executor1"); + executorManager->addExecutor("executor1", executor); + + auto blockNumber = 100lu; + // Generate a test block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(blockNumber); + + for (size_t i = 0; i < 1000; ++i) + { + auto metaTx = std::make_shared( + h256(i + 1), "contract" + boost::lexical_cast((i + 1) % 10)); + metaTx->setAttribute(metaTx->attribute() | bcos::protocol::Transaction::Attribute::DAG); + block->appendTransactionMetaData(std::move(metaTx)); + } + + std::promise executedHeader; + scheduler->executeBlock(block, false, + [&](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr&& header, bool) { + BOOST_CHECK(!error); + BOOST_CHECK(header); + + executedHeader.set_value(std::move(header)); + }); + + auto header = executedHeader.get_future().get(); + + BOOST_CHECK(header); + BOOST_CHECK_NE(header->stateRoot(), h256()); + + SCHEDULER_LOG(TRACE) << "StateRoot: " << header->stateRoot(); + auto commitHeader = blockHeaderFactory->createBlockHeader(); + commitHeader->setNumber(blockNumber); + + scheduler->commitBlock(commitHeader, + [](bcos::Error::Ptr&& error, bcos::ledger::LedgerConfig::Ptr&&) { BOOST_CHECK(!error); }); + + // Try execute a same block + auto newHeader = blockHeaderFactory->createBlockHeader(); + newHeader->setNumber(blockNumber); + block->setBlockHeader(newHeader); + + scheduler->executeBlock( + block, false, [](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr&&, bool) { + BOOST_CHECK(error); + BOOST_CHECK_EQUAL( + error->errorCode(), bcos::scheduler::SchedulerError::InvalidBlockNumber); + BOOST_CHECK_GT(error->errorMessage().size(), 0); + }); +} + +BOOST_AUTO_TEST_CASE(executeWithSystemError) +{ + // Add executor + auto executor = std::make_shared("executor1"); + executorManager->addExecutor("executor1", executor); + + auto blockNumber = 100lu; + // Generate a test block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(blockNumber); + + for (size_t i = 0; i < 10; ++i) + { + auto metaTx = std::make_shared( + h256(i + 1), "contract" + boost::lexical_cast((i + 1) % 10)); + // metaTx->setAttribute(metaTx->attribute() | bcos::protocol::Transaction::Attribute::DAG); + block->appendTransactionMetaData(std::move(metaTx)); + } + + // Add an error transaction + auto metaTx = std::make_shared( + h256(10086), "contract" + boost::lexical_cast(10086)); + block->appendTransactionMetaData(std::move(metaTx)); + + std::promise executedHeader; + scheduler->executeBlock(block, false, + [&](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr&& header, bool) { + BOOST_CHECK(error); + BOOST_CHECK_EQUAL(error->errorCode(), bcos::scheduler::SchedulerError::UnknownError); + BOOST_CHECK_GT(error->errorMessage().size(), 0); + BOOST_CHECK(!header); + + executedHeader.set_value(); + }); + + executedHeader.get_future().get(); +} + +BOOST_AUTO_TEST_CASE(getCode) +{ + // Add executor + auto executor = std::make_shared("executor1"); + executorManager->addExecutor("executor1", executor); + + scheduler->getCode("hello world!", [](Error::Ptr error, bcos::bytes code) { + BOOST_CHECK(!error); + BOOST_CHECK(code.empty()); + }); +} + +BOOST_AUTO_TEST_CASE(executeWithDeadLock) +{ + auto executor = std::make_shared("executor10"); + executorManager->addExecutor("executor10", executor); + + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(900); + for (size_t i = 0; i < 2; ++i) + { + auto metaTx = std::make_shared( + h256(i + 1), "contract" + boost::lexical_cast(i + 1)); + block->appendTransactionMetaData(std::move(metaTx)); + } + + scheduler->executeBlock(block, false, + [](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr&& blockHeader, bool) { + BOOST_CHECK(!error); + BOOST_CHECK(blockHeader); + }); +} + +BOOST_AUTO_TEST_SUITE_END() + */ +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-scheduler/test/testSchedulerImpl.cpp" "b/BFPL\345\243\271/bcos-scheduler/test/testSchedulerImpl.cpp" new file mode 100644 index 00000000..b62d135c --- /dev/null +++ "b/BFPL\345\243\271/bcos-scheduler/test/testSchedulerImpl.cpp" @@ -0,0 +1,606 @@ +#include "bcos-crypto/interfaces/crypto/KeyPairInterface.h" +#include "bcos-executor/test/unittest/mock/MockTxPool.h" +#include "bcos-framework/executor/ExecutionMessage.h" +#include "bcos-framework/ledger/LedgerInterface.h" +#include "bcos-framework/protocol/BlockHeaderFactory.h" +#include "bcos-framework/protocol/TransactionReceiptFactory.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-protocol/bcos-protocol/TransactionSubmitResultFactoryImpl.h" +#include "bcos-scheduler/src/BlockExecutive.h" +#include "bcos-scheduler/src/SchedulerImpl.h" +#include "bcos-table/src/KeyPageStorage.h" +#include "bcos-table/src/StateStorage.h" +#include "bcos-table/src/StateStorageInterface.h" +#include "mock/MockBlockExecutive.h" +#include "mock/MockBlockExecutiveFactory.h" +#include "mock/MockDmcExecutor.h" +#include "mock/MockExecutor.h" +#include "mock/MockExecutorForCall.h" +#include "mock/MockExecutorForCreate.h" +#include "mock/MockLedger3.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace std; +using namespace bcos; +using namespace rocksdb; +using namespace bcos::ledger; +using namespace bcos::storage; +using namespace bcos::scheduler; +using namespace bcos::crypto; +using namespace bcos::protocol; +using namespace bcos::txpool; +using namespace bcostars::protocol; + +namespace bcos::test +{ +struct schedulerImplFixture +{ + schedulerImplFixture() + { + hashImpl = std::make_shared(); + signature = std::make_shared(); + suite = std::make_shared(hashImpl, signature, nullptr); + ledger = std::make_shared(); + executorManager = std::make_shared(); + + // create RocksDBStorage + rocksdb::DB* db; + rocksdb::Options options; + options.create_if_missing = true; + std::string path = "./unittestdb"; + // open DB + rocksdb::Status s = rocksdb::DB::Open(options, path, &db); + storage = std::make_shared(std::unique_ptr(db), nullptr); + + transactionFactory = std::make_shared(suite); + transactionReceiptFactory = + std::make_shared(suite); + blockHeaderFactory = std::make_shared(suite); + executionMessageFactory = std::make_shared(); + + blockFactory = std::make_shared( + suite, blockHeaderFactory, transactionFactory, transactionReceiptFactory); + + txPool = std::make_shared(); + transactionSubmitResultFactory = + std::make_shared(); + + auto scheduler = std::make_shared(executorManager, ledger, + storage, executionMessageFactory, blockFactory, txPool, transactionSubmitResultFactory, + hashImpl, false, false, false, 0); + auto blockExecutiveFactory = std::make_shared(false); + scheduler->setBlockExecutiveFactory(blockExecutiveFactory); + }; + + ~schedulerImplFixture() {} + bcos::test::MockLedger3::Ptr ledger; + bcos::scheduler::ExecutorManager::Ptr executorManager; + bcos::protocol::ExecutionMessageFactory::Ptr executionMessageFactory; + bcos::protocol::TransactionReceiptFactory::Ptr transactionReceiptFactory; + bcos::protocol::BlockHeaderFactory::Ptr blockHeaderFactory; + bcos::crypto::Hash::Ptr hashImpl; + bcos::scheduler::SchedulerImpl::Ptr scheduler; + bcostars::protocol::TransactionFactoryImpl::Ptr transactionFactory; + bcos::crypto::SignatureCrypto::Ptr signature; + bcos::crypto::CryptoSuite::Ptr suite; + bcostars::protocol::BlockFactoryImpl::Ptr blockFactory; + bcos::txpool::TxPoolInterface::Ptr txPool; + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory; + bcos::scheduler::BlockExecutiveFactory::Ptr blockExecutiveFactory; + std::string path = "./unittestdb"; + bcos::storage::RocksDBStorage::Ptr storage = nullptr; +}; + +BOOST_FIXTURE_TEST_SUITE(testSchedulerImpl, schedulerImplFixture) + +BOOST_AUTO_TEST_CASE(executeBlockTest) +{ + auto scheduler = std::make_shared(executorManager, ledger, + storage, executionMessageFactory, blockFactory, txPool, transactionSubmitResultFactory, + hashImpl, false, false, false, 0); + auto blockExecutiveFactory = std::make_shared(false); + scheduler->setBlockExecutiveFactory(blockExecutiveFactory); + bool executeBlockError = false; + + // execute BlockNumber=7 (storage = 5) block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(7); + for (size_t j = 0; j < 10; ++j) + { + auto metaTx = + std::make_shared(h256(j), "contract1"); + block->appendTransactionMetaData(std::move(metaTx)); + } + // executeBlock + bcos::protocol::BlockHeader::Ptr blockHeader; + + SCHEDULER_LOG(DEBUG) << LOG_KV("BlockHash", block) + << LOG_KV("BlockHeaderNumber", block->blockHeader()->number()); + + scheduler->executeBlock( + block, false, [&](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr header, bool) { + if (error) + { + executeBlockError = true; + BOOST_CHECK(error); + SCHEDULER_LOG(ERROR) << "ExecuteBlock callback error"; + } + else + { + BOOST_CHECK(!error); + BOOST_CHECK(header); + blockHeader = std::move(header); + } + }); + BOOST_CHECK(executeBlockError); + executeBlockError = false; + + // first time executeBlock, add m_block cache + for (size_t i = 5; i < 10; ++i) + { + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(i); + SCHEDULER_LOG(DEBUG) << LOG_KV("BlockNumber", i); + for (size_t j = 0; j < 10; ++j) + { + auto metaTx = + std::make_shared(h256(j), "contract1"); + block->appendTransactionMetaData(std::move(metaTx)); + } + // executeBlock + bcos::protocol::BlockHeader::Ptr blockHeader; + SCHEDULER_LOG(DEBUG) << LOG_KV("BlockHash", block) + << LOG_KV("BlockHeaderNumber", block->blockHeader()->number()); + + scheduler->executeBlock(block, false, + [&](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr header, bool) { + SCHEDULER_LOG(DEBUG) << LOG_KV("BlockHeader", header); + if (error) + { + BOOST_CHECK(error); + executeBlockError = true; + SCHEDULER_LOG(ERROR) << "ExecuteBlock callback error"; + } + else + { + BOOST_CHECK(!error); + BOOST_CHECK(header); + blockHeader = std::move(header); + } + }); + if (!executeBlockError) + { + BOOST_CHECK(blockHeader); + } + executeBlockError = false; + blockHeader = nullptr; + } + // secondTime executeBlock + for (size_t i = 5; i < 8; i++) + { + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(i); + SCHEDULER_LOG(DEBUG) << LOG_KV("BlockNumber", i); + for (size_t j = 0; j < 10; ++j) + { + auto metaTx = + std::make_shared(h256(j), "contract2"); + block->appendTransactionMetaData(std::move(metaTx)); + } + bcos::protocol::BlockHeader::Ptr executeHeader1; + // execute olderBlock whenQueueFront whenInQueue + scheduler->executeBlock(block, false, + [&](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr header, bool) { + if (error) + { + executeBlockError = true; + BOOST_CHECK(error); + SCHEDULER_LOG(ERROR) << "ExecuteBlock callback error"; + } + else + { + BOOST_CHECK(!error); + BOOST_CHECK(header); + executeHeader1 = std::move(header); + } + }); + if (!executeBlockError) + { + BOOST_CHECK(executeHeader1); + } + executeBlockError = false; + executeHeader1 = nullptr; + } + + auto block11 = blockFactory->createBlock(); + block11->blockHeader()->setNumber(11); + for (size_t j = 0; j < 10; ++j) + { + auto metaTx = + std::make_shared(h256(j), "contract2"); + block11->appendTransactionMetaData(std::move(metaTx)); + } + bcos::protocol::BlockHeader::Ptr executeHeader11; + // requestBlock = backNumber + 1 + scheduler->executeBlock(block11, false, + [&](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr header, bool) { + SCHEDULER_LOG(DEBUG) << LOG_KV("BlockHeader", header); + if (error) + { + BOOST_CHECK(error); + executeBlockError = true; + SCHEDULER_LOG(ERROR) << "ExecuteBlock callback error"; + } + else + { + BOOST_CHECK(!error); + BOOST_CHECK(header); + executeHeader11 = std::move(header); + } + }); + BOOST_CHECK(executeBlockError); +} +BOOST_AUTO_TEST_CASE(commitBlock) +{ + auto scheduler = std::make_shared(executorManager, ledger, + storage, executionMessageFactory, blockFactory, txPool, transactionSubmitResultFactory, + hashImpl, false, false, false, 0); + auto blockExecutiveFactory = std::make_shared(false); + scheduler->setBlockExecutiveFactory(blockExecutiveFactory); + + // executeBlock, Fill m_block + bool executeBlockError = false; + for (size_t i = 5; i < 10; ++i) + { + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(i); + for (size_t j = 0; j < 20; ++j) + { + auto metaTx = + std::make_shared(h256(j), "contract2"); + block->appendTransactionMetaData(std::move(metaTx)); + } + // executeBlock + bcos::protocol::BlockHeader::Ptr blockHeader; + scheduler->executeBlock(block, false, + [&](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr header, bool) { + SCHEDULER_LOG(DEBUG) << LOG_KV("BlockHeader", header); + if (error) + { + executeBlockError = true; + BOOST_CHECK(error); + SCHEDULER_LOG(ERROR) << "ExecuteBlock callback error"; + } + else + { + BOOST_CHECK(!error); + BOOST_CHECK(header); + blockHeader = std::move(header); + } + }); + if (!executeBlockError) + { + BOOST_CHECK(blockHeader); + } + executeBlockError = false; + blockHeader = nullptr; + } + // commit + bool commitBlockError = false; + size_t errorNumber = 0; + size_t queueFrontNumber = 0; + ledger->commitSuccess(true); + for (size_t i = 7; i < 11; ++i) + { + auto blockHeader = blockHeaderFactory->createBlockHeader(); + blockHeader->setNumber(i); + std::promise committedPromise; + scheduler->commitBlock( + blockHeader, [&](bcos::Error::Ptr&& error, bcos::ledger::LedgerConfig::Ptr&& config) { + if (error) + { + SCHEDULER_LOG(ERROR) << "CommitBlock error"; + commitBlockError = true; + ++errorNumber; + committedPromise.set_value(false); + } + else + { + ++queueFrontNumber; + BOOST_CHECK(config); + BOOST_CHECK_EQUAL(config->blockTxCountLimit(), 100); + BOOST_CHECK_EQUAL(config->leaderSwitchPeriod(), 300); + BOOST_CHECK_EQUAL(config->consensusNodeList().size(), 1); + BOOST_CHECK_EQUAL(config->observerNodeList().size(), 2); + BOOST_CHECK_EQUAL(config->hash().hex(), h256(6).hex()); + committedPromise.set_value(true); + } + }); + if (commitBlockError) + { + commitBlockError = false; + } + } + BOOST_CHECK_EQUAL(errorNumber, 1); + BOOST_CHECK_EQUAL(queueFrontNumber, 3); + BOOST_CHECK(!commitBlockError); + SCHEDULER_LOG(DEBUG) << LOG_KV("errorNumber", errorNumber) + << LOG_KV("queueFrontNumber", queueFrontNumber); + + // commit blockNumber <= 5 + auto blockHeader0 = blockHeaderFactory->createBlockHeader(); + blockHeader0->setNumber(0); + std::promise committedPromise; + scheduler->commitBlock( + blockHeader0, [&](bcos::Error::Ptr&& error, bcos::ledger::LedgerConfig::Ptr&& config) { + if (error) + { + SCHEDULER_LOG(ERROR) << "CommitBlock error"; + commitBlockError = true; + ++errorNumber; + committedPromise.set_value(false); + } + else + { + ++queueFrontNumber; + BOOST_CHECK(config); + BOOST_CHECK_EQUAL(config->blockTxCountLimit(), 100); + BOOST_CHECK_EQUAL(config->leaderSwitchPeriod(), 300); + BOOST_CHECK_EQUAL(config->consensusNodeList().size(), 1); + BOOST_CHECK_EQUAL(config->observerNodeList().size(), 2); + BOOST_CHECK_EQUAL(config->hash().hex(), h256(5).hex()); + committedPromise.set_value(true); + } + }); + SCHEDULER_LOG(DEBUG) << LOG_KV("errorNumber", errorNumber) + << LOG_KV("queueFrontNumber", queueFrontNumber); +} + +BOOST_AUTO_TEST_CASE(handlerBlockTest) +{ + auto scheduler = + std::make_shared(executorManager, ledger, storage, executionMessageFactory, + blockFactory, txPool, transactionSubmitResultFactory, hashImpl, false, false, false, 0); + auto blockExecutiveFactory = std::make_shared(false); + scheduler->setBlockExecutiveFactory(blockExecutiveFactory); + + // create Block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(6); + + for (size_t i = 0; i < 10; ++i) + { + auto metaTx = + std::make_shared(h256(i), "contract1"); + block->appendTransactionMetaData(std::move(metaTx)); + } + for (size_t i = 10; i < 20; ++i) + { + auto metaTx = + std::make_shared(h256(i), "contract2"); + block->appendTransactionMetaData(std::move(metaTx)); + } + for (size_t i = 20; i < 30; ++i) + { + auto metaTx = + std::make_shared(h256(i), "contract3"); + block->appendTransactionMetaData(std::move(metaTx)); + } + // preExecuteBlock + scheduler->preExecuteBlock( + block, false, [&](bcos::Error::Ptr&& error) { BOOST_CHECK(!error); }); + + + // executeBlock + bool executeBlockError = false; + bcos::protocol::BlockHeader::Ptr blockHeader; + scheduler->executeBlock( + block, false, [&](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr header, bool) { + if (error) + { + executeBlockError = true; + BOOST_CHECK(error); + SCHEDULER_LOG(ERROR) << "ExecuteBlock callback error"; + } + else + { + BOOST_CHECK(!error); + BOOST_CHECK(header); + blockHeader = std::move(header); + } + }); + + BOOST_CHECK(blockHeader); + + + // commitBlock + bool commitBlockError = false; + scheduler->commitBlock( + blockHeader, [&](bcos::Error::Ptr&& error, bcos::ledger::LedgerConfig::Ptr&& config) { + if (error) + { + SCHEDULER_LOG(ERROR) << "CommitBlock error"; + commitBlockError = true; + } + else + { + BOOST_CHECK(config); + BOOST_CHECK_EQUAL(config->blockTxCountLimit(), 100); + BOOST_CHECK_EQUAL(config->leaderSwitchPeriod(), 300); + BOOST_CHECK_EQUAL(config->consensusNodeList().size(), 1); + BOOST_CHECK_EQUAL(config->observerNodeList().size(), 2); + BOOST_CHECK_EQUAL(config->hash().hex(), h256(5).hex()); + } + }); + + BOOST_CHECK(!commitBlockError); +} + + +BOOST_AUTO_TEST_CASE(getCode) +{ + auto scheduler = + std::make_shared(executorManager, ledger, storage, executionMessageFactory, + blockFactory, txPool, transactionSubmitResultFactory, hashImpl, false, false, false, 0); + auto blockExecutiveFactory = std::make_shared(false); + scheduler->setBlockExecutiveFactory(blockExecutiveFactory); + + // Add executor + auto executor = std::make_shared("executor1"); + executorManager->addExecutor("executor1", executor); + SCHEDULER_LOG(DEBUG) << "----- add Executor ------"; + scheduler->getCode("hello world!", [](Error::Ptr error, bcos::bytes code) { + BOOST_CHECK(!error); + BOOST_CHECK(code.empty()); + }); +} + +BOOST_AUTO_TEST_CASE(call) +{ + // Add executor + auto scheduler = + std::make_shared(executorManager, ledger, storage, executionMessageFactory, + blockFactory, txPool, transactionSubmitResultFactory, hashImpl, false, false, false, 0); + // auto blockExecutiveFactory = std::make_shared(false); + // scheduler->setBlockExecutiveFactory(blockExecutiveFactory); + auto executor = std::make_shared("executor1"); + executorManager->addExecutor("executor1", executor); + + + std::string inputStr = "Hello world! request"; + bcos::crypto::KeyPairInterface::Ptr keyPair = + blockFactory->cryptoSuite()->signatureImpl()->generateKeyPair(); + auto tx = blockFactory->transactionFactory()->createTransaction(0, "address_to", + bytes(inputStr.begin(), inputStr.end()), 200, 300, "chain", "group", 500, keyPair); + + auto empty_to = blockFactory->transactionFactory()->createTransaction( + 0, "", bytes(inputStr.begin(), inputStr.end()), 200, 300, "chain", "group", 500, keyPair); + + // call + { + bcos::protocol::TransactionReceipt::Ptr receipt; + + scheduler->call(tx, + [&](bcos::Error::Ptr error, bcos::protocol::TransactionReceipt::Ptr receiptResponse) { + auto blockNumber = receiptResponse->blockNumber(); + std::cout << "blockNumber" << blockNumber << std::endl; + BOOST_CHECK(!error); + BOOST_CHECK(receiptResponse); + + receipt = std::move(receiptResponse); + }); + + BOOST_CHECK_NE(receipt->blockNumber(), 0); + BOOST_CHECK_EQUAL(receipt->status(), 0); + BOOST_CHECK_GT(receipt->gasUsed(), 0); + auto output = receipt->output(); + + std::string outputStr((char*)output.data(), output.size()); + SCHEDULER_LOG(DEBUG) << LOG_KV("outputStr", outputStr); + BOOST_CHECK_EQUAL(outputStr, "Hello world! response"); + } + + // call empty to + { + scheduler->call(empty_to, + [&](bcos::Error::Ptr error, bcos::protocol::TransactionReceipt::Ptr receiptResponse) { + BOOST_CHECK(error); + BOOST_CHECK(error->errorMessage() == "Call address is empty"); + BOOST_CHECK(receiptResponse == nullptr); + }); + } +} + +BOOST_AUTO_TEST_CASE(registerExecutor) +{ + auto scheduler = + std::make_shared(executorManager, ledger, storage, executionMessageFactory, + blockFactory, txPool, transactionSubmitResultFactory, hashImpl, false, false, false, 0); + auto executor1 = std::make_shared("executor1"); + auto executor2 = std::make_shared("executor2"); + scheduler->registerExecutor( + "executor1", executor1, [&](Error::Ptr&& error) { BOOST_CHECK(!error); }); + scheduler->registerExecutor( + "executor2", executor2, [&](Error::Ptr&& error) { BOOST_CHECK(!error); }); +} + + +BOOST_AUTO_TEST_CASE(testDeploySysContract) +{ + auto scheduler = + std::make_shared(executorManager, ledger, storage, executionMessageFactory, + blockFactory, txPool, transactionSubmitResultFactory, hashImpl, false, false, false, 0); + // Add executor + auto executor1 = std::make_shared("executor1"); + executorManager->addExecutor("executor1", executor1); + + // Generate a test block + auto block = blockFactory->createBlock(); + block->blockHeader()->setNumber(0); + + auto tx = blockFactory->transactionFactory()->createTransaction( + 3, precompiled::AUTH_COMMITTEE_ADDRESS, {}, u256(1), 500, "chainId", "groupId", utcTime()); + block->appendTransaction(std::move(tx)); + + std::promise executedHeader; + scheduler->executeBlock(block, false, + [&](bcos::Error::Ptr&& error, bcos::protocol::BlockHeader::Ptr&& header, bool) { + // callback(BCOS_ERROR_UNIQUE_PTR(-1, "deploy sys contract!"), nullptr); + BOOST_CHECK(error == nullptr); + executedHeader.set_value(std::move(header)); + }); + auto header = executedHeader.get_future().get(); + + BOOST_CHECK(header); + BOOST_CHECK_NE(header->stateRoot(), h256()); +} + +BOOST_AUTO_TEST_CASE(testCallSysContract) +{ + auto scheduler = + std::make_shared(executorManager, ledger, storage, executionMessageFactory, + blockFactory, txPool, transactionSubmitResultFactory, hashImpl, false, false, false, 0); + // Add executor + auto executor1 = std::make_shared("executor1"); + executorManager->addExecutor("executor1", executor1); + + auto tx = blockFactory->transactionFactory()->createTransaction( + 3, precompiled::AUTH_COMMITTEE_ADDRESS, {}, u256(1), 500, "chainId", "groupId", utcTime()); + + bcos::protocol::TransactionReceipt::Ptr receipt; + + scheduler->call( + tx, [&](bcos::Error::Ptr error, bcos::protocol::TransactionReceipt::Ptr receiptResponse) { + BOOST_CHECK(!error); + BOOST_CHECK(receiptResponse); + + receipt = std::move(receiptResponse); + }); + BOOST_CHECK_NE(receipt->blockNumber(), 0); + BOOST_CHECK_EQUAL(receipt->status(), 0); + BOOST_CHECK_GT(receipt->gasUsed(), 0); +} + + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/CMakeLists.txt" "b/BFPL\345\243\271/bcos-sdk/CMakeLists.txt" new file mode 100644 index 00000000..0dc5ed9a --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/CMakeLists.txt" @@ -0,0 +1,33 @@ +file(GLOB_RECURSE SRC_LIST "bcos-cpp-sdk/*.cpp") + +find_package(Boost REQUIRED log serialization) +find_package(wedprcrypto REQUIRED) +find_package(jsoncpp REQUIRED) +find_package(OpenSSL REQUIRED) +find_package(Microsoft.GSL REQUIRED) +find_package(range-v3 REQUIRED) + +set(BCOS_CPP_SDK_TARGET bcos-cpp-sdk) +add_library(${BCOS_CPP_SDK_TARGET} ${SRC_LIST}) +target_include_directories(${BCOS_CPP_SDK_TARGET} PUBLIC + $ + $) +target_link_libraries(${BCOS_CPP_SDK_TARGET} PUBLIC + wedprcrypto::crypto + ${TARS_PROTOCOL_TARGET} + bcos-crypto + bcos-boostssl + bcos-utilities + jsoncpp_static + OpenSSL::SSL + OpenSSL::Crypto) + +if (TESTS) + enable_testing() + add_subdirectory(tests) + add_subdirectory(sample) +endif() + +include(GNUInstallDirs) +install(TARGETS ${BCOS_CPP_SDK_TARGET} EXPORT fiscobcosTargets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") +install(DIRECTORY "bcos-cpp-sdk" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/" FILES_MATCHING PATTERN "*.h") \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/Sdk.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/Sdk.h" new file mode 100644 index 00000000..f224071a --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/Sdk.h" @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2021 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 Sdk.h + * @author: octopus + * @date 2021-12-04 + */ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace cppsdk +{ +class Sdk +{ +public: + using Ptr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + +public: + Sdk(bcos::cppsdk::service::Service::Ptr _service, + bcos::cppsdk::jsonrpc::JsonRpcImpl::Ptr _jsonRpc, bcos::cppsdk::amop::AMOP::Ptr _amop, + bcos::cppsdk::event::EventSub::Ptr _eventSub) + : m_service(_service), m_jsonRpc(_jsonRpc), m_amop(_amop), m_eventSub(_eventSub) + {} + virtual ~Sdk() { stop(); } + +private: + bcos::cppsdk::service::Service::Ptr m_service; + bcos::cppsdk::jsonrpc::JsonRpcImpl::Ptr m_jsonRpc; + bcos::cppsdk::amop::AMOP::Ptr m_amop; + bcos::cppsdk::event::EventSub::Ptr m_eventSub; + +public: + virtual void start() + { + if (m_jsonRpc) + { + m_jsonRpc->start(); + } + + if (m_amop) + { + m_amop->start(); + } + + if (m_eventSub) + { + m_eventSub->start(); + } + + if (m_service) + { + m_service->start(); + } + } + + virtual void stop() + { + if (m_service) + { + m_service->stop(); + } + + if (m_jsonRpc) + { + m_jsonRpc->stop(); + } + + if (m_amop) + { + m_amop->stop(); + } + + if (m_eventSub) + { + m_eventSub->stop(); + } + } + +public: + bcos::cppsdk::service::Service::Ptr service() const { return m_service; } + bcos::cppsdk::jsonrpc::JsonRpcImpl::Ptr jsonRpc() const { return m_jsonRpc; } + bcos::cppsdk::amop::AMOP::Ptr amop() const { return m_amop; } + + bcos::cppsdk::event::EventSub::Ptr eventSub() const { return m_eventSub; } +}; + +} // namespace cppsdk +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/SdkFactory.cpp" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/SdkFactory.cpp" new file mode 100644 index 00000000..f6e5cda7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/SdkFactory.cpp" @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2021 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 SdkFactory.cpp + * @author: octopus + * @date 2021-08-21 + */ +#include "SdkFactory.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; +using namespace bcos; +using namespace bcos::cppsdk::amop; +using namespace bcos::cppsdk; +using namespace bcos::cppsdk::config; +using namespace bcos::cppsdk::jsonrpc; +using namespace bcos::cppsdk::event; +using namespace bcos::cppsdk::service; + +SdkFactory::SdkFactory() +{ + // TODO: how to init log in cpp sdk + // LogInitializer::initLog(); +} + +bcos::cppsdk::Sdk::UniquePtr SdkFactory::buildSdk( + std::shared_ptr _config) +{ + if (!_config) + { + _config = m_config; + } + + auto service = buildService(_config); + auto amop = buildAMOP(service); + auto jsonRpc = buildJsonRpc(service); + auto eventSub = buildEventSub(service); + + auto sdk = std::make_unique(service, jsonRpc, amop, eventSub); + return sdk; +} + +bcos::cppsdk::Sdk::UniquePtr SdkFactory::buildSdk(const std::string& _configFile) +{ + auto config = std::make_shared(); + auto wsConfig = config->loadConfig(_configFile); + return buildSdk(wsConfig); +} + +Service::Ptr SdkFactory::buildService(std::shared_ptr _config) +{ + auto groupInfoCodec = std::make_shared(); + auto groupInfoFactory = std::make_shared(); + auto service = std::make_shared(groupInfoCodec, groupInfoFactory, "SDK"); + auto initializer = std::make_shared(); + initializer->setConfig(_config); + initializer->initWsService(service); + service->registerMsgHandler(bcos::protocol::MessageType::BLOCK_NOTIFY, + [service]( + std::shared_ptr _msg, std::shared_ptr _session) { + auto blkMsg = std::string(_msg->payload()->begin(), _msg->payload()->end()); + + service->onRecvBlockNotifier(blkMsg); + + BCOS_LOG(INFO) << "[WS]" << LOG_DESC("receive block notify") + << LOG_KV("endpoint", _session->endPoint()) << LOG_KV("blk", blkMsg); + }); + + service->registerMsgHandler(bcos::protocol::MessageType::GROUP_NOTIFY, + [service]( + std::shared_ptr _msg, std::shared_ptr _session) { + std::string groupInfo = std::string(_msg->payload()->begin(), _msg->payload()->end()); + + service->onNotifyGroupInfo(groupInfo, _session); + + BCOS_LOG(INFO) << "[WS]" << LOG_DESC("receive group info notify") + << LOG_KV("endpoint", _session->endPoint()) + << LOG_KV("groupInfo", groupInfo); + }); + + return service; +} + +bcos::cppsdk::jsonrpc::JsonRpcImpl::Ptr SdkFactory::buildJsonRpc(Service::Ptr _service) +{ + auto groupInfoCodec = std::make_shared(); + auto jsonRpc = std::make_shared(groupInfoCodec); + auto factory = std::make_shared(); + jsonRpc->setFactory(factory); + jsonRpc->setService(_service); + + jsonRpc->setSender([_service](const std::string& _group, const std::string& _node, + const std::string& _request, bcos::cppsdk::jsonrpc::RespFunc _respFunc) { + auto data = std::make_shared(_request.begin(), _request.end()); + auto msg = _service->messageFactory()->buildMessage(); + msg->setSeq(_service->messageFactory()->newSeq()); + msg->setPacketType(bcos::protocol::MessageType::RPC_REQUEST); + msg->setPayload(data); + + _service->asyncSendMessageByGroupAndNode(_group, _node, msg, Options(), + [_respFunc](Error::Ptr _error, std::shared_ptr _msg, + std::shared_ptr _session) { + (void)_session; + _respFunc(_error, _msg ? _msg->payload() : nullptr); + }); + }); + + return jsonRpc; +} + +bcos::cppsdk::amop::AMOP::Ptr SdkFactory::buildAMOP(bcos::cppsdk::service::Service::Ptr _service) +{ + auto amop = std::make_shared(); + + auto topicManager = std::make_shared(); + auto requestFactory = std::make_shared(); + auto messageFactory = std::make_shared(); + + amop->setTopicManager(topicManager); + amop->setRequestFactory(requestFactory); + amop->setMessageFactory(messageFactory); + amop->setService(_service); + + auto amopWeakPtr = std::weak_ptr(amop); + + _service->registerMsgHandler(bcos::cppsdk::amop::MessageType::AMOP_REQUEST, + [amopWeakPtr]( + std::shared_ptr _msg, std::shared_ptr _session) { + auto amop = amopWeakPtr.lock(); + if (amop) + { + amop->onRecvAMOPRequest(_msg, _session); + } + }); + _service->registerMsgHandler(bcos::cppsdk::amop::MessageType::AMOP_RESPONSE, + [amopWeakPtr]( + std::shared_ptr _msg, std::shared_ptr _session) { + auto amop = amopWeakPtr.lock(); + if (amop) + { + amop->onRecvAMOPResponse(_msg, _session); + } + }); + _service->registerMsgHandler(bcos::cppsdk::amop::MessageType::AMOP_BROADCAST, + [amopWeakPtr]( + std::shared_ptr _msg, std::shared_ptr _session) { + auto amop = amopWeakPtr.lock(); + if (amop) + { + amop->onRecvAMOPBroadcast(_msg, _session); + } + }); + _service->registerWsHandshakeSucHandler([amopWeakPtr](std::shared_ptr _session) { + auto amop = amopWeakPtr.lock(); + if (amop) + { + // service handshake successfully + amop->updateTopicsToRemote(_session); + } + }); + return amop; +} + +bcos::cppsdk::event::EventSub::Ptr SdkFactory::buildEventSub(Service::Ptr _service) +{ + auto eventSub = std::make_shared(); + auto messageFactory = std::make_shared(); + + eventSub->setMessageFactory(messageFactory); + eventSub->setService(_service); + eventSub->setConfig(_service->config()); + + auto eventWeakPtr = std::weak_ptr(eventSub); + _service->registerMsgHandler(bcos::cppsdk::event::MessageType::EVENT_LOG_PUSH, + [eventWeakPtr]( + std::shared_ptr _msg, std::shared_ptr _session) { + auto eventSub = eventWeakPtr.lock(); + if (eventSub) + { + eventSub->onRecvEventSubMessage(_msg, _session); + } + }); + + _service->registerDisconnectHandler([eventWeakPtr](std::shared_ptr _session) { + auto eventSub = eventWeakPtr.lock(); + if (eventSub) + { + eventSub->suspendTasks(_session); + } + }); + + return eventSub; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/SdkFactory.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/SdkFactory.h" new file mode 100644 index 00000000..d2e7e995 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/SdkFactory.h" @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 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 Initializer.h + * @author: octopus + * @date 2021-08-21 + */ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace cppsdk +{ +class SdkFactory : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + + SdkFactory(); + +public: + bcos::cppsdk::service::Service::Ptr buildService( + std::shared_ptr _config); + bcos::cppsdk::jsonrpc::JsonRpcImpl::Ptr buildJsonRpc( + bcos::cppsdk::service::Service::Ptr _service); + bcos::cppsdk::amop::AMOP::Ptr buildAMOP(bcos::cppsdk::service::Service::Ptr _service); + bcos::cppsdk::event::EventSub::Ptr buildEventSub(bcos::cppsdk::service::Service::Ptr _service); + +public: + bcos::cppsdk::Sdk::UniquePtr buildSdk( + std::shared_ptr _config = nullptr); + bcos::cppsdk::Sdk::UniquePtr buildSdk(const std::string& _configFile); + +public: + std::shared_ptr config() const { return m_config; } + void setConfig(std::shared_ptr _config) { m_config = _config; } + +private: + std::shared_ptr m_config; +}; +} // namespace cppsdk +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOP.cpp" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOP.cpp" new file mode 100644 index 00000000..43cfc89d --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOP.cpp" @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2021 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 AMOP.cpp + * @author: octopus + * @date 2021-08-23 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace bcos; +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::cppsdk::amop; + +void AMOP::start() +{ + if (m_service) + { // start websocket service + m_service->start(); + } + else + { + AMOP_CLIENT(WARNING) << LOG_BADGE("start") + << LOG_DESC("websocket service is not uninitialized"); + } + + AMOP_CLIENT(INFO) << LOG_BADGE("start") << LOG_DESC("start amop"); +} +void AMOP::stop() +{ + AMOP_CLIENT(INFO) << LOG_BADGE("stop") << LOG_DESC("stop amop"); +} + +// subscribe topics +void AMOP::subscribe(const std::set& _topics) +{ + // add topics to manager and update topics to server + auto result = m_topicManager->addTopics(_topics); + if (result) + { + updateTopicsToRemote(); + } +} + +// subscribe topics +void AMOP::unsubscribe(const std::set& _topics) +{ + // add topics to manager + auto result = m_topicManager->removeTopics(_topics); + if (result) + { + updateTopicsToRemote(); + } +} + +// query all subscribed topics +void AMOP::querySubTopics(std::set& _topics) +{ + _topics = m_topicManager->topics(); + AMOP_CLIENT(INFO) << LOG_BADGE("querySubTopics") << LOG_KV("topics size", _topics.size()); +} + +// subscribe topic with callback +void AMOP::subscribe(const std::string& _topic, SubCallback _callback) +{ + auto r = m_topicManager->addTopic(_topic); + addTopicCallback(_topic, _callback); + if (r) + { + updateTopicsToRemote(); + } + + AMOP_CLIENT(INFO) << LOG_BADGE("subscribe") << LOG_DESC("subscribe topic with callback") + << LOG_KV("topic", _topic) << LOG_KV("r", r); +} + +// +void AMOP::sendResponse( + const std::string& _endPoint, const std::string& _seq, bcos::bytesConstRef _data) +{ + auto msg = m_messageFactory->buildMessage(); + msg->setSeq(_seq); + msg->setPayload(std::make_shared(_data.begin(), _data.end())); + msg->setPacketType(bcos::cppsdk::amop::MessageType::AMOP_RESPONSE); + + m_service->asyncSendMessageByEndPoint(_endPoint, msg); +} + +// publish message +void AMOP::publish( + const std::string& _topic, bcos::bytesConstRef _data, uint32_t _timeout, PubCallback _callback) +{ + auto request = m_requestFactory->buildRequest(); + request->setTopic(_topic); + request->setData(_data); + + auto buffer = std::make_shared(); + request->encode(*buffer); + + auto sendMsg = m_messageFactory->buildMessage(); + sendMsg->setSeq(m_messageFactory->newSeq()); + sendMsg->setPacketType(bcos::cppsdk::amop::MessageType::AMOP_REQUEST); + sendMsg->setPayload(buffer); + + auto sendBuffer = std::make_shared(); + sendMsg->encode(*sendBuffer); + + AMOP_CLIENT(TRACE) << LOG_BADGE("publish") << LOG_DESC("publish message") + << LOG_KV("topic", _topic); + m_service->asyncSendMessage(sendMsg, bcos::boostssl::ws::Options(_timeout), + [_callback](Error::Ptr _error, std::shared_ptr _msg, + std::shared_ptr _session) { + auto wsMessage = std::dynamic_pointer_cast(_msg); + if (!_error && wsMessage && wsMessage->status() != 0) + { + auto errorNew = std::make_shared(); + errorNew->setErrorCode(wsMessage->status()); + errorNew->setErrorMessage( + std::string(wsMessage->payload()->begin(), wsMessage->payload()->end())); + + AMOP_CLIENT(WARNING) << LOG_BADGE("publish") << LOG_DESC("publish response error") + << LOG_KV("errorCode", errorNew->errorCode()) + << LOG_KV("errorMessage", errorNew->errorMessage()); + + _error = errorNew; + } + + _callback(_error, wsMessage, _session); + }); +} + +// broadcast message +void AMOP::broadcast(const std::string& _topic, bcos::bytesConstRef _data) +{ + auto request = m_requestFactory->buildRequest(); + request->setTopic(_topic); + request->setData(_data); + + auto buffer = std::make_shared(); + request->encode(*buffer); + + auto sendMsg = m_messageFactory->buildMessage(); + sendMsg->setSeq(m_messageFactory->newSeq()); + sendMsg->setPacketType(bcos::cppsdk::amop::MessageType::AMOP_BROADCAST); + sendMsg->setPayload(buffer); + + auto sendBuffer = std::make_shared(); + sendMsg->encode(*sendBuffer); + + AMOP_CLIENT(TRACE) << LOG_BADGE("broadcast") << LOG_DESC("broadcast message") + << LOG_KV("topic", _topic); + m_service->broadcastMessage(sendMsg); +} + + +void AMOP::updateTopicsToRemote() +{ + auto ss = m_service->sessions(); + for (auto session : ss) + { + updateTopicsToRemote(session); + } +} + +void AMOP::updateTopicsToRemote(std::shared_ptr _session) +{ + std::string request = m_topicManager->toJson(); + auto msg = m_messageFactory->buildMessage(); + msg->setSeq(m_messageFactory->newSeq()); + msg->setPacketType(bcos::cppsdk::amop::MessageType::AMOP_SUBTOPIC); + msg->setPayload(std::make_shared(request.begin(), request.end())); + + _session->asyncSendMessage(msg); + + AMOP_CLIENT(INFO) << LOG_BADGE("updateTopicsToRemote") + << LOG_DESC("send subscribe message to server") + << LOG_KV("endpoint", _session->endPoint()) << LOG_KV("topics", request); +} + +void AMOP::onRecvAMOPRequest(std::shared_ptr _msg, + std::shared_ptr _session) +{ + auto seq = _msg->seq(); + auto request = m_requestFactory->buildRequest(); + auto ret = + request->decode(bcos::bytesConstRef(_msg->payload()->data(), _msg->payload()->size())); + if (ret < 0) + { + AMOP_CLIENT(WARNING) << LOG_BADGE("onRecvAMOPRequest") + << LOG_DESC("decode amop request message error") + << LOG_KV("endpoint", _session->endPoint()) << LOG_KV("seq", seq); + return; + } + auto topic = request->topic(); + // AMOP_CLIENT(INFO) << LOG_DESC("onRecvAMOPRequest") + // << LOG_KV("endpoint", _session->endPoint()) + // << LOG_KV("data size", data->size()); + + auto callback = getCallbackByTopic(topic); + if (!callback && m_callback) + { + callback = m_callback; + } + + if (callback) + { + callback(nullptr, _session->connectedEndPoint(), seq, request->data(), _session); + } + else + { + AMOP_CLIENT(WARNING) << LOG_BADGE("onRecvAMOPRequest") + << LOG_DESC("there has no callback register for the topic") + << LOG_KV("topic", topic); + } +} + +void AMOP::onRecvAMOPResponse(std::shared_ptr _msg, + std::shared_ptr _session) +{ + auto seq = _msg->seq(); + AMOP_CLIENT(WARNING) << LOG_BADGE("onRecvAMOPResponse") + << LOG_DESC("maybe the amop request callback timeout") + << LOG_KV("seq", seq) << LOG_KV("endpoint", _session->endPoint()); +} + +void AMOP::onRecvAMOPBroadcast(std::shared_ptr _msg, + std::shared_ptr _session) +{ + auto seq = _msg->seq(); + auto request = m_requestFactory->buildRequest(); + auto ret = + request->decode(bcos::bytesConstRef(_msg->payload()->data(), _msg->payload()->size())); + if (ret < 0) + { + AMOP_CLIENT(WARNING) << LOG_BADGE("onRecvAMOPBroadcast") + << LOG_DESC("decode amop request message error") + << LOG_KV("endpoint", _session->endPoint()) << LOG_KV("seq", seq); + return; + } + + auto topic = request->topic(); + + AMOP_CLIENT(DEBUG) << LOG_BADGE("onRecvAMOPBroadcast") + << LOG_KV("endpoint", _session->endPoint()) << LOG_KV("seq", seq) + << LOG_KV("data size", request->data().size()); + + auto callback = getCallbackByTopic(topic); + if (!callback && m_callback) + { + callback = m_callback; + } + + if (callback) + { + callback(nullptr, _session->connectedEndPoint(), seq, request->data(), _session); + } + else + { + AMOP_CLIENT(WARNING) << LOG_BADGE("onRecvAMOPBroadcast") + << LOG_DESC("there has no callback register for the topic") + << LOG_KV("endpoint", _session->endPoint()) << LOG_KV("seq", seq) + << LOG_KV("topic", topic); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOP.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOP.h" new file mode 100644 index 00000000..d3c82fb9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOP.h" @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2021 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 AMOP.h + * @author: octopus + * @date 2021-08-23 + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace cppsdk +{ +namespace amop +{ +class AMOP : public AMOPInterface +{ +public: + using Ptr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + + virtual ~AMOP() { stop(); } + +public: + virtual void start() override; + virtual void stop() override; + +public: + // subscribe topics + virtual void subscribe(const std::set& _topics) override; + // subscribe topics + virtual void unsubscribe(const std::set& _topics) override; + // subscribe topic with callback + virtual void subscribe(const std::string& _topic, SubCallback _callback) override; + // publish message + virtual void publish(const std::string& _topic, bcos::bytesConstRef _data, uint32_t timeout, + PubCallback _callback) override; + // broadcast message + virtual void broadcast(const std::string& _topic, bcos::bytesConstRef _data) override; + // + virtual void sendResponse( + const std::string& _endPoint, const std::string& _seq, bcos::bytesConstRef _data) override; + // query all subscribed topics + virtual void querySubTopics(std::set& _topics) override; + // set default callback + virtual void setSubCallback(SubCallback _callback) override { m_callback = _callback; } + virtual SubCallback subCallback() const { return m_callback; } + + void updateTopicsToRemote(); + void updateTopicsToRemote(std::shared_ptr _session); + +public: + void onRecvAMOPRequest(std::shared_ptr _msg, + std::shared_ptr _session); + void onRecvAMOPResponse(std::shared_ptr _msg, + std::shared_ptr _session); + void onRecvAMOPBroadcast(std::shared_ptr _msg, + std::shared_ptr _session); + +public: + std::shared_ptr messageFactory() const + { + return m_messageFactory; + } + void setMessageFactory(std::shared_ptr _messageFactory) + { + m_messageFactory = _messageFactory; + } + + std::shared_ptr requestFactory() const + { + return m_requestFactory; + } + void setRequestFactory(std::shared_ptr _requestFactory) + { + m_requestFactory = _requestFactory; + } + + std::shared_ptr topicManager() const { return m_topicManager; } + void setTopicManager(std::shared_ptr _topicManager) + { + m_topicManager = _topicManager; + } + + std::shared_ptr service() const { return m_service; } + void setService(std::shared_ptr _service) + { + m_service = _service; + } + + void addTopicCallback(const std::string& _topic, SubCallback _callback) + { + boost::unique_lock lock(x_topic2Callback); + m_topic2Callback[_topic] = _callback; + } + + SubCallback getCallbackByTopic(const std::string& _topic) + { + boost::shared_lock lock(x_topic2Callback); + auto it = m_topic2Callback.find(_topic); + if (it == m_topic2Callback.end()) + { + return nullptr; + } + return it->second; + } + +private: + SubCallback m_callback; + std::shared_ptr m_topicManager; + std::shared_ptr m_messageFactory; + std::shared_ptr m_requestFactory; + + mutable boost::shared_mutex x_topic2Callback; + std::unordered_map m_topic2Callback; + + std::shared_ptr m_service; +}; +} // namespace amop +} // namespace cppsdk +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOPInterface.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOPInterface.h" new file mode 100644 index 00000000..79450e62 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOPInterface.h" @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2021 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 AMOPInterface.h + * @author: octopus + * @date 2021-08-23 + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace cppsdk +{ +namespace amop +{ +using SubCallback = std::function)>; +using PubCallback = + std::function, + std::shared_ptr)>; +class AMOPInterface +{ +public: + using Ptr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + + virtual ~AMOPInterface() {} + +public: + virtual void start() = 0; + virtual void stop() = 0; + +public: + // subscribe topics + virtual void subscribe(const std::set& _topics) = 0; + // subscribe topics + virtual void unsubscribe(const std::set& _topics) = 0; + // subscribe topic with callback + virtual void subscribe(const std::string& _topic, SubCallback _callback) = 0; + // publish message + virtual void publish(const std::string& _topic, bcos::bytesConstRef _data, uint32_t timeout, + PubCallback _callback) = 0; + // broadcast message + virtual void broadcast(const std::string& _topic, bcos::bytesConstRef _data) = 0; + // query all subscribed topics + virtual void querySubTopics(std::set& _topics) = 0; + // set default callback + virtual void setSubCallback(SubCallback _callback) = 0; + // + virtual void sendResponse( + const std::string& _endPoint, const std::string& _seq, bcos::bytesConstRef _data) = 0; +}; + +} // namespace amop +} // namespace cppsdk +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOPRequest.cpp" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOPRequest.cpp" new file mode 100644 index 00000000..58104b31 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOPRequest.cpp" @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2021 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 AMOPRequest.cpp + * @author: octopus + * @date 2021-09-06 + */ +#include +#include + +// check offset length overflow when decode message +#define OFFSET_CHECK(offset, step, length) \ + do \ + { \ + if (offset + step > length) \ + { \ + throw std::runtime_error("offset overflow, offset: " + std::to_string(offset) + \ + ",step: " + std::to_string(step) + \ + " ,length: " + std::to_string(length)); \ + } \ + } while (0) + +using namespace bcos; +using namespace bcos::protocol; +using namespace bcos; + +const std::size_t AMOPRequest::TOPIC_MAX_LENGTH; +const std::size_t AMOPRequest::MESSAGE_MIN_LENGTH; + +bool AMOPRequest::encode(bytes& _buffer) +{ + if (m_topic.size() > TOPIC_MAX_LENGTH) + { + return false; + } + // the version + auto version = boost::asio::detail::socket_ops::host_to_network_long(m_version); + auto versionLen = sizeof(m_version) / sizeof(uint8_t); + _buffer.insert(_buffer.end(), (byte*)&version, (byte*)&version + versionLen); + // the topic length + uint16_t length = + boost::asio::detail::socket_ops::host_to_network_short((uint16_t)m_topic.size()); + _buffer.insert(_buffer.end(), (byte*)&length, (byte*)&length + 2); + // the topic data + _buffer.insert(_buffer.end(), m_topic.begin(), m_topic.end()); + // the data + _buffer.insert(_buffer.end(), m_data.begin(), m_data.end()); + return true; +} + +int64_t AMOPRequest::decode(bcos::bytesConstRef _data) +{ + if (_data.size() < MESSAGE_MIN_LENGTH) + { + return -1; + } + + try + { + std::size_t length = _data.size(); + std::size_t offset = 0; + // decode version + auto versionLen = sizeof(m_version) / sizeof(uint8_t); + OFFSET_CHECK(offset, versionLen, length); + offset += versionLen; + m_version = + boost::asio::detail::socket_ops::network_to_host_long(*((uint32_t*)_data.data())); + + // decode topicLength + OFFSET_CHECK(offset, 2, length); + uint16_t topicLen = boost::asio::detail::socket_ops::network_to_host_short( + *((uint16_t*)(_data.data() + versionLen))); + offset += 2; + + // decode topic + OFFSET_CHECK(offset, topicLen, length); + m_topic = std::string(_data.data() + offset, _data.data() + offset + topicLen); + offset += topicLen; + + // decode data + m_data = _data.getCroppedData(offset); + return _data.size(); + } + catch (const std::string&) + { + return -1; + } +} diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOPRequest.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOPRequest.h" new file mode 100644 index 00000000..d4ea96d4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/AMOPRequest.h" @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2021 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 AMOPRequest.h + * @author: octopus + * @date 2021-08-23 + */ +#pragma once +#include + +namespace bcos +{ +namespace protocol +{ +class AMOPRequest +{ +public: + using Ptr = std::shared_ptr; + AMOPRequest() = default; + AMOPRequest(bcos::bytesConstRef _data) { decode(_data); } + virtual ~AMOPRequest() {} + + // topic field length + const static size_t TOPIC_MAX_LENGTH = 65535; + const static size_t MESSAGE_MIN_LENGTH = 2; + + + std::string topic() const { return m_topic; } + void setTopic(const std::string& _topic) { m_topic = _topic; } + void setData(bcos::bytesConstRef _data) { m_data = _data; } + bcos::bytesConstRef data() const { return m_data; } + + virtual bool encode(bcos::bytes& _buffer); + virtual int64_t decode(bcos::bytesConstRef _data); + + virtual uint32_t version() const { return m_version; } + virtual void setVersion(uint32_t _version) { m_version = _version; } + +private: + std::string m_topic; + bcos::bytesConstRef m_data = bcos::bytesConstRef(); + uint32_t m_version = 0; +}; + +class AMOPRequestFactory +{ +public: + using Ptr = std::shared_ptr; + AMOPRequestFactory() = default; + virtual ~AMOPRequestFactory() {} + + std::shared_ptr buildRequest() { return std::make_shared(); } + std::shared_ptr buildRequest(bcos::bytesConstRef _data) + { + return std::make_shared(_data); + } +}; + +} // namespace protocol +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/Common.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/Common.h" new file mode 100644 index 00000000..b00954d8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/Common.h" @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2021 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: octopus + * @date 2021-08-25 + */ +#pragma once + +#define AMOP_CLIENT(LEVEL) BCOS_LOG(LEVEL) << "[AMOP][CLIENT]" +#define AMOP_TOPIC_MANAGER(LEVEL) BCOS_LOG(LEVEL) << "[AMOP][TOPICMANAGER]" + +namespace bcos +{ +namespace cppsdk +{ +namespace amop +{ +/** + * @brief: amop message types + */ +enum MessageType +{ + // ------------AMOP begin --------- + + AMOP_SUBTOPIC = 0x110, // 272 + AMOP_REQUEST = 0x111, // 273 + AMOP_BROADCAST = 0x112, // 274 + AMOP_RESPONSE = 0x113 // 275 + + // ------------AMOP end --------- + +}; +} // namespace amop +} // namespace cppsdk +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/TopicManager.cpp" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/TopicManager.cpp" new file mode 100644 index 00000000..1e9fc583 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/TopicManager.cpp" @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2021 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 TopicManager.cpp + * @author: octopus + * @date 2021-08-26 + */ + +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::cppsdk::amop; + +bool TopicManager::addTopic(const std::string& _topic) +{ + boost::unique_lock lock(x_topics); + auto result = m_topics.insert(_topic); + return result.second; +} +bool TopicManager::addTopics(const std::set& _topics) +{ + boost::unique_lock lock(x_topics); + auto oldSize = m_topics.size(); + m_topics.insert(_topics.begin(), _topics.end()); + return m_topics.size() > oldSize; +} +bool TopicManager::removeTopic(const std::string& _topic) +{ + boost::unique_lock lock(x_topics); + auto result = m_topics.erase(_topic); + return result > 0; +} +bool TopicManager::removeTopics(const std::set& _topics) +{ + size_t removeCount = 0; + for (auto& topic : _topics) + { + auto r = removeTopic(topic); + removeCount += (r ? 1 : 0); + } + return removeCount > 0; +} +std::set TopicManager::topics() const +{ + boost::shared_lock lock(x_topics); + return m_topics; +} + +std::string TopicManager::toJson() +{ + auto totalTopics = topics(); + Json::Value jTopics(Json::arrayValue); + for (const auto& topic : totalTopics) + { + jTopics.append(topic); + } + Json::Value jReq; + jReq["topics"] = jTopics; + Json::FastWriter writer; + std::string request = writer.write(jReq); + return request; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/TopicManager.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/TopicManager.h" new file mode 100644 index 00000000..d1fd351e --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/amop/TopicManager.h" @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2021 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 TopicManager.h + * @author: octopus + * @date 2021-08-23 + */ +#pragma once + +#include +#include +#include +#include + +namespace bcos +{ +namespace cppsdk +{ +namespace amop +{ +// manage the topics user subscribed +class TopicManager +{ +public: + using Ptr = std::shared_ptr; + + bool addTopic(const std::string& _topic); + bool addTopics(const std::set& _topics); + bool removeTopic(const std::string& _topic); + bool removeTopics(const std::set& _topics); + std::set topics() const; + std::string toJson(); + +private: + // mutex for m_sessions + mutable boost::shared_mutex x_topics; + std::set m_topics; +}; + +} // namespace amop +} // namespace cppsdk +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/config/Config.cpp" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/config/Config.cpp" new file mode 100644 index 00000000..520179fb --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/config/Config.cpp" @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2021 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 Config.cpp + * @author: octopus + * @date 2021-12-14 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; +using namespace bcos::boostssl::context; +using namespace bcos; +using namespace bcos::cppsdk::config; + +std::shared_ptr Config::loadConfig(const std::string& _configPath) +{ + try + { + boost::property_tree::ptree pt; + boost::property_tree::ini_parser::read_ini(_configPath, pt); + + BCOS_LOG(INFO) << LOG_BADGE("loadConfig") << LOG_DESC("loadConfig ok") + << LOG_KV("configPath", _configPath); + return loadConfig(pt); + } + catch (const std::exception& e) + { + BCOS_LOG(WARNING) << LOG_BADGE("loadConfig") << LOG_DESC("loadConfig failed") + << LOG_KV("configPath", _configPath) + << LOG_KV("error", boost::diagnostic_information(e)); + + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment(boost::diagnostic_information(e))); + } +} + +std::shared_ptr Config::loadConfig( + boost::property_tree::ptree const& _pt) +{ + auto config = std::make_shared(); + config->setModel(WsModel::Client); + + loadCommon(_pt, *config); + loadPeers(_pt, *config); + if (!config->disableSsl()) + { + loadCert(_pt, *config); + } + + return config; +} + +void Config::loadCommon( + boost::property_tree::ptree const& _pt, bcos::boostssl::ws::WsConfig& _config) +{ + /* + [common] + ; if disable ssl connection, default: false + ; disable_ssl = true + ; thread pool size for network msg sending recving handing + thread_pool_size = 8 + ; send message timeout(ms) + message_timeout_ms = 10000 + */ + bool disableSsl = _pt.get("common.disable_ssl", false); + int threadPoolSize = _pt.get("common.thread_pool_size", 8); + int messageTimeOut = _pt.get("common.message_timeout_ms", 10000); + + _config.setDisableSsl(disableSsl); + _config.setSendMsgTimeout(messageTimeOut); + _config.setThreadPoolSize(threadPoolSize); + + + BCOS_LOG(INFO) << LOG_BADGE("loadCommon") << LOG_DESC("load common section config items ok") + << LOG_KV("disableSsl", disableSsl) << LOG_KV("threadPoolSize", threadPoolSize) + << LOG_KV("messageTimeOut", messageTimeOut); +} + +void Config::loadPeers( + boost::property_tree::ptree const& _pt, bcos::boostssl::ws::WsConfig& _config) +{ + /* + [peers] + # supported ipv4 and ipv6 + node.0=127.0.0.1:20200 + node.1=127.0.0.1:20201 + */ + + EndPointsPtr peers = std::make_shared>(); + _config.setConnectPeers(peers); + + for (auto it : _pt.get_child("peers")) + { + if (it.first.find("node.") != 0) + { + continue; + } + + NodeIPEndpoint ep; + if (!WsTools::stringToEndPoint(it.second.data(), ep)) + { + BCOS_LOG(WARNING) + << LOG_BADGE("loadPeers") + << LOG_DESC( + "invalid endpoint, endpoint must be in IP:Port format, eg: 127.0.0.1:20200") + << LOG_KV("endpoint", it.second.data()); + continue; + } + + BCOS_LOG(INFO) << LOG_BADGE("loadPeers") << LOG_DESC("add one connected peer") + << LOG_KV("endpoint", it.second.data()); + + peers->insert(ep); + } + + BCOS_LOG(DEBUG) << LOG_BADGE("loadPeers") << LOG_DESC("load connected peers ok") + << LOG_KV("peers size", peers->size()); +} + +void Config::loadCert(boost::property_tree::ptree const& _pt, bcos::boostssl::ws::WsConfig& _config) +{ + std::string sslType = _pt.get("cert.ssl_type", "ssl"); + if (sslType == "sm_ssl") + { + loadSMSslCert(_pt, _config); + } + else + { + loadSslCert(_pt, _config); + } +} + +void Config::loadSslCert( + boost::property_tree::ptree const& _pt, bcos::boostssl::ws::WsConfig& _config) +{ + /* + [cert] + ; directory the certificates located in, default: ./conf + ca_path=./conf + ; the ca certificate file + ca_cert=ca.crt + ; the node private key file + sdk_key=sdk.key + ; the node certificate file + sdk_cert=sdk.crt + */ + std::string caPath = _pt.get("cert.ca_path", "./conf"); + std::string caCert = caPath + "/" + _pt.get("cert.ca_cert", "ca.crt"); + std::string sdkKey = caPath + "/" + _pt.get("cert.sdk_key", "sdk.key"); + std::string sdkCert = caPath + "/" + _pt.get("cert.sdk_cert", "sdk.crt"); + + BCOS_LOG(INFO) << LOG_BADGE("loadCert") << LOG_DESC("load ssl cert ok") + << LOG_KV("ca_path", caPath) << LOG_KV("caCert", caCert) + << LOG_KV("sdkCert", sdkCert) << LOG_KV("sdkKey", sdkKey); + + ContextConfig::CertConfig certConfig; + certConfig.caCert = caCert; + certConfig.nodeCert = sdkCert; + certConfig.nodeKey = sdkKey; + + auto contextConfig = std::make_shared(); + contextConfig->setSslType("ssl"); + contextConfig->setCertConfig(certConfig); + _config.setContextConfig(contextConfig); + return; +} + +void Config::loadSMSslCert( + boost::property_tree::ptree const& _pt, bcos::boostssl::ws::WsConfig& _config) +{ + /* + [cert] + ; directory the certificates located in, default: ./conf + ca_path=./conf + ; the ca certificate file + sm_ca_cert=sm_ca.crt + ; the node private key file + sm_sdk_key=sm_sdk.key + ; the node certificate file + sm_sdk_cert=sm_sdk.crt + ; the node private key file + sm_ensdk_key=sm_ensdk.key + ; the node certificate file + sm_ensdk_cert=sm_ensdk.crt + */ + std::string caPath = _pt.get("cert.ca_path", "./conf"); + std::string smCaCert = caPath + "/" + _pt.get("cert.sm_ca_cert", "sm_ca.crt"); + std::string smSdkKey = caPath + "/" + _pt.get("cert.sm_sdk_key", "sm_sdk.key"); + std::string smSdkCert = caPath + "/" + _pt.get("cert.sm_sdk_cert", "sm_sdk.crt"); + std::string smEnSdkKey = + caPath + "/" + _pt.get("cert.sm_ensdk_key", "sm_ensdk.key"); + std::string smEnSdkCert = + caPath + "/" + _pt.get("cert.sm_ensdk_cert", "sm_ensdk.crt"); + + BCOS_LOG(INFO) << LOG_DESC("loadSMCert") << LOG_DESC("load sm ssl cert ok") + << LOG_KV("ca_path", caPath) << LOG_KV("smCaCert", smCaCert) + << LOG_KV("smSdkKey", smSdkKey) << LOG_KV("smSdkCert", smSdkCert) + << LOG_KV("smEnSdkCert", smEnSdkCert) << LOG_KV("smEnSdkKey", smEnSdkKey); + + + ContextConfig::SMCertConfig cert; + cert.caCert = smCaCert; + cert.nodeCert = smSdkCert; + cert.nodeKey = smSdkKey; + cert.enNodeCert = smEnSdkCert; + cert.enNodeKey = smEnSdkKey; + + auto ctxConfig = std::make_shared(); + ctxConfig->setSslType("sm_ssl"); + ctxConfig->setSmCertConfig(cert); + _config.setContextConfig(ctxConfig); + return; +} diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/config/Config.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/config/Config.h" new file mode 100644 index 00000000..22ad86ea --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/config/Config.h" @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 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 Config.h + * @author: octopus + * @date 2021-12-14 + */ +#pragma once + +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace cppsdk +{ +namespace config +{ +class Config +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + +public: + std::shared_ptr loadConfig(const std::string& _configPath); + std::shared_ptr loadConfig( + boost::property_tree::ptree const& _pt); + + void loadCommon(boost::property_tree::ptree const& _pt, bcos::boostssl::ws::WsConfig& _config); + void loadPeers(boost::property_tree::ptree const& _pt, bcos::boostssl::ws::WsConfig& _config); + void loadCert(boost::property_tree::ptree const& _pt, bcos::boostssl::ws::WsConfig& _config); + void loadSslCert(boost::property_tree::ptree const& _pt, bcos::boostssl::ws::WsConfig& _config); + void loadSMSslCert( + boost::property_tree::ptree const& _pt, bcos::boostssl::ws::WsConfig& _config); +}; + +} // namespace config +} // namespace cppsdk +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/Common.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/Common.h" new file mode 100644 index 00000000..5fdaa8cf --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/Common.h" @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 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: octopus + * @date 2021-09-01 + */ + +#pragma once + +#include +namespace bcos +{ +namespace cppsdk +{ +namespace event +{ +/** + * @brief: event sub message types + */ +enum MessageType +{ + // ------------event begin --------- + + EVENT_SUBSCRIBE = 0x120, // 288 + EVENT_UNSUBSCRIBE = 0x121, // 289 + EVENT_LOG_PUSH = 0x122, // 290 + + // ------------event end --------- +}; +} // namespace event +} // namespace cppsdk +} // namespace bcos + + +// The largest number of topic in one event log +#define EVENT_LOG_TOPICS_MAX_INDEX (4) + +#define EVENT_TASK(LEVEL) BCOS_LOG(LEVEL) << "[EVENT][TASK]" +#define EVENT_PARAMS(LEVEL) BCOS_LOG(LEVEL) << "[EVENT][PARAMS]" +#define EVENT_REQUEST(LEVEL) BCOS_LOG(LEVEL) << "[EVENT][REQUEST]" +#define EVENT_RESPONSE(LEVEL) BCOS_LOG(LEVEL) << "[EVENT][RESPONSE]" +#define EVENT_SUB(LEVEL) BCOS_LOG(LEVEL) << "[EVENT][SUB]" diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSub.cpp" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSub.cpp" new file mode 100644 index 00000000..9410363b --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSub.cpp" @@ -0,0 +1,515 @@ +/* + * Copyright (C) 2021 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 EvenPush.cpp + * @author: octopus + * @date 2021-09-01 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::cppsdk::event; + +void EventSub::start() +{ + if (m_running) + { + EVENT_SUB(INFO) << LOG_BADGE("start") << LOG_DESC("event sub is running"); + return; + } + m_running = true; + + // start websocket service + m_service->start(); + + m_timer = std::make_shared(m_config->reconnectPeriod(), "doLoop"); + m_timer->registerTimeoutHandler([this]() { doLoop(); }); + m_timer->start(); + EVENT_SUB(INFO) << LOG_BADGE("start") << LOG_DESC("start event sub successfully") + << LOG_KV("sendMsgTimeout", m_config->sendMsgTimeout()) + << LOG_KV("reconnectPeriod", m_config->reconnectPeriod()); +} + +void EventSub::stop() +{ + if (!m_running) + { + EVENT_SUB(INFO) << LOG_BADGE("stop") << LOG_DESC("event sub is not running"); + return; + } + + m_running = false; + if (m_timer) + { + m_timer->stop(); + } + + EVENT_SUB(INFO) << LOG_BADGE("stop") << LOG_DESC("stop event sub successfully"); +} + +void EventSub::doLoop() +{ + m_timer->restart(); + { + boost::shared_lock lock(x_tasks); + EVENT_SUB(INFO) << LOG_BADGE("doLoop") << LOG_DESC("event sub tasks") + << LOG_KV("working event sub count", m_workingTasks.size()) + << LOG_KV("suspend event sub count", m_suspendTasks.size()); + } + + if (m_suspendTasksCount.load()) + { + auto ss = m_service->sessions(); + if (ss.empty()) + { + EVENT_SUB(INFO) + << LOG_BADGE("doLoop") + << LOG_DESC( + "no active sessions available and discard resend suspend event sub tasks"); + } + else + { + boost::shared_lock lock(x_tasks); + for (const auto& taskEntry : m_suspendTasks) + { + auto task = taskEntry.second; + std::string id = task->id(); + if (!this->addWaitResp(id)) + { + continue; + } + + subscribeEvent( + task, [id, this](Error::Ptr, const std::string&) { this->removeWaitResp(id); }); + } + } + } +} + +bool EventSub::addTask(EventSubTask::Ptr _task) +{ + boost::unique_lock lock(x_tasks); + removeSuspendTask(_task->id()); + if (m_workingTasks.find(_task->id()) == m_workingTasks.end()) + { + m_workingTasks[_task->id()] = _task; + return true; + } + + return false; +} + +EventSubTask::Ptr EventSub::getTask(const std::string& _id, bool includeSuspendTask) +{ + EventSubTask::Ptr task = nullptr; + + boost::shared_lock lock(x_tasks); + auto it = m_workingTasks.find(_id); + if (it != m_workingTasks.end()) + { + task = it->second; + + EVENT_SUB(TRACE) << LOG_BADGE("getTask") << LOG_DESC("event sub task is working") + << LOG_KV("id", _id); + } + else if (includeSuspendTask) + { + auto innerIt = m_suspendTasks.find(_id); + if (innerIt != m_suspendTasks.end()) + { + task = innerIt->second; + + EVENT_SUB(TRACE) << LOG_BADGE("getTask") << LOG_DESC("event sub task suspend") + << LOG_KV("id", _id); + } + else + { + EVENT_SUB(DEBUG) << LOG_BADGE("getTask") << LOG_DESC("cannot found event sub task") + << LOG_KV("id", _id); + } + } + + return task; +} + +EventSubTask::Ptr EventSub::getTaskAndRemove(const std::string& _id, bool includeSuspendTask) +{ + EventSubTask::Ptr task = nullptr; + + boost::unique_lock lock(x_tasks); + auto it = m_workingTasks.find(_id); + if (it != m_workingTasks.end()) + { // remove from m_workingTasks + task = it->second; + m_workingTasks.erase(it); + + EVENT_SUB(TRACE) << LOG_BADGE("getTaskAndRemove") << LOG_DESC("event sub task is working") + << LOG_KV("id", _id); + } + else if (includeSuspendTask) + { + // remove from m_suspendTasks + auto innerIt = m_suspendTasks.find(_id); + if (innerIt != m_suspendTasks.end()) + { + task = innerIt->second; + m_suspendTasksCount--; + m_suspendTasks.erase(innerIt); + + EVENT_SUB(TRACE) << LOG_BADGE("getTaskAndRemove") << LOG_DESC("event sub task suspend") + << LOG_KV("id", _id); + } + } + + return task; +} + +bool EventSub::addSuspendTask(EventSubTask::Ptr _task) +{ + if (m_suspendTasks.find(_task->id()) == m_suspendTasks.end()) + { + m_suspendTasksCount++; + m_suspendTasks[_task->id()] = _task; + return true; + } + + return false; +} + +bool EventSub::removeSuspendTask(const std::string& _id) +{ + // remove from suspendTasks + auto it = m_suspendTasks.find(_id); + if (it != m_suspendTasks.end()) + { + m_suspendTasksCount--; + m_suspendTasks.erase(it); + return true; + } + return false; +} + +std::size_t EventSub::suspendTasks(std::shared_ptr _session) +{ + if (!_session) + { + return 0; + } + + std::size_t count = 0; + { + boost::unique_lock lock(x_tasks); + for (auto it = m_workingTasks.begin(); it != m_workingTasks.end();) + { + auto task = it->second; + auto s = task->session(); + if (s && s->endPoint() != _session->endPoint()) + { + ++it; + continue; + } + + EVENT_SUB(INFO) << LOG_BADGE("suspendTasks") + << LOG_DESC("suspend event sub task for disconnection") + << LOG_KV("id", task->id()) << LOG_KV("endPoint", _session->endPoint()); + it = m_workingTasks.erase(it); + task->setSession(nullptr); + addSuspendTask(task); + count++; + } + } + + EVENT_SUB(INFO) << LOG_BADGE("suspendTasks") + << LOG_DESC("suspend event sub tasks for disconnection") + << LOG_KV("endPoint", _session->endPoint()) << LOG_KV("count", count); + + return count; +} + +void EventSub::onRecvEventSubMessage( + std::shared_ptr _msg, std::shared_ptr _session) +{ + /* + { + "id": "", + "status": 0, + "result": { + [ + {}, + {}, + {} + ] + } + } + */ + auto strResp = std::string(_msg->payload()->begin(), _msg->payload()->end()); + + EVENT_SUB(TRACE) << LOG_BADGE("onRecvEventSubMessage") << LOG_DESC("receive event sub message") + << LOG_KV("endpoint", _session->endPoint()) << LOG_KV("response", strResp); + + auto resp = std::make_shared(); + if (!resp->fromJson(strResp)) + { + EVENT_SUB(WARNING) << LOG_BADGE("onRecvEventSubMessage") + << LOG_DESC("recv invalid event sub message") + << LOG_KV("endpoint", _session->endPoint()) + << LOG_KV("response", strResp); + return; + } + + auto task = getTask(resp->id()); + if (task == nullptr) + { + EVENT_SUB(WARNING) << LOG_BADGE("onRecvEventSubMessage") + << LOG_DESC("event sub task not exist") << LOG_KV("id", resp->id()) + << LOG_KV("endpoint", _session->endPoint()) + << LOG_KV("response", strResp); + return; + } + + if (resp->status() == StatusCode::EndOfPush) + { // event sub end + getTaskAndRemove(resp->id()); + task->callback()(nullptr, strResp); + + EVENT_SUB(INFO) << LOG_BADGE("onRecvEventSubMessage") << LOG_DESC("end of push") + << LOG_KV("id", task->id()) << LOG_KV("endpoint", _session->endPoint()) + << LOG_KV("response", strResp); + } + else if (resp->status() != StatusCode::Success) + { // event sub error + getTaskAndRemove(resp->id()); + task->callback()(nullptr, strResp); + + EVENT_SUB(INFO) << LOG_BADGE("onRecvEventSubMessage") << LOG_DESC("event sub error") + << LOG_KV("id", task->id()) << LOG_KV("endpoint", _session->endPoint()) + << LOG_KV("response", strResp); + } + else + { + // NOTE: update the latest blocknumber of event sub for network disconnect continue + auto jResp = resp->jResp(); + try + { + int64_t blockNumber = -1; + if (jResp["result"][0]["blockNumber"].isInt64()) + { + blockNumber = jResp["result"][0]["blockNumber"].asInt64(); + task->state()->setCurrentBlockNumber(blockNumber); + } + task->callback()(nullptr, strResp); + + EVENT_SUB(TRACE) << LOG_BADGE("onRecvEventSubMessage") << LOG_DESC("event sub") + << LOG_KV("id", task->id()) << LOG_KV("endpoint", _session->endPoint()) + << LOG_KV("blockNumber", blockNumber) << LOG_KV("response", strResp); + } + catch (const std::exception& e) + { + EVENT_SUB(WARNING) << LOG_BADGE("onRecvEventSubMessage") + << LOG_DESC("unrecognized event sub response") + << LOG_KV("id", task->id()) + << LOG_KV("endpoint", _session->endPoint()) + << LOG_KV("resp", strResp); + } + } +} + +void EventSub::subscribeEvent(EventSubTask::Ptr _task, Callback _callback) +{ + auto id = _task->id(); + auto group = _task->group(); + + auto request = std::make_shared(); + request->setId(id); + request->setParams(_task->params()); + request->setGroup(_task->group()); + request->setState(_task->state()); + + auto jsonReq = request->generateJson(); + + auto message = m_messagefactory->buildMessage(); + message->setSeq(m_messagefactory->newSeq()); + message->setPacketType(bcos::cppsdk::event::MessageType::EVENT_SUBSCRIBE); + message->setPayload(std::make_shared(jsonReq.begin(), jsonReq.end())); + + EVENT_SUB(INFO) << LOG_BADGE("subscribeEvent") << LOG_DESC("subscribe event") + << LOG_KV("id", id) << LOG_KV("group", group) << LOG_KV("request", jsonReq); + + m_service->asyncSendMessageByGroupAndNode(_task->group(), "", message, Options(), + [id, _task, _callback, this](Error::Ptr _error, std::shared_ptr _msg, + std::shared_ptr _session) { + if (_error && _error->errorCode() != 0) + { + EVENT_SUB(WARNING) + << LOG_BADGE("subscribeEvent") << LOG_DESC("callback response error") + << LOG_KV("id", id) << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()); + + _callback(_error, ""); + return; + } + + auto strResp = std::string(_msg->payload()->begin(), _msg->payload()->end()); + auto resp = std::make_shared(); + if (!resp->fromJson(strResp)) + { + EVENT_SUB(WARNING) + << LOG_BADGE("subscribeEvent") << LOG_DESC("invalid subscribe event response") + << LOG_KV("id", id) << LOG_KV("response", strResp); + _callback(nullptr, strResp); + } + else if (resp->status() != StatusCode::Success) + { + _callback(nullptr, strResp); + EVENT_SUB(WARNING) + << LOG_BADGE("subscribeEvent") << LOG_DESC("callback response error") + << LOG_KV("id", id) << LOG_KV("response", strResp); + } + else + { + // subscribe event successfully, set network session for unsubscribe + _task->setSession(_session); + + this->addTask(_task); + + _callback(nullptr, strResp); + EVENT_SUB(INFO) << LOG_BADGE("subscribeEvent") + << LOG_DESC("callback response success") << LOG_KV("id", id) + << LOG_KV("response", strResp); + } + }); +} + +std::string EventSub::subscribeEvent( + const std::string& _group, const std::string& _params, Callback _callback) +{ + EventSubParams::Ptr params = std::make_shared(); + if (!params->fromJsonString(_params)) + { + // invalid request params string format + auto error = std::make_shared(-1, "invalid request JSON string"); + _callback(error, ""); + return ""; + } + + return subscribeEvent(_group, params, _callback); +} + +std::string EventSub::subscribeEvent( + const std::string& _group, EventSubParams::Ptr _params, Callback _callback) +{ + // invalid request params string format + if (!_params->verifyParams()) + { + auto error = std::make_shared(-1, "params verification failure"); + _callback(error, ""); + return ""; + } + + auto taskId = m_messagefactory->newSeq(); + auto task = std::make_shared(); + + task->setId(taskId); + task->setGroup(_group); + task->setParams(_params); + task->setCallback(_callback); + task->setState(std::make_shared()); + + subscribeEvent(task, _callback); + return taskId; +} + +void EventSub::unsubscribeEvent(const std::string& _id) +{ + auto task = getTaskAndRemove(_id); + if (task == nullptr) + { + EVENT_SUB(WARNING) << LOG_BADGE("unsubscribeEvent") << LOG_DESC("event sub not found") + << LOG_KV("id", _id); + return; + } + + auto session = task->session(); + if (!session) + { + EVENT_SUB(INFO) << LOG_BADGE("unsubscribeEvent") << LOG_DESC("task is suspend") + << LOG_KV("id", _id); + return; + } + + auto request = std::make_shared(); + request->setId(_id); + request->setGroup(task->group()); + auto strReq = request->generateJson(); + + auto message = m_messagefactory->buildMessage(); + message->setSeq(m_messagefactory->newSeq()); + message->setPacketType(bcos::cppsdk::event::MessageType::EVENT_UNSUBSCRIBE); + message->setPayload(std::make_shared(strReq.begin(), strReq.end())); + + session->asyncSendMessage(message, Options(), + [_id](Error::Ptr _error, std::shared_ptr _msg, + std::shared_ptr) { + if (_error && _error->errorCode() != 0) + { + EVENT_SUB(WARNING) + << LOG_BADGE("unsubscribeEvent") << LOG_DESC("callback response error") + << LOG_KV("id", _id) << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()); + return; + } + + auto strResp = std::string(_msg->payload()->begin(), _msg->payload()->end()); + auto resp = std::make_shared(); + if (!resp->fromJson(strResp)) + { + EVENT_SUB(WARNING) + << LOG_BADGE("unsubscribeEvent") << LOG_DESC("callback invalid response") + << LOG_KV("id", _id) << LOG_KV("response", strResp); + return; + } + + if (resp->status() != StatusCode::Success) + { + EVENT_SUB(WARNING) + << LOG_BADGE("unsubscribeEvent") << LOG_DESC("callback response error") + << LOG_KV("id", _id) << LOG_KV("status", resp->status()) + << LOG_KV("response", strResp); + } + else + { + EVENT_SUB(INFO) << LOG_BADGE("unsubscribeEvent") + << LOG_DESC("callback response success") << LOG_KV("id", _id) + << LOG_KV("status", resp->status()) << LOG_KV("response", strResp); + } + }); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSub.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSub.h" new file mode 100644 index 00000000..c0b7f7f4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSub.h" @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2021 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 EvenPush.h + * @author: octopus + * @date 2021-09-01 + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace cppsdk +{ +namespace event +{ +class EventSub : public EventSubInterface +{ +public: + using Ptr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + + EventSub() {} + virtual ~EventSub() { stop(); } + +public: + virtual void start() override; + virtual void stop() override; + + void doLoop(); + + virtual std::string subscribeEvent( + const std::string& _group, const std::string& _params, Callback _callback) override; + virtual std::string subscribeEvent( + const std::string& _group, EventSubParams::Ptr _params, Callback _callback) override; + virtual void unsubscribeEvent(const std::string& _id) override; + +public: + void subscribeEvent(EventSubTask::Ptr _task, Callback _callback); + +public: + void onRecvEventSubMessage(std::shared_ptr _msg, + std::shared_ptr _session); + +public: + bool addTask(EventSubTask::Ptr _task); + EventSubTask::Ptr getTask(const std::string& _id, bool includeSuspendTask = true); + EventSubTask::Ptr getTaskAndRemove(const std::string& _id, bool includeSuspendTask = true); + + bool addSuspendTask(EventSubTask::Ptr _task); + bool removeSuspendTask(const std::string& _id); + + bool removeWaitResp(const std::string& _id) + { + boost::lock_guard lock(x_waitRespTasks); + return 0 != m_waitRespTasks.erase(_id); + } + + bool addWaitResp(const std::string& _id) + { + boost::lock_guard lock(x_waitRespTasks); + auto r = m_waitRespTasks.insert(_id); + return r.second; + } + + std::size_t suspendTasks(std::shared_ptr _session); + + void setService(bcos::cppsdk::service::Service::Ptr _service) { m_service = _service; } + bcos::cppsdk::service::Service::Ptr service() const { return m_service; } + void setMessageFactory(std::shared_ptr _messageFactory) + { + m_messagefactory = _messageFactory; + } + std::shared_ptr messageFactory() const + { + return m_messagefactory; + } + + boostssl::ws::WsConfig::ConstPtr config() const { return m_config; } + void setConfig(boostssl::ws::WsConfig::ConstPtr _config) { m_config = _config; } + + uint32_t suspendTasksCount() const { return m_suspendTasksCount.load(); } + const std::unordered_map& suspendTasks() const + { + return m_suspendTasks; + } + const std::unordered_map& workingtasks() const + { + return m_workingTasks; + } + +private: + bool m_running = false; + + std::atomic m_suspendTasksCount{0}; + + mutable boost::shared_mutex x_tasks; + std::unordered_map m_workingTasks; + std::unordered_map m_suspendTasks; + + mutable boost::mutex x_waitRespTasks; + std::set m_waitRespTasks; + + // timer + std::shared_ptr m_timer; + // message factory + std::shared_ptr m_messagefactory; + // websocket service + bcos::cppsdk::service::Service::Ptr m_service; + // + boostssl::ws::WsConfig::ConstPtr m_config; +}; +} // namespace event +} // namespace cppsdk +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubInterface.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubInterface.h" new file mode 100644 index 00000000..2d0bab68 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubInterface.h" @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 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 EvenPushInterface.h + * @author: octopus + * @date 2021-09-01 + */ + +#pragma once +#include +#include +#include + +namespace bcos +{ +namespace cppsdk +{ +namespace event +{ +using Callback = std::function; + +class EventSubInterface +{ +public: + using Ptr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + + virtual ~EventSubInterface() {} + +public: + virtual void start() = 0; + virtual void stop() = 0; + +public: + virtual std::string subscribeEvent( + const std::string& _group, const std::string& _params, Callback _callback) = 0; + virtual std::string subscribeEvent( + const std::string& _group, EventSubParams::Ptr _params, Callback _callback) = 0; + virtual void unsubscribeEvent(const std::string& _id) = 0; +}; +} // namespace event +} // namespace cppsdk +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubParams.cpp" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubParams.cpp" new file mode 100644 index 00000000..71054b8e --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubParams.cpp" @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2021 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 EvenPushParams.cpp + * @author: octopus + * @date 2021-09-01 + */ + +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::cppsdk::event; + +bool EventSubParams::verifyParams() +{ + // topic + if (m_topics.size() > EVENT_LOG_TOPICS_MAX_INDEX) + { + return false; + } + + for (std::size_t i = 0; i < m_topics.size(); ++i) + { + for (const auto& topic : m_topics[i]) + { + if (!codec::abi::ContractEventTopic::validEventTopic(topic)) + { + return false; + } + } + } + + // check address + for (const auto& addr : m_addresses) + { + if (addr.empty()) + { + return false; + } + } + + // from to range check + if (m_fromBlock > 0 && m_toBlock > 0) + { + return m_fromBlock <= m_toBlock; + } + + return true; +} + +bool EventSubParams::fromJsonString(const std::string& _jsonString) +{ + Json::Value root; + Json::Reader jsonReader; + + try + { + if (!jsonReader.parse(_jsonString, root)) + { + EVENT_PARAMS(WARNING) << LOG_BADGE("fromJsonString") << LOG_DESC("invalid json object") + << LOG_KV("jsonString", _jsonString); + return false; + } + + fromJson(root); + + EVENT_PARAMS(INFO) << LOG_BADGE("fromJsonString") << LOG_KV("jsonString", _jsonString) + << LOG_KV("params", *this); + return true; + } + catch (const std::exception& _e) + { + EVENT_PARAMS(WARNING) << LOG_BADGE("fromJsonString") + << LOG_DESC("invalid event sub params json object") + << LOG_KV("jsonString", _jsonString) + << LOG_KV("error", boost::diagnostic_information(_e)); + return false; + } +} + +void EventSubParams::fromJson(const Json::Value& jParams) +{ + if (jParams.isMember("fromBlock")) + { + setFromBlock(jParams["fromBlock"].asInt64()); + } + + if (jParams.isMember("toBlock")) + { + setToBlock(jParams["toBlock"].asInt64()); + } + + if (jParams.isMember("addresses")) + { + auto& jAddr = jParams["addresses"]; + for (Json::Value::ArrayIndex index = 0; index < jAddr.size(); ++index) + { + addAddress(jAddr[index].asString()); + } + } + + if (jParams.isMember("topics")) + { + auto& jTopics = jParams["topics"]; + 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, jIndex[innerIndex].asString()); + } + } + else + { // single topic, string value + addTopic(index, jIndex.asString()); + } + } + } + + EVENT_PARAMS(DEBUG) << LOG_BADGE("fromJson") << LOG_KV("EventSubParams", *this); +} + +std::string EventSubParams::toJsonString() +{ + Json::FastWriter writer; + std::string result = writer.write(toJson()); + return result; +} + +Json::Value EventSubParams::toJson() +{ + Json::Value jParams; + // fromBlock + jParams["fromBlock"] = fromBlock(); + // toBlock + jParams["toBlock"] = toBlock(); + + // addresses + Json::Value jAddresses(Json::arrayValue); + for (const auto& addr : addresses()) + { + jAddresses.append(addr); + } + jParams["addresses"] = jAddresses; + + // topics + Json::Value jTopics(Json::arrayValue); + for (const auto& inTopics : topics()) + { + if (inTopics.empty()) + { + Json::Value jInTopics(Json::nullValue); + jTopics.append(jInTopics); + continue; + } + + Json::Value jInTopics(Json::arrayValue); + for (const auto& topic : inTopics) + { + jInTopics.append(topic); + } + jTopics.append(jInTopics); + } + + jParams["topics"] = jTopics; + return jParams; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubParams.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubParams.h" new file mode 100644 index 00000000..cdec6d46 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubParams.h" @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2021 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 EvenPushParams.h + * @author: octopus + * @date 2021-09-01 + */ + +#pragma once +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace cppsdk +{ +namespace event +{ +class EventSubParams +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + +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; } + + 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); } + bool addTopic(std::size_t _index, const std::string& _topic) + { + m_topics.resize(_index + 1); + m_topics[_index].insert(_topic); + + return _index < EVENT_LOG_TOPICS_MAX_INDEX; + } + +public: + bool fromJsonString(const std::string& _jsonString); + void fromJson(const Json::Value& _json); + + std::string toJsonString(); + Json::Value toJson(); + +public: + bool verifyParams(); + +private: + int64_t m_fromBlock = -1; + int64_t m_toBlock = -1; + std::set m_addresses; + std::vector> m_topics; +}; + +inline std::ostream& operator<<(std::ostream& _out, const EventSubParams& _params) +{ + _out << "{"; + _out << "fromBlock: " << _params.fromBlock(); + _out << "toBlock: " << _params.toBlock(); + + _out << "addresses: "; + for (const auto& addr : _params.addresses()) + { + _out << addr << " "; + } + + _out << "topics: "; + for (std::size_t i = 0; i < _params.topics().size(); ++i) + { + _out << "index: " << i; + for (const auto& topic : _params.topics()[i]) + { + _out << topic << " " << i; + } + _out << ","; + } + + _out << "}"; + return _out; +} + +} // namespace event +} // namespace cppsdk +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubRequest.cpp" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubRequest.cpp" new file mode 100644 index 00000000..717f299a --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubRequest.cpp" @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2021 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 EvenPushRequest.cpp + * @author: octopus + * @date 2021-09-03 + */ + +#include +#include + +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::cppsdk::event; + +std::string EventSubUnsubRequest::generateJson() const +{ + /* + { + "id": "", + "group": "" + } + */ + Json::Value jResult; + // id + jResult["id"] = m_id; + // group + jResult["group"] = m_group; + + Json::FastWriter writer; + std::string result = writer.write(jResult); + return result; +} + +bool EventSubUnsubRequest::fromJson(const std::string& _request) +{ + std::string id; + std::string group; + EventSubParams::Ptr params = std::make_shared(); + + try + { + Json::Value root; + Json::Reader jsonReader; + std::string errorMessage; + do + { + if (!jsonReader.parse(_request, root)) + { + errorMessage = "invalid json object, parse request failed"; + break; + } + + if (!root.isMember("id")) + { // id field not exist + errorMessage = "\'id\' field not exist"; + break; + } + id = root["id"].asString(); + + if (!root.isMember("group")) + { + // group field not exist + errorMessage = "\'group\' field not exist"; + break; + } + group = root["group"].asString(); + + m_id = id; + m_group = group; + + EVENT_REQUEST(INFO) << LOG_BADGE("fromJson") + << LOG_DESC("parse event sub request success") + << LOG_KV("group", m_group) << LOG_KV("id", m_id); + + return true; + + } while (0); + + EVENT_REQUEST(WARNING) << LOG_BADGE("fromJson") << LOG_DESC("invalid event sub request") + << LOG_KV("request", _request) + << LOG_KV("errorMessage", errorMessage); + } + catch (const std::exception& e) + { + EVENT_REQUEST(WARNING) << LOG_BADGE("fromJson") << LOG_DESC("invalid json object") + + << LOG_KV("request", _request) + << LOG_KV("error", boost::diagnostic_information(e)); + } + + return false; +} + + +std::string EventSubSubRequest::generateJson() const +{ + /* + { + "id": "", + "group": "", + "params": { + "fromBlock": -1, + "toBlock": -1, + "addresses": [ + "0xca5ed56862869c25da0bdf186e634aac6c6361ee" + ], + "topics": [ + "0x91c95f04198617c60eaf2180fbca88fc192db379657df0e412a9f7dd4ebbe95d" + ] + } + } + */ + Json::Value jResult; + // id + jResult["id"] = id(); + // group + jResult["group"] = group(); + + Json::Value jParams; + // fromBlock + jParams["fromBlock"] = m_state->currentBlockNumber() > 0 ? m_state->currentBlockNumber() + 1 : + m_params->fromBlock(); + // toBlock + jParams["toBlock"] = m_params->toBlock(); + // addresses + Json::Value jAddresses(Json::arrayValue); + for (const auto& addr : m_params->addresses()) + { + jAddresses.append(addr); + } + jParams["addresses"] = jAddresses; + // topics + Json::Value jTopics(Json::arrayValue); + for (const auto& inTopics : m_params->topics()) + { + if (inTopics.empty()) + { + Json::Value jInTopics(Json::nullValue); + jTopics.append(jInTopics); + continue; + } + + Json::Value jInTopics(Json::arrayValue); + for (const auto& topic : inTopics) + { + jInTopics.append(topic); + } + jTopics.append(jInTopics); + } + + jParams["topics"] = jTopics; + jResult["params"] = jParams; + + Json::FastWriter writer; + std::string result = writer.write(jResult); + return result; +} + +bool EventSubSubRequest::fromJson(const std::string& _request) +{ + std::string id; + std::string group; + EventSubParams::Ptr params = std::make_shared(); + + try + { + Json::Value root; + Json::Reader jsonReader; + std::string errorMessage; + do + { + if (!jsonReader.parse(_request, root)) + { + errorMessage = "invalid json object, parse request failed"; + break; + } + + if (!root.isMember("id")) + { // id field not exist + errorMessage = "\'id\' field not exist"; + break; + } + id = root["id"].asString(); + + if (!root.isMember("group")) + { + // group field not exist + errorMessage = "\'group\' field not exist"; + break; + } + group = root["group"].asString(); + + if (!root.isMember("params")) + { // params field not exist + errorMessage = "\'params\' field not exist"; + break; + } + + auto& jParams = root["params"]; + if (jParams.isMember("fromBlock")) + { + params->setFromBlock(jParams["fromBlock"].asInt64()); + } + + if (jParams.isMember("toBlock")) + { + params->setToBlock(jParams["toBlock"].asInt64()); + } + + if (jParams.isMember("addresses")) + { + auto& jAddresses = jParams["addresses"]; + for (Json::Value::ArrayIndex index = 0; index < jAddresses.size(); ++index) + { + params->addAddress(jAddresses[index].asString()); + } + } + + if (jParams.isMember("topics")) + { + auto& jTopics = jParams["topics"]; + + 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) + { + params->addTopic(index, jIndex[innerIndex].asString()); + } + } + else + { // single topic, string value + params->addTopic(index, jIndex.asString()); + } + } + } + + setId(id); + setGroup(group); + setParams(params); + + EVENT_REQUEST(INFO) << LOG_BADGE("fromJson") + << LOG_DESC("parse event sub request success") + << LOG_KV("group", group) << LOG_KV("id", id); + + return true; + + } while (0); + + EVENT_REQUEST(WARNING) << LOG_BADGE("fromJson") << LOG_DESC("invalid event sub request") + << LOG_KV("request", _request) + << LOG_KV("errorMessage", errorMessage); + } + catch (const std::exception& e) + { + EVENT_REQUEST(WARNING) << LOG_BADGE("fromJson") << LOG_DESC("invalid json object") + + << LOG_KV("request", _request) + << LOG_KV("error", boost::diagnostic_information(e)); + } + + return false; +} diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubRequest.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubRequest.h" new file mode 100644 index 00000000..44fc7fc4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubRequest.h" @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2021 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 EvenPushRequest.h + * @author: octopus + * @date 2021-09-01 + */ + +#pragma once +#include +#include + +namespace bcos +{ +namespace cppsdk +{ +namespace event +{ +class EventSubUnsubRequest +{ +public: + using Ptr = std::shared_ptr; + + virtual ~EventSubUnsubRequest() {} + +public: + void setId(const std::string& _id) { m_id = _id; } + std::string id() const { return m_id; } + + void setGroup(const std::string& _group) { m_group = _group; } + std::string group() const { return m_group; } + + virtual std::string generateJson() const; + virtual bool fromJson(const std::string& _request); + +private: + std::string m_id; + std::string m_group; +}; + +class EventSubSubRequest : public EventSubUnsubRequest +{ +public: + using Ptr = std::shared_ptr; + + virtual ~EventSubSubRequest() {} + +public: + void setParams(std::shared_ptr _params) { m_params = _params; } + std::shared_ptr params() const { return m_params; } + + void setState(std::shared_ptr _state) { m_state = _state; } + std::shared_ptr state() const { return m_state; } + + std::string generateJson() const override; + bool fromJson(const std::string& _request) override; + +private: + std::shared_ptr m_params; + std::shared_ptr m_state; +}; + +} // namespace event +} // namespace cppsdk +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubResponse.cpp" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubResponse.cpp" new file mode 100644 index 00000000..b724e7d2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubResponse.cpp" @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2021 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 EvenPushResponse.cpp + * @author: octopus + * @date 2021-09-09 + */ +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::cppsdk::event; + +std::string EventSubResponse::generateJson() +{ + /* + { + "id": "0x123", + "status": 0, + "result": { + "blockNumber": 111, + "events":[] + } + } + */ + Json::Value jResult; + // id + jResult["id"] = m_id; + // status + jResult["status"] = m_status; + + Json::FastWriter writer; + std::string result = writer.write(jResult); + return result; +} + +bool EventSubResponse::fromJson(const std::string& _response) +{ + std::string id; + int status; + + try + { + Json::Value root; + Json::Reader jsonReader; + std::string errorMessage; + do + { + if (!jsonReader.parse(_response, root)) + { + errorMessage = "invalid json object, parse response failed"; + break; + } + + if (!root.isMember("id")) + { // id field not exist + errorMessage = "\'id\' field not exist"; + break; + } + id = root["id"].asString(); + + if (!root.isMember("status")) + { + // group field not exist + errorMessage = "\'status\' field not exist"; + break; + } + status = root["status"].asInt(); + + m_id = id; + m_status = status; + m_jResp = root; + + EVENT_RESPONSE(TRACE) << LOG_BADGE("fromJson") + << LOG_DESC("parse event sub response success") + << LOG_KV("id", m_id) << LOG_KV("status", m_status); + + return true; + + } while (0); + + EVENT_RESPONSE(WARNING) << LOG_BADGE("fromJson") << LOG_DESC("invalid event sub reponse") + << LOG_KV("response", _response) << LOG_KV("error", errorMessage); + } + catch (const std::exception& e) + { + EVENT_RESPONSE(WARNING) << LOG_BADGE("fromJson") << LOG_DESC("invalid json object") + << LOG_KV("response", _response) + << LOG_KV("error", boost::diagnostic_information(e)); + } + + return false; +} diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubResponse.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubResponse.h" new file mode 100644 index 00000000..4fa91f77 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubResponse.h" @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021 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 EvenPushResponse.h + * @author: octopus + * @date 2021-09-09 + */ + +#pragma once + +#include +#include +#include +namespace bcos +{ +namespace cppsdk +{ +namespace event +{ +class EventSubResponse +{ +public: + using Ptr = std::shared_ptr; + +public: + std::string id() const { return m_id; } + void setId(const std::string& _id) { m_id = _id; } + int status() const { return m_status; } + void setStatus(int _status) { m_status = _status; } + + void setJResp(const Json::Value& _jResp) { m_jResp = _jResp; } + Json::Value jResp() const { return m_jResp; } + +public: + std::string generateJson(); + bool fromJson(const std::string& _response); + +private: + std::string m_id; + int m_status; + + Json::Value m_jResp; +}; +} // namespace event +} // namespace cppsdk +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubStatus.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubStatus.h" new file mode 100644 index 00000000..b78e5658 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubStatus.h" @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 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 EventSubError.h + * @author: octopus + * @date 2021-09-01 + */ + +#pragma once +namespace bcos +{ +namespace cppsdk +{ +namespace event +{ +enum StatusCode +{ + Success = 0, + EndOfPush = 1, // push completed +}; +} // namespace event +} // namespace cppsdk +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubTask.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubTask.h" new file mode 100644 index 00000000..c5b570d0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/event/EventSubTask.h" @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2021 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 EvenPushTask.h + * @author: octopus + * @date 2021-09-01 + */ + +#pragma once +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace cppsdk +{ +namespace event +{ +class EventSubTaskState +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + +public: + int64_t currentBlockNumber() const { return m_currentBlockNumber.load(); } + void setCurrentBlockNumber(int64_t _currentBlockNumber) + { + if (_currentBlockNumber > m_currentBlockNumber.load()) + { + m_currentBlockNumber.store(_currentBlockNumber); + } + } + +private: + std::atomic m_currentBlockNumber = -1; +}; + +class EventSubTask +{ +public: + using Ptr = std::shared_ptr; + EventSubTask() { EVENT_TASK(DEBUG) << LOG_KV("[NEWOBJ][EventSubTask]", this); } + ~EventSubTask() { EVENT_TASK(DEBUG) << LOG_KV("[DELOBJ][EventSubTask]", this); } + +public: + void setSession(std::shared_ptr _session) + { + m_session = _session; + } + std::shared_ptr session() const { return m_session; } + + void setId(const std::string& _id) { m_id = _id; } + std::string id() const { return m_id; } + + void setGroup(const std::string& _group) { m_group = _group; } + std::string group() const { return m_group; } + + void setParams(std::shared_ptr _params) { m_params = _params; } + std::shared_ptr params() const { return m_params; } + + void setState(std::shared_ptr _state) { m_state = _state; } + std::shared_ptr state() const { return m_state; } + + void setCallback(Callback _callback) { m_callback = _callback; } + Callback callback() const { return m_callback; } + +private: + std::string m_id; + std::string m_group; + Callback m_callback; + std::shared_ptr m_session; + std::shared_ptr m_params; + std::shared_ptr m_state; +}; + +using EventSubTaskPtrs = std::vector; +} // namespace event +} // namespace cppsdk +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/multigroup/JsonChainNodeInfoCodec.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/multigroup/JsonChainNodeInfoCodec.h" new file mode 100644 index 00000000..b24ed11d --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/multigroup/JsonChainNodeInfoCodec.h" @@ -0,0 +1,196 @@ +/** + * Copyright (C) 2021 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. + * + * @brief the information used to deploy new node + * @file JsonChainNodeInfoCodec.h + * @author: yujiechen + * @date 2021-09-08 + */ +#pragma once +#include +#include +#include +#include +#include +#include +namespace bcos +{ +namespace group +{ +class JsonChainNodeInfoCodec +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + JsonChainNodeInfoCodec() { m_chainNodeInfoFactory = std::make_shared(); } + virtual ~JsonChainNodeInfoCodec() {} + + virtual void deserializeIniConfig(ChainNodeInfo::Ptr _chainNodeInfo) + { + Json::Value value; + Json::Reader jsonReader; + if (!jsonReader.parse(_chainNodeInfo->iniConfig(), value)) + { + BOOST_THROW_EXCEPTION(bcos::InvalidParameter() << bcos::errinfo_comment( + "The chain node ini config must be valid json string.")); + } + + // required: isWasm + if (!value.isMember("isWasm")) + { + BOOST_THROW_EXCEPTION(bcos::InvalidParameter() << bcos::errinfo_comment( + "The chain node ini config must set is wasm info.")); + } + _chainNodeInfo->setWasm(value["isWasm"].asBool()); + + // required: smCryptoType + if (!value.isMember("smCryptoType")) + { + BOOST_THROW_EXCEPTION(bcos::InvalidParameter() << bcos::errinfo_comment( + "The chain node ini config must set sm crypto type.")); + } + _chainNodeInfo->setSmCryptoType(value["smCryptoType"].asBool()); + } + + virtual ChainNodeInfo::Ptr deserialize(const std::string& _json) + { + auto chainNodeInfo = m_chainNodeInfoFactory->createNodeInfo(); + Json::Value value; + Json::Reader jsonReader; + if (!jsonReader.parse(_json, value)) + { + BOOST_THROW_EXCEPTION(bcos::InvalidParameter() << bcos::errinfo_comment( + "The chain node information must be valid json string.")); + } + // required: parse nodeName + if (!value.isMember("name")) + { + BOOST_THROW_EXCEPTION(bcos::InvalidParameter() << bcos::errinfo_comment( + "The chain node information must set the chain node name.")); + } + chainNodeInfo->setNodeName(value["name"].asString()); + + // required: parse nodeType + if (!value.isMember("type")) + { + BOOST_THROW_EXCEPTION(bcos::InvalidParameter() << bcos::errinfo_comment( + "The chain node information must set the chain node type.")); + } + NodeCryptoType type = (NodeCryptoType)(value["type"].asUInt()); + chainNodeInfo->setNodeCryptoType(type); + + // required: parse iniConfig + if (!value.isMember("iniConfig")) + { + BOOST_THROW_EXCEPTION(bcos::InvalidParameter() << bcos::errinfo_comment( + "The chain node information must set the init config info")); + } + chainNodeInfo->setIniConfig(value["iniConfig"].asString()); + deserializeIniConfig(chainNodeInfo); + + // required: parse deployInfo + if (!value.isMember("serviceInfo")) + { + BOOST_THROW_EXCEPTION(bcos::InvalidParameter() << bcos::errinfo_comment( + "The chain node information must set the service info")); + } + + if (!value["serviceInfo"].isArray()) + { + BOOST_THROW_EXCEPTION(bcos::InvalidParameter() + << bcos::errinfo_comment("The service info must be array.")); + } + + auto const& serviceInfo = value["serviceInfo"]; + for (Json::ArrayIndex i = 0; i < serviceInfo.size(); i++) + { + auto const& serviceInfoItem = serviceInfo[i]; + if (!serviceInfoItem.isObject() || !serviceInfoItem.isMember("type") || + !serviceInfoItem.isMember("serviceName")) + { + BOOST_THROW_EXCEPTION( + bcos::InvalidParameter() << bcos::errinfo_comment( + "Invalid service info: must contain the service type and name")); + } + chainNodeInfo->appendServiceInfo( + (bcos::protocol::ServiceType)serviceInfoItem["type"].asInt(), + serviceInfoItem["serviceName"].asString()); + } + + // optional: parse m_nodeID + if (value.isMember("nodeID")) + { + chainNodeInfo->setNodeID(value["nodeID"].asString()); + } + + // optional: parse microService + if (value.isMember("microService")) + { + chainNodeInfo->setMicroService(value["microService"].asBool()); + } + // optional: protocol info + auto protocolInfo = std::make_shared(); + if (value.isMember("protocol")) + { + auto const& protocol = value["protocol"]; + if (protocol.isMember("minSupportedVersion")) + { + protocolInfo->setMinVersion(protocol["minSupportedVersion"].asUInt()); + } + if (protocol.isMember("maxSupportedVersion")) + { + protocolInfo->setMaxVersion(protocol["maxSupportedVersion"].asUInt()); + } + chainNodeInfo->setNodeProtocol(std::move(*protocolInfo)); + if (protocol.isMember("compatibilityVersion")) + { + chainNodeInfo->setCompatibilityVersion(protocol["compatibilityVersion"].asUInt()); + } + } + return chainNodeInfo; + } + + virtual Json::Value serialize(ChainNodeInfo::Ptr _chainNodeInfo) + { + Json::Value jResp; + jResp["name"] = _chainNodeInfo->nodeName(); + jResp["type"] = _chainNodeInfo->nodeCryptoType(); + jResp["iniConfig"] = _chainNodeInfo->iniConfig(); + // set deployInfo + jResp["serviceInfo"] = Json::Value(Json::arrayValue); + + auto const& infos = _chainNodeInfo->serviceInfo(); + for (auto const& innerIt : infos) + { + Json::Value item; + item["type"] = innerIt.first; + item["serviceName"] = innerIt.second; + jResp["serviceInfo"].append(item); + } + // set protocol info + auto protocol = _chainNodeInfo->nodeProtocol(); + Json::Value protocolResponse; + protocolResponse["minSupportedVersion"] = protocol->minVersion(); + protocolResponse["maxSupportedVersion"] = protocol->maxVersion(); + protocolResponse["compatibilityVersion"] = _chainNodeInfo->compatibilityVersion(); + jResp["protocol"] = protocolResponse; + return jResp; + } + +private: + ChainNodeInfoFactory::Ptr m_chainNodeInfoFactory; +}; +} // namespace group +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/multigroup/JsonGroupInfoCodec.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/multigroup/JsonGroupInfoCodec.h" new file mode 100644 index 00000000..622e3e77 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/multigroup/JsonGroupInfoCodec.h" @@ -0,0 +1,147 @@ +/** + * Copyright (C) 2021 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. + * + * @brief the information used to manager group + * @file JsonGroupInfoCodec.h + * @author: yujiechen + * @date 2021-09-08 + */ +#pragma once +#include "JsonChainNodeInfoCodec.h" +#include +#include +#include +#include +namespace bcos +{ +namespace group +{ +class JsonGroupInfoCodec : public GroupInfoCodec +{ +public: + using Ptr = std::shared_ptr; + JsonGroupInfoCodec() + { + m_groupInfoFactory = std::make_shared(); + m_chainNodeInfoCodec = std::make_shared(); + } + ~JsonGroupInfoCodec() override {} + + GroupInfo::Ptr deserialize(const std::string& _json) override + { + auto groupInfo = m_groupInfoFactory->createGroupInfo(); + Json::Value root; + Json::Reader jsonReader; + + if (!jsonReader.parse(_json, root)) + { + BOOST_THROW_EXCEPTION(bcos::InvalidParameter() << bcos::errinfo_comment( + "The group information must be valid json string.")); + } + + if (!root.isMember("chainID")) + { + BOOST_THROW_EXCEPTION(bcos::InvalidParameter() << bcos::errinfo_comment( + "The group information must contain chainID")); + } + groupInfo->setChainID(root["chainID"].asString()); + + if (!root.isMember("groupID")) + { + BOOST_THROW_EXCEPTION(bcos::InvalidParameter() << bcos::errinfo_comment( + "The group information must contain groupID")); + } + groupInfo->setGroupID(root["groupID"].asString()); + + if (root.isMember("genesisConfig")) + { + groupInfo->setGenesisConfig(root["genesisConfig"].asString()); + } + + + if (!root.isMember("iniConfig")) + { + BOOST_THROW_EXCEPTION(bcos::InvalidParameter() << bcos::errinfo_comment( + "The group information must contain iniConfig")); + } + groupInfo->setIniConfig(root["iniConfig"].asString()); + + // nodeList + if (!root.isMember("nodeList") || !root["nodeList"].isArray()) + { + BOOST_THROW_EXCEPTION(bcos::InvalidParameter() << bcos::errinfo_comment( + "The group information must contain nodeList")); + } + + bool isFirst = true; + for (Json::ArrayIndex i = 0; i < root["nodeList"].size(); ++i) + { + auto& nodeInfo = root["nodeList"][i]; + Json::FastWriter writer; + std::string nodeStr = writer.write(nodeInfo); + auto node = m_chainNodeInfoCodec->deserialize(nodeStr); + groupInfo->appendNodeInfo(node); + if (isFirst) + { + groupInfo->setWasm(node->wasm()); + groupInfo->setSmCryptoType(node->smCryptoType()); + isFirst = false; + } + } + return groupInfo; + } + + Json::Value serialize(GroupInfo::Ptr _groupInfo) override + { + Json::Value jResp; + jResp["chainID"] = _groupInfo->chainID(); + jResp["groupID"] = _groupInfo->groupID(); + jResp["wasm"] = _groupInfo->wasm(); + jResp["smCryptoType"] = _groupInfo->smCryptoType(); + jResp["genesisConfig"] = _groupInfo->genesisConfig(); + jResp["iniConfig"] = _groupInfo->iniConfig(); + jResp["nodeList"] = Json::Value(Json::arrayValue); + const auto& nodes = _groupInfo->nodeInfos(); + for (auto const& it : nodes) + { + jResp["nodeList"].append(m_chainNodeInfoCodec->serialize(it.second)); + } + return jResp; + } + void serialize(std::string& _encodedData, GroupInfo::Ptr _groupInfo) override + { + Json::Value jResp; + jResp["chainID"] = _groupInfo->chainID(); + jResp["groupID"] = _groupInfo->groupID(); + jResp["wasm"] = _groupInfo->wasm(); + jResp["smCryptoType"] = _groupInfo->smCryptoType(); + jResp["genesisConfig"] = _groupInfo->genesisConfig(); + jResp["iniConfig"] = _groupInfo->iniConfig(); + jResp["nodeList"] = Json::Value(Json::arrayValue); + const auto& nodes = _groupInfo->nodeInfos(); + for (auto const& it : nodes) + { + jResp["nodeList"].append(m_chainNodeInfoCodec->serialize(it.second)); + } + Json::FastWriter writer; + _encodedData = writer.write(jResp); + } + +private: + GroupInfoFactory::Ptr m_groupInfoFactory; + JsonChainNodeInfoCodec::Ptr m_chainNodeInfoCodec; +}; +} // namespace group +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/Common.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/Common.h" new file mode 100644 index 00000000..992eabcd --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/Common.h" @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2021 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: octopus + * @date 2021-08-10 + */ + +#pragma once +#include +#include +#include + +#define RPCREQ_LOG(LEVEL) BCOS_LOG(LEVEL) << "[RPC][REQUEST]" +#define RPCIMPL_LOG(LEVEL) BCOS_LOG(LEVEL) << "[RPC][IMPL]" + +namespace bcos +{ +namespace cppsdk +{ +namespace jsonrpc +{ +struct JsonResponse +{ + struct Error + { + int32_t code{0}; + std::string message{"success"}; + + std::string toString() const + { + return "{\"code\":" + std::to_string(code) + "\"message\":" + message + "}"; + } + }; + std::string jsonrpc; + int64_t id; + Error error; + Json::Value result; + +public: + inline Json::Value toJson() + { + Json::Value jResp; + + jResp["jsonrpc"] = jsonrpc; + jResp["id"] = id; + + if (error.code == 0) + { // success + jResp["result"] = result; + } + else + { // error + Json::Value jError; + jError["code"] = error.code; + jError["message"] = error.message; + + jResp["error"] = jError; + } + + return jResp; + } + + inline std::string toJsonString() + { + auto jResp = toJson(); + Json::FastWriter writer; + std::string resp = writer.write(jResp); + return resp; + } +}; + +} // namespace jsonrpc +} // namespace cppsdk +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcImpl.cpp" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcImpl.cpp" new file mode 100644 index 00000000..177a4cfb --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcImpl.cpp" @@ -0,0 +1,509 @@ +/* + * Copyright (C) 2021 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 JsonRpcImpl.cpp + * @author: octopus + * @date 2021-08-10 + */ + +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace cppsdk; +using namespace jsonrpc; +using namespace bcos; + +void JsonRpcImpl::start() +{ + if (m_service) + { // start websocket service + m_service->start(); + } + else + { + RPCIMPL_LOG(WARNING) << LOG_BADGE("start") + << LOG_DESC("websocket service is not uninitialized"); + } + RPCIMPL_LOG(INFO) << LOG_BADGE("start") << LOG_DESC("start rpc"); +} + +void JsonRpcImpl::stop() +{ + RPCIMPL_LOG(INFO) << LOG_BADGE("stop") << LOG_DESC("stop rpc"); +} + +void JsonRpcImpl::genericMethod(const std::string& _data, RespFunc _respFunc) +{ + m_sender("", "", _data, _respFunc); + RPCIMPL_LOG(TRACE) << LOG_BADGE("genericMethod") << LOG_KV("request", _data); +} + +void JsonRpcImpl::genericMethod( + const std::string& _groupID, const std::string& _data, RespFunc _respFunc) +{ + m_sender(_groupID, "", _data, _respFunc); + RPCIMPL_LOG(TRACE) << LOG_BADGE("genericMethod") << LOG_KV("group", _groupID) + << LOG_KV("request", _data); +} + +void JsonRpcImpl::genericMethod(const std::string& _groupID, const std::string& _nodeName, + const std::string& _data, RespFunc _respFunc) +{ + std::string name = _nodeName; + if (name.empty()) + { + m_service->randomGetHighestBlockNumberNode(_groupID, name); + } + + m_sender(_groupID, name, _data, _respFunc); + RPCIMPL_LOG(TRACE) << LOG_BADGE("genericMethod") << LOG_KV("group", _groupID) + << LOG_KV("nodeName", name) << LOG_KV("request", _data); +} + +void JsonRpcImpl::call(const std::string& _groupID, const std::string& _nodeName, + const std::string& _to, const std::string& _data, RespFunc _respFunc) +{ + std::string name = _nodeName; + if (name.empty()) + { + m_service->randomGetHighestBlockNumberNode(_groupID, name); + } + + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + params.append(name); + params.append(_to); + params.append(_data); + + auto request = m_factory->buildRequest("call", params); + auto s = request->toJson(); + m_sender(_groupID, name, s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("call") << LOG_KV("request", s); +} + +void JsonRpcImpl::sendTransaction(const std::string& _groupID, const std::string& _nodeName, + const std::string& _data, bool _requireProof, RespFunc _respFunc) +{ + std::string name = _nodeName; + if (name.empty()) + { + m_service->randomGetHighestBlockNumberNode(_groupID, name); + } + + auto groupInfo = m_service->getGroupInfo(_groupID); + if (!groupInfo) + { + auto error = std::make_shared(bcos::boostssl::ws::WsError::EndPointNotExist, + "the group does not exist, group: " + _groupID); + _respFunc(error, nullptr); + return; + } + + auto txBytes = fromHexString(_data); + + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + params.append(name); + params.append(_data); + params.append(_requireProof); + + auto request = m_factory->buildRequest("sendTransaction", params); + auto s = request->toJson(); + m_sender(_groupID, name, s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("sendTransaction") << LOG_KV("request", s); +} + +void JsonRpcImpl::getTransaction(const std::string& _groupID, const std::string& _nodeName, + const std::string& _txHash, bool _requireProof, RespFunc _respFunc) +{ + std::string name = _nodeName; + if (name.empty()) + { + m_service->randomGetHighestBlockNumberNode(_groupID, name); + } + + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + params.append(name); + params.append(_txHash); + params.append(_requireProof); + + auto request = m_factory->buildRequest("getTransaction", params); + auto s = request->toJson(); + m_sender(_groupID, name, s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getTransaction") << LOG_KV("request", s); +} + +void JsonRpcImpl::getTransactionReceipt(const std::string& _groupID, const std::string& _nodeName, + const std::string& _txHash, bool _requireProof, RespFunc _respFunc) +{ + std::string name = _nodeName; + if (name.empty()) + { + m_service->randomGetHighestBlockNumberNode(_groupID, name); + } + + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + params.append(name); + params.append(_txHash); + params.append(_requireProof); + + auto request = m_factory->buildRequest("getTransactionReceipt", params); + auto s = request->toJson(); + m_sender(_groupID, name, s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getTransactionReceipt") << LOG_KV("request", s); +} + +void JsonRpcImpl::getBlockByHash(const std::string& _groupID, const std::string& _nodeName, + const std::string& _blockHash, bool _onlyHeader, bool _onlyTxHash, RespFunc _respFunc) +{ + std::string name = _nodeName; + if (name.empty()) + { + m_service->randomGetHighestBlockNumberNode(_groupID, name); + } + + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + params.append(name); + params.append(_blockHash); + params.append(_onlyHeader); + params.append(_onlyTxHash); + + auto request = m_factory->buildRequest("getBlockByHash", params); + auto s = request->toJson(); + m_sender(_groupID, name, s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getBlockByHash") << LOG_KV("request", s); +} + +void JsonRpcImpl::getBlockByNumber(const std::string& _groupID, const std::string& _nodeName, + int64_t _blockNumber, bool _onlyHeader, bool _onlyTxHash, RespFunc _respFunc) +{ + std::string name = _nodeName; + if (name.empty()) + { + m_service->randomGetHighestBlockNumberNode(_groupID, name); + } + + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + params.append(name); + params.append(_blockNumber); + params.append(_onlyHeader); + params.append(_onlyTxHash); + + auto request = m_factory->buildRequest("getBlockByNumber", params); + auto s = request->toJson(); + m_sender(_groupID, name, s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getBlockByNumber") << LOG_KV("request", s); +} + +void JsonRpcImpl::getBlockHashByNumber(const std::string& _groupID, const std::string& _nodeName, + int64_t _blockNumber, RespFunc _respFunc) +{ + std::string name = _nodeName; + if (name.empty()) + { + m_service->randomGetHighestBlockNumberNode(_groupID, name); + } + + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + params.append(name); + params.append(_blockNumber); + + auto request = m_factory->buildRequest("getBlockHashByNumber", params); + auto s = request->toJson(); + m_sender(_groupID, name, s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getBlockHashByNumber") << LOG_KV("request", s); +} + +void JsonRpcImpl::getBlockNumber( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) +{ + std::string name = _nodeName; + if (name.empty()) + { + m_service->randomGetHighestBlockNumberNode(_groupID, name); + } + + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + params.append(name); + + auto request = m_factory->buildRequest("getBlockNumber", params); + auto s = request->toJson(); + m_sender(_groupID, name, s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getBlockNumber") << LOG_KV("request", s); +} + +void JsonRpcImpl::getCode(const std::string& _groupID, const std::string& _nodeName, + const std::string _contractAddress, RespFunc _respFunc) +{ + std::string name = _nodeName; + if (name.empty()) + { + m_service->randomGetHighestBlockNumberNode(_groupID, name); + } + + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + params.append(name); + params.append(_contractAddress); + + auto request = m_factory->buildRequest("getCode", params); + auto s = request->toJson(); + m_sender(_groupID, name, s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getCode") << LOG_KV("request", s); +} + +void JsonRpcImpl::getSealerList( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) +{ + std::string name = _nodeName; + if (name.empty()) + { + m_service->randomGetHighestBlockNumberNode(_groupID, name); + } + + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + params.append(name); + + auto request = m_factory->buildRequest("getSealerList", params); + auto s = request->toJson(); + m_sender(_groupID, name, s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getSealerList") << LOG_KV("request", s); +} + +void JsonRpcImpl::getObserverList( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) +{ + std::string name = _nodeName; + if (name.empty()) + { + m_service->randomGetHighestBlockNumberNode(_groupID, name); + } + + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + params.append(name); + + auto request = m_factory->buildRequest("getObserverList", params); + auto s = request->toJson(); + m_sender(_groupID, name, s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getObserverList") << LOG_KV("request", s); +} + +void JsonRpcImpl::getPbftView( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) +{ + std::string name = _nodeName; + if (name.empty()) + { + m_service->randomGetHighestBlockNumberNode(_groupID, name); + } + + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + params.append(name); + + auto request = m_factory->buildRequest("getPbftView", params); + auto s = request->toJson(); + m_sender(_groupID, name, s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getPbftView") << LOG_KV("request", s); +} + +void JsonRpcImpl::getPendingTxSize( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) +{ + std::string name = _nodeName; + if (name.empty()) + { + m_service->randomGetHighestBlockNumberNode(_groupID, name); + } + + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + params.append(name); + + auto request = m_factory->buildRequest("getPendingTxSize", params); + auto s = request->toJson(); + m_sender(_groupID, name, s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getPendingTxSize") << LOG_KV("request", s); +} + +void JsonRpcImpl::getSyncStatus( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) +{ + std::string name = _nodeName; + if (name.empty()) + { + m_service->randomGetHighestBlockNumberNode(_groupID, name); + } + + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + params.append(name); + + auto request = m_factory->buildRequest("getSyncStatus", params); + auto s = request->toJson(); + m_sender(_groupID, name, s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getSyncStatus") << LOG_KV("request", s); +} + +void JsonRpcImpl::getConsensusStatus( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) +{ + std::string name = _nodeName; + if (name.empty()) + { + m_service->randomGetHighestBlockNumberNode(_groupID, name); + } + + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + params.append(name); + + auto request = m_factory->buildRequest("getConsensusStatus", params); + auto s = request->toJson(); + m_sender(_groupID, name, s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getConsensusStatus") << LOG_KV("request", s); +} + +void JsonRpcImpl::getSystemConfigByKey(const std::string& _groupID, const std::string& _nodeName, + const std::string& _keyValue, RespFunc _respFunc) +{ + std::string name = _nodeName; + if (name.empty()) + { + m_service->randomGetHighestBlockNumberNode(_groupID, name); + } + + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + params.append(name); + params.append(_keyValue); + + auto request = m_factory->buildRequest("getSystemConfigByKey", params); + auto s = request->toJson(); + m_sender(_groupID, name, s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getSystemConfigByKey") << LOG_KV("request", s); +} + +void JsonRpcImpl::getTotalTransactionCount( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) +{ + std::string name = _nodeName; + if (name.empty()) + { + m_service->randomGetHighestBlockNumberNode(_groupID, name); + } + + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + params.append(name); + + auto request = m_factory->buildRequest("getTotalTransactionCount", params); + auto s = request->toJson(); + m_sender(_groupID, name, s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getTotalTransactionCount") << LOG_KV("request", s); +} + +void JsonRpcImpl::getPeers(RespFunc _respFunc) +{ + Json::Value params = Json::Value(Json::arrayValue); + auto request = m_factory->buildRequest("getPeers", params); + auto s = request->toJson(); + m_sender("", "", s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getPeers") << LOG_KV("request", s); +} + +void JsonRpcImpl::getGroupList(RespFunc _respFunc) +{ + Json::Value params = Json::Value(Json::arrayValue); + auto request = m_factory->buildRequest("getGroupList", params); + auto s = request->toJson(); + m_sender("", "", s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getGroupList") << LOG_KV("request", s); +} + +void JsonRpcImpl::getGroupInfo(const std::string& _groupID, RespFunc _respFunc) +{ + JsonResponse jsonResp; + jsonResp.jsonrpc = "2.0"; + jsonResp.id = m_factory->nextId(); + bool hitCache = false; + + auto groupInfo = m_service->getGroupInfo(_groupID); + if (groupInfo) + { + jsonResp.result = m_groupInfoCodec->serialize(groupInfo); + hitCache = true; + } + else + { + jsonResp.result = Json::Value(Json::nullValue); + hitCache = false; + } + + auto jsonString = jsonResp.toJsonString(); + auto jsonData = std::make_shared(jsonString.begin(), jsonString.end()); + _respFunc(nullptr, jsonData); + + RPCIMPL_LOG(INFO) << LOG_BADGE("getGroupInfo") << LOG_BADGE("get group info from cache") + << LOG_KV("hitCache", hitCache) << LOG_KV("response", jsonString); +} + +void JsonRpcImpl::getGroupInfoList(RespFunc _respFunc) +{ + Json::Value params = Json::Value(Json::arrayValue); + + auto request = m_factory->buildRequest("getGroupInfoList", params); + auto s = request->toJson(); + m_sender("", "", s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getGroupNodeInfo") << LOG_KV("request", s); +} + +void JsonRpcImpl::getGroupNodeInfo( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) +{ + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + params.append(_nodeName); + + auto request = m_factory->buildRequest("getGroupNodeInfo", params); + auto s = request->toJson(); + m_sender(_groupID, _nodeName, s, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getGroupNodeInfo") << LOG_KV("request", s); +} + +void JsonRpcImpl::getGroupPeers(std::string const& _groupID, RespFunc _respFunc) +{ + Json::Value params = Json::Value(Json::arrayValue); + params.append(_groupID); + + auto request = m_factory->buildRequest("getGroupPeers", params); + auto requestStr = request->toJson(); + m_sender("", "", requestStr, _respFunc); + RPCIMPL_LOG(DEBUG) << LOG_BADGE("getGroupPeers") << LOG_KV("request", requestStr); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcImpl.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcImpl.h" new file mode 100644 index 00000000..f30239fe --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcImpl.h" @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2021 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 RpcInterface.h + * @author: octopus + * @date 2021-08-10 + */ + +#pragma once +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace cppsdk +{ +namespace jsonrpc +{ +using JsonRpcSendFunc = std::function; + +class JsonRpcImpl : public JsonRpcInterface, public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + +public: + JsonRpcImpl(bcos::group::GroupInfoCodec::Ptr _groupInfoCodec) + : m_groupInfoCodec(_groupInfoCodec) + {} + + virtual ~JsonRpcImpl() { stop(); } + +public: + virtual void start() override; + virtual void stop() override; + +public: + //------------------------------------------------------------------------------------- + virtual void genericMethod(const std::string& _data, RespFunc _respFunc) override; + + virtual void genericMethod( + const std::string& _groupID, const std::string& _data, RespFunc _respFunc) override; + + virtual void genericMethod(const std::string& _groupID, const std::string& _nodeName, + const std::string& _data, RespFunc _respFunc) override; + //------------------------------------------------------------------------------------- + + virtual void call(const std::string& _groupID, const std::string& _nodeName, + const std::string& _to, const std::string& _data, RespFunc _respFunc) override; + + virtual void sendTransaction(const std::string& _groupID, const std::string& _nodeName, + const std::string& _data, bool _requireProof, RespFunc _respFunc) override; + + virtual void getTransaction(const std::string& _groupID, const std::string& _nodeName, + const std::string& _txHash, bool _requireProof, RespFunc _respFunc) override; + + virtual void getTransactionReceipt(const std::string& _groupID, const std::string& _nodeName, + const std::string& _txHash, bool _requireProof, RespFunc _respFunc) override; + + virtual void getBlockByHash(const std::string& _groupID, const std::string& _nodeName, + const std::string& _blockHash, bool _onlyHeader, bool _onlyTxHash, + RespFunc _respFunc) override; + + virtual void getBlockByNumber(const std::string& _groupID, const std::string& _nodeName, + int64_t _blockNumber, bool _onlyHeader, bool _onlyTxHash, RespFunc _respFunc) override; + + virtual void getBlockHashByNumber(const std::string& _groupID, const std::string& _nodeName, + int64_t _blockNumber, RespFunc _respFunc) override; + + virtual void getBlockNumber( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) override; + + virtual void getCode(const std::string& _groupID, const std::string& _nodeName, + const std::string _contractAddress, RespFunc _respFunc) override; + + virtual void getSealerList( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) override; + + virtual void getObserverList( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) override; + + virtual void getPbftView( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) override; + + virtual void getPendingTxSize( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) override; + + virtual void getSyncStatus( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) override; + + virtual void getConsensusStatus( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) override; + + virtual void getSystemConfigByKey(const std::string& _groupID, const std::string& _nodeName, + const std::string& _keyValue, RespFunc _respFunc) override; + + virtual void getTotalTransactionCount( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) override; + + virtual void getGroupPeers(std::string const& _groupID, RespFunc _respFunc) override; + + virtual void getPeers(RespFunc _respFunc) override; + + virtual void getGroupList(RespFunc _respFunc) override; + + virtual void getGroupInfo(const std::string& _groupID, RespFunc _respFunc) override; + + virtual void getGroupInfoList(RespFunc _respFunc) override; + + virtual void getGroupNodeInfo( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) override; + + +public: + JsonRpcRequestFactory::Ptr factory() const { return m_factory; } + void setFactory(JsonRpcRequestFactory::Ptr _factory) { m_factory = _factory; } + + JsonRpcSendFunc sender() const { return m_sender; } + void setSender(JsonRpcSendFunc _sender) { m_sender = _sender; } + + std::shared_ptr service() const { return m_service; } + void setService(std::shared_ptr _service) + { + m_service = _service; + } + +private: + std::shared_ptr m_service; + JsonRpcRequestFactory::Ptr m_factory; + std::function + m_sender; + bcos::group::GroupInfoCodec::Ptr m_groupInfoCodec; +}; + +} // namespace jsonrpc +} // namespace cppsdk +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcInterface.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcInterface.h" new file mode 100644 index 00000000..cd68accb --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcInterface.h" @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2021 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 JsonRPCInterface.h + * @author: octopus + * @date 2021-05-24 + */ + +#pragma once +#include +#include +#include +#include + +namespace bcos +{ +namespace cppsdk +{ +namespace jsonrpc +{ +using RespFunc = std::function)>; + +class JsonRpcInterface +{ +public: + using Ptr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + + JsonRpcInterface() = default; + virtual ~JsonRpcInterface() {} + +public: + virtual void start() = 0; + virtual void stop() = 0; + +public: + //------------------------------------------------------------------------------------- + virtual void genericMethod(const std::string& _data, RespFunc _respFunc) = 0; + virtual void genericMethod( + const std::string& _groupID, const std::string& _data, RespFunc _respFunc) = 0; + virtual void genericMethod(const std::string& _groupID, const std::string& _nodeName, + const std::string& _data, RespFunc _respFunc) = 0; + //------------------------------------------------------------------------------------- + + virtual void call(const std::string& _groupID, const std::string& _nodeName, + const std::string& _to, const std::string& _data, RespFunc _respFunc) = 0; + + virtual void sendTransaction(const std::string& _groupID, const std::string& _nodeName, + const std::string& _data, bool _requireProof, RespFunc _respFunc) = 0; + + virtual void getTransaction(const std::string& _groupID, const std::string& _nodeName, + const std::string& _txHash, bool _requireProof, RespFunc _respFunc) = 0; + + virtual void getTransactionReceipt(const std::string& _groupID, const std::string& _nodeName, + const std::string& _txHash, bool _requireProof, RespFunc _respFunc) = 0; + + virtual void getBlockByHash(const std::string& _groupID, const std::string& _nodeName, + const std::string& _blockHash, bool _onlyHeader, bool _onlyTxHash, RespFunc _respFunc) = 0; + + virtual void getBlockByNumber(const std::string& _groupID, const std::string& _nodeName, + int64_t _blockNumber, bool _onlyHeader, bool _onlyTxHash, RespFunc _respFunc) = 0; + + virtual void getBlockHashByNumber(const std::string& _groupID, const std::string& _nodeName, + int64_t _blockNumber, RespFunc _respFunc) = 0; + + virtual void getBlockNumber( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) = 0; + + virtual void getCode(const std::string& _groupID, const std::string& _nodeName, + const std::string _contractAddress, RespFunc _respFunc) = 0; + + virtual void getSealerList( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) = 0; + + virtual void getObserverList( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) = 0; + + virtual void getPbftView( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) = 0; + + virtual void getPendingTxSize( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) = 0; + + virtual void getSyncStatus( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) = 0; + + virtual void getConsensusStatus( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) = 0; + + virtual void getSystemConfigByKey(const std::string& _groupID, const std::string& _nodeName, + const std::string& _keyValue, RespFunc _respFunc) = 0; + + virtual void getTotalTransactionCount( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) = 0; + + virtual void getGroupPeers(std::string const& _groupID, RespFunc _respFunc) = 0; + + virtual void getPeers(RespFunc _respFunc) = 0; + + virtual void getGroupList(RespFunc _respFunc) = 0; + + virtual void getGroupInfo(const std::string& _groupID, RespFunc _respFunc) = 0; + + virtual void getGroupInfoList(RespFunc _respFunc) = 0; + + virtual void getGroupNodeInfo( + const std::string& _groupID, const std::string& _nodeName, RespFunc _respFunc) = 0; +}; + +} // namespace jsonrpc +} // namespace cppsdk +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcRequest.cpp" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcRequest.cpp" new file mode 100644 index 00000000..91b045e1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcRequest.cpp" @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2021 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 JsonRpcRequest.cpp + * @author: octopus + * @date 2021-05-24 + */ + +#include +#include +#include + +using namespace bcos; +using namespace cppsdk; +using namespace jsonrpc; + +std::string JsonRpcRequest::toJson() +{ + Json::Value jReq; + jReq["jsonrpc"] = m_jsonrpc; + jReq["method"] = m_method; + jReq["id"] = m_id; + jReq["params"] = params(); + + Json::FastWriter writer; + std::string s = writer.write(jReq); + RPCREQ_LOG(TRACE) << LOG_BADGE("toJson") << LOG_KV("request", s); + return s; +} + +void JsonRpcRequest::fromJson(const std::string& _request) +{ + Json::Value root; + Json::Reader jsonReader; + std::string errorMessage; + + try + { + std::string jsonrpc = ""; + std::string method = ""; + int64_t id = 0; + do + { + if (!jsonReader.parse(_request, root)) + { + errorMessage = "invalid request json object"; + break; + } + + if (!root.isMember("jsonrpc")) + { + errorMessage = "request has no jsonrpc field"; + break; + } + jsonrpc = root["jsonrpc"].asString(); + + if (!root.isMember("method")) + { + errorMessage = "request has no method field"; + break; + } + method = root["method"].asString(); + + if (root.isMember("id")) + { + id = root["id"].asInt64(); + } + + if (!root.isMember("params")) + { + errorMessage = "request has no params field"; + break; + } + + if (!root["params"].isArray()) + { + errorMessage = "request params is not array object"; + break; + } + + auto jParams = root["params"]; + + m_jsonrpc = jsonrpc; + m_method = method; + m_id = id; + m_params = jParams; + + // RPCREQ_LOG(DEBUG) << LOG_BADGE("fromJson") << LOG_KV("method", method) + // << LOG_KV("request", _request); + + return; + + } while (0); + } + catch (const std::exception& e) + { + RPCREQ_LOG(WARNING) << LOG_BADGE("fromJson") << LOG_KV("request", _request) + << LOG_KV("error", boost::diagnostic_information(e)); + BOOST_THROW_EXCEPTION( + JsonRpcException(JsonRpcError::ParseError, "Invalid JSON was received by the server.")); + } + + RPCREQ_LOG(WARNING) << LOG_BADGE("fromJson") << LOG_KV("request", _request) + << LOG_KV("errorMessage", errorMessage); + + BOOST_THROW_EXCEPTION(JsonRpcException( + JsonRpcError::InvalidRequest, "The JSON sent is not a valid Request object.")); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcRequest.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcRequest.h" new file mode 100644 index 00000000..54d06d0e --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/rpc/JsonRpcRequest.h" @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2021 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 JsonRpcRequest.h + * @author: octopus + * @date 2021-08-10 + */ + +#pragma once +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace cppsdk +{ +namespace jsonrpc +{ +class JsonRpcException : public std::exception +{ +public: + JsonRpcException(int32_t _code, std::string const& _msg) : m_code(_code), m_msg(_msg) {} + virtual const char* what() const noexcept override { return m_msg.c_str(); } + +public: + int32_t code() const noexcept { return m_code; } + std::string msg() const noexcept { return m_msg; } + +private: + int32_t m_code; + std::string m_msg; +}; + +enum JsonRpcError : int32_t +{ + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603 + // -32000 to -32099: Server error Reserved for implementation-defined server-errors. +}; + +class JsonRpcRequest +{ +public: + using Ptr = std::shared_ptr; + + JsonRpcRequest() {} + ~JsonRpcRequest() {} + +public: + void setJsonrpc(const std::string _jsonrpc) { m_jsonrpc = _jsonrpc; } + std::string jsonrpc() { return m_jsonrpc; } + void setMethod(const std::string _method) { m_method = _method; } + std::string method() { return m_method; } + void setId(int64_t _id) { m_id = _id; } + int64_t id() { return m_id; } + Json::Value params() { return m_params; } + void setParams(const Json::Value& _params) { m_params = _params; } + +public: + std::string toJson(); + void fromJson(const std::string& _request); + +private: + std::string m_jsonrpc = "2.0"; + std::string m_method; + int64_t m_id{1}; + Json::Value m_params; +}; + +class JsonRpcRequestFactory +{ +public: + using Ptr = std::shared_ptr; + JsonRpcRequestFactory() {} + +public: + JsonRpcRequest::Ptr buildRequest() + { + auto request = std::make_shared(); + request->setId(nextId()); + return request; + } + + JsonRpcRequest::Ptr buildRequest(const std::string& _method, const Json::Value& _params) + { + auto request = buildRequest(); + request->setMethod(_method); + request->setParams(_params); + return request; + } + + int64_t nextId() + { + int64_t _id = ++id; + return _id; + } + +private: + std::atomic id{0}; +}; + +} // namespace jsonrpc +} // namespace cppsdk +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractABICodec.cpp" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractABICodec.cpp" new file mode 100644 index 00000000..1f7b71e7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractABICodec.cpp" @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2021 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. + * + * @brief Contract ABI serialize and deserialize tool. + * @author: octopuswang + * @date: 2019-04-01 + */ + +#include + +using namespace std; +using namespace bcos; +using namespace bcos::codec::abi; + +const int ContractABICodec::MAX_BYTE_LENGTH; + +bool ContractABICodec::abiOutByFuncSelector( + bytesConstRef _data, const std::vector& _allTypes, std::vector& _out) +{ + data = _data; + offset = 0; + + for (const std::string& type : _allTypes) + { + if ("int" == type || "int256" == type) + { + s256 s; + deserialize(s, offset); + _out.push_back(toString(s)); + } + else if ("uint" == type || "uint256" == type) + { + u256 u; + deserialize(u, offset); + _out.push_back(toString(u)); + } + else if ("address" == type) + { + Address addr; + deserialize(addr, offset); + _out.push_back(addr.hex()); + } + else if ("string" == type) + { + u256 stringOffset; + deserialize(stringOffset, offset); + + std::string str; + deserialize(str, static_cast(stringOffset)); + _out.push_back(str); + } + else + { // unsupported type + return false; + } + + offset += MAX_BYTE_LENGTH; + } + + return true; +} + +// unsigned integer type uint256. +bytes ContractABICodec::serialise(const int& _in) +{ + return serialise((s256)_in); +} + +// unsigned integer type uint256. +bytes ContractABICodec::serialise(const u256& _in) +{ + return h256(_in).asBytes(); +} + +// two’s complement signed integer type int256. +bytes ContractABICodec::serialise(const s256& _in) +{ + return h256(_in.convert_to()).asBytes(); +} + +// equivalent to uint8 restricted to the values 0 and 1. For computing the function selector, +// bool is used +bytes ContractABICodec::serialise(const bool& _in) +{ + return h256(u256(_in ? 1 : 0)).asBytes(); +} + +// equivalent to uint160, except for the assumed interpretation and language typing. For +// computing the function selector, address is used. +// bool is used. +bytes ContractABICodec::serialise(const Address& _in) +{ + return bytes(12, 0) + _in.asBytes(); +} + +// binary type of 32 bytes +bytes ContractABICodec::serialise(const string32& _in) +{ + bytes ret(32, 0); + bytesConstRef((byte const*)_in.data(), 32).populate(bytesRef(&ret)); + return ret; +} + +bytes ContractABICodec::serialise(const bytes& _in) +{ + bytes ret; + ret = h256(u256(_in.size())).asBytes(); + ret.resize(ret.size() + (_in.size() + 31) / MAX_BYTE_LENGTH * MAX_BYTE_LENGTH); + bytesConstRef(&_in).populate(bytesRef(&ret).getCroppedData(32)); + return ret; +} + +// dynamic sized unicode string assumed to be UTF-8 encoded. +bytes ContractABICodec::serialise(const std::string& _in) +{ + bytes ret; + ret = h256(u256(_in.size())).asBytes(); + ret.resize(ret.size() + (_in.size() + 31) / MAX_BYTE_LENGTH * MAX_BYTE_LENGTH); + bytesConstRef(&_in).populate(bytesRef(&ret).getCroppedData(32)); + return ret; +} + +void ContractABICodec::deserialize(s256& out, std::size_t _offset) +{ + validOffset(_offset + MAX_BYTE_LENGTH - 1); + + u256 u = fromBigEndian(data.getCroppedData(_offset, MAX_BYTE_LENGTH)); + if (u > u256("0x8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) + { + auto r = + (bcos::u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - u) + + 1; + out = s256("-" + r.str()); + } + else + { + out = u.convert_to(); + } +} + +void ContractABICodec::deserialize(u256& _out, std::size_t _offset) +{ + validOffset(_offset + MAX_BYTE_LENGTH - 1); + + _out = fromBigEndian(data.getCroppedData(_offset, MAX_BYTE_LENGTH)); +} + +void ContractABICodec::deserialize(bool& _out, std::size_t _offset) +{ + validOffset(_offset + MAX_BYTE_LENGTH - 1); + + u256 ret = fromBigEndian(data.getCroppedData(_offset, MAX_BYTE_LENGTH)); + _out = ret > 0 ? true : false; +} + +void ContractABICodec::deserialize(Address& _out, std::size_t _offset) +{ + validOffset(_offset + MAX_BYTE_LENGTH - 1); + + data.getCroppedData(_offset + MAX_BYTE_LENGTH - 20, 20).populate(_out.ref()); +} + +void ContractABICodec::deserialize(string32& _out, std::size_t _offset) +{ + validOffset(_offset + MAX_BYTE_LENGTH - 1); + + data.getCroppedData(_offset, MAX_BYTE_LENGTH) + .populate(bytesRef((byte*)_out.data(), MAX_BYTE_LENGTH)); +} + +void ContractABICodec::deserialize(std::string& _out, std::size_t _offset) +{ + validOffset(_offset + MAX_BYTE_LENGTH - 1); + + u256 len = fromBigEndian(data.getCroppedData(_offset, MAX_BYTE_LENGTH)); + validOffset(_offset + MAX_BYTE_LENGTH + (std::size_t)len - 1); + auto result = data.getCroppedData(_offset + MAX_BYTE_LENGTH, static_cast(len)); + _out.assign((const char*)result.data(), result.size()); +} + +void ContractABICodec::deserialize(bytes& _out, std::size_t _offset) +{ + validOffset(_offset + MAX_BYTE_LENGTH - 1); + + u256 len = fromBigEndian(data.getCroppedData(_offset, MAX_BYTE_LENGTH)); + validOffset(_offset + MAX_BYTE_LENGTH + (std::size_t)len - 1); + _out = data.getCroppedData(_offset + MAX_BYTE_LENGTH, static_cast(len)).toBytes(); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractABICodec.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractABICodec.h" new file mode 100644 index 00000000..de366986 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractABICodec.h" @@ -0,0 +1,651 @@ +/* + * Copyright (C) 2021 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. + * + * @brief Contract ABI serialize and deserialize tool. + * @author: octopuswang + * @date: 2019-04-01 + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace codec +{ +namespace abi +{ +// check if T type of uint256, int256, bool, string, bytes32 +template +struct ABIElementType : std::false_type +{ +}; + +// string +template <> +struct ABIElementType : std::true_type +{ +}; + +template <> +struct ABIElementType : std::true_type +{ +}; + +template <> +struct ABIElementType : std::true_type +{ +}; + +// uint256 +template <> +struct ABIElementType : std::true_type +{ +}; + +// int256 +template <> +struct ABIElementType : std::true_type +{ +}; + +// bool +template <> +struct ABIElementType : std::true_type +{ +}; + +// byte32 +template <> +struct ABIElementType : std::true_type +{ +}; + +template +struct ABIElementType> +{ + static bool constexpr value = ABIElementType::value && ABIElementType::value; +}; + +template +struct ABIElementType> +{ + static bool constexpr value = ABIElementType::value; +}; + +template +struct ABIElementType> +{ + static bool constexpr value = + ABIElementType::value && ABIElementType>::value; +}; + +template +struct ABIElementType> +{ + static bool constexpr value = ABIElementType::value; +}; + +// check if T type of string +template +struct ABIStringType : std::false_type +{ +}; + +template <> +struct ABIStringType : std::true_type +{ +}; + +// check if type of static array +template +struct ABIStaticArray : std::false_type +{ +}; + +// stringN type => bytesN +template +struct ABIStaticArray> : std::false_type +{ +}; + +// a fixed-length array of N elements of type T. +template +struct ABIStaticArray> : std::true_type +{ +}; + +// check if type of dynamic array +template +struct ABIDynamicArray : std::false_type +{ +}; + +// a fixed-length array of N elements of type T. +template +struct ABIDynamicArray> : std::true_type +{ +}; + +template +struct ABIDynamicTuple : std::false_type +{ +}; + +template +struct ABIDynamicTuple> : std::true_type +{ +}; + +template +struct ABIDynamicTuple> : std::true_type +{ +}; + +// Definition: The following types are called “dynamic”: +// bytes +// string +// T[] for any T +// T[k] for any dynamic T and any k >= 0 +// (T1,...,Tk) if Ti is dynamic for some 1 <= i <= k +template +struct ABIDynamicType : std::false_type +{ +}; + +template +struct remove_dimension +{ + typedef T type; +}; + +template +struct remove_dimension> +{ + typedef typename remove_dimension::type type; +}; + +template +struct ABIDynamicType::type>::value || + ABIDynamicArray::type>::value || + ABIDynamicTuple::type>::value>::type> + : std::true_type +{ +}; + +// fixed length of type, default 1 except static array type +template +struct Length +{ + enum + { + value = 1 + }; +}; + +// length of static array type +template +struct Length::value && !ABIDynamicType::value>::type> +{ + enum + { + value = std::tuple_size::value * Length::type>::value + }; +}; + +// static offset for types +template +struct Offset; + +template +struct Offset +{ + enum + { + value = Offset::value + Offset::value + }; +}; + +template <> +struct Offset<> +{ + enum + { + value = 0 + }; +}; + +template +struct Offset +{ + enum + { + value = Length::value + }; +}; + +/** + * @brief Class for Solidity ABI + * @by octopuswang + * + * Class for serialise and deserialize c++ object in Solidity ABI format. + * @ref https://solidity.readthedocs.io/en/develop/abi-spec.html + */ +class ContractABICodec +{ +public: + explicit ContractABICodec(bcos::crypto::Hash::Ptr _hashImpl) : m_hashImpl(_hashImpl) {} + + template + bytes serialise(const T& _t) + { // unsupported type + (void)_t; + static_assert(ABIElementType::value, "ABI not support type."); + return bytes{}; + } + // unsigned integer type int. + bytes serialise(const int& _in); + + // unsigned integer type uint256. + bytes serialise(const u256& _in); + + // two’s complement signed integer type int256. + bytes serialise(const s256& _in); + + // equivalent to uint8 restricted to the values 0 and 1. For computing the function selector, + // bool is used + bytes serialise(const bool& _in); + + // equivalent to uint160, except for the assumed interpretation and language typing. For + // computing the function selector, address is used. + // bool is used. + bytes serialise(const Address& _in); + + // binary type of 32 bytes + bytes serialise(const string32& _in); + + bytes serialise(const bytes& _in); + + // dynamic sized unicode string assumed to be UTF-8 encoded. + bytes serialise(const std::string& _in); + + // static array + template + bytes serialise(const std::array& _in); + // dynamic array + template + bytes serialise(const std::vector& _in); + + // dynamic tuple + template + bytes serialise(const std::tuple& _in); + + template + void deserialize(const T& _t, std::size_t _offset) + { // unsupported type + (void)_t; + (void)_offset; + static_assert(ABIElementType::value, "ABI not support type."); + } + + void deserialize(s256& out, std::size_t _offset); + + void deserialize(u256& _out, std::size_t _offset); + + void deserialize(bool& _out, std::size_t _offset); + + void deserialize(Address& _out, std::size_t _offset); + + void deserialize(string32& _out, std::size_t _offset); + + void deserialize(std::string& _out, std::size_t _offset); + void deserialize(bytes& _out, std::size_t _offset); + + // static array + template + void deserialize(std::array& _out, std::size_t _offset); + // dynamic array + template + void deserialize(std::vector& _out, std::size_t _offset); + + template + void deserialize(std::tuple& out, std::size_t _offset); + +private: + bcos::crypto::Hash::Ptr m_hashImpl; + static const int MAX_BYTE_LENGTH = 32; + // encode or decode offset + std::size_t offset{0}; + // encode temp bytes + bytes fixed; + bytes dynamic; + + // decode data + bytesConstRef data; + +private: + size_t getOffset() { return offset; } + // check if offset valid and std::length_error will be throw + void validOffset(std::size_t _offset) + { + if (_offset >= data.size()) + { + std::stringstream ss; + ss << " deserialize failed, invalid offset , offset is " << _offset << " , length is " + << data.size() << " , data is " << *toHexString(data); + + throw std::length_error(ss.str().c_str()); + } + } + + template + std::string toString(const T& _t) + { + std::stringstream ss; + ss << _t; + return ss.str(); + } + + inline void abiInAux() { return; } + + template + void abiInAux(T const& _t, U const&... _u) + { + bytes out = serialise(_t); + + if (ABIDynamicType::value) + { // dynamic type + dynamic += out; + fixed += serialise((u256)offset); + offset += out.size(); + } + else + { // static type + fixed += out; + } + + abiInAux(_u...); + } + + void abiOutAux() { return; } + + template + void abiOutAux(T& _t, U&... _u) + { + std::size_t _offset = offset; + // dynamic type, offset position + if (ABIDynamicType::value) + { + u256 dynamicOffset; + deserialize(dynamicOffset, offset); + _offset = static_cast(dynamicOffset); + } + + deserialize(_t, _offset); + // update offset + offset = offset + Offset::value * MAX_BYTE_LENGTH; + // decode next element + abiOutAux(_u...); + } + + template + void traverseTuple(std::tuple& tuple, F func, std::index_sequence) + { + return (void(func(std::get(tuple))), ...); + } + + template + void traverseTuple(std::tuple& tuple, F func) + { + traverseTuple(tuple, func, std::make_index_sequence()); + } + +public: + template + bool abiOut(bytesConstRef _data, T&... _t) + { + data = _data; + offset = 0; + try + { + abiOutAux(_t...); + return true; + } + catch (...) + { // error occur + return false; + } + } + + template + bool abiOutHex(const std::string& _data, T&... _t) + { + auto dataFromHex = *fromHexString(_data); + return abiOut(bytesConstRef(&dataFromHex), _t...); + } + + bool abiOutByFuncSelector(bytesConstRef _data, const std::vector& _allTypes, + std::vector& _out); + + template + bytes abiIn(const std::string& _sig, T const&... _t) + { + offset = Offset::value * MAX_BYTE_LENGTH; + fixed.clear(); + dynamic.clear(); + + abiInAux(_t...); + + return _sig.empty() ? + fixed + dynamic : + m_hashImpl->hash(_sig).ref().getCroppedData(0, 4).toBytes() + fixed + dynamic; + } + + template + std::string abiInHex(const std::string& _sig, T const&... _t) + { + return *toHexString(abiIn(_sig, _t...)); + } +}; + +// a fixed-length array of elements of the given type. +template +bytes ContractABICodec::serialise(const std::array& _in) +{ + bytes offset_bytes; + bytes content; + + auto length = N * MAX_BYTE_LENGTH; + + for (const auto& e : _in) + { + bytes out = serialise(e); + content += out; + if (ABIDynamicType::value) + { // dynamic + offset_bytes += serialise(static_cast(length)); + length += out.size(); + } + } + + return offset_bytes + content; +} + +// a variable-length array of elements of the given type. +template +bytes ContractABICodec::serialise(const std::vector& _in) +{ + bytes offset_bytes; + bytes content; + + auto length = _in.size() * MAX_BYTE_LENGTH; + + offset_bytes += serialise(static_cast(_in.size())); + for (const auto& t : _in) + { + bytes out = serialise(t); + content += out; + if (ABIDynamicType::value) + { // dynamic + offset_bytes += serialise(static_cast(length)); + length += out.size(); + } + } + + return offset_bytes + content; +} + +template +bytes ContractABICodec::serialise(const std::tuple& _in) +{ + bytes offsetBytes; + bytes dynamicContent; + auto tupleSize = std::tuple_size::type>::value; + auto length = tupleSize * MAX_BYTE_LENGTH; + + traverseTuple(const_cast&>(_in), [&](auto& _tupleItem) { + bytes out = serialise(_tupleItem); + + if (ABIDynamicType::type>::type>::value) + { + // dynamic + dynamicContent += out; + offsetBytes += serialise(static_cast(length)); + length += out.size(); + } + else + { + // static + offsetBytes += out; + } + }); + return offsetBytes + dynamicContent; +} + +template +void ContractABICodec::deserialize(std::array& _out, std::size_t _offset) +{ + for (std::size_t u = 0; u < N; ++u) + { + auto thisOffset = _offset; + + if (ABIDynamicType::value) + { // dynamic type + // N element offset + u256 length; + deserialize(length, _offset + u * Offset::value * MAX_BYTE_LENGTH); + thisOffset = thisOffset + static_cast(length); + } + else + { + thisOffset = _offset + u * Offset::value * MAX_BYTE_LENGTH; + } + deserialize(_out[u], thisOffset); + } +} + +template +void ContractABICodec::deserialize(std::vector& _out, std::size_t _offset) +{ + u256 length; + // vector length + deserialize(length, _offset); + _offset += MAX_BYTE_LENGTH; + _out.resize(static_cast(length)); + + for (std::size_t u = 0; u < static_cast(length); ++u) + { + std::size_t thisOffset = _offset; + + if (ABIDynamicType::value) + { // dynamic type + // N element offset + u256 thisEleOffset; + deserialize(thisEleOffset, _offset + u * Offset::value * MAX_BYTE_LENGTH); + thisOffset += static_cast(thisEleOffset); + } + else + { + thisOffset = _offset + u * Offset::value * MAX_BYTE_LENGTH; + } + deserialize(_out[u], thisOffset); + } +} + +template +void ContractABICodec::deserialize(std::tuple& _out, std::size_t _offset) +{ + std::size_t localOffset = _offset; + std::size_t tupleOffset = 0; + traverseTuple(_out, [&](auto& _tupleItem) { + if (ABIDynamicType::type>::type>::value) + { + // dynamic + u256 dynamicOffset; + deserialize(dynamicOffset, _offset + tupleOffset); + localOffset = _offset + static_cast(dynamicOffset); + deserialize(_tupleItem, localOffset); + } + else + { + // static + deserialize(_tupleItem, _offset + tupleOffset); + } + tupleOffset += + Offset::type>::type>::value * + MAX_BYTE_LENGTH; + }); +} +} // namespace abi + +inline string32 toString32(std::string const& _s) +{ + string32 ret; + for (unsigned i = 0; i < 32; ++i) + ret[i] = i < _s.size() ? _s[i] : 0; + return ret; +} + +inline string32 toString32(bcos::h256 const& _hashData) +{ + string32 ret; + for (unsigned i = 0; i < 32; i++) + { + ret[i] = _hashData[i]; + } + return ret; +} + +inline bcos::h256 fromString32(string32 const& _str) +{ + bcos::h256 hashData; + for (unsigned i = 0; i < 32; i++) + { + hashData[i] = _str[i]; + } + return hashData; +} +} // namespace codec +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractABIType.cpp" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractABIType.cpp" new file mode 100644 index 00000000..c25d2626 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractABIType.cpp" @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2021 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. + * + * @brief Contract ABI function signature parser tool. + * @author: octopuswang + * @date: 2019-04-01 + */ + +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::codec; +using namespace bcos::codec::abi; + +// uint: unsigned integer type of M bits, 0 < M <= 256, M % 8 == 0. e.g. uint32, uint8, +// uint256. +static const std::set setUint{"uint", "uint8", "uint16", "uint24", "uint32", "uint40", + "uint48", "uint56", "uint64", "uint72", "uint80", "uint88", "uint96", "uint104", "uint112", + "uint120", "uint128", "uint136", "uint144", "uint152", "uint160", "uint168", "uint176", + "uint184", "uint192", "uint200", "uint208", "uint216", "uint224", "uint232", "uint240", + "uint248", "uint256"}; + +// int: two’s complement signed integer type of M bits, 0 < M <= 256, M % 8 == 0. +static const std::set setInt{"int", "int8", "int16", "int24", "int32", "int40", + "int48", "int56", "int64", "int72", "int80", "int88", "int96", "int104", "int112", "int120", + "int128", "int136", "int144", "int152", "int160", "int168", "int176", "int184", "int192", + "int200", "int208", "int216", "int224", "int232", "int240", "int248", "int256"}; + +// bytes: binary type of M bytes, 0 < M <= 32. +static const std::set setByteN{"bytes1", "bytes2", "bytes3", "bytes4", "bytes5", + "bytes6", "bytes7", "bytes8", "bytes9", "bytes10", "bytes11", "bytes12", "bytes13", "bytes14", + "bytes15", "bytes16", "bytes17", "bytes18", "bytes19", "bytes20", "bytes21", "bytes22", + "bytes23", "bytes24", "bytes25", "bytes26", "bytes27", "bytes28", "bytes29", "bytes30", + "bytes31", "bytes32"}; + +// bool: equivalent to uint8 restricted to the values 0 and 1. For computing the function +// selector, bool is used. +const std::string strBool = "bool"; +// bytes: dynamic sized byte sequence. +const std::string strBytes = "bytes"; +// bytes: dynamic sized byte sequence. +const std::string strString = "string"; +// address: equivalent to uint160, except for the assumed interpretation and language typing. +// For computing the function selector, address is used. +const std::string strAddr = "address"; + +// Remove the white space characters on both sides +static void trim(std::string& _str) +{ + _str.erase(0, _str.find_first_not_of(" ")); + _str.erase(_str.find_last_not_of(" ") + 1); +} + +ABI_ELEMENTARY_TYPE ABIInType::getEnumType(const std::string& _strType) +{ + auto type = ABI_ELEMENTARY_TYPE::INVALID; + if (_strType == strBool) + { + type = ABI_ELEMENTARY_TYPE::BOOL; + } + else if (_strType == strAddr) + { + type = ABI_ELEMENTARY_TYPE::ADDR; + } + else if (_strType == strString) + { + type = ABI_ELEMENTARY_TYPE::STRING; + } + else if (_strType == strBytes) + { + type = ABI_ELEMENTARY_TYPE::BYTES; + } + else if (setUint.find(_strType) != setUint.end()) + { + type = ABI_ELEMENTARY_TYPE::UINT; + } + else if (setInt.find(_strType) != setInt.end()) + { + type = ABI_ELEMENTARY_TYPE::INT; + } + else if (setByteN.find(_strType) != setByteN.end()) + { + type = ABI_ELEMENTARY_TYPE::BYTESN; + } + + return type; +} + +void ABIInType::clear() +{ + aet = ABI_ELEMENTARY_TYPE::INVALID; + strEleType.clear(); + strType.clear(); + extents.clear(); +} + +bool ABIInType::reset(const std::string& _s) +{ + clear(); + + std::string strType = _s; + // eg: int[1][2][][3] + // trim blank character + trim(strType); + auto firstLeftBracket = strType.find('['); + // int + std::string strEleType = strType.substr(0, firstLeftBracket); + trim(strEleType); + auto t = getEnumType(strEleType); + // invalid solidity abi string + if (t == ABI_ELEMENTARY_TYPE::INVALID) + { + return false; + } + + // eg : [10][2][3][] + std::vector r; + std::string::size_type leftBracket = firstLeftBracket; + std::string::size_type rigthBracket = 0; + std::string::size_type length = strType.size(); + + while (leftBracket < length) + { + auto leftBracketBak = leftBracket; + leftBracket = strType.find('[', leftBracketBak); + rigthBracket = strType.find(']', leftBracketBak); + + if (leftBracket == std::string::npos || rigthBracket == std::string::npos || + leftBracket >= rigthBracket) + { + // invalid format + return false; + } + + std::string digit = strType.substr(leftBracket + 1, rigthBracket - leftBracket - 1); + trim(digit); + bool ok = + std::all_of(digit.begin(), digit.end(), [](char c) { return c >= '0' && c <= '9'; }); + if (!ok) + { + // invalid format + return false; + } + + if (digit.empty()) + { + r.push_back(0); + } + else + { + std::size_t size = strtoul(digit.c_str(), NULL, 10); + r.push_back(size); + } + + leftBracket = rigthBracket + 1; + } + + this->aet = t; + this->extents = r; + this->strType = strType; + this->strEleType = strEleType; + + return true; +} + +bool ABIInType::dynamic() +{ + // string or bytes + if (aet == ABI_ELEMENTARY_TYPE::STRING || aet == ABI_ELEMENTARY_TYPE::BYTES) + { + return true; + } + + // dynamic array + auto length = rank(); + for (std::size_t i = 0; i < length; ++i) + { + if (extent(i + 1) == 0) + { + return true; + } + } + + return false; +} + +// +bool ABIInType::removeExtent() +{ + auto length = rank(); + if (length > 0) + { + extents.resize(length - 1); + return true; + } + + return false; +} + +std::vector ABIFunc::getParamsType() const +{ + std::vector r; + for (auto it = allParamsType.begin(); it != allParamsType.end(); ++it) + { + r.push_back(it->getType()); + } + + return r; +} + +// parser contract abi function signature, eg: transfer(string,string,uint256) +bool ABIFunc::parser(const std::string& _sig) +{ + auto i0 = _sig.find("("); + auto i1 = _sig.find(")"); + // transfer(string,string,uint256) + if ((i0 == std::string::npos) || (i1 == std::string::npos) || (i1 <= i0)) + { + return false; + } + + // function name , eg: transfer + std::string strFuncName = _sig.substr(0, i0); + trim(strFuncName); + // parameters "string,string,uint256" + std::string strTypes = _sig.substr(i0 + 1, i1 - i0 - 1); + + std::string sig = strFuncName + "("; + + std::vector types; + boost::split(types, strTypes, boost::is_any_of(",")); + ABIInType at; + for (std::string& type : types) + { + trim(type); + if (!type.empty()) + { + sig += type; + sig += ","; + auto ok = at.reset(type); + if (!ok) + { + // invalid format + return false; + } + allParamsType.push_back(at); + continue; + } + } + + if (',' == sig.back()) + { + sig.back() = ')'; + } + else + { + sig += ")"; + } + + // set function name + this->strFuncName = strFuncName; + // set function sigature + this->strFuncSignature = sig; + + return true; +} diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractABIType.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractABIType.h" new file mode 100644 index 00000000..4a2c289d --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractABIType.h" @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2021 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. + * + * @brief Contract ABI function signature parser tool. + * @author: octopuswang + * @date: 2019-04-01 + */ + +#pragma once +#include +#include +#include + +namespace bcos +{ +namespace codec +{ +namespace abi +{ +enum class ABI_ELEMENTARY_TYPE +{ // solidity ABI elementary types + INVALID, // invalid + BOOL, // bool + INT, // int8 ~ int256 + UINT, // uint8 ~ uint256 + BYTESN, // bytesN + ADDR, // address + BYTES, // bytes + STRING, // string + FIXED, // fixed, unsupported + UNFIXED // unfixed, unsupported +}; + +class ABIInType +{ +public: + ABIInType() = default; + + bool reset(const std::string& _str); + void clear(); + +public: + // the number of dimensions of T or zero + std::size_t rank() { return extents.size(); } + // obtains the size of an array type along a specified dimension + std::size_t extent(std::size_t index) { return index > rank() ? 0 : extents[index - 1]; } + bool removeExtent(); + bool dynamic(); + bool valid() { return aet != ABI_ELEMENTARY_TYPE::INVALID; } + std::string getType() const { return strType; } + std::string getEleType() const { return strEleType; } + + // get abi elementary type by string + ABI_ELEMENTARY_TYPE getEnumType(const std::string& _strType); + +private: + ABI_ELEMENTARY_TYPE aet{ABI_ELEMENTARY_TYPE::INVALID}; + std::string strEleType; + std::string strType; + std::vector extents; +}; + +using ABIOutType = ABIInType; + +class ABIFunc +{ +private: + std::string strFuncName; + std::string strFuncSignature; + std::vector allParamsType; + +public: + // parser contract abi function signature, eg: transfer(string,string,uint256) + bool parser(const std::string& _sig); + +public: + std::vector getParamsType() const; + inline std::string getSignature() const { return strFuncSignature; } + inline std::string getFuncName() const { return strFuncName; } +}; + +} // namespace abi +} // namespace codec +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractEventTopic.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractEventTopic.h" new file mode 100644 index 00000000..87aa4650 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/utilities/abi/ContractEventTopic.h" @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2021 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 ContractEventTopic.h + * @author: octopus + * @date 2021-09-01 + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#define CONTRACT_EVENT_TOPIC_LENGTH (64) + +namespace bcos +{ +namespace codec +{ +namespace abi +{ +class ContractEventTopic +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + +public: + explicit ContractEventTopic(bcos::crypto::Hash::Ptr _hashImpl) + : m_hashImpl(_hashImpl), m_contractABICodec(_hashImpl) + {} + +public: + bcos::crypto::Hash::Ptr hashImpl() const { return m_hashImpl; } + +public: + /** + * @brief + * + * @param _topic + * @return true + * @return false + */ + static bool validEventTopic(const std::string& _topic) + { + if ((_topic.compare(0, 2, "0x") == 0) || (_topic.compare(0, 2, "0X") == 0)) + { + return _topic.length() == (CONTRACT_EVENT_TOPIC_LENGTH + 2); + } + + return _topic.length() == CONTRACT_EVENT_TOPIC_LENGTH; + } + +public: + // u256 => topic + std::string u256ToTopic(bcos::u256 _u) + { + auto data = m_contractABICodec.serialise(_u); + return toHexStringWithPrefix(data); + } + + // s256 => topic + std::string i256ToTopic(bcos::s256 _i) + { + auto data = m_contractABICodec.serialise(_i); + return toHexStringWithPrefix(data); + } + + // string => topic + std::string stringToTopic(const std::string& _str) + { + auto hashValue = m_hashImpl->hash(_str); + return hashValue.hexPrefixed(); + } + + // bytes => topic + std::string bytesToTopic(const bcos::bytes& _bs) + { + auto hashValue = m_hashImpl->hash(_bs); + return hashValue.hexPrefixed(); + } + + // bytesN(eg:bytes32) => topic + std::string bytesNToTopic(const bcos::bytes& _bsn) + { + if (_bsn.size() > 32) + { + BOOST_THROW_EXCEPTION(bcos::InvalidParameter() + << bcos::errinfo_comment("byteN can't be more than 32 byte.")); + } + + auto temp = _bsn; + temp.resize(32); + + return toHexStringWithPrefix(temp); + } + +private: + bcos::crypto::Hash::Ptr m_hashImpl; + codec::abi::ContractABICodec m_contractABICodec; +}; + +} // namespace abi +} // namespace codec +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/BlockNumberInfo.cpp" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/BlockNumberInfo.cpp" new file mode 100644 index 00000000..e0234f87 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/BlockNumberInfo.cpp" @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2021 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 BlockNumberInfo.cpp + * @author: octopus + * @date 2021-10-04 + */ + +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::cppsdk::service; + +std::string BlockNumberInfo::toJson() +{ + // eg: {"blockNumber": 11, "group": "group", "nodeName": "node"} + + Json::Value jValue; + jValue["group"] = m_group; + jValue["blockNumber"] = m_blockNumber; + jValue["nodeName"] = m_blockNumber; + + Json::FastWriter writer; + std::string s = writer.write(jValue); + return s; +} + +bool BlockNumberInfo::fromJson(const std::string& _json) +{ + std::string errorMessage; + try + { + std::string group; + std::string node; + int64_t blockNumber = -1; + do + { + Json::Value root; + Json::Reader jsonReader; + if (!jsonReader.parse(_json, root)) + { + errorMessage = "invalid json object"; + break; + } + + if (!root.isMember("blockNumber")) + { + errorMessage = "request has no blockNumber field"; + break; + } + blockNumber = root["blockNumber"].asInt64(); + + if (!root.isMember("group")) + { + errorMessage = "request has no group field"; + break; + } + group = root["group"].asString(); + + if (!root.isMember("nodeName")) + { + errorMessage = "request has no nodeName field"; + break; + } + node = root["nodeName"].asString(); + + m_blockNumber = blockNumber; + m_group = group; + m_node = node; + + RPC_BLOCKNUM_LOG(INFO) + << LOG_BADGE("fromJson") << LOG_KV("group", m_group) << LOG_KV("node", m_node) + << LOG_KV("blockNumber", m_blockNumber); + + return true; + + } while (0); + + RPC_BLOCKNUM_LOG(WARNING) << LOG_BADGE("fromJson") << LOG_DESC("Invalid JSON") + << LOG_KV("errorMessage", errorMessage); + } + catch (const std::exception& e) + { + RPC_BLOCKNUM_LOG(WARNING) << LOG_BADGE("fromJson") << LOG_DESC("Invalid JSON") + << LOG_KV("error", boost::diagnostic_information(e)); + } + + return false; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/BlockNumberInfo.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/BlockNumberInfo.h" new file mode 100644 index 00000000..4d3d6f21 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/BlockNumberInfo.h" @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 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 BlockNumberInfo.h + * @author: octopus + * @date 2021-10-04 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace cppsdk +{ +namespace service +{ +class BlockNumberInfo +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + +public: + std::string group() const { return m_group; } + void setGroup(const std::string& _group) { m_group = _group; } + std::string node() const { return m_node; } + void setNode(const std::string& _node) { m_node = _node; } + int64_t blockNumber() const { return m_blockNumber; } + void setBlockNumber(int64_t _blockNumber) { m_blockNumber = _blockNumber; } + +public: + std::string toJson(); + bool fromJson(const std::string& _json); + +private: + std::string m_group; + std::string m_node; + int64_t m_blockNumber; +}; + +} // namespace service +} // namespace cppsdk +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/Common.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/Common.h" new file mode 100644 index 00000000..9a06e48c --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/Common.h" @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2021 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: octopus + * @date 2021-08-10 + */ + +#pragma once +#include + +#define RPC_WS_LOG(LEVEL) BCOS_LOG(LEVEL) << "[RPCWS][SERVICE]" +#define RPC_BLOCKNUM_LOG(LEVEL) BCOS_LOG(LEVEL) << "[RPC][BLOCK][NUMBER]" \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/HandshakeResponse.cpp" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/HandshakeResponse.cpp" new file mode 100644 index 00000000..ca9b2ef5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/HandshakeResponse.cpp" @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2021 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 HandshakeResponse.h + * @author: octopus + * @date 2021-10-26 + */ + +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::cppsdk::service; + +bool HandshakeResponse::decode(std::string const& _data) +{ + try + { + Json::Value root; + Json::Reader reader; + if (!reader.parse(_data, root)) + { + RPC_WS_LOG(WARNING) << LOG_BADGE("HandshakeResponse decode: invalid json object") + << LOG_KV("data", _data); + return false; + } + + if (!root.isMember("protocolVersion")) + { + // id field not exist + RPC_WS_LOG(WARNING) << LOG_BADGE( + "HandshakeResponse decode: invalid for empty " + "protocolVersion field") + << LOG_KV("data", _data); + return false; + } + // set protocolVersion + m_protocolVersion = root["protocolVersion"].asInt(); + + if (root.isMember("groupInfoList") && root["groupInfoList"].isArray()) + { + auto& jGroupInfoList = root["groupInfoList"]; + for (Json::ArrayIndex i = 0; i < jGroupInfoList.size(); ++i) + { + Json::FastWriter writer; + std::string str = writer.write(jGroupInfoList[i]); + auto groupInfo = m_groupInfoCodec->deserialize(str); + + RPC_WS_LOG(INFO) << LOG_BADGE("fromJson") << LOG_DESC(" new group info") + << LOG_KV("groupInfo", printGroupInfo(groupInfo)); + + m_groupInfoList.push_back(groupInfo); + } + } + + // "groupBlockNumber": [{"group0": 1}, {"group1": 2}, {"group2": 3}] + if (root.isMember("groupBlockNumber") && root["groupBlockNumber"].isArray()) + { + for (Json::ArrayIndex i = 0; i < root["groupBlockNumber"].size(); ++i) + { + Json::Value jGroupBlockNumber = root["groupBlockNumber"][i]; + for (auto const& groupID : jGroupBlockNumber.getMemberNames()) + { + int64_t blockNumber = jGroupBlockNumber[groupID].asInt64(); + + m_groupBlockNumber[groupID] = blockNumber; + } + } + } + + RPC_WS_LOG(INFO) << LOG_BADGE("fromJson") << LOG_DESC("parser protocol version") + << LOG_KV("protocolVersion", m_protocolVersion) + << LOG_KV("groupInfoList size", m_groupInfoList.size()) + << LOG_KV("groupBlockNumber size", m_groupBlockNumber.size()); + + return true; + } + catch (const std::exception& e) + { + RPC_WS_LOG(WARNING) << LOG_BADGE("fromJson") + << LOG_DESC("invalid protocol version json string") + << LOG_KV("data", _data) + << LOG_KV("exception", boost::diagnostic_information(e)); + } + return false; +} + +void HandshakeResponse::encode(std::string& _encodedData) const +{ + Json::Value encodedJson; + + encodedJson["protocolVersion"] = m_protocolVersion; + encodedJson["groupInfoList"] = Json::Value(Json::arrayValue); + for (const auto& groupInfo : m_groupInfoList) + { + auto groupInfoResponse = m_groupInfoCodec->serialize(groupInfo); + encodedJson["groupInfoList"].append(groupInfoResponse); + } + + // Encode group block number array + Json::Value groupBlockNumberArray(Json::arrayValue); + for (auto const& groupBlockNumber : m_groupBlockNumber) + { + Json::Value group0(Json::objectValue); + group0[groupBlockNumber.first] = groupBlockNumber.second; + groupBlockNumberArray.append(std::move(group0)); + } + + encodedJson["groupBlockNumber"] = std::move(groupBlockNumberArray); + + Json::FastWriter writer; + _encodedData = writer.write(encodedJson); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/HandshakeResponse.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/HandshakeResponse.h" new file mode 100644 index 00000000..6c37b0b2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/HandshakeResponse.h" @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2021 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 HandshakeResponse.h + * @author: octopus + * @date 2021-10-26 + */ + +#pragma once +#include +#include +#include +#include + + +namespace bcos +{ +namespace cppsdk +{ +namespace service +{ +class HandshakeResponse +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + HandshakeResponse(bcos::group::GroupInfoCodec::Ptr _groupInfoCodec) + : m_groupInfoCodec(_groupInfoCodec) + {} + virtual ~HandshakeResponse() {} + + virtual bool decode(std::string const& _data); + virtual void encode(std::string& _encodedData) const; + + int protocolVersion() const { return m_protocolVersion; } + const std::vector& groupInfoList() const + { + return m_groupInfoList; + } + const std::unordered_map& groupBlockNumber() const + { + return m_groupBlockNumber; + } + std::unordered_map& mutableGroupBlockNumber() + { + return m_groupBlockNumber; + } + + void setProtocolVersion(int _protocolVersion) { m_protocolVersion = _protocolVersion; } + void setGroupInfoList(const std::vector& _groupInfoList) + { + m_groupInfoList = _groupInfoList; + } + +private: + std::vector m_groupInfoList; + std::unordered_map m_groupBlockNumber; + bcos::group::GroupInfoCodec::Ptr m_groupInfoCodec; + // Note: the nodes determine the protocol version + uint32_t m_protocolVersion; + bcos::protocol::ProtocolInfo::Ptr m_localProtocol; +}; + +} // namespace service +} // namespace cppsdk +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/Service.cpp" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/Service.cpp" new file mode 100644 index 00000000..d5880f21 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/Service.cpp" @@ -0,0 +1,783 @@ +/* + * Copyright (C) 2021 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 Service.cpp + * @author: octopus + * @date 2021-10-22 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::cppsdk::service; +using namespace bcos::boostssl; +using namespace bcos::boostssl::ws; +using namespace bcos; + +static const int32_t BLOCK_LIMIT_RANGE = 500; + +Service::Service(bcos::group::GroupInfoCodec::Ptr _groupInfoCodec, + bcos::group::GroupInfoFactory::Ptr _groupInfoFactory, std::string _moduleName) + : WsService(_moduleName), m_groupInfoCodec(_groupInfoCodec), m_groupInfoFactory(_groupInfoFactory) +{ + m_localProtocol = g_BCOSConfig.protocolInfo(bcos::protocol::ProtocolModuleID::RpcService); + RPC_WS_LOG(INFO) << LOG_DESC("init the local protocol") + << LOG_KV("minVersion", m_localProtocol->minVersion()) + << LOG_KV("maxVersion", m_localProtocol->maxVersion()) + << LOG_KV("module", m_localProtocol->protocolModuleID()); +} + +void Service::start() +{ + bcos::boostssl::ws::WsService::start(); + + waitForConnectionEstablish(); +} + +void Service::stop() +{ + bcos::boostssl::ws::WsService::stop(); +} + +void Service::waitForConnectionEstablish() +{ + auto start = std::chrono::high_resolution_clock::now(); + auto end = start + std::chrono::milliseconds(waitConnectFinishTimeout()); + + while (true) + { + // sleep for connection establish + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + if (handshakeSucCount()) + { + RPC_WS_LOG(INFO) << LOG_BADGE("waitForConnectionEstablish") + << LOG_DESC("wait for websocket connection handshake success") + << LOG_KV("suc count", handshakeSucCount()); + break; + } + + if (std::chrono::high_resolution_clock::now() < end) + { + continue; + } + else + { + stop(); + RPC_WS_LOG(WARNING) << LOG_BADGE("waitForConnectionEstablish") + << LOG_DESC("wait for websocket connection handshake timeout") + << LOG_KV("timeout", waitConnectFinishTimeout()); + + BOOST_THROW_EXCEPTION(std::runtime_error("The websocket connection handshake timeout")); + return; + } + } +} + +void Service::onConnect(Error::Ptr _error, std::shared_ptr _session) +{ + bcos::boostssl::ws::WsService::onConnect(_error, _session); + + startHandshake(_session); +} + +void Service::onDisconnect(Error::Ptr _error, std::shared_ptr _session) +{ + bcos::boostssl::ws::WsService::onDisconnect(_error, _session); + + std::string endPoint = _session ? _session->endPoint() : std::string(); + if (!endPoint.empty()) + { + clearGroupInfoByEp(endPoint); + } +} + +void Service::onRecvMessage(std::shared_ptr _msg, std::shared_ptr _session) +{ + auto seq = _msg->seq(); + if (!checkHandshakeDone(_session)) + { + // Note: The message is received before the handshake with the node is complete + RPC_WS_LOG(WARNING) << LOG_BADGE("onRecvMessage") + << LOG_DESC( + "websocket service unable to handler message before handshake" + "with the node successfully") + << LOG_KV("endpoint", _session ? _session->endPoint() : std::string("")) + << LOG_KV("seq", seq); + + _session->drop(bcos::boostssl::ws::WsError::UserDisconnect); + return; + } + + bcos::boostssl::ws::WsService::onRecvMessage(_msg, _session); +} + +// ---------------------overide end --------------------------------------------------------------- + +// ---------------------send message begin--------------------------------------------------------- +void Service::asyncSendMessageByGroupAndNode(const std::string& _group, const std::string& _node, + std::shared_ptr _msg, bcos::boostssl::ws::Options _options, + bcos::boostssl::ws::RespCallBack _respFunc) +{ + std::set endPoints; + if (_group.empty()) + { + // asyncSendMessage(_msg, _options, _respFunc); + auto ss = sessions(); + for (const auto& session : ss) + { + if (session->isConnected()) + { + endPoints.insert(session->endPoint()); + } + } + + if (endPoints.empty()) + { + auto error = std::make_shared(WsError::EndPointNotExist, + "there has no connection available, maybe all connections disconnected"); + _respFunc(error, nullptr, nullptr); + return; + } + } + else if (_node.empty()) + { + // all connections available for the group + getEndPointsByGroup(_group, endPoints); + if (endPoints.empty()) + { + auto error = std::make_shared(WsError::EndPointNotExist, + "there has no connection available for the group, maybe all connections " + "disconnected or " + "the group does not exist, group: " + + _group); + _respFunc(error, nullptr, nullptr); + return; + } + } + else + { + // all connections available for the node + getEndPointsByGroupAndNode(_group, _node, endPoints); + if (endPoints.empty()) + { + auto error = std::make_shared(WsError::EndPointNotExist, + "there has no connection available for the node of the group, maybe all " + "connections " + "disconnected or the node does not exist, group: " + + _group + " ,node: " + _node); + _respFunc(error, nullptr, nullptr); + return; + } + } + + std::vector vecEndPoints(endPoints.begin(), endPoints.end()); + + auto seed = std::chrono::system_clock::now().time_since_epoch().count(); + std::default_random_engine e(seed); + std::shuffle(vecEndPoints.begin(), vecEndPoints.end(), e); + + asyncSendMessageByEndPoint(*vecEndPoints.begin(), _msg, _options, _respFunc); +} +// ---------------------send message end--------------------------------------------------------- + + +bool Service::checkHandshakeDone(std::shared_ptr _session) +{ + return _session && _session->version(); +} + +void Service::startHandshake(std::shared_ptr _session) +{ + auto message = messageFactory()->buildMessage(); + message->setSeq(messageFactory()->newSeq()); + message->setPacketType(bcos::protocol::MessageType::HANDESHAKE); + bcos::rpc::HandshakeRequest request(m_localProtocol); + auto requestData = request.encode(); + message->setPayload(requestData); + + RPC_WS_LOG(INFO) << LOG_BADGE("startHandshake") + << LOG_KV("endpoint", _session ? _session->endPoint() : std::string("")); + + auto session = _session; + auto service = std::dynamic_pointer_cast(shared_from_this()); + _session->asyncSendMessage(message, Options(m_wsHandshakeTimeout), + [message, session, service](Error::Ptr _error, std::shared_ptr _msg, + std::shared_ptr _session) { + if (_error && _error->errorCode() != 0) + { + RPC_WS_LOG(WARNING) + << LOG_BADGE("startHandshake") << LOG_DESC("callback response error") + << LOG_KV("endpoint", session ? session->endPoint() : std::string("")) + << LOG_KV("errorCode", _error ? _error->errorCode() : -1) + << LOG_KV("errorMessage", _error ? _error->errorMessage() : std::string("")); + session->drop(bcos::boostssl::ws::WsError::UserDisconnect); + return; + } + + auto endPoint = session ? session->endPoint() : std::string(""); + auto response = std::string(_msg->payload()->begin(), _msg->payload()->end()); + auto handshakeResponse = std::make_shared(service->m_groupInfoCodec); + if (!handshakeResponse->decode(response)) + { + _session->drop(bcos::boostssl::ws::WsError::UserDisconnect); + + RPC_WS_LOG(WARNING) << LOG_BADGE("startHandshake") + << LOG_DESC("invalid protocol version json string") + << LOG_KV("endpoint", endPoint); + return; + } + + // set protocol version + session->setVersion(handshakeResponse->protocolVersion()); + auto groupInfoList = handshakeResponse->groupInfoList(); + for (auto& groupInfo : groupInfoList) + { + service->updateGroupInfoByEp(endPoint, groupInfo); + + RPC_WS_LOG(DEBUG) << LOG_BADGE("startHandshake") << LOG_DESC("group info") + << LOG_KV("endPoint", endPoint) + << LOG_KV("smCrypto", groupInfo->smCryptoType()) + << LOG_KV("wasm", groupInfo->wasm()); + } + + auto groupBlockNumber = handshakeResponse->groupBlockNumber(); + for (auto entry : groupBlockNumber) + { + service->updateGroupBlockNumber(entry.first, entry.second); + } + + service->increaseHandshakeSucCount(); + service->callWsHandshakeSucHandlers(_session); + + RPC_WS_LOG(INFO) << LOG_BADGE("startHandshake") << LOG_DESC("handshake successfully") + << LOG_KV("endPoint", endPoint) + << LOG_KV("handshake version", _session->version()) + << LOG_KV("groupInfoList size", groupInfoList.size()) + << LOG_KV("groupBlockNumber size", groupBlockNumber.size()) + << LOG_KV("handshake string", response); + }); +} + + +void Service::onNotifyGroupInfo( + const std::string& _groupInfoJson, std::shared_ptr _session) +{ + std::string endPoint = _session->endPoint(); + RPC_WS_LOG(TRACE) << LOG_BADGE("onNotifyGroupInfo") << LOG_KV("endPoint", endPoint) + << LOG_KV("groupInfoJson", _groupInfoJson); + + try + { + auto groupInfo = m_groupInfoCodec->deserialize(_groupInfoJson); + updateGroupInfoByEp(endPoint, groupInfo); + } + catch (const std::exception& e) + { + RPC_WS_LOG(WARNING) << LOG_BADGE("onNotifyGroupInfo") << LOG_KV("endPoint", endPoint) + << LOG_KV("e", boost::diagnostic_information(e)) + << LOG_KV("groupInfoJson", _groupInfoJson); + } +} + +void Service::onNotifyGroupInfo(std::shared_ptr _msg, + std::shared_ptr _session) +{ + std::string groupInfo = std::string(_msg->payload()->begin(), _msg->payload()->end()); + + RPC_WS_LOG(INFO) << LOG_BADGE("onNotifyGroupInfo") << LOG_KV("groupInfo", groupInfo); + + return onNotifyGroupInfo(groupInfo, _session); +} + +void Service::clearGroupInfoByEp(const std::string& _endPoint) +{ + RPC_WS_LOG(INFO) << LOG_BADGE("clearGroupInfoByEp") << LOG_KV("endPoint", _endPoint); + { + boost::unique_lock lock(x_endPointLock); + { + for (auto it = m_group2Node2Endpoints.begin(); it != m_group2Node2Endpoints.end();) + { + for (auto innerIt = it->second.begin(); innerIt != it->second.end();) + { + innerIt->second.erase(_endPoint); + + if (innerIt->second.empty()) + { + RPC_WS_LOG(INFO) + << LOG_BADGE("clearGroupInfoByEp") + << LOG_DESC("group2Node2Endpoints, clear node") + << LOG_KV("group", it->first) << LOG_KV("node", innerIt->first); + innerIt = it->second.erase(innerIt); + } + else + { + innerIt++; + } + } + + if (it->second.empty()) + { + RPC_WS_LOG(INFO) << LOG_BADGE("clearGroupInfoByEp") + << LOG_DESC("group2Node2Endpoints, clear group") + << LOG_KV("group", it->first); + it = m_group2Node2Endpoints.erase(it); + } + else + { + it++; + } + } + } + + { + auto it = m_endPoint2GroupId2GroupInfo.find(_endPoint); + if (it != m_endPoint2GroupId2GroupInfo.end()) + { + m_endPoint2GroupId2GroupInfo.erase(it); + + RPC_WS_LOG(INFO) << LOG_BADGE("clearGroupInfoByEp") << LOG_DESC("clear endPoint") + << LOG_KV("endPoint", _endPoint); + } + } + } + + // Note: for debug + printGroupInfo(); +} + +void Service::clearGroupInfoByEp(const std::string& _endPoint, const std::string& _groupID) +{ + RPC_WS_LOG(INFO) << LOG_BADGE("clearGroupInfoByEp") << LOG_KV("endPoint", _endPoint) + << LOG_KV("group", _groupID); + + { + boost::unique_lock lock(x_endPointLock); + auto it = m_group2Node2Endpoints.find(_groupID); + if (it == m_group2Node2Endpoints.end()) + { + return; + } + + auto& groupMapper = it->second; + for (auto innerIt = groupMapper.begin(); innerIt != groupMapper.end();) + { + innerIt->second.erase(_endPoint); + if (innerIt->second.empty()) + { + RPC_WS_LOG(INFO) << LOG_BADGE("clearGroupInfoByEp") << LOG_DESC("clear node") + << LOG_KV("group", it->first) << LOG_KV("endPoint", _endPoint) + << LOG_KV("node", innerIt->first); + innerIt = it->second.erase(innerIt); + } + else + { + innerIt++; + } + } + } + + // Note: for debug + // printGroupInfo(); +} + +void Service::updateGroupInfoByEp( + const std::string& _endPoint, bcos::group::GroupInfo::Ptr _groupInfo) +{ + RPC_WS_LOG(INFO) << LOG_BADGE("updateGroupInfoByEp") << LOG_KV("endPoint", _endPoint) + << LOG_KV("group", _groupInfo->groupID()) + << LOG_KV("chainID", _groupInfo->chainID()) + << LOG_KV("genesisConfig", _groupInfo->genesisConfig()) + << LOG_KV("iniConfig", _groupInfo->iniConfig()) + << LOG_KV("nodesNum", _groupInfo->nodesNum()); + const auto& group = _groupInfo->groupID(); + const auto& nodes = _groupInfo->nodeInfos(); + + { + updateGroupInfo(_endPoint, _groupInfo); + } + + { + // remove first + clearGroupInfoByEp(_endPoint, group); + } + + { + // update + boost::unique_lock lock(x_endPointLock); + auto& groupMapper = m_group2Node2Endpoints[group]; + for (const auto& node : nodes) + { + auto& nodeMapper = groupMapper[node.first]; + nodeMapper.insert(_endPoint); + } + } + + // Note: for debug + printGroupInfo(); +} + +bool Service::hasEndPointOfNodeAvailable(const std::string& _group, const std::string& _node) +{ + boost::shared_lock lock(x_endPointLock); + auto it = m_group2Node2Endpoints.find(_group); + if (it == m_group2Node2Endpoints.end()) + { + return false; + } + + auto& nodes = it->second; + + return nodes.find(_node) != nodes.end(); +} + +bool Service::getEndPointsByGroup(const std::string& _group, std::set& _endPoints) +{ + boost::shared_lock lock(x_endPointLock); + auto it = m_group2Node2Endpoints.find(_group); + if (it == m_group2Node2Endpoints.end()) + { + RPC_WS_LOG(WARNING) << LOG_BADGE("getEndPointsByGroup") << LOG_DESC("group not exist") + << LOG_KV("group", _group); + return false; + } + + for (auto& nodeMapper : it->second) + { + _endPoints.insert(nodeMapper.second.begin(), nodeMapper.second.end()); + } + + RPC_WS_LOG(TRACE) << LOG_BADGE("getEndPointsByGroup") << LOG_KV("group", _group) + << LOG_KV("endPoints", _endPoints.size()); + return true; +} + +bool Service::getEndPointsByGroupAndNode( + const std::string& _group, const std::string& _node, std::set& _endPoints) +{ + boost::shared_lock lock(x_endPointLock); + auto it = m_group2Node2Endpoints.find(_group); + if (it == m_group2Node2Endpoints.end()) + { + RPC_WS_LOG(WARNING) << LOG_BADGE("getEndPointsByGroupAndNode") + << LOG_DESC("group not exist") << LOG_KV("group", _group) + << LOG_KV("node", _node); + return false; + } + + auto innerIt = it->second.find(_node); + if (innerIt == it->second.end()) + { + RPC_WS_LOG(WARNING) << LOG_BADGE("getEndPointsByGroupAndNode") << LOG_DESC("node not exist") + << LOG_KV("group", _group) << LOG_KV("node", _node); + return false; + } + + _endPoints = innerIt->second; + + RPC_WS_LOG(TRACE) << LOG_BADGE("getEndPointsByGroupAndNode") << LOG_KV("group", _group) + << LOG_KV("node", _node) << LOG_KV("endPoints", _endPoints.size()); + return true; +} + +void Service::printGroupInfo() +{ + boost::shared_lock lock(x_endPointLock); + + RPC_WS_LOG(INFO) << LOG_BADGE("printGroupInfo") + << LOG_KV("total count", m_group2Node2Endpoints.size()); + + for (const auto& groupMapper : m_group2Node2Endpoints) + { + RPC_WS_LOG(INFO) << LOG_BADGE("printGroupInfo") << LOG_DESC("group list") + << LOG_KV("group", groupMapper.first) + << LOG_KV("count", groupMapper.second.size()); + for (const auto& nodeMapper : groupMapper.second) + { + RPC_WS_LOG(INFO) << LOG_BADGE("printGroupInfo") << LOG_DESC("node list") + << LOG_KV("group", groupMapper.first) + << LOG_KV("node", nodeMapper.first) + << LOG_KV("count", nodeMapper.second.size()); + } + } +} + +bcos::group::GroupInfo::Ptr Service::getGroupInfo(const std::string& _groupID) +{ + std::vector groupInfos; + { + boost::shared_lock lock(x_endPointLock); + for (const auto& group2GroupInfoMapper : m_endPoint2GroupId2GroupInfo) + { + auto& group2GroupInfo = group2GroupInfoMapper.second; + auto it = group2GroupInfo.find(_groupID); + if (it == group2GroupInfo.end()) + { + continue; + } + + groupInfos.push_back(it->second); + } + } + + if (groupInfos.empty()) + { + RPC_WS_LOG(INFO) << LOG_BADGE("getGroupInfo") << LOG_DESC("group not exist") + << LOG_KV("group", _groupID); + return nullptr; + } + + RPC_WS_LOG(INFO) << LOG_BADGE("getGroupInfo") << LOG_KV("count", groupInfos.size()); + + auto firstGroupInfo = *groupInfos.begin(); + auto groupInfo = + m_groupInfoFactory->createGroupInfo(firstGroupInfo->chainID(), firstGroupInfo->groupID()); + groupInfo->setSmCryptoType(firstGroupInfo->smCryptoType()); + groupInfo->setWasm(firstGroupInfo->wasm()); + groupInfo->setGenesisConfig(firstGroupInfo->genesisConfig()); + groupInfo->setIniConfig(firstGroupInfo->iniConfig()); + + for (const auto& g : groupInfos) + { + for (const auto& node : g->nodeInfos()) + { + if (groupInfo->nodeInfo(node.second->nodeName())) + { + continue; + } + + groupInfo->appendNodeInfo(node.second); + } + } + + return groupInfo; +} + +void Service::updateGroupInfo(const std::string& _endPoint, bcos::group::GroupInfo::Ptr _groupInfo) +{ + RPC_WS_LOG(INFO) << LOG_BADGE("updateGroupInfo") << LOG_KV("endPoint", _endPoint) + << LOG_KV("group", _groupInfo->groupID()) + << LOG_KV("chainID", _groupInfo->chainID()) + << LOG_KV("nodesNum", _groupInfo->nodesNum()); + { + boost::unique_lock lock(x_endPointLock); + m_endPoint2GroupId2GroupInfo[_endPoint][_groupInfo->groupID()] = _groupInfo; + } +} + +//------------------------------ Block Notifier Begin -------------------------- +bool Service::getBlockNumber(const std::string& _group, int64_t& _blockNumber) +{ + { + boost::shared_lock lock(x_blockNotifierLock); + auto it = m_group2BlockNumber.find(_group); + if (it == m_group2BlockNumber.end()) + { + return false; + } + + _blockNumber = it->second; + } + /* + RPC_WS_LOG(TRACE) << LOG_BADGE("getBlockNumber") << LOG_KV("group", _group) + << LOG_KV("blockNumber", _blockNumber); + */ + return true; +} + +bool Service::getBlockLimit(const std::string& _groupID, int64_t& _blockLimit) +{ + int64_t blockNumber = -1; + auto r = getBlockNumber(_groupID, blockNumber); + if (r) + { + _blockLimit = blockNumber + BLOCK_LIMIT_RANGE; + return true; + } + return false; +} + +std::pair Service::updateGroupBlockNumber( + const std::string& _groupID, int64_t _blockNumber) +{ + bool newBlockNumber = false; + bool highestBlockNumber = false; + { + boost::unique_lock lock(x_blockNotifierLock); + auto it = m_group2BlockNumber.find(_groupID); + if (it != m_group2BlockNumber.end()) + { + if (_blockNumber > it->second) + { + it->second = _blockNumber; + newBlockNumber = true; + highestBlockNumber = true; + } + else if (_blockNumber == it->second) + { + highestBlockNumber = true; + } + } + else + { + m_group2BlockNumber[_groupID] = _blockNumber; + newBlockNumber = true; + highestBlockNumber = true; + } + } + + if (newBlockNumber) + { + RPC_WS_LOG(INFO) << LOG_BADGE("updateGroupBlockNumber") << LOG_KV("groupID", _groupID) + << LOG_KV("_blockNumber", _blockNumber); + } + + return std::make_pair(newBlockNumber, highestBlockNumber); +} + +bool Service::randomGetHighestBlockNumberNode(const std::string& _group, std::string& _node) +{ + std::set setNodes; + if (!getHighestBlockNumberNodes(_group, setNodes)) + { + return false; + } + + std::vector vectorNodes(setNodes.begin(), setNodes.end()); + std::default_random_engine e(std::chrono::system_clock::now().time_since_epoch().count()); + std::shuffle(vectorNodes.begin(), vectorNodes.end(), e); + + _node = *vectorNodes.begin(); + return true; +} + +bool Service::getHighestBlockNumberNodes(const std::string& _group, std::set& _nodes) +{ + std::set tempNodes; + + { + boost::shared_lock lock(x_blockNotifierLock); + auto it = m_group2LatestBlockNumberNodes.find(_group); + if (it == m_group2LatestBlockNumberNodes.end()) + { + return false; + } + + tempNodes = it->second; + } + + for (auto it = tempNodes.begin(); it != tempNodes.end(); ++it) + { + auto& node = *it; + if (!hasEndPointOfNodeAvailable(_group, node)) + { + RPC_WS_LOG(WARNING) << LOG_BADGE("getHighestBlockNumberNodes") + << LOG_DESC("node has no endpoint available") + << LOG_KV("group", _group) << LOG_KV("nodes", node); + + continue; + } + + _nodes.insert(*it); + } + + RPC_WS_LOG(TRACE) << LOG_BADGE("getHighestBlockNumberNodes") << LOG_KV("group", _group) + << LOG_KV("nodes size", _nodes.size()); + return !_nodes.empty(); +} + +void Service::removeBlockNumberInfo(const std::string& _group) +{ + RPC_WS_LOG(INFO) << LOG_BADGE("removeBlockNumberInfo") << LOG_KV("group", _group); + boost::unique_lock lock(x_blockNotifierLock); + m_group2callbacks.erase(_group); + m_group2BlockNumber.erase(_group); + m_group2LatestBlockNumberNodes.erase(_group); +} + +void Service::onRecvBlockNotifier(const std::string& _msg) +{ + auto blockNumberInfo = std::make_shared(); + if (blockNumberInfo->fromJson(_msg)) + { + onRecvBlockNotifier(blockNumberInfo); + } +} + +void Service::onRecvBlockNotifier(BlockNumberInfo::Ptr _blockNumber) +{ + RPC_WS_LOG(INFO) << LOG_BADGE("onRecvBlockNotifier") + << LOG_DESC("receive block number notifier") + << LOG_KV("group", _blockNumber->group()) + << LOG_KV("node", _blockNumber->node()) + << LOG_KV("blockNumber", _blockNumber->blockNumber()); + + auto r = updateGroupBlockNumber(_blockNumber->group(), _blockNumber->blockNumber()); + bool isNewBlock = r.first; + bool isHighestBlock = r.second; + if (isNewBlock || isHighestBlock) + { + boost::unique_lock lock(x_blockNotifierLock); + if (isNewBlock) + { + m_group2LatestBlockNumberNodes[_blockNumber->group()].clear(); + } + + if (isHighestBlock) + { + m_group2LatestBlockNumberNodes[_blockNumber->group()].insert(_blockNumber->node()); + } + } + + if (isNewBlock) + { + RPC_WS_LOG(INFO) << LOG_BADGE("onRecvBlockNotifier") << LOG_DESC("block notifier callback") + << LOG_KV("group", _blockNumber->group()) + << LOG_KV("node", _blockNumber->node()) + << LOG_KV("blockNumber", _blockNumber->blockNumber()); + + boost::shared_lock lock(x_blockNotifierLock); + auto it = m_group2callbacks.find(_blockNumber->group()); + if (it != m_group2callbacks.end()) + { + for (auto& callback : it->second) + { + callback(_blockNumber->group(), _blockNumber->blockNumber()); + } + } + } +} + +void Service::registerBlockNumberNotifier( + const std::string& _group, BlockNotifierCallback _callback) +{ + RPC_WS_LOG(INFO) << LOG_BADGE("registerBlockNumberNotifier") << LOG_KV("group", _group); + boost::unique_lock lock(x_blockNotifierLock); + m_group2callbacks[_group].push_back(_callback); +} +//------------------------------ Block Notifier End -------------------------- diff --git "a/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/Service.h" "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/Service.h" new file mode 100644 index 00000000..6bd384ac --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/bcos-cpp-sdk/ws/Service.h" @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2021 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 Service.h + * @author: octopus + * @date 2021-10-22 + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace cppsdk +{ +namespace service +{ +using WsHandshakeSucHandler = std::function; +using BlockNotifierCallback = std::function; +using BlockNotifierCallbacks = std::vector; + +class Service : public bcos::boostssl::ws::WsService +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + Service(bcos::group::GroupInfoCodec::Ptr _groupInfoCodec, + bcos::group::GroupInfoFactory::Ptr _groupInfoFactory, std::string _moduleName); + + // ---------------------overide begin------------------------------------ + + virtual void start() override; + virtual void stop() override; + + virtual void onConnect( + bcos::Error::Ptr _error, std::shared_ptr _session) override; + + virtual void onDisconnect( + bcos::Error::Ptr _error, std::shared_ptr _session) override; + + virtual void onRecvMessage(std::shared_ptr _msg, + std::shared_ptr _session) override; + // ---------------------overide end ------------------------------------- + + void waitForConnectionEstablish(); + + + // ---------------------send message begin------------------------------- + virtual void asyncSendMessageByGroupAndNode(const std::string& _group, const std::string& _node, + std::shared_ptr _msg, bcos::boostssl::ws::Options _options, + bcos::boostssl::ws::RespCallBack _respFunc); + // ---------------------oversend message begin---------------------------- + + virtual void startHandshake(std::shared_ptr _session); + virtual bool checkHandshakeDone(std::shared_ptr _session); + + void clearGroupInfoByEp(const std::string& _endPoint); + void clearGroupInfoByEp(const std::string& _endPoint, const std::string& _groupID); + void updateGroupInfoByEp(const std::string& _endPoint, bcos::group::GroupInfo::Ptr _groupInfo); + void onNotifyGroupInfo( + const std::string& _groupInfo, std::shared_ptr _session); + void onNotifyGroupInfo(std::shared_ptr _msg, + std::shared_ptr _session); + + //------------------------------ Block Notifier begin -------------------------- + bool getBlockNumber(const std::string& _group, int64_t& _blockNumber); + bool getBlockLimit(const std::string& _group, int64_t& _blockLimit); + std::pair updateGroupBlockNumber(const std::string& _groupID, int64_t _blockNumber); + + bool randomGetHighestBlockNumberNode(const std::string& _group, std::string& _node); + bool getHighestBlockNumberNodes(const std::string& _group, std::set& _nodes); + + void onRecvBlockNotifier(const std::string& _msg); + void onRecvBlockNotifier(BlockNumberInfo::Ptr _blockNumberInfo); + void removeBlockNumberInfo(const std::string& _group); + + void registerBlockNumberNotifier(const std::string& _group, BlockNotifierCallback _callback); + //------------------------------ Block Notifier end ---------------------------- + + bcos::group::GroupInfo::Ptr getGroupInfo(const std::string& _groupID); + void updateGroupInfo(const std::string& _endPoint, bcos::group::GroupInfo::Ptr _groupInfo); + + bool hasEndPointOfNodeAvailable(const std::string& _groupID, const std::string& _node); + bool getEndPointsByGroup(const std::string& _group, std::set& _endPoints); + bool getEndPointsByGroupAndNode( + const std::string& _group, const std::string& _node, std::set& _endPoints); + + void printGroupInfo(); + bcos::group::GroupInfoFactory::Ptr groupInfoFactory() const { return m_groupInfoFactory; } + + uint32_t wsHandshakeTimeout() const { return m_wsHandshakeTimeout; } + void setWsHandshakeTimeout(uint32_t _wsHandshakeTimeout) + { + m_wsHandshakeTimeout = _wsHandshakeTimeout; + } + + uint32_t handshakeSucCount() const { return m_handshakeSucCount.load(); } + + void increaseHandshakeSucCount() { m_handshakeSucCount++; } + + void registerWsHandshakeSucHandler(WsHandshakeSucHandler _handler) + { + m_wsHandshakeSucHandlers.push_back(_handler); + } + + void callWsHandshakeSucHandlers(std::shared_ptr _session) + { + for (auto& handler : m_wsHandshakeSucHandlers) + { + handler(_session); + } + } + +private: + uint32_t m_wsHandshakeTimeout = 10000; // 10s + std::atomic m_handshakeSucCount = 0; + // + std::vector m_wsHandshakeSucHandlers; + +private: + mutable boost::shared_mutex x_endPointLock; + // group => node => endpoints + std::unordered_map>> + m_group2Node2Endpoints; + + // endpoint => group => groupInfo + std::unordered_map> + m_endPoint2GroupId2GroupInfo; + + mutable boost::shared_mutex x_blockNotifierLock; + // group => blockNotifier callback + std::unordered_map m_group2callbacks; + // group => blockNumber + std::unordered_map m_group2BlockNumber; + // group => nodes + std::unordered_map> m_group2LatestBlockNumberNodes; + + // the groupInfo codec + bcos::group::GroupInfoCodec::Ptr m_groupInfoCodec; + // the groupInfo factory + bcos::group::GroupInfoFactory::Ptr m_groupInfoFactory; + + bcos::protocol::ProtocolInfo::ConstPtr m_localProtocol; +}; + +} // namespace service +} // namespace cppsdk +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-sdk/sample/CMakeLists.txt" "b/BFPL\345\243\271/bcos-sdk/sample/CMakeLists.txt" new file mode 100644 index 00000000..59e75155 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/sample/CMakeLists.txt" @@ -0,0 +1,4 @@ +add_subdirectory(rpc) +add_subdirectory(amop) +add_subdirectory(eventsub) +add_subdirectory(tx) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/sample/amop/CMakeLists.txt" "b/BFPL\345\243\271/bcos-sdk/sample/amop/CMakeLists.txt" new file mode 100644 index 00000000..ab00813c --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/sample/amop/CMakeLists.txt" @@ -0,0 +1,22 @@ +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") + +file(GLOB SRC_LIST "*.cpp") +file(GLOB HEADERS "*.h") + +add_executable(publish publish.cpp) +if (NOT WIN32) + target_compile_options(publish PRIVATE) +endif() +target_link_libraries(publish PUBLIC ${BCOS_CPP_SDK_TARGET} bcos-boostssl bcos-utilities jsoncpp_static OpenSSL::SSL OpenSSL::Crypto) + +add_executable(broadcast broadcast.cpp) +if (NOT WIN32) + target_compile_options(broadcast PRIVATE) +endif() +target_link_libraries(broadcast PUBLIC ${BCOS_CPP_SDK_TARGET} bcos-boostssl bcos-utilities jsoncpp_static OpenSSL::SSL OpenSSL::Crypto) + +add_executable(subscribe subscribe.cpp) +if (NOT WIN32) + target_compile_options(subscribe PRIVATE) +endif() +target_link_libraries(subscribe PUBLIC ${BCOS_CPP_SDK_TARGET} bcos-boostssl bcos-utilities jsoncpp_static OpenSSL::SSL OpenSSL::Crypto) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/sample/amop/broadcast.cpp" "b/BFPL\345\243\271/bcos-sdk/sample/amop/broadcast.cpp" new file mode 100644 index 00000000..f4ac833f --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/sample/amop/broadcast.cpp" @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2021 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 broadcast.cpp + * @author: octopus + * @date 2021-08-24 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::boostssl; +using namespace bcos; +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +void usage() +{ + std::cerr << "Desc: broadcast amop message by command params\n"; + std::cerr << "Usage: broadcast \n" + << "Example:\n" + << " ./broadcast ./config_sample.ini topic HelloWorld\n"; + std::exit(0); +} + + +int main(int argc, char** argv) +{ + if (argc < 4) + { + usage(); + } + + std::string config = argv[1]; + std::string topic = argv[2]; + std::string msg = argv[3]; + + std::cout << LOG_DESC(" [AMOP][Broadcast]] params ===>>>> ") << LOG_KV("\n\t # config", config) + << LOG_KV("\n\t # topic", topic) << LOG_KV("\n\t # message", msg) << std::endl; + + auto factory = std::make_shared(); + // construct cpp-sdk object + auto sdk = factory->buildSdk(config); + // start sdk + sdk->start(); + + std::cout << LOG_DESC(" [AMOP][Broadcast] start sdk ... ") << std::endl; + + int i = 0; + while (true) + { + std::cout << LOG_DESC(" broadcast message ===>>>> ") << LOG_KV("topic", topic) + << LOG_KV("message", msg) << std::endl; + + sdk->amop()->broadcast(topic, bytesConstRef((byte*)msg.data(), msg.size())); + std::this_thread::sleep_for(std::chrono::milliseconds(5000)); + i++; + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/sample/amop/publish.cpp" "b/BFPL\345\243\271/bcos-sdk/sample/amop/publish.cpp" new file mode 100644 index 00000000..9d84f52e --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/sample/amop/publish.cpp" @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2021 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 publish.cpp + * @author: octopus + * @date 2021-08-24 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::boostssl; +using namespace bcos; +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +void usage() +{ + std::cerr << "Desc: publish amop message by command params\n"; + std::cerr << "Usage: publish \n" + << "Example:\n" + << " ./publish ./config_sample.ini topic HelloWorld\n"; + std::exit(0); +} + +int main(int argc, char** argv) +{ + if (argc < 4) + { + usage(); + } + + std::string config = argv[1]; + std::string topic = argv[2]; + std::string msg = argv[3]; + + std::cout << LOG_DESC(" [AMOP][Publish]] params ===>>>> ") << LOG_KV("\n\t # config", config) + << LOG_KV("\n\t # topic", topic) << LOG_KV("\n\t # message", msg) << std::endl; + + auto factory = std::make_shared(); + // construct cpp-sdk object + auto sdk = factory->buildSdk(config); + // start sdk + sdk->start(); + + std::cout << LOG_DESC(" [AMOP][Publish] start sdk ... ") << std::endl; + + int i = 0; + while (true) + { + std::cout << LOG_DESC(" publish message ===>>>> ") << LOG_KV("topic", topic) + << LOG_KV("message", msg) << std::endl; + + sdk->amop()->publish(topic, bytesConstRef((byte*)msg.data(), msg.size()), -1, + [](Error::Ptr _error, std::shared_ptr _msg, + std::shared_ptr _session) { + boost::ignore_unused(_session); + if (_error) + { + std::cout << " \t something is wrong" << LOG_KV("error", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()) << std::endl; + return; + } + else + { + if (_msg->status() != 0) + { + std::cout << " \t something is wrong" << LOG_KV("error", _msg->status()) + << LOG_KV("errorMessage", std::string(_msg->payload()->begin(), + _msg->payload()->end())) + << std::endl; + return; + } + + std::cout << " \t recv response message ===>>>> " + << LOG_KV("msg", + std::string(_msg->payload()->begin(), _msg->payload()->end())) + << std::endl; + } + }); + std::this_thread::sleep_for(std::chrono::milliseconds(5000)); + i++; + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/sample/amop/subscribe.cpp" "b/BFPL\345\243\271/bcos-sdk/sample/amop/subscribe.cpp" new file mode 100644 index 00000000..9daf8d72 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/sample/amop/subscribe.cpp" @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2021 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 sub_client.cpp + * @author: octopus + * @date 2021-08-24 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::boostssl; +using namespace bcos; +//------------------------------------------------------------------------------ + +void usage() +{ + std::cerr << "Desc: subscribe amop topic by command params\n"; + std::cerr << "Usage: subscribe \n" + << "Example:\n" + << " ./subscribe ./config_sample.ini topic\n"; + std::exit(0); +} + +//------------------------------------------------------------------------------ +int main(int argc, char** argv) +{ + if (argc < 3) + { + usage(); + } + + std::string config = argv[1]; + std::set topicList; + for (int i = 2; i < argc; i++) + { + topicList.insert(argv[i]); + } + std::string topic = argv[2]; + + std::cout << LOG_DESC(" [AMOP][Subscribe] params ===>>>> ") << LOG_KV("\n\t # config", config) + << LOG_KV("\n\t # topic", topic) << std::endl; + + auto factory = std::make_shared(); + // construct cpp-sdk object + auto sdk = factory->buildSdk(config); + // start sdk + sdk->start(); + + std::cout << LOG_DESC(" [AMOP][Subscribe] start sdk ... ") << std::endl; + sdk->amop()->setSubCallback( + [&sdk](Error::Ptr _error, const std::string& _endPoint, const std::string& _seq, + bytesConstRef _data, std::shared_ptr _session) { + boost::ignore_unused(_session); + if (_error) + { + std::cout << " \t something is wrong" << LOG_KV("error", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()) << std::endl; + } + else + { + std::cout << " \t recv message and would echo message to publish ===>>>> " + << LOG_KV("endPoint", _endPoint) + << LOG_KV("msg", std::string(_data.begin(), _data.end())) << std::endl; + + sdk->amop()->sendResponse(_endPoint, _seq, _data); + } + }); + sdk->amop()->subscribe(topicList); + + int i = 0; + while (true) + { + std::cout << LOG_DESC(" Main thread running ") << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); + i++; + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/sample/config/config_sample.ini" "b/BFPL\345\243\271/bcos-sdk/sample/config/config_sample.ini" new file mode 100644 index 00000000..690ff55a --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/sample/config/config_sample.ini" @@ -0,0 +1,26 @@ + +[common] + ; if ssl connection is disabled, default: false + ; disable_ssl = true + ; thread pool size for network message sending recving handing + thread_pool_size = 8 + ; send message timeout(ms) + message_timeout_ms = 10000 + +; ssl cert config items, +[cert] + ; ssl_type: ssl or sm_ssl, default: ssl + ssl_type = ssl + ; directory the certificates located in, defaul: ./conf + ca_path=./conf + ; the ca certificate file + ca_cert=ca.crt + ; the node private key file + sdk_key=sdk.key + ; the node certificate file + sdk_cert=sdk.crt + +[peers] +# supported ipv4 and ipv6 + node.0=127.0.0.1:20200 + node.1=127.0.0.1:20201 \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/sample/config/sm_config_sample.ini" "b/BFPL\345\243\271/bcos-sdk/sample/config/sm_config_sample.ini" new file mode 100644 index 00000000..86437ee2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/sample/config/sm_config_sample.ini" @@ -0,0 +1,28 @@ +[common] + ; if ssl connection is disabled, default: false + ; disable_ssl = true + ; thread pool size for network message sending recving handing + thread_pool_size = 8 + ; send message timeout(ms) + message_timeout_ms = 10000 + +[cert] + ; ssl_type: ssl or sm_ssl, default: ssl + ssl_type = sm_ssl + ; directory the certificates located in, defaul: ./conf + ca_path=./conf + ; the ca certificate file + sm_ca_cert=sm_ca.crt + ; the node private key file + sm_sdk_key=sm_sdk.key + ; the node certificate file + sm_sdk_cert=sm_sdk.crt + ; the node private key file + sm_ensdk_key=sm_ensdk.key + ; the node certificate file + sm_ensdk_cert=sm_ensdk.crt + +[peers] +# supported ipv4 and ipv6 + node.0=127.0.0.1:20200 + node.1=127.0.0.1:20201 \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/sample/eventsub/CMakeLists.txt" "b/BFPL\345\243\271/bcos-sdk/sample/eventsub/CMakeLists.txt" new file mode 100644 index 00000000..461d95a4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/sample/eventsub/CMakeLists.txt" @@ -0,0 +1,10 @@ +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") + +file(GLOB SRC_LIST "*.cpp") +file(GLOB HEADERS "*.h") + +add_executable(eventsub eventsub.cpp) +if (NOT WIN32) + target_compile_options(eventsub PRIVATE -Wno-unused-variable) +endif() +target_link_libraries(eventsub PUBLIC ${BCOS_CPP_SDK_TARGET} bcos-boostssl bcos-utilities jsoncpp_static OpenSSL::SSL OpenSSL::Crypto) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/sample/eventsub/eventsub.cpp" "b/BFPL\345\243\271/bcos-sdk/sample/eventsub/eventsub.cpp" new file mode 100644 index 00000000..74133cdd --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/sample/eventsub/eventsub.cpp" @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2021 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 eventsub.cpp + * @author: octopus + * @date 2021-08-24 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::boostssl; +using namespace bcos; +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +void usage() +{ + std::cerr << "Desc: subscribe contract events by command params\n"; + std::cerr << "Usage: eventsub
[Optional]\n"; + + std::cerr << "Example:\n" + << " ./eventsub ./config_sample.ini group -1 -1" + "\n"; + std::exit(0); +} + +int main(int argc, char** argv) +{ + if (argc < 5) + { + usage(); + } + + // option params + std::string config = argv[1]; + std::string group = argv[2]; + int64_t from = atoi(argv[3]); + int64_t to = atoi(argv[4]); + + std::string address; + if (argc > 5) + { + address = argv[5]; + } + + std::cout << LOG_DESC(" [EventSub] params ===>>>> ") << LOG_KV("\n\t # config", config) + << LOG_KV("\n\t # group", group) << LOG_KV("\n\t # from", from) + << LOG_KV("\n\t # to", to) << LOG_KV("\n\t # address", address) << std::endl; + + auto factory = std::make_shared(); + // construct cpp-sdk object + auto sdk = factory->buildSdk(config); + // start sdk + sdk->start(); + + std::cout << LOG_DESC(" [EventSub] start sdk ... ") << std::endl; + + // construct eventsub params + auto params = std::make_shared(); + params->setFromBlock(from); + params->setToBlock(to); + if (!address.empty()) + { + params->addAddress(address); + } + + sdk->service()->registerBlockNumberNotifier(group, [](const std::string& _group, + int64_t _blockNumber) { + // recv block number from server + std::cout << " \t recv block number notifier from server ===>>>> " + << LOG_KV("group", _group) << LOG_KV("blockNumber", _blockNumber) << std::endl; + }); + + // subscribe event + sdk->eventSub()->subscribeEvent( + group, params, [](Error::Ptr _error, const std::string& _events) { + if (_error) + { + // error and exit + std::cout << " \t something is wrong" << LOG_KV("error", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()) << std::endl; + std::exit(-1); + } + else + { + // recv events from server + std::cout << " \t recv events from server ===>>>> " << LOG_KV("events", _events) + << std::endl; + } + }); + + int i = 0; + while (true) + { + std::cout << LOG_DESC(" Main thread running ") << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); + i++; + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/sample/rpc/CMakeLists.txt" "b/BFPL\345\243\271/bcos-sdk/sample/rpc/CMakeLists.txt" new file mode 100644 index 00000000..8cd7252b --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/sample/rpc/CMakeLists.txt" @@ -0,0 +1,10 @@ +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") + +file(GLOB SRC_LIST "*.cpp") +file(GLOB HEADERS "*.h") + +add_executable(blocknotifier blocknotifier.cpp) +if (NOT WIN32) + target_compile_options(blocknotifier PRIVATE -Wno-unused-variable) +endif() +target_link_libraries(blocknotifier PUBLIC ${BCOS_CPP_SDK_TARGET} bcos-boostssl bcos-utilities jsoncpp_static OpenSSL::SSL OpenSSL::Crypto) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/sample/rpc/blocknotifier.cpp" "b/BFPL\345\243\271/bcos-sdk/sample/rpc/blocknotifier.cpp" new file mode 100644 index 00000000..88652455 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/sample/rpc/blocknotifier.cpp" @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2021 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 blocknotifier.cpp + * @author: octopus + * @date 2021-08-24 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::boostssl; +using namespace bcos; +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +void usage() +{ + std::cerr << "Desc: print block notifier by command params\n"; + std::cerr << "Usage: blocknotifier \n" + << "Example:\n" + << " ./blocknotifier ./config_sample.ini group " + "\n"; + std::exit(0); +} + + +int main(int argc, char** argv) +{ + if (argc < 3) + { + usage(); + } + + std::string config = argv[1]; + std::string group = argv[2]; + + std::cout << LOG_DESC(" [BlockNotifier] params ===>>>> ") << LOG_KV("\n\t # config", config) + << LOG_KV("\n\t # group", group) << std::endl; + + auto factory = std::make_shared(); + // construct cpp-sdk object + auto sdk = factory->buildSdk(config); + // start sdk + sdk->start(); + + std::cout << LOG_DESC(" [BlockNotifier] start sdk ... ") << std::endl; + + sdk->service()->registerBlockNumberNotifier( + group, [](const std::string& _group, int64_t _blockNumber) { + std::cout << " \t block notifier ===>>>> " << LOG_KV("group", _group) + << LOG_KV("blockNumber", _blockNumber) << std::endl; + }); + + int i = 0; + while (true) + { + std::cout << LOG_DESC(" Main thread running ") << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); + i++; + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/sample/tx/CMakeLists.txt" "b/BFPL\345\243\271/bcos-sdk/sample/tx/CMakeLists.txt" new file mode 100644 index 00000000..eafb2aac --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/sample/tx/CMakeLists.txt" @@ -0,0 +1,13 @@ +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") + +file(GLOB SRC_LIST "*.cpp") +file(GLOB HEADERS "*.h") + +add_executable(deploy_hello deploy_hello.cpp) +target_link_libraries(deploy_hello PUBLIC ${BCOS_CPP_SDK_TARGET} ${TARS_PROTOCOL_TARGET} bcos-crypto bcos-boostssl bcos-utilities jsoncpp_static OpenSSL::SSL OpenSSL::Crypto) + +add_executable(tx_sign_perf tx_sign_perf.cpp) +target_link_libraries(tx_sign_perf PUBLIC ${BCOS_CPP_SDK_TARGET} ${TARS_PROTOCOL_TARGET}) + +add_executable(random_perf random_perf.cpp) +target_link_libraries(random_perf PUBLIC ${BCOS_CPP_SDK_TARGET} ${TARS_PROTOCOL_TARGET}) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/sample/tx/deploy_hello.cpp" "b/BFPL\345\243\271/bcos-sdk/sample/tx/deploy_hello.cpp" new file mode 100644 index 00000000..38f7949d --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/sample/tx/deploy_hello.cpp" @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2021 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 deploy_hello.cpp + * @author: octopus + * @date 2022-01-16 + */ + +#include + +#include "bcos-concepts/Serialize.h" +#include "bcos-crypto/interfaces/crypto/KeyPairFactory.h" +#include "bcos-crypto/interfaces/crypto/KeyPairInterface.h" +#include "bcos-crypto/interfaces/crypto/Signature.h" +#include "bcos-crypto/signature/secp256k1/Secp256k1KeyPair.h" +#include "bcos-crypto/signature/sm2/SM2Crypto.h" +#include "bcos-crypto/signature/sm2/SM2KeyPairFactory.h" +#include "bcos-tars-protocol/protocol/TransactionImpl.h" +#include "bcos-tars-protocol/tars/Transaction.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::boostssl; +//---------------------------------------------------------------------------------------------------- +// HelloWorld Source Code: +/** +pragma solidity>=0.4.24 <0.6.11; + +contract HelloWorld { + string name; + + constructor() public { + name = "Hello, World!"; + } + + function get() public view returns (string memory) { + return name; + } + + function set(string memory n) public { + name = n; + } +} +*/ +constexpr static std::string_view hwBIN = + "608060405234801561001057600080fd5b506040518060400160405280600d81526020017f48656c6c6f2c20576f72" + "6c6421000000000000000000000000000000000000008152506000908051906020019061005c929190610062565b50" + "610107565b828054600181600116156101000203166002900490600052602060002090601f01602090048101928260" + "1f106100a357805160ff19168380011785556100d1565b828001600101855582156100d1579182015b828111156100" + "d05782518255916020019190600101906100b5565b5b5090506100de91906100e2565b5090565b61010491905b8082" + "11156101005760008160009055506001016100e8565b5090565b90565b610310806101166000396000f3fe60806040" + "5234801561001057600080fd5b50600436106100365760003560e01c80634ed3885e1461003b5780636d4ce63c1461" + "00f6575b600080fd5b6100f46004803603602081101561005157600080fd5b81019080803590602001906401000000" + "0081111561006e57600080fd5b82018360208201111561008057600080fd5b80359060200191846001830284011164" + "0100000000831117156100a257600080fd5b91908080601f0160208091040260200160405190810160405280939291" + "90818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050" + "50610179565b005b6100fe610193565b60405180806020018281038252838181518152602001915080519060200190" + "80838360005b8381101561013e578082015181840152602081019050610123565b50505050905090810190601f1680" + "1561016b5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b8060" + "00908051906020019061018f929190610235565b5050565b6060600080546001816001161561010002031660029004" + "80601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203" + "1660029004801561022b5780601f106102005761010080835404028352916020019161022b565b8201919060005260" + "20600020905b81548152906001019060200180831161020e57829003601f168201915b5050505050905090565b8280" + "54600181600116156101000203166002900490600052602060002090601f016020900481019282601f106102765780" + "5160ff19168380011785556102a4565b828001600101855582156102a4579182015b828111156102a3578251825591" + "602001919060010190610288565b5b5090506102b191906102b5565b5090565b6102d791905b808211156102d35760" + "008160009055506001016102bb565b5090565b9056fea2646970667358221220b5943f43c48cc93c6d71cdcf27aee5" + "072566c88755ce9186e32ce83b24e8dc6c64736f6c634300060a0033"; + +constexpr static std::string_view hwSmBIN = + "608060405234801561001057600080fd5b506040518060400160405280600d81526020017f48656c6c6f2c20576f72" + "6c6421000000000000000000000000000000000000008152506000908051906020019061005c929190610062565b50" + "610107565b828054600181600116156101000203166002900490600052602060002090601f01602090048101928260" + "1f106100a357805160ff19168380011785556100d1565b828001600101855582156100d1579182015b828111156100" + "d05782518255916020019190600101906100b5565b5b5090506100de91906100e2565b5090565b61010491905b8082" + "11156101005760008160009055506001016100e8565b5090565b90565b610310806101166000396000f3fe60806040" + "5234801561001057600080fd5b50600436106100365760003560e01c8063299f7f9d1461003b5780633590b49f1461" + "00be575b600080fd5b610043610179565b604051808060200182810382528381815181526020019150805190602001" + "9080838360005b83811015610083578082015181840152602081019050610068565b50505050905090810190601f16" + "80156100b05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61" + "0177600480360360208110156100d457600080fd5b81019080803590602001906401000000008111156100f1576000" + "80fd5b82018360208201111561010357600080fd5b8035906020019184600183028401116401000000008311171561" + "012557600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380" + "828437600081840152601f19601f82011690508083019250505050505050919291929050505061021b565b005b6060" + "60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190" + "818152602001828054600181600116156101000203166002900480156102115780601f106101e65761010080835404" + "0283529160200191610211565b820191906000526020600020905b8154815290600101906020018083116101f45782" + "9003601f168201915b5050505050905090565b8060009080519060200190610231929190610235565b5050565b8280" + "54600181600116156101000203166002900490600052602060002090601f016020900481019282601f106102765780" + "5160ff19168380011785556102a4565b828001600101855582156102a4579182015b828111156102a3578251825591" + "602001919060010190610288565b5b5090506102b191906102b5565b5090565b6102d791905b808211156102d35760" + "008160009055506001016102bb565b5090565b9056fea26469706673582212209871cb2bcf390d53645807cbaedfe0" + "52d739ef9cff9d84787f74c4f379e1854664736f6c634300060a0033"; + +/* +{ + "6d4ce63c": "get()", + "4ed3885e": "set(string)" +} + +{ + "299f7f9d": "get()", + "3590b49f": "set(string)" +} +*/ + +//------------------------------------------------------------------------------------------------- + +std::string_view getBinary(bool _sm) +{ + return _sm ? hwSmBIN : hwBIN; +} + +void usage() +{ + std::cerr << "Desc: deploy HelloWorld contract\n"; + std::cerr << "Usage: deploy_hello \n" + << "Example:\n" + << " ./deploy_hello ./config_sample.ini group0\n" + "\n"; + std::exit(0); +} + +int main(int argc, char** argv) +{ + if (argc < 3) + { + usage(); + } + + std::string config = argv[1]; + std::string group = argv[2]; + + std::cout << LOG_DESC(" [DeployHello] params ===>>>> ") << LOG_KV("\n\t # config", config) + << LOG_KV("\n\t # groupID", group) << std::endl; + + auto factory = std::make_shared(); + // construct cpp-sdk object + auto sdk = factory->buildSdk(config); + // start sdk + sdk->start(); + + std::cout << LOG_DESC(" [DeployHello] start sdk ... ") << std::endl; + + // get group info + bcos::group::GroupInfo::Ptr groupInfo = sdk->service()->getGroupInfo(group); + if (!groupInfo) + { + std::cout << LOG_DESC(" [DeployHello] group not exist") << LOG_KV("group", group) + << std::endl; + exit(-1); + } + + crypto::SignatureCrypto::Ptr keyPairFactory; + crypto::KeyPairInterface::UniquePtr keyPair; + if (groupInfo->smCryptoType()) + { + keyPairFactory = std::make_shared(); + keyPair = keyPairFactory->generateKeyPair(); + } + else + { + keyPairFactory = std::make_shared(); + keyPair = keyPairFactory->generateKeyPair(); + } + + std::cout << LOG_DESC(" [DeployHello] sm_crypto_type ") << groupInfo->smCryptoType() + << std::endl; + + std::cout << LOG_DESC(" [DeployHello] new account ") + << LOG_KV("address", keyPair->publicKey()->hex()) << std::endl; + + int64_t blockLimit = -1; + sdk->service()->getBlockLimit(group, blockLimit); + + std::cout << LOG_DESC(" [DeployHello] block limit ") << LOG_KV("blockLimit", blockLimit) + << std::endl; + + auto hexBin = getBinary(groupInfo->smCryptoType()); + + auto hashImpl = std::make_shared(); + bcos::crypto::CryptoSuite::Ptr cryptoSuite = + std::make_shared(hashImpl, keyPairFactory, nullptr); + auto transactionFactory = + std::make_shared(cryptoSuite); + bcos::bytes inputData; + boost::algorithm::unhex(hexBin.begin(), hexBin.end(), std::back_inserter(inputData)); + auto tx = transactionFactory->createTransaction(0, "to", inputData, bcos::u256(100), 200, + "chain0", group, 1112, std::shared_ptr(std::move(keyPair))); + // auto r = + // transactionBuilderService->createSignedTransaction(*keyPair, "", *binBytes.get(), "", 0); + + std::cout << LOG_DESC(" [DeployHello] create signed transaction success") + << LOG_KV("tx hash", tx->hash(false)) << std::endl; + + std::promise p; + auto f = p.get_future(); + std::string buffer; + bcos::concepts::serialize::encode( + std::dynamic_pointer_cast(tx)->inner(), buffer); + + auto hexBuffer = boost::algorithm::hex_lower(buffer); + + sdk->jsonRpc()->sendTransaction(group, "", hexBuffer, false, + [&p](bcos::Error::Ptr _error, std::shared_ptr _resp) { + if (_error && _error->errorCode() != 0) + { + std::cout << LOG_DESC(" [DeployHello] send transaction response error") + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()) << std::endl; + } + else + { + std::string receipt = std::string(_resp->begin(), _resp->end()); + std::cout << LOG_DESC(" [DeployHello] recv response success ") + << LOG_KV("transaction receipt", receipt) << std::endl; + + Json::Value root; + Json::Reader jsonReader; + + try + { + if (!jsonReader.parse(receipt, root)) + { + std::cout << LOG_DESC(" [DeployHello] [ERROR] recv invalid json object") + << LOG_KV("resp", receipt) << std::endl; + return; + } + + std::cout << LOG_DESC(" [DeployHello] contract address ==> " + + root["result"]["contractAddress"].asString()) + << std::endl; + } + catch (const std::exception& _e) + { + std::cout << LOG_DESC(" [DeployHello] [ERROR] recv invalid json object") + << LOG_KV("resp", receipt) << std::endl; + } + } + p.set_value(true); + }); + f.get(); + + return 0; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/sample/tx/random_perf.cpp" "b/BFPL\345\243\271/bcos-sdk/sample/tx/random_perf.cpp" new file mode 100644 index 00000000..2d504fdb --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/sample/tx/random_perf.cpp" @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2021 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 random_perf.cpp + * @author: octopus + * @date 2022-04-07 + */ + +#include +#include +#include +#include +#include +#include +#include + +void usage() +{ + printf("Desc: random biginteger perf test\n"); + printf("Usage: ./random_perf count\n"); + printf("Example:\n"); + printf(" ./random_perf 30000\n"); + exit(0); +} + +int main(int argc, char** argv) +{ + if (argc < 2) + { + usage(); + } + + long long count = std::stoul(argv[1]); + + printf("[Random Gen Test] ===>>>> count: %lld\n", count); + + long long i = 0; + long long _10Per = count / 10; + + auto startPoint = std::chrono::high_resolution_clock::now(); + while (i++ < count) + { + if (i % _10Per == 0) + { + std::cerr << " ..process : " << ((double)i / count) * 100 << "%" << std::endl; + } + + // auto fixBytes = bcos::FixedBytes<32>().generateRandomFixedBytes(); + // auto u256Value = transactionBuilder->genRandomUint256(); + // (void)u256Value; + } + + auto endPoint = std::chrono::high_resolution_clock::now(); + auto elapsedMS = + (long long)std::chrono::duration_cast(endPoint - startPoint) + .count(); + + printf( + " [Random Gen Test] total count: %lld, total elapsed(ms): %lld, " + "count/s: %lld \n", + count, elapsedMS, 1000 * count / elapsedMS); + + return 0; +} diff --git "a/BFPL\345\243\271/bcos-sdk/sample/tx/tx_sign_perf.cpp" "b/BFPL\345\243\271/bcos-sdk/sample/tx/tx_sign_perf.cpp" new file mode 100644 index 00000000..69820f2f --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/sample/tx/tx_sign_perf.cpp" @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2021 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 tx_sign_perf.cpp + * @author: octopus + * @date 2022-01-17 + */ + +#include "bcos-crypto/interfaces/crypto/CryptoSuite.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +// HelloWorld Source Code: + +// HelloWorld Source Code: +/** +pragma solidity>=0.4.24 <0.6.11; + +contract HelloWorld { + string name; + + constructor() public { + name = "Hello, World!"; + } + + function get() public view returns (string memory) { + return name; + } + + function set(string memory n) public { + name = n; + } +} +*/ +const char* hwBIN = + "608060405234801561001057600080fd5b506040518060400160405280600d81526020017f48656c6c6f2c20576f72" + "6c6421000000000000000000000000000000000000008152506000908051906020019061005c929190610062565b50" + "610107565b828054600181600116156101000203166002900490600052602060002090601f01602090048101928260" + "1f106100a357805160ff19168380011785556100d1565b828001600101855582156100d1579182015b828111156100" + "d05782518255916020019190600101906100b5565b5b5090506100de91906100e2565b5090565b61010491905b8082" + "11156101005760008160009055506001016100e8565b5090565b90565b610310806101166000396000f3fe60806040" + "5234801561001057600080fd5b50600436106100365760003560e01c80634ed3885e1461003b5780636d4ce63c1461" + "00f6575b600080fd5b6100f46004803603602081101561005157600080fd5b81019080803590602001906401000000" + "0081111561006e57600080fd5b82018360208201111561008057600080fd5b80359060200191846001830284011164" + "0100000000831117156100a257600080fd5b91908080601f0160208091040260200160405190810160405280939291" + "90818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050" + "50610179565b005b6100fe610193565b60405180806020018281038252838181518152602001915080519060200190" + "80838360005b8381101561013e578082015181840152602081019050610123565b50505050905090810190601f1680" + "1561016b5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b8060" + "00908051906020019061018f929190610235565b5050565b6060600080546001816001161561010002031660029004" + "80601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203" + "1660029004801561022b5780601f106102005761010080835404028352916020019161022b565b8201919060005260" + "20600020905b81548152906001019060200180831161020e57829003601f168201915b5050505050905090565b8280" + "54600181600116156101000203166002900490600052602060002090601f016020900481019282601f106102765780" + "5160ff19168380011785556102a4565b828001600101855582156102a4579182015b828111156102a3578251825591" + "602001919060010190610288565b5b5090506102b191906102b5565b5090565b6102d791905b808211156102d35760" + "008160009055506001016102bb565b5090565b9056fea2646970667358221220b5943f43c48cc93c6d71cdcf27aee5" + "072566c88755ce9186e32ce83b24e8dc6c64736f6c634300060a0033"; + +const char* hwSmBIN = + "608060405234801561001057600080fd5b506040518060400160405280600d81526020017f48656c6c6f2c20576f72" + "6c6421000000000000000000000000000000000000008152506000908051906020019061005c929190610062565b50" + "610107565b828054600181600116156101000203166002900490600052602060002090601f01602090048101928260" + "1f106100a357805160ff19168380011785556100d1565b828001600101855582156100d1579182015b828111156100" + "d05782518255916020019190600101906100b5565b5b5090506100de91906100e2565b5090565b61010491905b8082" + "11156101005760008160009055506001016100e8565b5090565b90565b610310806101166000396000f3fe60806040" + "5234801561001057600080fd5b50600436106100365760003560e01c8063299f7f9d1461003b5780633590b49f1461" + "00be575b600080fd5b610043610179565b604051808060200182810382528381815181526020019150805190602001" + "9080838360005b83811015610083578082015181840152602081019050610068565b50505050905090810190601f16" + "80156100b05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61" + "0177600480360360208110156100d457600080fd5b81019080803590602001906401000000008111156100f1576000" + "80fd5b82018360208201111561010357600080fd5b8035906020019184600183028401116401000000008311171561" + "012557600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380" + "828437600081840152601f19601f82011690508083019250505050505050919291929050505061021b565b005b6060" + "60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190" + "818152602001828054600181600116156101000203166002900480156102115780601f106101e65761010080835404" + "0283529160200191610211565b820191906000526020600020905b8154815290600101906020018083116101f45782" + "9003601f168201915b5050505050905090565b8060009080519060200190610231929190610235565b5050565b8280" + "54600181600116156101000203166002900490600052602060002090601f016020900481019282601f106102765780" + "5160ff19168380011785556102a4565b828001600101855582156102a4579182015b828111156102a3578251825591" + "602001919060010190610288565b5b5090506102b191906102b5565b5090565b6102d791905b808211156102d35760" + "008160009055506001016102bb565b5090565b9056fea26469706673582212209871cb2bcf390d53645807cbaedfe0" + "52d739ef9cff9d84787f74c4f379e1854664736f6c634300060a0033"; + +/* +{ + "6d4ce63c": "get()", + "4ed3885e": "set(string)" +} + +{ + "299f7f9d": "get()", + "3590b49f": "set(string)" +} +*/ + +const char* getBinary(int _sm) +{ + return _sm ? hwSmBIN : hwBIN; +} + +void usage() +{ + printf("Desc: create signed transaction[HelloWorld set] perf test\n"); + printf("Usage: tx_sign_perf isSM txCount\n"); + printf("Example:\n"); + printf(" ./tx_sign_perf true 30000\n"); + printf(" ./tx_sign_perf false 30000\n"); + exit(0); +} + +int main(int argc, char** argv) +{ + if (argc < 2) + { + usage(); + } + + bool smCrypto = (std::string(argv[1]) == "true"); + uint32_t txCount = std::stoul(argv[2]); + + printf("[Create Signed Tx Perf Test] ===>>>> smCrypto: %d, txCount: %u\n", smCrypto, txCount); + + // auto keyPairBuilder = std::make_shared(); + // auto keyPair = + // keyPairBuilder->genKeyPair(smCrypto ? bcos::cppsdk::utilities::CryptoType::SM2 : + // bcos::cppsdk::utilities::CryptoType::Secp256K1); + + // auto transactionBuilder = std::make_shared(); + // auto code = *bcos::fromHexString(getBinary(smCrypto ? 1 : 0)); + + int64_t block_limit = 111111; + const char* group_id = "group0"; + const char* chain_id = "chain0"; + + std::string txHash = ""; + uint32_t i = 0; + uint32_t _10Per = txCount / 10; + auto keyPairFactory = std::make_shared(); + + auto startPoint = std::chrono::high_resolution_clock::now(); + while (i++ < txCount) + { + if (i % _10Per == 0) + { + std::cerr << " ..process : " << ((double)i / txCount) * 100 << "%" << std::endl; + } + + auto keyPair = keyPairFactory->generateKeyPair(); + auto hashImpl = std::make_shared(); + bcos::crypto::CryptoSuite::Ptr cryptoSuite = + std::make_shared(hashImpl, keyPairFactory, nullptr); + + auto transactionFactory = + std::make_shared(cryptoSuite); + bcos::bytes inputData; + auto tx = + transactionFactory->createTransaction(0, "to", inputData, bcos::u256(100), 200, "chain", + "group", 1112, std::shared_ptr(std::move(keyPair))); + } + + auto endPoint = std::chrono::high_resolution_clock::now(); + auto elapsedMS = + (long long)std::chrono::duration_cast(endPoint - startPoint) + .count(); + auto elapsedUS = + (long long)std::chrono::duration_cast(endPoint - startPoint) + .count(); + + printf( + " [Create Signed Tx Perf Test] total txs: %u, total elapsed(ms): %lld, avg(us): %lld, " + "txs/s: %lld \n", + txCount, elapsedMS, elapsedUS / txCount, 1000 * txCount / elapsedMS); + + return 0; +} diff --git "a/BFPL\345\243\271/bcos-sdk/tests/CMakeLists.txt" "b/BFPL\345\243\271/bcos-sdk/tests/CMakeLists.txt" new file mode 100644 index 00000000..37a577c7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/tests/CMakeLists.txt" @@ -0,0 +1,29 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of bcos-rpc +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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(GLOB_RECURSE SOURCES "unittests/*.cpp" "unittests/*.h" "unittests/*.sol") + +set(TEST_BINARY_NAME test-bcos-cpp-sdk) + +find_package(Boost REQUIRED log serialization unit_test_framework) + +add_executable(${TEST_BINARY_NAME} ${SOURCES}) +target_include_directories(${TEST_BINARY_NAME} PRIVATE . ${CMAKE_SOURCE_DIR}) + +find_package(Boost REQUIRED unit_test_framework) +target_link_libraries(${TEST_BINARY_NAME} ${BCOS_CPP_SDK_TARGET} bcos-utilities ${TARS_PROTOCOL_TARGET} Boost::unit_test_framework) +add_test(NAME ${TEST_BINARY_NAME} COMMAND ${TEST_BINARY_NAME}) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/tests/unittests/abi/ContractEventTopicTest.cpp" "b/BFPL\345\243\271/bcos-sdk/tests/unittests/abi/ContractEventTopicTest.cpp" new file mode 100644 index 00000000..1fddcdb6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/tests/unittests/abi/ContractEventTopicTest.cpp" @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2021 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 ContractEventTopicTest.cpp + * @author: octopus + * @date 2022-02-24 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::codec::abi; +using namespace bcos::test; + +BOOST_FIXTURE_TEST_SUITE(ContractEventTopicTest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(test_EventTopic_Keccak256) +{ + auto hashImpl = std::make_shared(); + auto contactEventTopic = std::make_shared(hashImpl); + + // int + { + s256 i0 = 0; + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000000000", + contactEventTopic->i256ToTopic(i0)); + + s256 i1 = 1; + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000000001", + contactEventTopic->i256ToTopic(i1)); + + s256 i2 = 12345; + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000003039", + contactEventTopic->i256ToTopic(i2)); + + s256 i3 = std::numeric_limits::max(); + BOOST_CHECK_EQUAL("0x000000000000000000000000000000000000000000000000000000007fffffff", + contactEventTopic->i256ToTopic(i3)); + + s256 i4 = -1; + BOOST_CHECK_EQUAL("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + contactEventTopic->i256ToTopic(i4)); + + s256 i5 = -1234567890; + BOOST_CHECK_EQUAL("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffb669fd2e", + contactEventTopic->i256ToTopic(i5)); + } + + // uint + { + u256 u0 = 0; + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000000000", + contactEventTopic->u256ToTopic(u0)); + + u256 u1 = 1; + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000000001", + contactEventTopic->u256ToTopic(u1)); + + u256 u2 = 12345; + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000003039", + contactEventTopic->u256ToTopic(u2)); + + u256 u3 = std::numeric_limits::max(); + BOOST_CHECK_EQUAL("0x00000000000000000000000000000000000000000000000000000000ffffffff", + contactEventTopic->u256ToTopic(u3)); + + u256 u4 = -1; + BOOST_CHECK_EQUAL("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + contactEventTopic->u256ToTopic(u4)); + + u256 u5 = 1234567890; + BOOST_CHECK_EQUAL("0x00000000000000000000000000000000000000000000000000000000499602d2", + contactEventTopic->u256ToTopic(u5)); + } + + // string + { + std::string s0 = ""; + BOOST_CHECK_EQUAL("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + contactEventTopic->stringToTopic(s0)); + + std::string s1 = "HelloWorld"; + BOOST_CHECK_EQUAL("0x7c5ea36004851c764c44143b1dcb59679b11c9a68e5f41497f6cf3d480715331", + contactEventTopic->stringToTopic(s1)); + + std::string s2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + BOOST_CHECK_EQUAL("0x6bf4107d5e7ac7a9c23a4e8d6581b098e5323fe49df3596168d3710d50526dad", + contactEventTopic->stringToTopic(s2)); + } + + // bytesN + { + bcos::bytes bs0; + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000000000", + contactEventTopic->bytesNToTopic(bs0)); + + bcos::bytes bs1(10, '1'); + BOOST_CHECK_EQUAL("0x3131313131313131313100000000000000000000000000000000000000000000", + contactEventTopic->bytesNToTopic(bs1)); + + bcos::bytes bs2(32, '1'); + BOOST_CHECK_EQUAL("0x3131313131313131313131313131313131313131313131313131313131313131", + contactEventTopic->bytesNToTopic(bs2)); + + bcos::bytes bs3(33, '1'); + BOOST_CHECK_THROW(contactEventTopic->bytesNToTopic(bs3), bcos::InvalidParameter); + } + + // bytes + { + bcos::bytes bs0; + BOOST_CHECK_EQUAL("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + contactEventTopic->bytesToTopic(bs0)); + + std::string hex = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"; + BOOST_CHECK_EQUAL("0xe166801d00a45901e2b3ca692a6a95e367d4a976218b485546a2da464b6c88b5", + contactEventTopic->bytesToTopic(bcos::bytes(hex.begin(), hex.end()))); + } +} + +BOOST_AUTO_TEST_CASE(test_EventTopic_SM3) +{ + auto hashImpl = std::make_shared(); + auto contactEventTopic = std::make_shared(hashImpl); + + // int + { + s256 i0 = 0; + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000000000", + contactEventTopic->i256ToTopic(i0)); + + s256 i1 = 1; + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000000001", + contactEventTopic->i256ToTopic(i1)); + + s256 i2 = 12345; + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000003039", + contactEventTopic->i256ToTopic(i2)); + + s256 i3 = std::numeric_limits::max(); + BOOST_CHECK_EQUAL("0x000000000000000000000000000000000000000000000000000000007fffffff", + contactEventTopic->i256ToTopic(i3)); + + s256 i4 = -1; + BOOST_CHECK_EQUAL("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + contactEventTopic->i256ToTopic(i4)); + + s256 i5 = -1234567890; + BOOST_CHECK_EQUAL("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffb669fd2e", + contactEventTopic->i256ToTopic(i5)); + } + + // uint + { + u256 u0 = 0; + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000000000", + contactEventTopic->u256ToTopic(u0)); + + u256 u1 = 1; + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000000001", + contactEventTopic->u256ToTopic(u1)); + + u256 u2 = 12345; + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000003039", + contactEventTopic->u256ToTopic(u2)); + + u256 u3 = std::numeric_limits::max(); + BOOST_CHECK_EQUAL("0x00000000000000000000000000000000000000000000000000000000ffffffff", + contactEventTopic->u256ToTopic(u3)); + + u256 u4 = -1; + BOOST_CHECK_EQUAL("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + contactEventTopic->u256ToTopic(u4)); + + u256 u5 = 1234567890; + BOOST_CHECK_EQUAL("0x00000000000000000000000000000000000000000000000000000000499602d2", + contactEventTopic->u256ToTopic(u5)); + } + + // string + { + std::string s0 = ""; + BOOST_CHECK_EQUAL("0x1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b", + contactEventTopic->stringToTopic(s0)); + + std::string s1 = "HelloWorld"; + BOOST_CHECK_EQUAL("0x44526eeba9235bae33f2bab8ff1f9ca8965b59d58be82af8111f336a00c1c432", + contactEventTopic->stringToTopic(s1)); + + std::string s2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + BOOST_CHECK_EQUAL("0x58cc358e7880b06962f996be258e454af7eecfd3455831dd690566c4bbe025b5", + contactEventTopic->stringToTopic(s2)); + } + + // bytesN + { + bcos::bytes bs0; + BOOST_CHECK_EQUAL("0x0000000000000000000000000000000000000000000000000000000000000000", + contactEventTopic->bytesNToTopic(bs0)); + + bcos::bytes bs1(10, '1'); + BOOST_CHECK_EQUAL("0x3131313131313131313100000000000000000000000000000000000000000000", + contactEventTopic->bytesNToTopic(bs1)); + + bcos::bytes bs2(32, '1'); + BOOST_CHECK_EQUAL("0x3131313131313131313131313131313131313131313131313131313131313131", + contactEventTopic->bytesNToTopic(bs2)); + + bcos::bytes bs3(33, '1'); + BOOST_CHECK_THROW(contactEventTopic->bytesNToTopic(bs3), bcos::InvalidParameter); + } + + // bytes + { + bcos::bytes bs0; + BOOST_CHECK_EQUAL("0x1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b", + contactEventTopic->bytesToTopic(bs0)); + + std::string hex = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"; + BOOST_CHECK_EQUAL("0x2cd6eb8aa7aed20ed9665df40f7b3ea261fb6555473d33aea100fe4cb5eda8f9", + contactEventTopic->bytesToTopic(bcos::bytes(hex.begin(), hex.end()))); + } +} +BOOST_AUTO_TEST_SUITE_END() diff --git "a/BFPL\345\243\271/bcos-sdk/tests/unittests/amop/TopicManagerTest.cpp" "b/BFPL\345\243\271/bcos-sdk/tests/unittests/amop/TopicManagerTest.cpp" new file mode 100644 index 00000000..339a0af6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/tests/unittests/amop/TopicManagerTest.cpp" @@ -0,0 +1,155 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for TopicManager + * @file TopicManagerTest.cpp + * @author: octopus + * @date 2021-09-22 + */ +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk::amop; +using namespace bcos::test; +using namespace bcos::protocol; + +BOOST_FIXTURE_TEST_SUITE(TopicManagerTest, TestPromptFixture) +BOOST_AUTO_TEST_CASE(test_AMOPRequestEncodeDecode) +{ + auto amopRequestFactory = std::make_shared(); + std::string dataStr = "testAMOPRequest"; + auto request = amopRequestFactory->buildRequest(); + request->setData(bcos::bytesConstRef((byte*)dataStr.data(), dataStr.size())); + request->setVersion(10023); + std::string topic = "testAMOPRequest+-@topic"; + request->setTopic(topic); + + BOOST_CHECK(request->version() == 10023); + BOOST_CHECK(request->topic() == topic); + BOOST_CHECK(*(request->data().data()) == *(dataStr.data())); + + // encode + bytes encodedData; + request->encode(encodedData); + + // decode + auto decodedRequest = amopRequestFactory->buildRequest(ref(encodedData)); + BOOST_CHECK(decodedRequest->version() == request->version()); + BOOST_CHECK(decodedRequest->topic() == request->topic()); + BOOST_CHECK(*(decodedRequest->data().data()) == *(request->data().data())); +} +BOOST_AUTO_TEST_CASE(test_TopicManager) +{ + { + auto topicManager = std::make_shared(); + auto topics = topicManager->topics(); + BOOST_CHECK(topics.size() == 0); + + std::string topic1 = "a"; + std::string topic2 = "a"; + std::string topic3 = "a"; + + auto r = topicManager->addTopic(topic1); + BOOST_CHECK(r); + r = topicManager->addTopic(topic1); + BOOST_CHECK(!r); + r = topicManager->addTopic(topic2); + BOOST_CHECK(!r); + r = topicManager->addTopic(topic3); + BOOST_CHECK(!r); + + topics = topicManager->topics(); + BOOST_CHECK(topics.size() == 1); + + r = topicManager->removeTopic(topic1); + BOOST_CHECK(r); + + r = topicManager->removeTopic(topic1); + BOOST_CHECK(!r); + + topics = topicManager->topics(); + BOOST_CHECK(topics.size() == 0); + + BOOST_CHECK(!topicManager->toJson().empty()); + } + + { + auto topicManager = std::make_shared(); + BOOST_CHECK(topicManager->topics().size() == 0); + + std::string topic1 = "a"; + std::string topic2 = "b"; + std::string topic3 = "c"; + std::set topics{topic1, topic2, topic3}; + + auto r = topicManager->addTopics(topics); + BOOST_CHECK(r); + r = topicManager->addTopics(topics); + BOOST_CHECK(!r); + + BOOST_CHECK(topics.size() == topics.size()); + + r = topicManager->removeTopics(topics); + BOOST_CHECK(r); + + r = topicManager->removeTopics(topics); + BOOST_CHECK(!r); + + topics = topicManager->topics(); + BOOST_CHECK(topics.size() == 0); + BOOST_CHECK(!topicManager->toJson().empty()); + } + + { + auto topicManager = std::make_shared(); + BOOST_CHECK(topicManager->topics().size() == 0); + + std::string topic1 = "a"; + std::string topic2 = "a"; + std::string topic3 = "a"; + std::set topics{topic1, topic2, topic3}; + + auto r = topicManager->addTopics(topics); + BOOST_CHECK(r); + r = topicManager->addTopics(topics); + BOOST_CHECK(!r); + + topics = topicManager->topics(); + BOOST_CHECK(topics.size() == topics.size()); + + r = topicManager->removeTopic(topic1); + BOOST_CHECK(r); + + r = topicManager->removeTopic(topic2); + BOOST_CHECK(!r); + + r = topicManager->removeTopic(topic3); + BOOST_CHECK(!r); + + r = topicManager->removeTopics(topics); + BOOST_CHECK(!r); + + topics = topicManager->topics(); + BOOST_CHECK(topics.size() == 0); + BOOST_CHECK(!topicManager->toJson().empty()); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git "a/BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubParamsTest.cpp" "b/BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubParamsTest.cpp" new file mode 100644 index 00000000..991465a0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubParamsTest.cpp" @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for EventSubParams + * @file EventSubParamsTest.cpp + * @author: octopus + * @date 2021-09-22 + */ +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::test; + +BOOST_FIXTURE_TEST_SUITE(EventSubParamsTest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(test_EventSubParams) +{ + { + auto params = std::make_shared(); + BOOST_CHECK(params->fromBlock() < 0); + BOOST_CHECK(params->toBlock() < 0); + BOOST_CHECK(params->addresses().empty()); + BOOST_CHECK(params->topics().empty()); + } + + { + int64_t fromBlk = 123; + int64_t toBlk = 456; + std::string addr = "0x123456"; + std::string topic = "0x45678"; + + auto params = std::make_shared(); + params->setFromBlock(fromBlk); + params->setToBlock(toBlk); + params->addAddress(addr); + + + BOOST_CHECK_EQUAL(params->fromBlock(), fromBlk); + BOOST_CHECK_EQUAL(params->toBlock(), toBlk); + BOOST_CHECK_EQUAL(params->addresses().size(), 1); + + auto r = params->addTopic(0, topic); + BOOST_CHECK(r); + r = params->addTopic(1, topic); + BOOST_CHECK(r); + r = params->addTopic(2, topic); + BOOST_CHECK(r); + r = params->addTopic(3, topic); + BOOST_CHECK(r); + r = params->addTopic(4, topic); + BOOST_CHECK(!r); + + BOOST_CHECK_EQUAL(params->topics().size(), 5); + for (auto& topics : params->topics()) + { + BOOST_CHECK_EQUAL(topics.size(), 1); + } + } +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubRequestTest.cpp" "b/BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubRequestTest.cpp" new file mode 100644 index 00000000..d998691d --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubRequestTest.cpp" @@ -0,0 +1,140 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for EventSubRequest + * @file EventSubTaskStateTest.cpp + * @author: octopus + * @date 2021-09-22 + */ +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::test; + +BOOST_FIXTURE_TEST_SUITE(EventSubRequestTest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(test_EventSubUnsubRequestTest) +{ + { + auto id = std::string("123"); + auto group = std::string("321"); + + auto request = std::make_shared(); + request->setId(id); + request->setGroup(group); + auto json = request->generateJson(); + + auto decodeReq = std::make_shared(); + auto r = decodeReq->fromJson(json); + BOOST_CHECK(r); + BOOST_CHECK_EQUAL(decodeReq->group(), group); + BOOST_CHECK_EQUAL(decodeReq->id(), id); + } + + { + auto decodeReq = std::make_shared(); + auto r = decodeReq->fromJson("{}"); + BOOST_CHECK(!r); + } + + { + auto decodeReq = std::make_shared(); + auto r = decodeReq->fromJson("aaa"); + BOOST_CHECK(!r); + } +} + + +BOOST_AUTO_TEST_CASE(test_EventSubSubRequestTest) +{ + { + auto id = std::string("123"); + auto group = std::string("321"); + auto params = std::make_shared(); + auto state = std::make_shared(); + + auto request = std::make_shared(); + request->setId(id); + request->setGroup(group); + request->setParams(params); + request->setState(state); + + auto json = request->generateJson(); + { + auto request = std::make_shared(); + auto r = request->fromJson(json); + BOOST_CHECK(r); + BOOST_CHECK_EQUAL(id, request->id()); + BOOST_CHECK_EQUAL(group, request->group()); + auto params = request->params(); + BOOST_CHECK(params->fromBlock() < 0); + BOOST_CHECK(params->toBlock() < 0); + BOOST_CHECK(params->addresses().empty()); + BOOST_CHECK(params->topics().empty()); + } + } + + { + auto id = std::string("111"); + auto group = std::string("222"); + auto fromBlk = 111; + auto toBlk = 222; + auto addr = std::string("0x1111"); + auto topic = std::string("topic"); + auto curBlk = 100; + + auto params = std::make_shared(); + params->setFromBlock(fromBlk); + params->setToBlock(toBlk); + params->addAddress(addr); + params->addTopic(1, topic); + params->addTopic(3, topic); + + auto state = std::make_shared(); + state->setCurrentBlockNumber(curBlk); + + auto request = std::make_shared(); + request->setId(id); + request->setGroup(group); + request->setParams(params); + request->setState(state); + + auto json = request->generateJson(); + { + auto request = std::make_shared(); + auto r = request->fromJson(json); + BOOST_CHECK(r); + BOOST_CHECK_EQUAL(id, request->id()); + BOOST_CHECK_EQUAL(group, request->group()); + auto params = request->params(); + BOOST_CHECK_EQUAL(params->fromBlock(), curBlk + 1); + BOOST_CHECK_EQUAL(params->toBlock(), toBlk); + BOOST_CHECK_EQUAL(params->addresses().size(), 1); + BOOST_CHECK(params->addresses().find(addr) != params->addresses().end()); + BOOST_CHECK_EQUAL(params->topics().size(), 4); + BOOST_CHECK_EQUAL(params->topics()[0].size(), 0); + BOOST_CHECK_EQUAL(params->topics()[1].size(), 1); + BOOST_CHECK_EQUAL(params->topics()[2].size(), 0); + BOOST_CHECK_EQUAL(params->topics()[3].size(), 1); + } + } +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubResponseTest.cpp" "b/BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubResponseTest.cpp" new file mode 100644 index 00000000..8e8f7db5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubResponseTest.cpp" @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for EventSubParams + * @file EventSubParamsTest.cpp + * @author: octopus + * @date 2021-09-22 + */ +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::test; + +BOOST_FIXTURE_TEST_SUITE(EventSubResponseTest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(test_EventSubResponse) +{ + { + std::string id = "0x12345"; + int status = 111; + auto resp = std::make_shared(); + resp->setId(id); + resp->setStatus(status); + auto json = resp->generateJson(); + + resp->setStatus(0); + resp->setId(""); + + auto r = resp->fromJson(json); + + BOOST_CHECK(r); + BOOST_CHECK_EQUAL(id, resp->id()); + BOOST_CHECK_EQUAL(status, resp->status()); + + BOOST_CHECK(!resp->fromJson("{}")); + BOOST_CHECK(!resp->fromJson("aaa")); + } +} + +BOOST_AUTO_TEST_CASE(test_EventSubResponse_Event) +{ + { + auto resp = std::make_shared(); + auto json = + "{\"id\":\"0x123\",\"status\":0,\"result\":{\"blockNumber\":111,\"events\":[]}}"; + auto r = resp->fromJson(json); + BOOST_CHECK(r); + + BOOST_CHECK_EQUAL("0x123", resp->id()); + BOOST_CHECK_EQUAL(0, resp->status()); + + BOOST_CHECK(resp->jResp().isMember("result")); + BOOST_CHECK(resp->jResp()["result"].isMember("blockNumber")); + BOOST_CHECK_EQUAL(resp->jResp()["result"]["blockNumber"].asInt64(), 111); + } +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubTaskStateTest.cpp" "b/BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubTaskStateTest.cpp" new file mode 100644 index 00000000..8234039f --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubTaskStateTest.cpp" @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for EventSubTaskState + * @file EventSubTaskStateTest.cpp + * @author: octopus + * @date 2021-09-22 + */ +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::test; + +BOOST_FIXTURE_TEST_SUITE(EventSubTaskStateTest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(test_EventSubTaskStateTest) +{ + auto state = std::make_shared(); + BOOST_CHECK(state->currentBlockNumber() < 0); + int64_t blockNumber = 10; + state->setCurrentBlockNumber(blockNumber); + BOOST_CHECK_EQUAL(state->currentBlockNumber(), blockNumber); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubTaskTest.cpp" "b/BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubTaskTest.cpp" new file mode 100644 index 00000000..e55ecc5f --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubTaskTest.cpp" @@ -0,0 +1,60 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for EventSubTask + * @file EventSubTaskTest.cpp + * @author: octopus + * @date 2021-09-22 + */ + +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::test; + +BOOST_FIXTURE_TEST_SUITE(EventSubTaskTest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(test_EventSubTask) +{ + auto id = std::string("123"); + auto group = std::string("321"); + auto params = std::shared_ptr(); + auto state = std::shared_ptr(); + + auto task = std::make_shared(); + task->setId(id); + task->setGroup(group); + task->setParams(params); + task->setState(state); + task->setCallback([](bcos::Error::Ptr _error, const std::string& _resp) { + boost::ignore_unused(_error, _resp); + }); + + BOOST_CHECK_EQUAL(id, task->id()); + BOOST_CHECK_EQUAL(group, task->group()); + BOOST_CHECK_EQUAL(params, task->params()); + BOOST_CHECK_EQUAL(state, task->state()); + BOOST_CHECK(task->callback()); +} + + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubTest.cpp" "b/BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubTest.cpp" new file mode 100644 index 00000000..1c458ba8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/tests/unittests/event/EventSubTest.cpp" @@ -0,0 +1,214 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for EventSub + * @file EventSubTest.cpp + * @author: octopus + * @date 2021-09-22 + */ +#include "../fake/WsServiceFake.h" +#include "../fake/WsSessionFake.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::test; + +BOOST_FIXTURE_TEST_SUITE(EventSubTest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(test_EventSub_suspendTask) +{ + auto es = std::make_shared(); + auto task = std::make_shared(); + std::string id = "123"; + task->setId(id); + + auto r = es->addSuspendTask(task); + BOOST_CHECK(r); + BOOST_CHECK_EQUAL(es->suspendTasksCount(), 1); + r = es->addSuspendTask(task); + BOOST_CHECK(!r); + BOOST_CHECK_EQUAL(es->suspendTasksCount(), 1); + + r = es->removeSuspendTask(id); + BOOST_CHECK(r); + BOOST_CHECK_EQUAL(es->suspendTasksCount(), 0); + + r = es->removeSuspendTask(id); + BOOST_CHECK(!r); + BOOST_CHECK_EQUAL(es->suspendTasksCount(), 0); +} + +BOOST_AUTO_TEST_CASE(test_EventSub_addTask) +{ + auto es = std::make_shared(); + auto task1 = std::make_shared(); + auto task2 = std::make_shared(); + + std::string id1 = "123"; + std::string id2 = "456"; + task1->setId(id1); + task2->setId(id2); + + { + // addTask + auto r = es->addTask(task1); + BOOST_CHECK(r); + r = es->addTask(task1); + BOOST_CHECK(!r); + + // getAndRemove + auto task = es->getTask(id1); + BOOST_CHECK(task); + task = es->getTaskAndRemove(id1); + BOOST_CHECK(task); + task = es->getTask(id1); + BOOST_CHECK(!task); + task = es->getTaskAndRemove(id1); + BOOST_CHECK(!task); + } + + { + // addTask + auto r = es->addTask(task1); + BOOST_CHECK(r); + r = es->addTask(task1); + BOOST_CHECK(!r); + + // getAndRemove + auto task = es->getTask(id1); + BOOST_CHECK(task); + task = es->getTaskAndRemove(id1); + BOOST_CHECK(task); + task = es->getTask(id1); + BOOST_CHECK(!task); + task = es->getTaskAndRemove(id1); + BOOST_CHECK(!task); + } + + { + auto r = es->addSuspendTask(task2); + BOOST_CHECK(r); + r = es->addSuspendTask(task2); + BOOST_CHECK(!r); + BOOST_CHECK_EQUAL(es->suspendTasksCount(), 1); + + auto task = es->getTask(id2, false); + BOOST_CHECK(!task); + + task = es->getTask(id2); + BOOST_CHECK(task); + + task = es->getTaskAndRemove(id2, false); + BOOST_CHECK(!task); + + task = es->getTaskAndRemove(id2); + BOOST_CHECK(task); + + BOOST_CHECK_EQUAL(es->suspendTasksCount(), 0); + } + + { + auto r = es->addSuspendTask(task2); + BOOST_CHECK(r); + BOOST_CHECK_EQUAL(es->suspendTasksCount(), 1); + + r = es->addTask(task2); + BOOST_CHECK(r); + + BOOST_CHECK_EQUAL(es->suspendTasksCount(), 0); + } +} + +BOOST_AUTO_TEST_CASE(test_EventSub_unsubscribeEvent) +{ + auto es = std::make_shared(); + auto messageFactory = std::make_shared(); + es->setMessageFactory(messageFactory); + + auto task = std::make_shared(); + std::string id = "123"; + task->setId(id); + { + // task is suspend + es->addSuspendTask(task); + BOOST_CHECK_EQUAL(es->suspendTasksCount(), 1); + std::promise p; + auto f = p.get_future(); + + BOOST_CHECK(!es->getTask(id, false)); + BOOST_CHECK_EQUAL(es->suspendTasksCount(), 1); + + BOOST_CHECK(es->getTask(id)); + BOOST_CHECK_EQUAL(es->suspendTasksCount(), 1); + + es->unsubscribeEvent(id); + + BOOST_CHECK(!es->getTask(id)); + BOOST_CHECK_EQUAL(es->suspendTasksCount(), 0); + } + + + { + // task is running + auto session = + std::make_shared("test_EventSub_unsubscribeEvent"); + task->setSession(session); + + std::string resp = "{}"; + session->setResp(std::make_shared(resp.begin(), resp.end())); + es->addTask(task); + + // callback error + es->unsubscribeEvent(id); + + BOOST_CHECK(!es->getTask(id)); + BOOST_CHECK_EQUAL(es->suspendTasksCount(), 0); + } + + { + // task is running + auto session = + std::make_shared("test_EventSub_unsubscribeEvent"); + + task->setSession(session); + + es->addTask(task); + + auto resp = std::make_shared(); + resp->setId(task->id()); + resp->setStatus(0); + + session->setError(nullptr); + auto respJson = resp->generateJson(); + session->setResp(std::make_shared(respJson.begin(), respJson.end())); + + es->unsubscribeEvent(id); + + BOOST_CHECK(!es->getTask(id)); + BOOST_CHECK_EQUAL(es->suspendTasksCount(), 0); + } +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/tests/unittests/fake/WsServiceFake.h" "b/BFPL\345\243\271/bcos-sdk/tests/unittests/fake/WsServiceFake.h" new file mode 100644 index 00000000..af0e5841 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/tests/unittests/fake/WsServiceFake.h" @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2021 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 WsServiceFake.h + * @author: octopus + * @date 2021-09-24 + */ +#pragma once +#include +#include +#include + +namespace bcos +{ +namespace cppsdk +{ +namespace test +{ +class WsServiceFake : public bcos::boostssl::ws::WsService +{ +public: + using Ptr = std::shared_ptr; + +public: + virtual void asyncSendMessage(std::shared_ptr _msg, + bcos::boostssl::ws::Options _options = bcos::boostssl::ws::Options(-1), + bcos::boostssl::ws::RespCallBack _respFunc = bcos::boostssl::ws::RespCallBack()) override + { + (void)_msg; + (void)_options; + + auto msg = std::make_shared(); + msg->setPayload(m_resp); + auto session = shared_from_this(); + _respFunc(m_error, _msg, m_session); + } + +public: + void setError(bcos::Error::Ptr _error) { m_error = _error; } + void setResp(std::shared_ptr _resp) { m_resp = _resp; } + void setSession(std::shared_ptr _session) + { + m_session = _session; + } + +private: + bcos::Error::Ptr m_error; + std::shared_ptr m_resp; + std::shared_ptr m_session; +}; +} // namespace test +} // namespace cppsdk +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/tests/unittests/fake/WsSessionFake.h" "b/BFPL\345\243\271/bcos-sdk/tests/unittests/fake/WsSessionFake.h" new file mode 100644 index 00000000..ed1cc62d --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/tests/unittests/fake/WsSessionFake.h" @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2021 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 WsSessionFake.h + * @author: octopus + * @date 2021-09-24 + */ +#pragma once +#include +#include +#include +#include + +namespace bcos +{ +namespace cppsdk +{ +namespace test +{ +class WsSessionFake : public bcos::boostssl::ws::WsSession +{ +public: + WsSessionFake(std::string _moduleName) : bcos::boostssl::ws::WsSession(_moduleName) + { + WEBSOCKET_SESSION(INFO) << LOG_KV("[NEWOBJ][WSSESSION]", this); + } + using Ptr = std::shared_ptr; + +public: + virtual void asyncSendMessage(std::shared_ptr _msg, + bcos::boostssl::ws::Options _options = bcos::boostssl::ws::Options(-1), + bcos::boostssl::ws::RespCallBack _respCallback = + bcos::boostssl::ws::RespCallBack()) override + { + (void)_msg; + (void)_options; + auto msg = std::make_shared(); + msg->setPayload(m_resp); + auto session = shared_from_this(); + _respCallback(m_error, msg, session); + } + +public: + virtual bool isConnected() override { return true; } + +public: + void setError(bcos::Error::Ptr _error) { m_error = _error; } + void setResp(std::shared_ptr _resp) { m_resp = _resp; } + +private: + bcos::Error::Ptr m_error; + std::shared_ptr m_resp; +}; +} // namespace test +} // namespace cppsdk +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/tests/unittests/main.cpp" "b/BFPL\345\243\271/bcos-sdk/tests/unittests/main.cpp" new file mode 100644 index 00000000..0cdfc68c --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/tests/unittests/main.cpp" @@ -0,0 +1,4 @@ +#define BOOST_TEST_MODULE FISCO_BCOS_Tests +#define BOOST_TEST_MAIN + +#include \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/tests/unittests/tx/TransactionTest.cpp" "b/BFPL\345\243\271/bcos-sdk/tests/unittests/tx/TransactionTest.cpp" new file mode 100644 index 00000000..fa5fd3c1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/tests/unittests/tx/TransactionTest.cpp" @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2021 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 TransactionTest.cpp + * @author: yujiechen + * @date 2022-05-31 + */ + +#include +#include +#include +#include +#include + +using namespace bcostars; +using namespace bcos; +using namespace bcos::crypto; + +namespace bcos::test +{ +BOOST_FIXTURE_TEST_SUITE(TransactionTest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(test_transaction) +{ + TransactionData txData; + std::string to("Target"); + bcos::bytes input(bcos::asBytes("Arguments")); + bcos::u256 nonce(800); + + txData.version = 0; + txData.to = to; + vector txInput(input.begin(), input.end()); + txData.input = std::move(txInput); + txData.nonce = boost::lexical_cast(nonce); + txData.blockLimit = 100; + txData.chainID = "testChain"; + txData.groupID = "testGroup"; + + auto cryptoSuite = + std::make_shared(std::make_shared(), + std::make_shared(), nullptr); + + // auto hash = txData.hash(cryptoSuite->hashImpl()); + // BOOST_CHECK_EQUAL( + // hash.hex(), "3577ef0338695b03c6f19d8b7c1aa1f443973214dde94879a44188490529ea70"); + + // // set version to 10 + // txData.version = 10; + // hash = txData.hash(cryptoSuite->hashImpl()); + // BOOST_CHECK_EQUAL( + // hash.hex(), "435da41370f4711de4259094c8362a7332cf752ec359d057bee97453ca9e5072"); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/tests/unittests/ws/BlockNumberInfoTest.cpp" "b/BFPL\345\243\271/bcos-sdk/tests/unittests/ws/BlockNumberInfoTest.cpp" new file mode 100644 index 00000000..119f771d --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/tests/unittests/ws/BlockNumberInfoTest.cpp" @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2021 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 BlockNumberTest.cpp + * @author: octopus + * @date 2021-10-26 + */ + +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::cppsdk::service; + +using namespace bcos; +using namespace bcos::test; + +BOOST_FIXTURE_TEST_SUITE(BlockNumberTest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(test_BlockNumber) +{ + { + int64_t blockNumber = 11; + std::string group = "group"; + std::string node = "node"; + + auto bni = std::make_shared(); + bni->setBlockNumber(blockNumber); + bni->setGroup(group); + bni->setNode(node); + + BOOST_CHECK_EQUAL(bni->blockNumber(), blockNumber); + BOOST_CHECK_EQUAL(bni->group(), group); + BOOST_CHECK_EQUAL(bni->node(), node); + } +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sdk/tests/unittests/ws/HandshakeResponseTest.cpp" "b/BFPL\345\243\271/bcos-sdk/tests/unittests/ws/HandshakeResponseTest.cpp" new file mode 100644 index 00000000..8b0446a2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sdk/tests/unittests/ws/HandshakeResponseTest.cpp" @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2021 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 HandshakeResponseTest.cpp + * @author: octopus + * @date 2021-10-26 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::cppsdk; +using namespace bcos::cppsdk::service; + +using namespace bcos; +using namespace bcos::rpc; +using namespace bcos::test; + +BOOST_FIXTURE_TEST_SUITE(HandshakeResponseTest, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(test_HandshakeResponse) +{ + auto groupInfoCodec = std::make_shared(); + { + int protocolVersion = 111; + auto response = std::make_shared(groupInfoCodec); + response->setProtocolVersion(protocolVersion); + BOOST_CHECK_EQUAL(response->protocolVersion(), protocolVersion); + } + + { + int protocolVersion = 111; + auto response = std::make_shared(groupInfoCodec); + response->setProtocolVersion(protocolVersion); + std::string encodedData; + response->encode(encodedData); + + auto response1 = std::make_shared(groupInfoCodec); + auto r = response1->decode(encodedData); + + BOOST_CHECK_EQUAL(r, true); + BOOST_CHECK_EQUAL(response1->protocolVersion(), protocolVersion); + } + + { + auto response = std::make_shared(groupInfoCodec); + BOOST_CHECK_EQUAL(response->decode("1adf"), false); + } +} +BOOST_AUTO_TEST_CASE(test_HandshakeRequest) +{ + auto protocolInfo = std::make_shared( + bcos::protocol::ProtocolModuleID::RpcService, 1, 10000); + HandshakeRequest request(protocolInfo); + auto encodedData = request.encode(); + + // decode + HandshakeRequest decodedRequest; + decodedRequest.decode(*encodedData); + BOOST_CHECK_EQUAL( + request.protocol().protocolModuleID(), decodedRequest.protocol().protocolModuleID()); + BOOST_CHECK_EQUAL(request.protocol().minVersion(), decodedRequest.protocol().minVersion()); + BOOST_CHECK_EQUAL(request.protocol().maxVersion(), decodedRequest.protocol().maxVersion()); + + // decode exception + HandshakeRequest exceptionRequest; + std::string invalidData = "invalidTest"; + BOOST_CHECK_EQUAL( + exceptionRequest.decode(bcos::bytes(invalidData.begin(), invalidData.end())), false); +} +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sealer/CMakeLists.txt" "b/BFPL\345\243\271/bcos-sealer/CMakeLists.txt" new file mode 100644 index 00000000..ea3e8c69 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sealer/CMakeLists.txt" @@ -0,0 +1,29 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for bcos-sealer +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 bcos-sealer +# 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. +#------------------------------------------------------------------------------ + +cmake_minimum_required(VERSION 3.10) +set(CMAKE_OSX_DEPLOYMENT_TARGET "11.3" CACHE STRING "Minimum OS X deployment version") + +include(Version) +project(bcos-sealer VERSION ${VERSION}) + +aux_source_directory(./bcos-sealer SRC_LIST) +include_directories(./bcos-sealer) +add_library(${SEALER_TARGET} ${SRC_LIST}) + +target_link_libraries(${SEALER_TARGET} PUBLIC ${UTILITIES_TARGET} bcos-framework) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sealer/bcos-sealer/Common.h" "b/BFPL\345\243\271/bcos-sealer/bcos-sealer/Common.h" new file mode 100644 index 00000000..335df601 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sealer/bcos-sealer/Common.h" @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021 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: yujiechen + * @date: 2021-05-14 + */ +#pragma once +#include +#define SEAL_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("CONSENSUS") << LOG_BADGE("SEALER") \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sealer/bcos-sealer/Sealer.cpp" "b/BFPL\345\243\271/bcos-sealer/bcos-sealer/Sealer.cpp" new file mode 100644 index 00000000..acd055c2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sealer/bcos-sealer/Sealer.cpp" @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2021 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 Sealer.cpp + * @author: yujiechen + * @date: 2021-05-14 + */ +#include "Sealer.h" +#include "Common.h" +#include + +using namespace bcos; +using namespace bcos::sealer; +using namespace bcos::protocol; + +void Sealer::start() +{ + if (m_running) + { + SEAL_LOG(INFO) << LOG_DESC("the sealer has already been started"); + return; + } + SEAL_LOG(INFO) << LOG_DESC("start the sealer"); + startWorking(); + m_running = true; +} + +void Sealer::stop() +{ + if (!m_running) + { + SEAL_LOG(INFO) << LOG_DESC("the sealer has already been stopped"); + return; + } + SEAL_LOG(INFO) << LOG_DESC("stop the sealer"); + m_running = false; + m_sealingManager->stop(); + finishWorker(); + if (isWorking()) + { + stopWorking(); + // will not restart worker, so terminate it + terminate(); + } +} + +void Sealer::init(bcos::consensus::ConsensusInterface::Ptr _consensus) +{ + m_sealerConfig->setConsensusInterface(_consensus); +} + +void Sealer::asyncNotifySealProposal(uint64_t _proposalStartIndex, uint64_t _proposalEndIndex, + uint64_t _maxTxsPerBlock, std::function _onRecvResponse) +{ + m_sealingManager->resetSealingInfo(_proposalStartIndex, _proposalEndIndex, _maxTxsPerBlock); + if (_onRecvResponse) + { + _onRecvResponse(nullptr); + } + SEAL_LOG(INFO) << LOG_DESC("asyncNotifySealProposal") + << LOG_KV("startIndex", _proposalStartIndex) + << LOG_KV("endIndex", _proposalEndIndex) + << LOG_KV("maxTxsPerBlock", _maxTxsPerBlock); +} + +void Sealer::asyncNoteLatestBlockNumber(int64_t _blockNumber) +{ + m_sealingManager->resetCurrentNumber(_blockNumber); + SEAL_LOG(INFO) << LOG_DESC("asyncNoteLatestBlockNumber") << LOG_KV("number", _blockNumber); +} + +void Sealer::asyncNoteUnSealedTxsSize( + uint64_t _unsealedTxsSize, std::function _onRecvResponse) +{ + m_sealingManager->setUnsealedTxsSize(_unsealedTxsSize); + if (_onRecvResponse) + { + _onRecvResponse(nullptr); + } +} + +void Sealer::executeWorker() +{ + if (!m_sealingManager->shouldGenerateProposal() && !m_sealingManager->shouldFetchTransaction()) + { + ///< 10 milliseconds to next loop + boost::unique_lock l(x_signalled); + m_signalled.wait_for(l, boost::chrono::milliseconds(1)); + } + // try to generateProposal + if (m_sealingManager->shouldGenerateProposal()) + { + auto ret = m_sealingManager->generateProposal(); + auto proposal = ret.second; + submitProposal(ret.first, proposal); + } + // try to fetch transactions + if (m_sealingManager->shouldFetchTransaction()) + { + m_sealingManager->fetchTransactions(); + } +} + +void Sealer::submitProposal(bool _containSysTxs, bcos::protocol::Block::Ptr _block) +{ + // Note: the block maybe empty + if (!_block) + { + return; + } + if (_block->blockHeader()->number() <= m_sealingManager->currentNumber()) + { + SEAL_LOG(INFO) << LOG_DESC("submitProposal return for the block has alreay been committed") + << LOG_KV("proposalIndex", _block->blockHeader()->number()) + << LOG_KV("currentNumber", m_sealingManager->currentNumber()); + m_sealingManager->notifyResetProposal(_block); + return; + } + // supplement the header info: set sealerList and weightList + std::vector sealerList; + std::vector weightList; + auto consensusNodeInfo = m_sealerConfig->consensus()->consensusNodeList(); + for (auto const& consensusNode : consensusNodeInfo) + { + sealerList.push_back(consensusNode->nodeID()->data()); + weightList.push_back(consensusNode->weight()); + } + _block->blockHeader()->setSealerList(std::move(sealerList)); + _block->blockHeader()->setConsensusWeights(std::move(weightList)); + _block->blockHeader()->setSealer(m_sealerConfig->consensus()->nodeIndex()); + // set the version + auto version = std::min(m_sealerConfig->consensus()->compatibilityVersion(), + (uint32_t)g_BCOSConfig.maxSupportedVersion()); + _block->blockHeader()->setVersion(version); + + auto encodedData = std::make_shared(); + _block->encode(*encodedData); + SEAL_LOG(INFO) << LOG_DESC("++++++++++++++++ Generate proposal") + << LOG_KV("index", _block->blockHeader()->number()) + << LOG_KV("curNum", m_sealingManager->currentNumber()) + << LOG_KV("hash", _block->blockHeader()->hash().abridged()) + << LOG_KV("sysTxs", _containSysTxs) + << LOG_KV("txsSize", _block->transactionsHashSize()) + << LOG_KV("version", version); + m_sealerConfig->consensus()->asyncSubmitProposal(_containSysTxs, ref(*encodedData), + _block->blockHeader()->number(), _block->blockHeader()->hash(), + [_block](Error::Ptr _error) { + if (_error == nullptr) + { + return; + } + SEAL_LOG(WARNING) << LOG_DESC("asyncSubmitProposal failed: put back the transactions") + << LOG_KV("txsSize", _block->transactionsHashSize()); + }); +} + +void Sealer::asyncResetSealing(std::function _onRecvResponse) +{ + m_sealingManager->resetSealing(); + if (_onRecvResponse) + { + _onRecvResponse(nullptr); + } +} diff --git "a/BFPL\345\243\271/bcos-sealer/bcos-sealer/Sealer.h" "b/BFPL\345\243\271/bcos-sealer/bcos-sealer/Sealer.h" new file mode 100644 index 00000000..5ca78df9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sealer/bcos-sealer/Sealer.h" @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2021 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 Sealer.h + * @author: yujiechen + * @date: 2021-05-14 + */ +#pragma once +#include "SealerConfig.h" +#include "SealingManager.h" +#include "bcos-framework/sealer/SealerInterface.h" +#include + +namespace bcos::sealer +{ +class Sealer : public Worker, public SealerInterface, public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + explicit Sealer(SealerConfig::Ptr _sealerConfig) + : Worker("Sealer", 0), m_sealerConfig(_sealerConfig) + { + m_sealingManager = std::make_shared(_sealerConfig); + m_sealingManager->onReady([=, this]() { this->noteGenerateProposal(); }); + } + ~Sealer() override = default; + + void start() override; + void stop() override; + + void asyncNotifySealProposal(uint64_t _proposalStartIndex, uint64_t _proposalEndIndex, + uint64_t _maxTxsPerBlock, std::function _onRecvResponse) override; + void asyncNoteUnSealedTxsSize( + uint64_t _unsealedTxsSize, std::function _onRecvResponse) override; + + void asyncNoteLatestBlockNumber(int64_t _blockNumber) override; + // interface for the consensus module to notify reset the sealing transactions + void asyncResetSealing(std::function _onRecvResponse) override; + + virtual void init(bcos::consensus::ConsensusInterface::Ptr _consensus); + +protected: + void executeWorker() override; + virtual void noteGenerateProposal() { m_signalled.notify_all(); } + + virtual void submitProposal(bool _containSysTxs, bcos::protocol::Block::Ptr _proposal); + +protected: + SealerConfig::Ptr m_sealerConfig; + SealingManager::Ptr m_sealingManager; + std::atomic_bool m_running = {false}; + + boost::condition_variable m_signalled; + // mutex to access m_signalled + boost::mutex x_signalled; +}; +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sealer/bcos-sealer/SealerConfig.h" "b/BFPL\345\243\271/bcos-sealer/bcos-sealer/SealerConfig.h" new file mode 100644 index 00000000..a5d292f6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sealer/bcos-sealer/SealerConfig.h" @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2021 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 SealerConfig.h + * @author: yujiechen + * @date: 2021-05-14 + */ +#pragma once +#include "bcos-framework/consensus/ConsensusInterface.h" +#include "bcos-framework/protocol/BlockFactory.h" +#include "bcos-framework/txpool/TxPoolInterface.h" +#include "bcos-tool/NodeTimeMaintenance.h" + +namespace bcos +{ +namespace sealer +{ +class SealerConfig +{ +public: + using Ptr = std::shared_ptr; + SealerConfig(bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::txpool::TxPoolInterface::Ptr _txpool, + bcos::tool::NodeTimeMaintenance::Ptr _nodeTimeMaintenance) + : m_txpool(_txpool), m_blockFactory(_blockFactory), + m_nodeTimeMaintenance(_nodeTimeMaintenance) + {} + virtual ~SealerConfig() {} + + virtual void setConsensusInterface(bcos::consensus::ConsensusInterface::Ptr _consensus) + { + m_consensus = _consensus; + } + virtual bcos::txpool::TxPoolInterface::Ptr txpool() { return m_txpool; } + + virtual unsigned minSealTime() const { return m_minSealTime; } + virtual void setMinSealTime(unsigned _minSealTime) { m_minSealTime = _minSealTime; } + + bcos::protocol::BlockFactory::Ptr blockFactory() { return m_blockFactory; } + bcos::consensus::ConsensusInterface::Ptr consensus() { return m_consensus; } + bcos::tool::NodeTimeMaintenance::Ptr nodeTimeMaintenance() { return m_nodeTimeMaintenance; } + +protected: + bcos::txpool::TxPoolInterface::Ptr m_txpool; + bcos::protocol::BlockFactory::Ptr m_blockFactory; + bcos::consensus::ConsensusInterface::Ptr m_consensus; + bcos::tool::NodeTimeMaintenance::Ptr m_nodeTimeMaintenance; + unsigned m_minSealTime = 500; +}; +} // namespace sealer +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sealer/bcos-sealer/SealerFactory.cpp" "b/BFPL\345\243\271/bcos-sealer/bcos-sealer/SealerFactory.cpp" new file mode 100644 index 00000000..0be138cb --- /dev/null +++ "b/BFPL\345\243\271/bcos-sealer/bcos-sealer/SealerFactory.cpp" @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 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 SealerFactory.cpp + * @author: yujiechen + * @date: 2021-05-20 + */ +#include "SealerFactory.h" + +#include "Sealer.h" +#include "bcos-tool/NodeTimeMaintenance.h" +#include + +using namespace bcos; +using namespace bcos::sealer; + +SealerFactory::SealerFactory(bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::txpool::TxPoolInterface::Ptr _txpool, unsigned _minSealTime, + bcos::tool::NodeTimeMaintenance::Ptr _nodeTimeMaintenance) + : m_blockFactory(std::move(_blockFactory)), + m_txpool(std::move(_txpool)), + m_minSealTime(_minSealTime), + m_nodeTimeMaintenance(std::move(_nodeTimeMaintenance)) +{} + +Sealer::Ptr SealerFactory::createSealer() +{ + auto sealerConfig = + std::make_shared(m_blockFactory, m_txpool, m_nodeTimeMaintenance); + sealerConfig->setMinSealTime(m_minSealTime); + return std::make_shared(sealerConfig); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sealer/bcos-sealer/SealerFactory.h" "b/BFPL\345\243\271/bcos-sealer/bcos-sealer/SealerFactory.h" new file mode 100644 index 00000000..20063083 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sealer/bcos-sealer/SealerFactory.h" @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2021 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 SealerFactory.h + * @author: yujiechen + * @date: 2021-05-20 + */ +#pragma once +#include "Sealer.h" +#include "SealerConfig.h" +namespace bcos +{ +namespace sealer +{ +class SealerFactory +{ +public: + using Ptr = std::shared_ptr; + SealerFactory(bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::txpool::TxPoolInterface::Ptr _txpool, unsigned _minSealTime, + bcos::tool::NodeTimeMaintenance::Ptr _nodeTimeMaintenance); + + virtual ~SealerFactory() = default; + Sealer::Ptr createSealer(); + +protected: + bcos::protocol::BlockFactory::Ptr m_blockFactory; + bcos::txpool::TxPoolInterface::Ptr m_txpool; + unsigned m_minSealTime; + bcos::tool::NodeTimeMaintenance::Ptr m_nodeTimeMaintenance; +}; +} // namespace sealer +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sealer/bcos-sealer/SealingManager.cpp" "b/BFPL\345\243\271/bcos-sealer/bcos-sealer/SealingManager.cpp" new file mode 100644 index 00000000..046897f0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sealer/bcos-sealer/SealingManager.cpp" @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2021 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 SealingManager.cpp + * @author: yujiechen + * @date: 2021-05-14 + */ +#include "SealingManager.h" +using namespace bcos; +using namespace bcos::sealer; +using namespace bcos::crypto; +using namespace bcos::protocol; + +void SealingManager::resetSealing() +{ + SEAL_LOG(INFO) << LOG_DESC("resetSealing") << LOG_KV("startNum", m_startSealingNumber) + << LOG_KV("endNum", m_endSealingNumber) << LOG_KV("sealingNum", m_sealingNumber) + << LOG_KV("pendingTxs", pendingTxsSize()) + << LOG_KV("unsealedTxs", m_unsealedTxsSize); + m_sealingNumber = m_endSealingNumber + 1; + clearPendingTxs(); +} + +void SealingManager::appendTransactions( + std::shared_ptr _txsQueue, Block::Ptr _fetchedTxs) +{ + WriteGuard l(x_pendingTxs); + // append the system transactions + for (size_t i = 0; i < _fetchedTxs->transactionsMetaDataSize(); i++) + { + _txsQueue->emplace_back( + std::const_pointer_cast(_fetchedTxs->transactionMetaData(i))); + } + m_onReady(); +} + +bool SealingManager::shouldGenerateProposal() +{ + if (m_sealingNumber < m_startSealingNumber || m_sealingNumber > m_endSealingNumber) + { + clearPendingTxs(); + return false; + } + // should wait the given block submit to the ledger + if (m_currentNumber < m_waitUntil) + { + return false; + } + // check the txs size + auto txsSize = pendingTxsSize(); + if (txsSize >= m_maxTxsPerBlock || reachMinSealTimeCondition()) + { + return true; + } + return false; +} + +void SealingManager::clearPendingTxs() +{ + UpgradableGuard l(x_pendingTxs); + auto pendingTxsSize = m_pendingTxs->size() + m_pendingSysTxs->size(); + if (pendingTxsSize == 0) + { + return; + } + // return the txs back to the txpool + SEAL_LOG(INFO) << LOG_DESC("clearPendingTxs: return back the unhandled transactions") + << LOG_KV("size", pendingTxsSize); + HashListPtr unHandledTxs = std::make_shared(); + for (const auto& txMetaData : *m_pendingTxs) + { + unHandledTxs->emplace_back(txMetaData->hash()); + } + for (const auto& txMetaData : *m_pendingSysTxs) + { + unHandledTxs->emplace_back(txMetaData->hash()); + } + auto self = weak_from_this(); + m_worker->enqueue([self, unHandledTxs]() { + try + { + auto sealerMgr = self.lock(); + if (!sealerMgr) + { + return; + } + sealerMgr->notifyResetTxsFlag(unHandledTxs, false); + } + catch (std::exception const& e) + { + SEAL_LOG(WARNING) << LOG_DESC( + "clearPendingTxs: return back the unhandled txs exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); + UpgradeGuard ul(l); + m_pendingTxs->clear(); + m_pendingSysTxs->clear(); +} + +void SealingManager::notifyResetTxsFlag(HashListPtr _txsHashList, bool _flag, size_t _retryTime) +{ + m_config->txpool()->asyncMarkTxs(_txsHashList, _flag, -1, HashType(), + [this, _txsHashList, _flag, _retryTime](Error::Ptr _error) { + if (_error == nullptr) + { + return; + } + SEAL_LOG(WARNING) << LOG_DESC("asyncMarkTxs failed, retry now"); + if (_retryTime >= 3) + { + return; + } + this->notifyResetTxsFlag(_txsHashList, _flag, _retryTime + 1); + }); +} + +void SealingManager::notifyResetProposal(bcos::protocol::Block::Ptr _block) +{ + auto txsHashList = std::make_shared(); + for (size_t i = 0; i < _block->transactionsHashSize(); i++) + { + txsHashList->push_back(_block->transactionHash(i)); + } + notifyResetTxsFlag(txsHashList, false); +} + +std::pair SealingManager::generateProposal() +{ + if (!shouldGenerateProposal()) + { + return std::pair(false, nullptr); + } + WriteGuard l(x_pendingTxs); + m_sealingNumber = std::max(m_sealingNumber.load(), m_currentNumber.load() + 1); + auto block = m_config->blockFactory()->createBlock(); + auto blockHeader = m_config->blockFactory()->blockHeaderFactory()->createBlockHeader(); + blockHeader->setNumber(m_sealingNumber); + blockHeader->setTimestamp(m_config->nodeTimeMaintenance()->getAlignedTime()); + block->setBlockHeader(blockHeader); + auto txsSize = + std::min((size_t)m_maxTxsPerBlock, (m_pendingTxs->size() + m_pendingSysTxs->size())); + // prioritize seal from the system txs list + auto systemTxsSize = std::min(txsSize, m_pendingSysTxs->size()); + if (m_pendingSysTxs->size() > 0) + { + m_waitUntil.store(m_sealingNumber); + SEAL_LOG(INFO) << LOG_DESC("seal the system transactions") + << LOG_KV("sealNextBlockUntil", m_waitUntil) + << LOG_KV("curNum", m_currentNumber); + } + bool containSysTxs = false; + for (size_t i = 0; i < systemTxsSize; i++) + { + block->appendTransactionMetaData(m_pendingSysTxs->front()); + m_pendingSysTxs->pop_front(); + containSysTxs = true; + } + for (size_t i = systemTxsSize; i < txsSize; i++) + { + block->appendTransactionMetaData(m_pendingTxs->front()); + m_pendingTxs->pop_front(); + } + m_sealingNumber++; + + m_lastSealTime = utcSteadyTime(); + // Note: When the last block(N) sealed by this node contains system transactions, + // if other nodes do not wait until block(N) is committed and directly seal block(N+1), + // will cause system exceptions. + return {containSysTxs, block}; +} + +size_t SealingManager::pendingTxsSize() +{ + ReadGuard l(x_pendingTxs); + return m_pendingSysTxs->size() + m_pendingTxs->size(); +} +bool SealingManager::reachMinSealTimeCondition() +{ + auto txsSize = pendingTxsSize(); + if (txsSize == 0) + { + return false; + } + if ((utcSteadyTime() - m_lastSealTime) < m_config->minSealTime()) + { + return false; + } + return true; +} + +bool SealingManager::shouldFetchTransaction() +{ + // fetching transactions currently + if (m_fetchingTxs.load() || m_unsealedTxsSize == 0) + { + return false; + } + // no need to sealing + if (m_sealingNumber < m_startSealingNumber || m_sealingNumber > m_endSealingNumber) + { + return false; + } + return true; +} + +int64_t SealingManager::txsSizeExpectedToFetch() +{ + auto txsSizeToFetch = (m_endSealingNumber - m_sealingNumber + 1) * m_maxTxsPerBlock; + auto txsSize = pendingTxsSize(); + if (txsSizeToFetch <= txsSize) + { + return 0; + } + return (txsSizeToFetch - txsSize); +} + +void SealingManager::fetchTransactions() +{ + if (!shouldFetchTransaction()) + { + return; + } + auto txsToFetch = txsSizeExpectedToFetch(); + if (txsToFetch == 0) + { + return; + } + // try to fetch transactions + m_fetchingTxs = true; + ssize_t startSealingNumber = m_startSealingNumber; + ssize_t endSealingNumber = m_endSealingNumber; + auto self = weak_from_this(); + m_config->txpool()->asyncSealTxs(txsToFetch, nullptr, + [self, startSealingNumber, endSealingNumber]( + Error::Ptr _error, Block::Ptr _txsHashList, Block::Ptr _sysTxsList) { + try + { + auto sealingMgr = self.lock(); + if (!sealingMgr) + { + return; + } + if (_error != nullptr) + { + SEAL_LOG(WARNING) << LOG_DESC("fetchTransactions exception") + << LOG_KV("returnCode", _error->errorCode()) + << LOG_KV("returnMsg", _error->errorMessage()); + sealingMgr->m_fetchingTxs = false; + return; + } + bool abort = true; + if ((sealingMgr->m_sealingNumber >= startSealingNumber) && + (sealingMgr->m_sealingNumber <= endSealingNumber)) + { + sealingMgr->appendTransactions(sealingMgr->m_pendingTxs, _txsHashList); + sealingMgr->appendTransactions(sealingMgr->m_pendingSysTxs, _sysTxsList); + abort = false; + } + else + { + SEAL_LOG(INFO) << LOG_DESC("fetchTransactions finish: abort the expired txs") + << LOG_KV("txsSize", _txsHashList->transactionsMetaDataSize()) + << LOG_KV("sysTxsSize", _sysTxsList->transactionsMetaDataSize()); + // Note: should reset the aborted txs + sealingMgr->notifyResetProposal(_txsHashList); + sealingMgr->notifyResetProposal(_sysTxsList); + } + sealingMgr->m_fetchingTxs = false; + sealingMgr->m_onReady(); + SEAL_LOG(DEBUG) << LOG_DESC("fetchTransactions finish") + << LOG_KV("txsSize", _txsHashList->transactionsMetaDataSize()) + << LOG_KV("sysTxsSize", _sysTxsList->transactionsMetaDataSize()) + << LOG_KV("startSealingNumber", startSealingNumber) + << LOG_KV("endSealingNumber", endSealingNumber) + << LOG_KV("sealingNumber", sealingMgr->m_sealingNumber) + << LOG_KV("abort", abort); + } + catch (std::exception const& e) + { + SEAL_LOG(WARNING) << LOG_DESC("fetchTransactions: onRecv sealed txs failed") + << LOG_KV("error", boost::diagnostic_information(e)) + << LOG_KV( + "fetchedTxsSize", _txsHashList->transactionsMetaDataSize()) + << LOG_KV( + "fetchedSysTxs", _sysTxsList->transactionsMetaDataSize()) + << LOG_KV("returnCode", _error->errorCode()) + << LOG_KV("returnMsg", _error->errorMessage()); + } + }); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sealer/bcos-sealer/SealingManager.h" "b/BFPL\345\243\271/bcos-sealer/bcos-sealer/SealingManager.h" new file mode 100644 index 00000000..7271e3b3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sealer/bcos-sealer/SealingManager.h" @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2021 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 SealingManager.h + * @author: yujiechen + * @date: 2021-05-14 + */ +#pragma once +#include "Common.h" +#include "SealerConfig.h" +#include "bcos-framework/protocol/BlockFactory.h" +#include "bcos-framework/protocol/TransactionMetaData.h" +#include +#include +namespace bcos +{ +namespace sealer +{ +using TxsMetaDataQueue = std::deque; +class SealingManager : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + explicit SealingManager(SealerConfig::Ptr _config) + : m_config(_config), + m_pendingTxs(std::make_shared()), + m_pendingSysTxs(std::make_shared()), + m_worker(std::make_shared("sealerWorker", 1)) + {} + + virtual ~SealingManager() { stop(); } + + virtual void stop() + { + if (m_worker) + { + m_worker->stop(); + } + } + + virtual bool shouldGenerateProposal(); + virtual bool shouldFetchTransaction(); + + std::pair generateProposal(); + virtual void setUnsealedTxsSize(size_t _unsealedTxsSize) + { + m_unsealedTxsSize = _unsealedTxsSize; + m_config->consensus()->asyncNoteUnSealedTxsSize(_unsealedTxsSize, [](Error::Ptr _error) { + if (_error) + { + SEAL_LOG(WARNING) << LOG_DESC( + "asyncNoteUnSealedTxsSize to the consensus module failed") + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + } + }); + } + + // the consensus module notify the sealer to reset sealing when viewchange + virtual void resetSealing(); + virtual void resetSealingInfo( + ssize_t _startSealingNumber, ssize_t _endSealingNumber, size_t _maxTxsPerBlock) + { + if (_startSealingNumber > _endSealingNumber) + { + return; + } + // non-continuous sealing request + if (m_sealingNumber > m_endSealingNumber || _startSealingNumber != (m_endSealingNumber + 1)) + { + clearPendingTxs(); + m_startSealingNumber = _startSealingNumber; + m_sealingNumber = _startSealingNumber; + m_lastSealTime = utcSteadyTime(); + if (m_waitUntil >= m_startSealingNumber) + { + SEAL_LOG(INFO) << LOG_DESC("resetSealingInfo: reset waitUntil for reseal"); + m_waitUntil.store(m_startSealingNumber - 1); + } + } + m_endSealingNumber = _endSealingNumber; + m_maxTxsPerBlock = _maxTxsPerBlock; + m_onReady(); + SEAL_LOG(INFO) << LOG_DESC("resetSealingInfo") << LOG_KV("start", m_startSealingNumber) + << LOG_KV("end", m_endSealingNumber) + << LOG_KV("sealingNumber", m_sealingNumber) + << LOG_KV("waitUntil", m_waitUntil) + << LOG_KV("unsealedTxs", m_unsealedTxsSize); + } + + virtual void resetCurrentNumber(int64_t _currentNumber) { m_currentNumber = _currentNumber; } + virtual int64_t currentNumber() const { return m_currentNumber; } + virtual void fetchTransactions(); + + template + bcos::Handler<> onReady(T const& _t) + { + return m_onReady.add(_t); + } + virtual void notifyResetProposal(bcos::protocol::Block::Ptr _block); + +protected: + virtual void appendTransactions( + std::shared_ptr _txsQueue, bcos::protocol::Block::Ptr _fetchedTxs); + virtual bool reachMinSealTimeCondition(); + virtual void clearPendingTxs(); + virtual void notifyResetTxsFlag( + bcos::crypto::HashListPtr _txsHash, bool _flag, size_t _retryTime = 0); + + virtual int64_t txsSizeExpectedToFetch(); + virtual size_t pendingTxsSize(); + +private: + SealerConfig::Ptr m_config; + std::shared_ptr m_pendingTxs; + std::shared_ptr m_pendingSysTxs; + SharedMutex x_pendingTxs; + + ThreadPool::Ptr m_worker; + + std::atomic m_lastSealTime = {0}; + + // the invalid sealingNumber is -1 + std::atomic m_sealingNumber = {-1}; + std::atomic m_unsealedTxsSize = {0}; + + std::atomic m_startSealingNumber = {0}; + std::atomic m_endSealingNumber = {0}; + std::atomic m_maxTxsPerBlock = {0}; + + std::atomic m_waitUntil = {0}; + + bcos::CallbackCollectionHandler<> m_onReady; + + std::atomic_bool m_fetchingTxs = {false}; + + std::atomic m_currentNumber = {0}; +}; +} // namespace sealer +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-security/CMakeLists.txt" "b/BFPL\345\243\271/bcos-security/CMakeLists.txt" new file mode 100644 index 00000000..d875a8c4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-security/CMakeLists.txt" @@ -0,0 +1,37 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for bcos-security +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 bcos-security +# 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. +#------------------------------------------------------------------------------ + +cmake_minimum_required(VERSION 3.10) +set(CMAKE_OSX_DEPLOYMENT_TARGET "11.3" CACHE STRING "Minimum OS X deployment version") + +include(Version) +project(bcos-security VERSION ${VERSION}) + +aux_source_directory(./bcos-security SRC_LIST) +include_directories(./bcos-security) +add_library(${SECURITY_TARGET} ${SRC_LIST}) + +find_package(jsoncpp REQUIRED) + +target_link_libraries(${SECURITY_TARGET} PUBLIC ${UTILITIES_TARGET} ${TOOL_TARGET} ${CRYPTO_TARGET} jsoncpp_static) + +if(TESTS) + enable_testing() + set(ENV{CTEST_OUTPUT_ON_FAILURE} True) + add_subdirectory(test) +endif() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-security/bcos-security/Common.h" "b/BFPL\345\243\271/bcos-security/bcos-security/Common.h" new file mode 100644 index 00000000..76dfef07 --- /dev/null +++ "b/BFPL\345\243\271/bcos-security/bcos-security/Common.h" @@ -0,0 +1,26 @@ +#pragma once +#include +#include + +// bcos-security is used to storage security + +namespace bcos +{ + +namespace security +{ +DERIVE_BCOS_EXCEPTION(KeyCenterAlreadyInit); +DERIVE_BCOS_EXCEPTION(KeyCenterDataKeyError); +DERIVE_BCOS_EXCEPTION(KeyCenterConnectionError); +DERIVE_BCOS_EXCEPTION(KeyCenterCall); +DERIVE_BCOS_EXCEPTION(KeyCenterInitError); +DERIVE_BCOS_EXCEPTION(KeyCenterCloseError); +DERIVE_BCOS_EXCEPTION(EncryptedFileError); +DERIVE_BCOS_EXCEPTION(EncryptedLevelDBEncryptFailed); +DERIVE_BCOS_EXCEPTION(EncryptedLevelDBDecryptFailed); +DERIVE_BCOS_EXCEPTION(EncryptFailed); +DERIVE_BCOS_EXCEPTION(DecryptFailed); + +} // namespace security + +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-security/bcos-security/DataEncryption.cpp" "b/BFPL\345\243\271/bcos-security/bcos-security/DataEncryption.cpp" new file mode 100644 index 00000000..465a0577 --- /dev/null +++ "b/BFPL\345\243\271/bcos-security/bcos-security/DataEncryption.cpp" @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2021 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. + * + */ +/** + * @brief : Encrypt file + * @author: jimmyshi, websterchen + * @date: 2018-12-06 + */ + +#include "DataEncryption.h" +#include "KeyCenter.h" +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace crypto; +using namespace tool; + +namespace bcos +{ +namespace security +{ +void DataEncryption::init() +{ + bool smCryptoType = m_nodeConfig->smCryptoType(); + + if (true == m_nodeConfig->storageSecurityEnable()) + { + std::string keyCenterIp = m_nodeConfig->storageSecurityKeyCenterIp(); + unsigned short keyCenterPort = m_nodeConfig->storageSecurityKeyCenterPort(); + std::string cipherDataKey = m_nodeConfig->storageSecurityCipherDataKey(); + + KeyCenter keyClient; + keyClient.setIpPort(keyCenterIp, keyCenterPort); + m_dataKey = asString(keyClient.getDataKey(cipherDataKey, smCryptoType)); + + BCOS_LOG(INFO) << LOG_BADGE("DataEncryption::init") << LOG_KV("key_center_ip:", keyCenterIp) + << LOG_KV("key_center_port:", keyCenterPort); + } + + if (false == smCryptoType) + { + m_symmetricEncrypt = std::make_shared(); + } + else + { + m_symmetricEncrypt = std::make_shared(); + } +} + +void DataEncryption::init(const std::string& dataKey, const bool smCryptoType) +{ + m_dataKey = dataKey; + + if (false == smCryptoType) + { + m_symmetricEncrypt = std::make_shared(); + } + else + { + m_symmetricEncrypt = std::make_shared(); + } +} + +std::shared_ptr DataEncryption::decryptContents(const std::shared_ptr& content) +{ + std::shared_ptr decFileBytes; + try + { + std::string encContextsStr((const char*)content->data(), content->size()); + + bytes encFileBytes = fromHex(encContextsStr); + BCOS_LOG(DEBUG) << LOG_BADGE("ENCFILE") << LOG_DESC("Enc file contents") + << LOG_KV("string", encContextsStr) << LOG_KV("bytes", toHex(encFileBytes)); + + bytesPointer decFileBytesBase64Ptr = + m_symmetricEncrypt->symmetricDecrypt((const unsigned char*)encFileBytes.data(), + encFileBytes.size(), (const unsigned char*)m_dataKey.data(), m_dataKey.size()); + + BCOS_LOG(DEBUG) << "[ENCFILE] EncryptedFile Base64 key: " + << asString(*decFileBytesBase64Ptr) << endl; + decFileBytes = base64DecodeBytes(asString(*decFileBytesBase64Ptr)); + } + catch (exception& e) + { + BCOS_LOG(ERROR) << LOG_DESC("[ENCFILE] EncryptedFile error") + << LOG_KV("what", boost::diagnostic_information(e)); + BOOST_THROW_EXCEPTION(EncryptedFileError()); + } + + return decFileBytes; +} + +std::shared_ptr DataEncryption::decryptFile(const std::string& filename) +{ + std::shared_ptr decFileBytes; + try + { + std::shared_ptr keyContent = readContents(boost::filesystem::path(filename)); + + std::string encContextsStr((const char*)keyContent->data(), keyContent->size()); + + bytes encFileBytes = fromHex(encContextsStr); + BCOS_LOG(DEBUG) << LOG_BADGE("ENCFILE") << LOG_DESC("Enc file contents") + << LOG_KV("string", encContextsStr) << LOG_KV("bytes", toHex(encFileBytes)); + + bytesPointer decFileBytesBase64Ptr = + m_symmetricEncrypt->symmetricDecrypt((const unsigned char*)encFileBytes.data(), + encFileBytes.size(), (const unsigned char*)m_dataKey.data(), m_dataKey.size()); + + BCOS_LOG(DEBUG) << "[ENCFILE] EncryptedFile Base64 key: " + << asString(*decFileBytesBase64Ptr) << endl; + decFileBytes = base64DecodeBytes(asString(*decFileBytesBase64Ptr)); + } + catch (exception& e) + { + BCOS_LOG(ERROR) << LOG_DESC("[ENCFILE] EncryptedFile error") + << LOG_KV("what", boost::diagnostic_information(e)); + BOOST_THROW_EXCEPTION(EncryptedFileError()); + } + + return decFileBytes; +} + +std::string DataEncryption::encrypt(const std::string& data) +{ + bytesPointer encData = m_symmetricEncrypt->symmetricEncrypt( + reinterpret_cast(data.data()), data.size(), + reinterpret_cast(m_dataKey.data()), m_dataKey.size()); + + std::string value(encData->size(), 0); + memcpy(value.data(), encData->data(), encData->size()); + + return value; +} + +std::string DataEncryption::decrypt(const std::string& data) +{ + bytesPointer decData = m_symmetricEncrypt->symmetricDecrypt( + reinterpret_cast(data.data()), data.size(), + reinterpret_cast(m_dataKey.data()), m_dataKey.size()); + + std::string value(decData->size(), 0); + memcpy(value.data(), decData->data(), decData->size()); + + return value; +} + +} // namespace security + +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-security/bcos-security/DataEncryption.h" "b/BFPL\345\243\271/bcos-security/bcos-security/DataEncryption.h" new file mode 100644 index 00000000..febd839f --- /dev/null +++ "b/BFPL\345\243\271/bcos-security/bcos-security/DataEncryption.h" @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2021 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. + * + */ +/** + * @brief : Data Encryption + * @author: chuwen + * @date: 2018-12-06 + */ + +#pragma once +#include "Common.h" +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace security +{ +class DataEncryption : public DataEncryptInterface +{ +public: + using Ptr = std::shared_ptr; + +public: + DataEncryption(const bcos::tool::NodeConfig::Ptr nodeConfig) : m_nodeConfig(nodeConfig) {} + ~DataEncryption() override {} + +public: + void init() override; + void init(const std::string& dataKey, const bool smCryptoType) override; + + std::shared_ptr decryptContents(const std::shared_ptr& contents) override; + + // use to decrypt node.key + std::shared_ptr decryptFile(const std::string& filename) override; + + // use to encrypt/decrypt in rocksdb + std::string encrypt(const std::string& data) override; + std::string decrypt(const std::string& data) override; + +private: + bcos::tool::NodeConfig::Ptr m_nodeConfig{nullptr}; + + std::string m_dataKey; + bcos::crypto::SymmetricEncryption::Ptr m_symmetricEncrypt{nullptr}; +}; + +} // namespace security + +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-security/bcos-security/KeyCenter.cpp" "b/BFPL\345\243\271/bcos-security/bcos-security/KeyCenter.cpp" new file mode 100644 index 00000000..9515b54b --- /dev/null +++ "b/BFPL\345\243\271/bcos-security/bcos-security/KeyCenter.cpp" @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2021 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. + * + */ +/** + * @brief : keycenter for disk encrytion + * @author: jimmyshi + * @date: 2018-12-03 + */ + +#include "KeyCenter.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace crypto; +using namespace security; + +namespace beast = boost::beast; // from +namespace http = beast::http; // from +namespace net = boost::asio; // from +using tcp = net::ip::tcp; // from + +KeyCenterHttpClient::KeyCenterHttpClient(const string& _ip, int _port) + : KeyCenterHttpClientInterface(), m_ip(_ip), m_port(_port), m_ioc(), m_socket(m_ioc) +{} + +KeyCenterHttpClient::~KeyCenterHttpClient() +{ + close(); +} + +void KeyCenterHttpClient::connect() +{ + WriteGuard l(x_clinetSocket); + try + { + if (m_socket.is_open()) + return; + // These objects perform our I/O + tcp::resolver resolver{m_ioc}; + // Look up the domain name + auto const results = resolver.resolve(m_ip, to_string(m_port).c_str()); + + // Make the connection on the IP address we get from a lookup + // TODO Add timeout in connect and read write + net::connect(m_socket, results.begin(), results.end()); + } + catch (exception& e) + { + KC_LOG(DEBUG) << LOG_DESC("Init key manager failed.") << LOG_KV("reason", e.what()) << endl; + BOOST_THROW_EXCEPTION(KeyCenterInitError()); + } +} + +void KeyCenterHttpClient::close() +{ + WriteGuard l(x_clinetSocket); + if (!m_socket.is_open()) + return; + // Gracefully close the socket + beast::error_code ec; + m_socket.shutdown(tcp::socket::shutdown_both, ec); + + if (ec && ec != beast::errc::not_connected) + { + KC_LOG(DEBUG) << LOG_DESC("Close key manager failed.") << LOG_KV("error_code", ec) << endl; + BOOST_THROW_EXCEPTION(KeyCenterCloseError()); + } +} + +Json::Value KeyCenterHttpClient::callMethod(const string& _method, Json::Value _params) +{ + if (!m_socket.is_open()) + connect(); // Jump out immediately if has connected + + Json::Value res; + try + { + /* + query is: + {"jsonrpc":"2.0","method":"encDataKey","params":["123456"],"id":83} + */ + + Json::Value queryJson; + queryJson["id"] = 83; + queryJson["jsonrpc"] = "2.0"; + queryJson["method"] = _method; + queryJson["params"] = _params; + + Json::FastWriter fastWriter; + std::string queryJsonStr = fastWriter.write(queryJson); + std::string url = m_ip + ":" + to_string(m_port); + // std::cout << queryJsonStr << " length: " << queryJsonStr.length() << std::endl; + + http::request req{http::verb::post, "/", 11}; + req.set(http::field::host, url.c_str()); + req.set(http::field::accept, "*/*"); + req.set(http::field::content_type, "application/json"); + req.set(http::field::accept_charset, "utf-8"); + + req.body() = queryJsonStr.c_str(); + req.prepare_payload(); + + // Send the HTTP request to the remote host + http::write(m_socket, req); + + // This buffer is used for reading and must be persisted + beast::flat_buffer buffer; + + // Declare a container to hold the response + http::response rsp; + + // Receive the HTTP response + http::read(m_socket, buffer, rsp); + + KC_LOG(DEBUG) << LOG_BADGE("callMethod") << LOG_DESC("key manager respond") + << LOG_KV("code", rsp.result_int()); + + if (rsp.result_int() != 200) + { + KC_LOG(DEBUG) << LOG_BADGE("callMethod") << LOG_DESC("http error") + << LOG_KV("reason", rsp.result_int()); + throw; + } + + Json::Reader reader; + bool parsingSuccessful = reader.parse(rsp.body().c_str(), res); + if (!parsingSuccessful) + { + KC_LOG(DEBUG) << LOG_BADGE("callMethod") << LOG_DESC("respond json error") + << LOG_KV("code", rsp.result_int()) << LOG_KV("string", rsp.body()); + throw; + } + + return res["result"]; + } + catch (exception& e) + { + KC_LOG(DEBUG) << LOG_DESC("CallMethod error") << LOG_KV("reason", e.what()); + BOOST_THROW_EXCEPTION(KeyCenterConnectionError()); + } + + return res; +} + +const bytes KeyCenter::getDataKey(const std::string& _cipherDataKey, const bool isSMCrypto) +{ + if (_cipherDataKey.empty()) + { + KC_LOG(DEBUG) << LOG_DESC("Get datakey exception. cipherDataKey is empty"); + BOOST_THROW_EXCEPTION(KeyCenterDataKeyError()); + } + + if (m_lastQueryCipherDataKey == _cipherDataKey) + { + return m_lastRcvDataKey; + } + + string dataKeyBytesStr; + try + { + // Create + KeyCenterHttpClientInterface::Ptr kcclient; + if (m_kcclient == nullptr) + { + kcclient = make_shared(m_ip, m_port); + } + else + { + kcclient = m_kcclient; + } + + // connect + kcclient->connect(); + + // send and receive + Json::Value params(Json::arrayValue); + params.append(_cipherDataKey); + Json::Value rsp = kcclient->callMethod("decDataKey", params); + + // parse respond + int error = rsp["error"].asInt(); + dataKeyBytesStr = rsp["dataKey"].asString(); + string info = rsp["info"].asString(); + if (error) + { + KC_LOG(DEBUG) << LOG_DESC("Get datakey exception") << LOG_KV("keycentr info", info); + BOOST_THROW_EXCEPTION(KeyCenterConnectionError() << errinfo_comment(info)); + } + + // update query cache + m_lastQueryCipherDataKey = _cipherDataKey; + bytes readableDataKey = fromHex(dataKeyBytesStr); + m_lastRcvDataKey = uniformDataKey(readableDataKey, isSMCrypto); + + // close + kcclient->close(); + } + catch (exception& e) + { + clearCache(); + KC_LOG(DEBUG) << LOG_DESC("Get datakey exception") << LOG_KV("reason", e.what()); + BOOST_THROW_EXCEPTION(KeyCenterConnectionError() << errinfo_comment(e.what())); + } + + return m_lastRcvDataKey; +} + +void KeyCenter::setIpPort(const std::string& _ip, int _port) +{ + m_ip = _ip; + m_port = _port; + m_url = m_ip + ":" + std::to_string(m_port); + KC_LOG(DEBUG) << LOG_DESC("Set instance url") << LOG_KV("IP", m_ip) << LOG_KV("port", m_port); +} + +bytes KeyCenter::uniformDataKey(const bytes& _readableDataKey, const bool isSMCrypto) +{ + if (true == isSMCrypto) + { + bytes oneTurn = sm3Hash(ref(_readableDataKey)).asBytes(); + return oneTurn + oneTurn + oneTurn + oneTurn; + } + else + { + bytes oneTurn = keccak256Hash(ref(_readableDataKey)).asBytes(); + return oneTurn; + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-security/bcos-security/KeyCenter.h" "b/BFPL\345\243\271/bcos-security/bcos-security/KeyCenter.h" new file mode 100644 index 00000000..a2d21af2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-security/bcos-security/KeyCenter.h" @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2021 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. + */ +/** + * @brief : keycenter for disk encrytion + * @author: jimmyshi + * @date: 2018-12-03 + */ +#pragma once +#include "Common.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Json +{ +class Value; +} + +namespace bcos +{ +namespace security +{ +#define KC_LOG(_OBV) \ + BCOS_LOG(_OBV) << "[g:null]" \ + << "[p:null][KeyManager]" + +class KeyCenterHttpClientInterface +{ +public: + using Ptr = std::shared_ptr; + virtual ~KeyCenterHttpClientInterface(){}; + virtual void connect() = 0; + virtual void close() = 0; + virtual Json::Value callMethod(const std::string& _method, Json::Value _params) = 0; +}; + +class KeyCenterHttpClient : public KeyCenterHttpClientInterface +{ +public: + using Ptr = std::shared_ptr; + + KeyCenterHttpClient(const std::string& _ip, int _port); + ~KeyCenterHttpClient(); + void connect() override; + void close() override; + Json::Value callMethod(const std::string& _method, Json::Value _params) override; + +private: + std::string m_ip; + int m_port; + boost::asio::io_context m_ioc; + boost::asio::ip::tcp::socket m_socket; + mutable SharedMutex x_clinetSocket; +}; + +class KeyCenter +{ +public: + using Ptr = std::shared_ptr; + + KeyCenter(){}; + virtual ~KeyCenter(){}; + virtual const bytes getDataKey(const std::string& _cipherDataKey, const bool isSMCrypto); + void setIpPort(const std::string& _ip, int _port); + const std::string url() { return m_ip + ":" + std::to_string(m_port); } + void setKcClient(KeyCenterHttpClientInterface::Ptr _kcclient) { m_kcclient = _kcclient; }; + bytes uniformDataKey(const bytes& _readableDataKey, const bool isSMCrypto); + + void clearCache() + { + m_lastQueryCipherDataKey.clear(); + m_lastRcvDataKey.clear(); + } + +private: + std::string m_ip; + int m_port; + std::string m_url; + + KeyCenterHttpClientInterface::Ptr m_kcclient = nullptr; + + // Query cache + std::string m_lastQueryCipherDataKey; + bytes m_lastRcvDataKey; +}; + +} // namespace security + +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-security/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-security/test/CMakeLists.txt" new file mode 100644 index 00000000..de3f31aa --- /dev/null +++ "b/BFPL\345\243\271/bcos-security/test/CMakeLists.txt" @@ -0,0 +1,28 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of bcos-tool +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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(GLOB_RECURSE SOURCES "*.cpp") + +# cmake settings +set(TEST_BINARY_NAME test-bcos-security) +find_package(Boost REQUIRED unit_test_framework) + +add_executable(${TEST_BINARY_NAME} ${SOURCES}) +target_include_directories(${TEST_BINARY_NAME} PRIVATE . ${CMAKE_SOURCE_DIR}) + +target_link_libraries(${TEST_BINARY_NAME} PUBLIC ${SECURITY_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 diff --git "a/BFPL\345\243\271/bcos-security/test/unittests/libsecurity/DataEncryptionTest.cpp" "b/BFPL\345\243\271/bcos-security/test/unittests/libsecurity/DataEncryptionTest.cpp" new file mode 100644 index 00000000..a1f5377e --- /dev/null +++ "b/BFPL\345\243\271/bcos-security/test/unittests/libsecurity/DataEncryptionTest.cpp" @@ -0,0 +1,34 @@ +#include +#include + +using namespace bcos::security; + +namespace bcos +{ +namespace test +{ + BOOST_AUTO_TEST_CASE(testDataEncryption_normal) + { + DataEncryption dataEncryption{ nullptr }; + dataEncryption.init("bcos_data_key", false); + + std::string originData = "hello world"; + std::string encryptData = dataEncryption.encrypt(originData); + std::string decryptData = dataEncryption.decrypt(encryptData); + + BOOST_CHECK_EQUAL(originData, decryptData); + } + + BOOST_AUTO_TEST_CASE(testDataEncryption_sm) + { + DataEncryption dataEncryption{ nullptr }; + dataEncryption.init("bcos_data_key", true); + + std::string originData = "hello world"; + std::string encryptData = dataEncryption.encrypt(originData); + std::string decryptData = dataEncryption.decrypt(encryptData); + + BOOST_CHECK_EQUAL(originData, decryptData); + } +} +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-security/test/unittests/main/main.cpp" "b/BFPL\345\243\271/bcos-security/test/unittests/main/main.cpp" new file mode 100644 index 00000000..5029377e --- /dev/null +++ "b/BFPL\345\243\271/bcos-security/test/unittests/main/main.cpp" @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 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 main.cpp + * @author: yujiechen, jimmyshi + * @date 2021-02-24 + */ +#define BOOST_TEST_MODULE FISCO_BCOS_Tests +#define BOOST_TEST_MAIN + +#include +#include diff --git "a/BFPL\345\243\271/bcos-storage/CMakeLists.txt" "b/BFPL\345\243\271/bcos-storage/CMakeLists.txt" new file mode 100644 index 00000000..85321139 --- /dev/null +++ "b/BFPL\345\243\271/bcos-storage/CMakeLists.txt" @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 3.14) +set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0" CACHE STRING "Minimum OS X deployment version") + +include(Version) + +project(bcos-storage VERSION ${VERSION}) + +find_package(zstd REQUIRED) +find_package(RocksDB REQUIRED) + +find_package(Boost REQUIRED serialization thread context filesystem) + +set(SRC_LIST bcos-storage/Common.cpp) +list(APPEND SRC_LIST bcos-storage/RocksDBStorage.cpp) + +set(LIB_LIST ${TABLE_TARGET} bcos-framework Boost::serialization Boost::filesystem zstd::libzstd_static RocksDB::rocksdb) + +if(WITH_TIKV) + include(ProjectTiKVClient) + list(APPEND SRC_LIST bcos-storage/TiKVStorage.cpp) + list(APPEND LIB_LIST kv_client) +endif() + +add_library(${STORAGE_TARGET} ${SRC_LIST}) +target_include_directories(${STORAGE_TARGET} PUBLIC ${HUNTER_INSTALL_PREFIX}/include .) +target_link_libraries(${STORAGE_TARGET} PUBLIC ${LIB_LIST}) + +if(TESTS) + enable_testing() + set(ENV{CTEST_OUTPUT_ON_FAILURE} True) + add_subdirectory(test) +endif() + +option(TOOLS "build tools" OFF) + +if(TOOLS) + add_subdirectory(tools) +endif() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-storage/bcos-storage/Common.cpp" "b/BFPL\345\243\271/bcos-storage/bcos-storage/Common.cpp" new file mode 100644 index 00000000..a5604571 --- /dev/null +++ "b/BFPL\345\243\271/bcos-storage/bcos-storage/Common.cpp" @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2021 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. + * + * @brief common functions + * @file Common.cpp + * @author: xingqiangbai + * @date: 2021-10-11 + */ + +#include "Common.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace bcos::storage +{ +} // namespace bcos::storage diff --git "a/BFPL\345\243\271/bcos-storage/bcos-storage/Common.h" "b/BFPL\345\243\271/bcos-storage/bcos-storage/Common.h" new file mode 100644 index 00000000..0a5a5730 --- /dev/null +++ "b/BFPL\345\243\271/bcos-storage/bcos-storage/Common.h" @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2021 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. + * + * @brief common functions + * @file Common.h + * @author: xingqiangbai + * @date: 2021-10-11 + */ + +#pragma once + +#include + +namespace bcos::storage +{ +const char* const TABLE_KEY_SPLIT = ":"; + +inline std::string toDBKey(const std::string_view& tableName, const std::string_view& key) +{ + std::string dbKey; + dbKey.reserve(tableName.size() + 1 + key.size()); + dbKey.append(tableName).append(TABLE_KEY_SPLIT).append(key); + return dbKey; +} + +inline bool isValid(const std::string_view& tableName) +{ + return !tableName.empty(); +} + +inline bool isValid(const std::string_view& tableName, const std::string_view&) +{ + return !tableName.empty(); +} + +} // namespace bcos::storage diff --git "a/BFPL\345\243\271/bcos-storage/bcos-storage/RocksDBStorage.cpp" "b/BFPL\345\243\271/bcos-storage/bcos-storage/RocksDBStorage.cpp" new file mode 100644 index 00000000..8df80ac2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-storage/bcos-storage/RocksDBStorage.cpp" @@ -0,0 +1,574 @@ +/* + * Copyright (C) 2021 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. + * + * @brief the header of storage + * @file Storage.h + * @author: xingqiangbai + * @date: 2021-04-16 + */ +#include "RocksDBStorage.h" +#include "Common.h" +#include "bcos-framework/protocol/ProtocolTypeDef.h" +#include "bcos-framework/storage/Table.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos::storage; +using namespace bcos::protocol; +using namespace rocksdb; +using namespace std; + +#define STORAGE_ROCKSDB_LOG(LEVEL) BCOS_LOG(LEVEL) << "[STORAGE-RocksDB]" + +RocksDBStorage::RocksDBStorage(std::unique_ptr>&& db, + const bcos::security::DataEncryptInterface::Ptr dataEncryption) + : m_db(std::move(db)), m_dataEncryption(dataEncryption) +{ + m_writeBatch = std::make_shared(); +} + +void RocksDBStorage::asyncGetPrimaryKeys(std::string_view _table, + const std::optional& _condition, + std::function)> _callback) +{ + auto start = utcTime(); + std::vector result; + + std::string keyPrefix; + keyPrefix = string(_table) + TABLE_KEY_SPLIT; + + ReadOptions read_options; + read_options.total_order_seek = true; + auto iter = m_db->NewIterator(read_options); + + // FIXME: check performance and add limit of primary keys + for (iter->Seek(keyPrefix); iter->Valid() && iter->key().starts_with(keyPrefix); iter->Next()) + { + size_t start = keyPrefix.size(); + if (!_condition || _condition->isValid(std::string_view( + iter->key().data() + start, iter->key().size() - start))) + { // filter by condition, the key need + // remove TABLE_PREFIX + result.emplace_back(iter->key().ToString().substr(start)); + } + } + delete iter; + auto end = utcTime(); + _callback(nullptr, std::move(result)); + STORAGE_ROCKSDB_LOG(TRACE) << LOG_DESC("asyncGetPrimaryKeys") << LOG_KV("table", _table) + << LOG_KV("count", result.size()) + << LOG_KV("read time(ms)", end - start) + << LOG_KV("callback time(ms)", utcTime() - end); +} + +void RocksDBStorage::asyncGetRow(std::string_view _table, std::string_view _key, + std::function)> _callback) +{ + try + { + if (!isValid(_table, _key)) + { + STORAGE_ROCKSDB_LOG(WARNING) << LOG_DESC("asyncGetRow empty tableName or key") + << LOG_KV("table", _table) << LOG_KV("key", _key); + _callback(BCOS_ERROR_UNIQUE_PTR(TableNotExists, "empty tableName or key"), {}); + return; + } + auto start = utcTime(); + std::string value; + auto dbKey = toDBKey(_table, _key); + + auto status = m_db->Get( + ReadOptions(), m_db->DefaultColumnFamily(), Slice(dbKey.data(), dbKey.size()), &value); + + if (!value.empty() && nullptr != m_dataEncryption) + { + value = m_dataEncryption->decrypt(value); + } + + if (!status.ok()) + { + if (status.IsNotFound()) + { + if (c_fileLogLevel <= TRACE) + { + STORAGE_ROCKSDB_LOG(TRACE) << LOG_DESC("not found") << LOG_KV("table", _table) + << LOG_KV("key", toHex(_key)); + } + _callback(nullptr, {}); + return; + } + + std::string errorMessage = + "RocksDB get failed!, " + boost::lexical_cast(status.ToString()); + STORAGE_ROCKSDB_LOG(WARNING) + << LOG_DESC("asyncGetRow failed") << LOG_KV("table", _table) << LOG_KV("key", _key) + << LOG_KV("error", errorMessage); + if (status.getState()) + { + errorMessage.append(" ").append(status.getState()); + } + _callback(BCOS_ERROR_UNIQUE_PTR(ReadError, errorMessage), {}); + + return; + } + auto end = utcTime(); + auto end2 = utcTime(); + + std::optional entry((Entry())); + + entry->set(std::move(value)); + + _callback(nullptr, entry); + + STORAGE_ROCKSDB_LOG(TRACE) + << LOG_DESC("asyncGetRow") << LOG_KV("table", _table) + << LOG_KV("key", boost::algorithm::hex_lower(std::string(_key))) + << LOG_KV("read time(ms)", end - start) << LOG_KV("tableInfo time(ms)", end2 - end) + << LOG_KV("callback time(ms)", utcTime() - end2); + } + catch (const std::exception& e) + { + STORAGE_ROCKSDB_LOG(WARNING) + << LOG_DESC("asyncGetRow exception") << LOG_KV("table", _table) + << LOG_KV("key", boost::algorithm::hex_lower(std::string(_key))) + << LOG_KV("exception", boost::diagnostic_information(e)); + _callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(UnknownEntryType, "Get row failed!", e), {}); + } +} + +void RocksDBStorage::asyncGetRows(std::string_view _table, + const std::variant, const gsl::span>& + _keys, + std::function>)> _callback) +{ + try + { + if (!isValid(_table)) + { + STORAGE_ROCKSDB_LOG(WARNING) + << LOG_DESC("asyncGetRow empty tableName") << LOG_KV("table", _table); + _callback(BCOS_ERROR_UNIQUE_PTR(TableNotExists, "empty tableName"), {}); + return; + } + auto start = utcTime(); + std::visit( + [&](auto const& keys) { + std::vector> entries(keys.size()); + + std::vector dbKeys(keys.size()); + std::vector slices(keys.size()); + tbb::parallel_for(tbb::blocked_range(0, keys.size()), + [&](const tbb::blocked_range& range) { + for (size_t i = range.begin(); i != range.end(); ++i) + { + dbKeys[i] = toDBKey(_table, keys[i]); + slices[i] = Slice(dbKeys[i].data(), dbKeys[i].size()); + } + }); + + std::vector values(keys.size()); + std::vector statusList(keys.size()); + m_db->MultiGet(ReadOptions(), m_db->DefaultColumnFamily(), slices.size(), + slices.data(), values.data(), statusList.data()); + auto end = utcTime(); + tbb::parallel_for(tbb::blocked_range(0, keys.size()), + [&](const tbb::blocked_range& range) { + for (size_t i = range.begin(); i != range.end(); ++i) + { + auto& status = statusList[i]; + auto& value = values[i]; + + if (status.ok()) + { + entries[i] = std::make_optional(Entry()); + + std::string v(value.data(), value.size()); + + // Storage Security + if (false == v.empty() && nullptr != m_dataEncryption) + v = m_dataEncryption->decrypt(v); + + entries[i]->set(std::move(v)); + } + else + { + if (status.IsNotFound()) + { + STORAGE_LOG(TRACE) + << "Multi get rows, not found key: " << keys[i]; + } + else if (status.getState()) + { + STORAGE_LOG(WARNING) + << "Multi get rows error: " << status.getState(); + } + else + { + STORAGE_LOG(WARNING) + << "Multi get rows error:" << status.ToString(); + } + } + } + }); + auto decode = utcTime(); + _callback(nullptr, std::move(entries)); + STORAGE_ROCKSDB_LOG(TRACE) + << LOG_DESC("asyncGetRows") << LOG_KV("table", _table) + << LOG_KV("count", entries.size()) << LOG_KV("read time(ms)", end - start) + << LOG_KV("decode time(ms)", decode - end) + << LOG_KV("callback time(ms)", utcTime() - decode); + }, + _keys); + } + catch (const std::exception& e) + { + _callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(UnknownEntryType, "Get rows failed! ", e), {}); + } +} + +void RocksDBStorage::asyncSetRow(std::string_view _table, std::string_view _key, Entry _entry, + std::function _callback) +{ + try + { + if (!isValid(_table, _key)) + { + STORAGE_ROCKSDB_LOG(WARNING) << LOG_DESC("asyncGetRow empty tableName or key") + << LOG_KV("table", _table) << LOG_KV("key", _key); + _callback(BCOS_ERROR_UNIQUE_PTR(TableNotExists, "empty tableName or key")); + return; + } + auto dbKey = toDBKey(_table, _key); + WriteOptions options; + rocksdb::Status status; + if (_entry.status() == Entry::DELETED) + { + STORAGE_ROCKSDB_LOG(TRACE) + << LOG_DESC("asyncSetRow delete") << LOG_KV("table", _table) + << LOG_KV("key", boost::algorithm::hex_lower(std::string(_key))); + status = m_db->Delete(options, dbKey); + } + else + { + STORAGE_ROCKSDB_LOG(TRACE) + << LOG_DESC("asyncSetRow") << LOG_KV("table", _table) + << LOG_KV("key", boost::algorithm::hex_lower(std::string(_key))); + + std::string value(_entry.get().data(), _entry.get().size()); + + // Storage Security + if (false == value.empty() && nullptr != m_dataEncryption) + value = m_dataEncryption->encrypt(value); + + status = m_db->Put(options, dbKey, std::move(value)); + } + + if (!status.ok()) + { + checkStatus(status); + std::string errorMessage = "Set row failed! " + status.ToString(); + if (status.getState()) + { + errorMessage.append(" ").append(status.getState()); + } + _callback(BCOS_ERROR_UNIQUE_PTR(WriteError, errorMessage)); + return; + } + + _callback(nullptr); + } + catch (const std::exception& e) + { + _callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(UnknownEntryType, "Set row failed! ", e)); + } +} + +void RocksDBStorage::asyncPrepare(const TwoPCParams& param, const TraverseStorageInterface& storage, + std::function callback) +{ + std::ignore = param; + try + { + STORAGE_ROCKSDB_LOG(INFO) << LOG_DESC("asyncPrepare") << LOG_KV("number", param.number); + auto start = utcTime(); + { + std::unique_lock lock(m_writeBatchMutex); + if (!m_writeBatch) + { + m_writeBatch = std::make_shared(); + } + } + std::atomic_uint64_t putCount{0}; + std::atomic_uint64_t deleteCount{0}; + atomic_bool isTableValid = true; + + tbb::concurrent_vector>> + dataChanges; + storage.parallelTraverse(true, [&](const std::string_view& table, + const std::string_view& key, Entry const& entry) { + if (!isValid(table, key)) + { + isTableValid = false; + return false; + } + auto dbKey = toDBKey(table, key); + + if (entry.status() == Entry::DELETED) + { + if (c_fileLogLevel <= TRACE) + { + STORAGE_ROCKSDB_LOG(TRACE) << LOG_DESC("delete") << LOG_KV("table", table) + << LOG_KV("key", toHex(key)); + } + ++deleteCount; + dataChanges.emplace_back( + std::tuple{entry.status(), std::move(dbKey), std::monostate{}}); + } + else + { + if (c_fileLogLevel <= TRACE) + { + STORAGE_ROCKSDB_LOG(TRACE) + << LOG_DESC("write") << LOG_KV("table", table) << LOG_KV("key", toHex(key)) + << LOG_KV("size", entry.size()); + } + ++putCount; + + // Storage security + if (auto value = entry.get(); !value.empty() && m_dataEncryption) + { + std::string encryptValue(value); + encryptValue = m_dataEncryption->encrypt(encryptValue); + dataChanges.emplace_back( + std::tuple{entry.status(), std::move(dbKey), std::move(encryptValue)}); + } + else + { + dataChanges.emplace_back(std::tuple{entry.status(), std::move(dbKey), entry}); + } + } + return true; + }); + + for (auto& [status, key, value] : dataChanges) + { + if (status == Entry::DELETED) + { + m_writeBatch->Delete(key); + } + else + { + auto& localKey = key; + std::visit( + [this, &localKey](auto&& valueStr) { + using ValueType = std::decay_t; + if constexpr (std::same_as) + { + m_writeBatch->Put(localKey, valueStr); + } + else if constexpr (std::same_as) + { + m_writeBatch->Put(localKey, valueStr.get()); + } + else + { + STORAGE_ROCKSDB_LOG(FATAL) << "Unexcepted monostate!"; + } + }, + value); + } + } + + if (!isTableValid) + { + { + std::unique_lock lock(m_writeBatchMutex); + m_writeBatch = nullptr; + } + STORAGE_ROCKSDB_LOG(ERROR) + << LOG_DESC("asyncPrepare invalidTable") << LOG_KV("number", param.number); + callback(BCOS_ERROR_UNIQUE_PTR(TableNotExists, "empty tableName or key"), 0, ""); + return; + } + auto end = utcTime(); + callback(nullptr, 0, ""); + STORAGE_ROCKSDB_LOG(INFO) << LOG_DESC("asyncPrepare") << LOG_KV("number", param.number) + << LOG_KV("put", putCount) << LOG_KV("delete", deleteCount) + << LOG_KV("startTS", param.timestamp) + << LOG_KV("time(ms)", end - start) + << LOG_KV("callback time(ms)", utcTime() - end); + } + catch (const std::exception& e) + { + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(UnknownEntryType, "Prepare failed! ", e), 0, ""); + } +} + +void RocksDBStorage::asyncCommit( + const TwoPCParams& params, std::function callback) +{ + size_t count = 0; + auto start = utcTime(); + std::ignore = params; + { + std::unique_lock lock(m_writeBatchMutex); + if (m_writeBatch) + { + WriteOptions options; + // options.sync = true; + count = m_writeBatch->Count(); + auto status = m_db->Write(options, m_writeBatch.get()); + auto err = checkStatus(status); + if (err) + { + STORAGE_ROCKSDB_LOG(WARNING) + << LOG_DESC("asyncCommit failed") << LOG_KV("number", params.number) + << LOG_KV("message", err->errorMessage()) << LOG_KV("startTS", params.timestamp) + << LOG_KV("time(ms)", utcTime() - start); + lock.release(); + callback(err, 0); + return; + } + m_writeBatch = nullptr; + } + } + auto end = utcTime(); + callback(nullptr, 0); + STORAGE_ROCKSDB_LOG(INFO) << LOG_DESC("asyncCommit") << LOG_KV("number", params.number) + << LOG_KV("startTS", params.timestamp) + << LOG_KV("time(ms)", utcTime() - start) + << LOG_KV("callback time(ms)", utcTime() - end) + << LOG_KV("count", count); +} + +void RocksDBStorage::asyncRollback( + const TwoPCParams& params, std::function callback) +{ + auto start = utcTime(); + + std::ignore = params; + { + std::unique_lock lock(m_writeBatchMutex); + m_writeBatch = nullptr; + } + auto end = utcTime(); + callback(nullptr); + STORAGE_ROCKSDB_LOG(INFO) << LOG_DESC("asyncRollback") << LOG_KV("number", params.number) + << LOG_KV("startTS", params.timestamp) + << LOG_KV("time(ms)", utcTime() - start) + << LOG_KV("callback time(ms)", utcTime() - end); +} + +bcos::Error::Ptr RocksDBStorage::setRows(std::string_view table, std::vector keys, + std::vector values) noexcept +{ + if (table.empty()) + { + STORAGE_ROCKSDB_LOG(WARNING) + << LOG_DESC("setRows empty tableName") << LOG_KV("table", table); + return BCOS_ERROR_PTR(TableNotExists, "empty tableName"); + } + if (keys.size() != values.size()) + { + STORAGE_ROCKSDB_LOG(WARNING) + << LOG_DESC("setRows values size mismatch keys size") << LOG_KV("keys", keys.size()) + << LOG_KV("values", values.size()); + return BCOS_ERROR_PTR(TableNotExists, "setRows values size mismatch keys size"); + } + if (keys.empty()) + { + STORAGE_ROCKSDB_LOG(WARNING) << LOG_DESC("setRows empty keys") << LOG_KV("table", table); + return nullptr; + } + std::vector realKeys(keys.size()); + + std::vector encryptedValues; + if (m_dataEncryption) + { + encryptedValues.resize(values.size()); + } + + tbb::parallel_for(tbb::blocked_range(0, keys.size(), 256), + [&](const tbb::blocked_range& range) { + for (size_t i = range.begin(); i != range.end(); ++i) + { + realKeys[i] = toDBKey(table, keys[i]); + if (m_dataEncryption) + { + encryptedValues[i] = m_dataEncryption->encrypt(std::string(values[i])); + } + } + }); + auto writeBatch = WriteBatch(); + for (size_t i = 0; i < values.size(); ++i) + { + // Storage Security + if (m_dataEncryption) + { + writeBatch.Put(realKeys[i], encryptedValues[i]); + } + else + { + writeBatch.Put(realKeys[i], values[i]); + } + } + WriteOptions options; + auto status = m_db->Write(options, &writeBatch); + auto err = checkStatus(status); + if (err) + { + STORAGE_ROCKSDB_LOG(WARNING) + << LOG_DESC("setRows failed") << LOG_KV("message", err->errorMessage()); + return err; + } + return nullptr; +} + +bcos::Error::Ptr RocksDBStorage::checkStatus(rocksdb::Status const& status) +{ + if (status.ok() || status.IsNotFound()) + { + return nullptr; + } + std::string errorInfo = "access rocksDB failed, status: " + status.ToString(); + // fatal exception + if (status.IsIOError() || status.IsCorruption() || status.IsNoSpace() || + status.IsNotSupported() || status.IsShutdownInProgress()) + { + std::raise(SIGTERM); + STORAGE_ROCKSDB_LOG(ERROR) << LOG_DESC(errorInfo); + return BCOS_ERROR_PTR(DatabaseError, errorInfo); + } + // exception that can be recovered by retry + // statuses are: Busy, TimedOut, TryAgain, Aborted, MergeInProgress, IsIncomplete, Expired, + // CompactionToolLarge + else + { + errorInfo = errorInfo + ", please try again!"; + STORAGE_ROCKSDB_LOG(WARNING) << LOG_DESC(errorInfo); + return BCOS_ERROR_PTR(DatabaseRetryable, errorInfo); + } +} diff --git "a/BFPL\345\243\271/bcos-storage/bcos-storage/RocksDBStorage.h" "b/BFPL\345\243\271/bcos-storage/bcos-storage/RocksDBStorage.h" new file mode 100644 index 00000000..97ca46aa --- /dev/null +++ "b/BFPL\345\243\271/bcos-storage/bcos-storage/RocksDBStorage.h" @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2021 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. + * + * @brief the implement of storage + * @file Storage.cpp + * @author: xingqiangbai + * @date: 2021-04-16 + * @file Storage.cpp + * @author: ancelmo + * @date: 2021-08-27 + */ +#pragma once + +#include +#include +#include +#include + +namespace rocksdb +{ +class WriteBatch; +} + +namespace bcos::storage +{ +class RocksDBStorage : public TransactionalStorageInterface +{ +public: + using Ptr = std::shared_ptr; + explicit RocksDBStorage(std::unique_ptr>&& db, + const bcos::security::DataEncryptInterface::Ptr dataEncryption); + + ~RocksDBStorage() {} + + void asyncGetPrimaryKeys(std::string_view _table, + const std::optional& _condition, + std::function)> _callback) override; + + void asyncGetRow(std::string_view table, std::string_view _key, + std::function)> _callback) override; + + void asyncGetRows(std::string_view table, + const std::variant, + const gsl::span>& _keys, + std::function>)> _callback) + override; + + void asyncSetRow(std::string_view table, std::string_view key, Entry entry, + std::function callback) override; + + void asyncPrepare(const bcos::protocol::TwoPCParams& params, + const TraverseStorageInterface& storage, + std::function callback) override; + + void asyncCommit(const bcos::protocol::TwoPCParams& params, + std::function callback) override; + + void asyncRollback(const bcos::protocol::TwoPCParams& params, + std::function callback) override; + Error::Ptr setRows(std::string_view table, std::vector keys, + std::vector values) noexcept override; + +private: + Error::Ptr checkStatus(rocksdb::Status const& status); + std::shared_ptr m_writeBatch = nullptr; + std::mutex m_writeBatchMutex; + std::unique_ptr> m_db; + + // Security Storage + bcos::security::DataEncryptInterface::Ptr m_dataEncryption{nullptr}; +}; +} // namespace bcos::storage diff --git "a/BFPL\345\243\271/bcos-storage/bcos-storage/TiKVStorage.cpp" "b/BFPL\345\243\271/bcos-storage/bcos-storage/TiKVStorage.cpp" new file mode 100644 index 00000000..f556a5f3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-storage/bcos-storage/TiKVStorage.cpp" @@ -0,0 +1,591 @@ +/* + * Copyright (C) 2021 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. + * + * @brief the implement of TiKVStorage + * @file TiKVStorage.h + * @author: xingqiangbai + * @date: 2021-09-26 + */ +#include "TiKVStorage.h" +#include "Common.h" +#include "bcos-framework/protocol/ProtocolTypeDef.h" +#include "bcos-framework/storage/Table.h" +#include "tikv_client.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +constexpr uint32_t MAX_RETRY_LIMIT = 32; + +using namespace bcos::storage; +using namespace bcos::protocol; +using namespace std; + +#define STORAGE_TIKV_LOG(LEVEL) BCOS_LOG(LEVEL) << "[STORAGE-TiKV]" +namespace bcos::storage +{ + +std::shared_ptr newTiKVClient( + const std::vector& pdAddrs, const std::string& logPath, const std::string& caPath, + const std::string& certPath, const std::string& keyPath, uint32_t grpcTimeout) +{ + if (!caPath.empty() && !certPath.empty() && !keyPath.empty()) + { + STORAGE_TIKV_LOG(INFO) << LOG_DESC("newTiKVClientWithSSL") << LOG_KV("logPath", logPath); + return std::make_shared( + pdAddrs, logPath, caPath, certPath, keyPath); + } + STORAGE_TIKV_LOG(INFO) << LOG_DESC("newTiKVClient") << LOG_KV("logPath", logPath); + return std::make_shared(pdAddrs, logPath, grpcTimeout); +} +} // namespace bcos::storage + +TiKVStorage::TiKVStorage( + std::shared_ptr _cluster, int32_t _commitTimeout) + : m_cluster(std::move(_cluster)), + m_lastCommitTimestamp(m_cluster->current_timestamp()), + m_commitTimeout(_commitTimeout) +{} + +void TiKVStorage::asyncGetPrimaryKeys(std::string_view _table, + const std::optional& _condition, + std::function)> _callback) noexcept +{ + try + { + auto start = utcTime(); + std::vector result; + + std::string keyPrefix; + keyPrefix = string(_table) + TABLE_KEY_SPLIT; + // snapshot is not threadsafe so create it every time + auto snap = m_cluster->snapshot(); + // TODO: check performance and add limit of primary keys + bool finished = false; + auto lastKey = keyPrefix; + int i = 0; + while (!finished) + { + auto keys = snap->scan_keys( + lastKey, Bound::Excluded, string(), Bound::Unbounded, scan_batch_size); + if (keys.empty()) + { + finished = true; + break; + } + lastKey = keys.back(); + for (auto& key : keys) + { + if (key.rfind(keyPrefix, 0) == 0) + { + size_t start = keyPrefix.size(); + auto realKey = key.substr(start); + if (!_condition || _condition->isValid(realKey)) + { // filter by condition, remove keyPrefix + result.push_back(std::move(realKey)); + } + } + else + { + finished = true; + break; + } + } + } + auto end = utcTime(); + STORAGE_TIKV_LOG(DEBUG) << LOG_DESC("asyncGetPrimaryKeys") << LOG_KV("table", _table) + << LOG_KV("count", result.size()) + << LOG_KV("read time(ms)", end - start) + << LOG_KV("callback time(ms)", utcTime() - end); + _callback(nullptr, std::move(result)); + } + catch (const std::exception& e) + { + STORAGE_TIKV_LOG(WARNING) << LOG_DESC("asyncGetPrimaryKeys failed, need trigger switch") + << LOG_KV("table", _table) << LOG_KV("message", e.what()); + _callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(ReadError, "asyncGetPrimaryKeys failed!", e), {}); + triggerSwitch(); + } +} + +void TiKVStorage::asyncGetRow(std::string_view _table, std::string_view _key, + std::function)> _callback) noexcept +{ + try + { + if (!isValid(_table, _key)) + { + STORAGE_TIKV_LOG(WARNING) << LOG_DESC("asyncGetRow empty tableName or key") + << LOG_KV("table", _table) << LOG_KV("key", toHex(_key)); + _callback(BCOS_ERROR_UNIQUE_PTR(TableNotExists, "empty tableName or key"), {}); + return; + } + auto start = utcTime(); + auto dbKey = toDBKey(_table, _key); + auto snap = m_cluster->snapshot(); + auto value = snap->get(dbKey); + auto end = utcTime(); + if (!value.has_value()) + { + if (c_fileLogLevel <= TRACE) + { + STORAGE_TIKV_LOG(TRACE) << LOG_DESC("asyncGetRow empty") << LOG_KV("table", _table) + << LOG_KV("key", _key.length() == 0 ? "" : toHex(_key)) + << LOG_KV("dbKey", dbKey); + } + _callback(nullptr, {}); + return; + } + + auto entry = std::make_optional(); + entry->set(value.value()); + if (c_fileLogLevel <= TRACE) + { + STORAGE_TIKV_LOG(TRACE) + << LOG_DESC("asyncGetRow") << LOG_KV("table", _table) << LOG_KV("key", toHex(_key)) + << LOG_KV("read time(ms)", end - start) + << LOG_KV("callback time(ms)", utcTime() - end); + } + _callback(nullptr, std::move(entry)); + } + catch (const std::exception& e) + { + STORAGE_TIKV_LOG(WARNING) << LOG_DESC("asyncGetRow failed, need trigger switch") + << LOG_KV("table", _table) << LOG_KV("key", toHex(_key)) + << LOG_KV("message", e.what()); + _callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(ReadError, "asyncGetRow failed!", e), {}); + triggerSwitch(); + } +} + +void TiKVStorage::asyncGetRows(std::string_view _table, + const std::variant, const gsl::span>& + _keys, + std::function>)> _callback) noexcept +{ + try + { + if (!isValid(_table)) + { + STORAGE_TIKV_LOG(WARNING) + << LOG_DESC("asyncGetRows empty tableName") << LOG_KV("table", _table); + _callback(BCOS_ERROR_UNIQUE_PTR(TableNotExists, "empty tableName"), {}); + return; + } + auto start = utcTime(); + std::visit( + [&](auto const& keys) { + std::vector> entries(keys.size()); + + std::vector realKeys(keys.size()); + tbb::parallel_for(tbb::blocked_range(0, keys.size()), + [&](const tbb::blocked_range& range) { + for (size_t i = range.begin(); i != range.end(); ++i) + { + realKeys[i] = toDBKey(_table, keys[i]); + } + }); + auto snap = m_cluster->snapshot(); + auto result = snap->batch_get(realKeys); + auto end = utcTime(); + size_t validCount = 0; + for (size_t i = 0; i < realKeys.size(); ++i) + { + auto node = result.extract(realKeys[i]); + if (node.empty() || node.mapped().empty()) + { + entries[i] = std::nullopt; + STORAGE_TIKV_LOG(TRACE) << "Multi get rows, not found key: " << keys[i]; + } + else + { + ++validCount; + entries[i] = std::make_optional(Entry()); + entries[i]->set(std::move(node.mapped())); + } + } + auto decode = utcTime(); + STORAGE_TIKV_LOG(DEBUG) + << LOG_DESC("asyncGetRows") << LOG_KV("table", _table) + << LOG_KV("count", entries.size()) << LOG_KV("validCount", validCount) + << LOG_KV("read time(ms)", end - start) + << LOG_KV("decode time(ms)", decode - end); + _callback(nullptr, std::move(entries)); + }, + _keys); + } + catch (const std::exception& e) + { + STORAGE_TIKV_LOG(WARNING) << LOG_DESC("asyncGetRows failed, need trigger switch") + << LOG_KV("table", _table) << LOG_KV("message", e.what()); + _callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(ReadError, "asyncGetRows failed! ", e), + std::vector>()); + triggerSwitch(); + } +} + +void TiKVStorage::asyncSetRow(std::string_view _table, std::string_view _key, Entry _entry, + std::function _callback) noexcept +{ + try + { + if (!isValid(_table, _key)) + { + STORAGE_TIKV_LOG(WARNING) << LOG_DESC("asyncGetRow empty tableName or key") + << LOG_KV("table", _table) << LOG_KV("key", _key); + _callback(BCOS_ERROR_UNIQUE_PTR(TableNotExists, "empty tableName or key")); + return; + } + auto dbKey = toDBKey(_table, _key); + auto txn = m_cluster->begin(); + + if (_entry.status() == Entry::DELETED) + { + STORAGE_TIKV_LOG(DEBUG) << LOG_DESC("asyncSetRow delete") << LOG_KV("table", _table) + << LOG_KV("key", _key) << LOG_KV("dbKey", dbKey); + txn.remove(dbKey); + } + else + { + if (c_fileLogLevel <= TRACE) + { + STORAGE_TIKV_LOG(TRACE) + << LOG_DESC("asyncSetRow") << LOG_KV("table", _table) << LOG_KV("key", _key); + } + std::string value = std::string(_entry.get()); + txn.put(dbKey, value); + } + txn.commit(); + _callback(nullptr); + } + catch (const std::exception& e) + { + STORAGE_TIKV_LOG(WARNING) << LOG_DESC("asyncSetRow failed") << LOG_KV("table", _table) + << LOG_KV("message", e.what()); + _callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(WriteError, "asyncSetRow failed! ", e)); + } + m_lastCommitTimestamp = m_cluster->current_timestamp(); +} + +void TiKVStorage::asyncPrepare(const TwoPCParams& params, const TraverseStorageInterface& storage, + std::function callback) noexcept +{ + try + { + std::unique_lock lock(x_committer, std::try_to_lock); + if (lock.owns_lock()) + { + STORAGE_TIKV_LOG(INFO) + << LOG_DESC("asyncPrepare") << LOG_KV("blockNumber", params.number) + << LOG_KV("primary", params.timestamp > 0 ? "false" : "true"); + auto start = utcTime(); + tbb::spin_mutex writeMutex; + atomic_bool isTableValid = true; + std::atomic_uint64_t putCount{0}; + std::atomic_uint64_t deleteCount{0}; + if (m_committer) + { // should wait for the previous committer to timeout + auto duration = std::chrono::duration_cast( + std::chrono::system_clock::now() - m_committerCreateTime) + .count(); + STORAGE_TIKV_LOG(DEBUG) + << "asyncPrepare clean old committer" << LOG_KV("blockNumber", params.number) + << LOG_KV("duration(ms)", duration); + if (duration < m_commitTimeout) + { + std::this_thread::sleep_for( + std::chrono::milliseconds(m_commitTimeout - duration)); + } + m_committer->rollback(); + m_committer = nullptr; + } + m_committerCreateTime = std::chrono::system_clock::now(); + m_committer = m_cluster->new_optimistic_transaction(MAX_RETRY_LIMIT); + storage.parallelTraverse(true, [&](const std::string_view& table, + const std::string_view& key, Entry const& entry) { + if (!isValid(table, key)) + { + isTableValid = false; + return false; + } + auto dbKey = toDBKey(table, key); + if (entry.status() == Entry::DELETED) + { + tbb::spin_mutex::scoped_lock lock(writeMutex); + m_committer->remove(dbKey); + ++deleteCount; + } + else + { + std::string value = std::string(entry.get()); + tbb::spin_mutex::scoped_lock lock(writeMutex); + m_committer->put(dbKey, value); + ++putCount; + } + return true; + }); + if (!isTableValid) + { + m_committer->rollback(); + m_committer = nullptr; + callback(BCOS_ERROR_UNIQUE_PTR(TableNotExists, "empty tableName or key"), 0, ""); + return; + } + auto encode = utcTime(); + auto size = putCount + deleteCount; + if (size == 0) + { + m_committer->rollback(); + m_committer = nullptr; + if (params.timestamp == 0) + { + STORAGE_TIKV_LOG(ERROR) << LOG_DESC("asyncPrepare empty storage") + << LOG_KV("blockNumber", params.number); + callback(BCOS_ERROR_UNIQUE_PTR(EmptyStorage, "commit storage is empty"), 0, ""); + } + else + { + STORAGE_TIKV_LOG(DEBUG) << LOG_DESC("asyncPrepare empty storage") + << LOG_KV("blockNumber", params.number); + callback(nullptr, 0, ""); + } + return; + } + auto primaryLock = params.primaryKey; + + if (params.timestamp == 0) + { + STORAGE_TIKV_LOG(INFO) + << LOG_DESC("asyncPrepare primary") << LOG_KV("blockNumber", params.number); + auto result = m_committer->prewrite_primary(primaryLock); + auto write = utcTime(); + m_currentStartTS = result.second; + lock.unlock(); + callback(nullptr, result.second, result.first); + STORAGE_TIKV_LOG(INFO) + << "asyncPrepare primary finished" << LOG_KV("blockNumber", params.number) + << LOG_KV("put", putCount) << LOG_KV("delete", deleteCount) + << LOG_KV("size", size) << LOG_KV("primaryLock", toHex(primaryLock)) + << LOG_KV("primary", toHex(result.first)) << LOG_KV("startTS", result.second) + << LOG_KV("encode time(ms)", encode - start) + << LOG_KV("prewrite time(ms)", write - encode) + << LOG_KV("callback time(ms)", utcTime() - write); + } + else + { + STORAGE_TIKV_LOG(INFO) + << "asyncPrepare secondary" << LOG_KV("blockNumber", params.number) + << LOG_KV("put", putCount) << LOG_KV("delete", deleteCount) + << LOG_KV("size", size) << LOG_KV("primaryLock", primaryLock) + << LOG_KV("startTS", params.timestamp) + << LOG_KV("encode time(ms)", encode - start); + m_currentStartTS = params.timestamp; + m_committer->prewrite_secondary(primaryLock, m_currentStartTS); + auto write = utcTime(); + // m_committer = nullptr; + STORAGE_TIKV_LOG(INFO) + << "asyncPrepare secondary finished" << LOG_KV("blockNumber", params.number) + << LOG_KV("prewrite time(ms)", write - encode); + callback(nullptr, 0, primaryLock); + } + } + else + { + STORAGE_TIKV_LOG(INFO) + << "asyncPrepare try_lock failed" << LOG_KV("blockNumber", params.number); + callback(BCOS_ERROR_UNIQUE_PTR(TryLockFailed, "asyncPrepare try_lock failed"), 0, ""); + } + } + catch (const std::exception& e) + { + STORAGE_TIKV_LOG(WARNING) << LOG_DESC("asyncPrepare failed") + << LOG_KV("blockNumber", params.number) + << LOG_KV("message", e.what()); + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(WriteError, "asyncPrepare failed! ", e), 0, ""); + } +} + +void TiKVStorage::asyncCommit( + const TwoPCParams& params, std::function callback) noexcept +{ + try + { + std::unique_lock lock(x_committer, std::try_to_lock); + if (lock.owns_lock()) + { + STORAGE_TIKV_LOG(INFO) + << LOG_DESC("asyncCommit") << LOG_KV("blockNumber", params.number) + << LOG_KV("timestamp", params.timestamp) + << LOG_KV("primary", params.timestamp > 0 ? "false" : "true"); + auto start = utcTime(); + uint64_t ts = 0; + if (m_committer) + { + if (params.timestamp > 0) + { + m_committer->commit_secondary(params.timestamp); + } + else + { + ts = m_committer->commit_primary(); + m_committer->commit_secondary(ts); + } + m_committer = nullptr; + } + auto end = utcTime(); + STORAGE_TIKV_LOG(INFO) + << LOG_DESC("asyncCommit finished") << LOG_KV("blockNumber", params.number) + << LOG_KV("commitTS", params.timestamp) << LOG_KV("primaryCommitTS", ts) + << LOG_KV("time(ms)", end - start); + lock.unlock(); + m_lastCommitTimestamp = m_cluster->current_timestamp(); + callback(nullptr, ts); + } + else + { + STORAGE_TIKV_LOG(INFO) + << "asyncCommit try_lock failed" << LOG_KV("blockNumber", params.number); + callback(BCOS_ERROR_UNIQUE_PTR(TryLockFailed, "asyncPrepare try_lock failed"), 0); + } + } + catch (const std::exception& e) + { + STORAGE_TIKV_LOG(WARNING) << LOG_DESC("asyncCommit failed") + << LOG_KV("blockNumber", params.number) + << LOG_KV("commitTS", params.timestamp) + << LOG_KV("message", e.what()); + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(WriteError, "asyncCommit failed! ", e), 0); + } +} + +void TiKVStorage::asyncRollback( + const TwoPCParams& params, std::function callback) noexcept +{ + try + { + std::unique_lock lock(x_committer, std::try_to_lock); + if (lock.owns_lock()) + { + if (m_currentStartTS != params.timestamp) + { + STORAGE_TIKV_LOG(INFO) + << "asyncRollback wrong timestamp" << LOG_KV("blockNumber", params.number) + << LOG_KV("expect", params.timestamp) << LOG_KV("current", m_currentStartTS); + callback(BCOS_ERROR_UNIQUE_PTR( + TimestampMismatch, "asyncRollback failed for TimestampMismatch")); + return; + } + STORAGE_TIKV_LOG(INFO) + << LOG_DESC("asyncRollback") << LOG_KV("blockNumber", params.number) + << LOG_KV("timestamp", params.timestamp); + RecursiveGuard guard(x_committer); + auto start = utcTime(); + if (m_committer) + { + m_committer->rollback(); + m_committer = nullptr; + } + auto end = utcTime(); + lock.unlock(); + callback(nullptr); + STORAGE_TIKV_LOG(INFO) + << LOG_DESC("asyncRollback finished") << LOG_KV("blockNumber", params.number) + << LOG_KV("startTS", params.timestamp) << LOG_KV("time(ms)", end - start) + << LOG_KV("callback time(ms)", utcTime() - end); + } + else + { + STORAGE_TIKV_LOG(INFO) + << "asyncRollback try_lock failed" << LOG_KV("blockNumber", params.number); + callback(BCOS_ERROR_UNIQUE_PTR(TryLockFailed, "asyncPrepare try_lock failed")); + } + } + catch (const std::exception& e) + { + STORAGE_TIKV_LOG(WARNING) << LOG_DESC("asyncRollback failed") + << LOG_KV("blockNumber", params.number) + << LOG_KV("message", e.what()); + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(WriteError, "asyncRollback failed! ", e)); + } +} + +bcos::Error::Ptr TiKVStorage::setRows(std::string_view table, std::vector keys, + std::vector values) noexcept +{ + try + { + if (table.empty()) + { + STORAGE_TIKV_LOG(WARNING) + << LOG_DESC("setRows empty tableName") << LOG_KV("table", table); + return BCOS_ERROR_PTR(TableNotExists, "empty tableName"); + } + if (keys.size() != values.size()) + { + STORAGE_TIKV_LOG(WARNING) + << LOG_DESC("setRows values size mismatch keys size") << LOG_KV("table", table) + << LOG_KV("keys", keys.size()) << LOG_KV("values", values.size()); + return BCOS_ERROR_PTR(TableNotExists, "setRows values size mismatch keys size"); + } + if (keys.empty()) + { + STORAGE_TIKV_LOG(WARNING) << LOG_DESC("setRows empty keys") << LOG_KV("table", table); + return nullptr; + } + std::vector realKeys(keys.size()); + tbb::parallel_for(tbb::blocked_range(0, keys.size()), + [&](const tbb::blocked_range& range) { + for (size_t i = range.begin(); i != range.end(); ++i) + { + realKeys[i] = toDBKey(table, keys[i]); + } + }); + auto txn = m_cluster->begin(); + for (size_t i = 0; i < values.size(); ++i) + { + txn.put(std::string(realKeys[i]), std::string(values[i])); + } + txn.commit(); + } + catch (std::exception& e) + { + STORAGE_TIKV_LOG(WARNING) << LOG_DESC("setRows failed") << LOG_KV("what", e.what()); + m_lastCommitTimestamp = m_cluster->current_timestamp(); + return BCOS_ERROR_WITH_PREV_PTR(WriteError, "setRows failed! ", e); + } + m_lastCommitTimestamp = m_cluster->current_timestamp(); + return nullptr; +} + +void TiKVStorage::triggerSwitch() +{ + if (f_onNeedSwitchEvent) + { + STORAGE_TIKV_LOG(WARNING) << LOG_DESC("Trigger switch"); + f_onNeedSwitchEvent(); + } +} + +void TiKVStorage::reset() +{ + m_lastCommitTimestamp = m_cluster->current_timestamp(); +} diff --git "a/BFPL\345\243\271/bcos-storage/bcos-storage/TiKVStorage.h" "b/BFPL\345\243\271/bcos-storage/bcos-storage/TiKVStorage.h" new file mode 100644 index 00000000..f625ce54 --- /dev/null +++ "b/BFPL\345\243\271/bcos-storage/bcos-storage/TiKVStorage.h" @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2021 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. + * + * @brief the header of TiKVStorage + * @file TiKVStorage.h + * @author: xingqiangbai + * @date: 2021-04-16 + */ + +#pragma once + +#include +#include +#include +#include + +namespace tikv_client +{ +struct TransactionClient; +struct Transaction; +struct Snapshot; +} // namespace tikv_client + +namespace bcos::storage +{ +constexpr int scan_batch_size = 64; + +std::shared_ptr newTiKVClient( + const std::vector& pdAddrs, const std::string& logPath, + const std::string& caPath = "", const std::string& certPath = "", + const std::string& keyPath = "", uint32_t grpcTimeout = 3); + +class TiKVStorage : public TransactionalStorageInterface +{ +public: + using Ptr = std::shared_ptr; + explicit TiKVStorage( + std::shared_ptr _cluster, int32_t _commitTimeout = 3000); + + void asyncGetPrimaryKeys(std::string_view _table, + const std::optional& _condition, + std::function)> _callback) noexcept + override; + + void asyncGetRow(std::string_view table, std::string_view _key, + std::function)> _callback) noexcept override; + + void asyncGetRows(std::string_view table, + const std::variant, + const gsl::span>& _keys, + std::function>)> _callback) noexcept + override; + + void asyncSetRow(std::string_view table, std::string_view key, Entry entry, + std::function callback) noexcept override; + + void asyncPrepare(const bcos::protocol::TwoPCParams& params, + const TraverseStorageInterface& storage, + std::function callback) noexcept override; + + void asyncCommit(const bcos::protocol::TwoPCParams& params, + std::function callback) noexcept override; + + void asyncRollback(const bcos::protocol::TwoPCParams& params, + std::function callback) noexcept override; + + Error::Ptr setRows(std::string_view table, std::vector keys, + std::vector values) noexcept override; + + void setSwitchHandler(std::function _onNeedSwitchEvent) + { + f_onNeedSwitchEvent = std::move(_onNeedSwitchEvent); + } + + void reset(); + +private: + void triggerSwitch(); + + std::shared_ptr m_cluster; + std::shared_ptr m_committer = nullptr; + uint64_t m_currentStartTS = 0; + std::atomic_uint64_t m_lastCommitTimestamp; + std::function f_onNeedSwitchEvent; + int32_t m_commitTimeout = 3000; + std::chrono::time_point m_committerCreateTime; + mutable RecursiveMutex x_committer; +}; +} // namespace bcos::storage diff --git "a/BFPL\345\243\271/bcos-storage/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-storage/test/CMakeLists.txt" new file mode 100644 index 00000000..f5c33d30 --- /dev/null +++ "b/BFPL\345\243\271/bcos-storage/test/CMakeLists.txt" @@ -0,0 +1,4 @@ +# hunter_add_package(wedpr-crypto) + +# add_subdirectory(perf) +add_subdirectory(unittest) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-storage/test/unittest/CMakeLists.txt" "b/BFPL\345\243\271/bcos-storage/test/unittest/CMakeLists.txt" new file mode 100644 index 00000000..4cdee5f5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-storage/test/unittest/CMakeLists.txt" @@ -0,0 +1,44 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of bcos-framework +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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. +# ------------------------------------------------------------------------------ +list(APPEND SOURCES "TestRocksDBStorage.cpp" "main.cpp") +# cmake settings +set(TEST_BINARY_NAME test-storage) + +if(WITH_TIKV) + list(APPEND SOURCES "TestTiKVStorage.cpp") +else() + set(EXCLUDE_SUITES "TestTiKVStorage") +endif() + +# config_test_cases("" "${SOURCES}" bin/${TEST_BINARY_NAME} "${EXCLUDE_SUITES}") + +add_executable(${TEST_BINARY_NAME} ${SOURCES}) +target_include_directories(${TEST_BINARY_NAME} PRIVATE . ../../src) + +find_package(Boost REQUIRED unit_test_framework log) + +target_link_libraries(${TEST_BINARY_NAME} PUBLIC ${SECURITY_TARGET} ${STORAGE_TARGET} Boost::unit_test_framework Boost::log Boost::context) +include(SearchTestCases) +config_test_cases("" "${SOURCES}" ${TEST_BINARY_NAME} "${EXCLUDE_SUITES}") +# add_test(NAME test-storage WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) + +# for code coverage +if (COVERAGE) + include(Coverage) + config_coverage("storage-coverage" "'/usr*' 'boost/**'") +endif () diff --git "a/BFPL\345\243\271/bcos-storage/test/unittest/TestRocksDBStorage.cpp" "b/BFPL\345\243\271/bcos-storage/test/unittest/TestRocksDBStorage.cpp" new file mode 100644 index 00000000..345d2e04 --- /dev/null +++ "b/BFPL\345\243\271/bcos-storage/test/unittest/TestRocksDBStorage.cpp" @@ -0,0 +1,694 @@ +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-table/src/StateStorage.h" +#include "boost/filesystem.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos::storage; +using namespace std; + +// ostream& operator<<(ostream& os, const std::unique_ptr& error) +// { +// os << error->what(); +// return os; +// } + +namespace bcos::test +{ +class Header256Hash : public bcos::crypto::Hash +{ +public: + typedef std::shared_ptr Ptr; + Header256Hash() = default; + virtual ~Header256Hash(){}; + bcos::crypto::HashType hash(bytesConstRef _data) override + { + std::hash hash; + return bcos::crypto::HashType( + hash(std::string_view((const char*)_data.data(), _data.size()))); + } + bcos::crypto::hasher::AnyHasher hasher() override + { + return bcos::crypto::hasher::AnyHasher{bcos::crypto::hasher::openssl::OpenSSL_SM3_Hasher{}}; + } +}; +struct TestRocksDBStorageFixture +{ + TestRocksDBStorageFixture() + { + boost::log::core::get()->set_logging_enabled(false); + rocksdb::DB* db; + rocksdb::Options options; + // Optimize RocksDB. This is the easiest way to get RocksDB to perform well + + // options.IncreaseParallelism(); + // options.OptimizeLevelStyleCompaction(); + + // create the DB if it's not already present + options.create_if_missing = true; + + // open DB + rocksdb::Status s = rocksdb::DB::Open(options, path, &db); + BOOST_CHECK_EQUAL(s.ok(), true); + + rocksDBStorage = + std::make_shared(std::unique_ptr(db), nullptr); + rocksDBStorage->asyncOpenTable(testTableName, [&](auto&& error, auto&& table) { + BOOST_CHECK(!error); + + if (error) + { + std::cout << boost::diagnostic_information(*error) << std::endl; + } + + if (table) + { + testTableInfo = table->tableInfo(); + } + }); + if (!testTableInfo) + { + std::promise> prom; + rocksDBStorage->asyncCreateTable(testTableName, "v1,v2,v3,v4,v5,v6", + [&prom](Error::UniquePtr error, std::optional
&& table) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(table.has_value(), true); + prom.set_value(table); + }); + auto table = prom.get_future().get(); + testTableInfo = table->tableInfo(); + } + } + + void prepareTestTableData() + { + for (size_t i = 0; i < 1000; ++i) + { + std::string key = "key" + boost::lexical_cast(i); + Entry entry(testTableInfo); + entry.importFields({"value_" + boost::lexical_cast(i)}); + rocksDBStorage->asyncSetRow(testTableName, key, entry, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + } + } + + void cleanupTestTableData() + { + for (size_t i = 0; i < 1000; ++i) + { + std::string key = "key" + boost::lexical_cast(i); + Entry entry(testTableInfo); + entry.setStatus(Entry::DELETED); + + rocksDBStorage->asyncSetRow(testTableName, key, entry, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + } + } + + void writeReadDeleteSingleTable(size_t count) + { + auto storage = rocksDBStorage; + size_t tableEntries = count; + auto hashImpl = std::make_shared(); + auto stateStorage = std::make_shared(storage); + auto testTable = stateStorage->openTable(testTableName); + BOOST_CHECK_EQUAL(testTable.has_value(), true); + for (size_t i = 0; i < tableEntries; ++i) + { + std::string key = "key" + boost::lexical_cast(i); + Entry entry(testTableInfo); + entry.importFields({"value_delete"}); + testTable->setRow(key, std::move(entry)); + } + + auto params1 = bcos::protocol::TwoPCParams(); + params1.number = 100; + params1.primaryKey = testTableName + ":key0"; + auto start = std::chrono::system_clock::now(); + // prewrite + storage->asyncPrepare(params1, *stateStorage, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(ts, 0); + params1.timestamp = ts; + }); + + // commit + storage->asyncCommit(bcos::protocol::TwoPCParams(), + [&](Error::Ptr error, uint64_t) { BOOST_CHECK_EQUAL(error, nullptr); }); + auto commitEnd = std::chrono::system_clock::now(); + // check commit success + storage->asyncGetPrimaryKeys(testTableName, std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), tableEntries); + storage->asyncGetRows(testTableName, keys, + [&](Error::UniquePtr error, std::vector> entries) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entries.size(), tableEntries); + for (size_t i = 0; i < tableEntries; ++i) + { + BOOST_CHECK_EQUAL(entries[i]->getField(0), "value_delete"); + } + }); + }); + auto getEnd = std::chrono::system_clock::now(); + + for (size_t i = 0; i < tableEntries; ++i) + { + auto entry = testTable->newEntry(); + auto key = "key" + boost::lexical_cast(i); + entry.setStatus(Entry::DELETED); + testTable->setRow(key, std::move(entry)); + } + params1.timestamp = 0; + storage->asyncPrepare(params1, *stateStorage, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(ts, 0); + params1.timestamp = ts; + }); + // commit + storage->asyncCommit(bcos::protocol::TwoPCParams(), + [&](Error::Ptr error, uint64_t) { BOOST_CHECK_EQUAL(error, nullptr); }); + auto deleteEnd = std::chrono::system_clock::now(); + // check if the data is deleted + storage->asyncGetPrimaryKeys(testTableName, std::optional(), + [](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 0); + }); + cerr << "entries count=" << tableEntries << "|>>>>>>>>> commit=" + << std::chrono::duration_cast(commitEnd - start).count() + << "ms|getAll=" + << std::chrono::duration_cast(getEnd - commitEnd).count() + << "ms|deleteAll=" + << std::chrono::duration_cast(deleteEnd - getEnd).count() << "ms" + << endl + << endl; + } + + ~TestRocksDBStorageFixture() + { + if (boost::filesystem::exists(path)) + { + boost::filesystem::remove_all(path); + } + boost::log::core::get()->set_logging_enabled(true); + } + + std::string path = "./unittestdb"; + RocksDBStorage::Ptr rocksDBStorage; + std::string testTableName = "TestTable"; + TableInfo::ConstPtr testTableInfo = nullptr; +}; + +BOOST_FIXTURE_TEST_SUITE(TestRocksDBStorage, TestRocksDBStorageFixture) + +BOOST_AUTO_TEST_CASE(asyncGetRow) +{ + prepareTestTableData(); + + for (size_t i = 0; i < 1050; ++i) + { + std::string key = "key" + boost::lexical_cast(i); + rocksDBStorage->asyncGetRow( + testTableName, key, [&](Error::UniquePtr error, std::optional entry) { + // #pragma omp critical + BOOST_REQUIRE(!error); + if (error) + { + std::cout << boost::diagnostic_information(*error); + } + if (i < 1000) + { + BOOST_CHECK_NE(entry.has_value(), false); + auto data = entry->get(); + BOOST_CHECK_EQUAL(data, "value_" + boost::lexical_cast(i)); + } + else + { + BOOST_CHECK_EQUAL(entry.has_value(), false); + } + }); + } + cleanupTestTableData(); +} + +BOOST_AUTO_TEST_CASE(asyncGetPrimaryKeys) +{ + prepareTestTableData(); + rocksDBStorage->asyncGetPrimaryKeys(testTableName, std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 1000); + + std::vector sortedKeys; + + for (size_t i = 0; i < 1000; ++i) + { + sortedKeys.emplace_back("key" + boost::lexical_cast(i)); + } + + std::sort(sortedKeys.begin(), sortedKeys.end()); + + BOOST_CHECK_EQUAL_COLLECTIONS( + sortedKeys.begin(), sortedKeys.end(), keys.begin(), keys.end()); + }); + + auto tableInfo = std::make_shared("new_table", vector{"value"}); + + for (size_t i = 1000; i < 2000; ++i) + { + std::string key = "newkey" + boost::lexical_cast(i); + auto entry = Entry(tableInfo); + entry.importFields({"value12345"}); + + rocksDBStorage->asyncSetRow(tableInfo->name(), key, entry, + [&](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + } + + // query old data + auto condition = std::optional(); + rocksDBStorage->asyncGetPrimaryKeys( + tableInfo->name(), condition, [](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 1000); + + std::vector sortedKeys; + + for (size_t i = 0; i < 1000; ++i) + { + sortedKeys.emplace_back("newkey" + boost::lexical_cast(i + 1000)); + } + std::sort(sortedKeys.begin(), sortedKeys.end()); + + BOOST_CHECK_EQUAL_COLLECTIONS( + sortedKeys.begin(), sortedKeys.end(), keys.begin(), keys.end()); + }); + + // re-query non table data + rocksDBStorage->asyncGetPrimaryKeys( + testTableName, condition, [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 1000); + + std::vector sortedKeys; + + for (size_t i = 0; i < 1000; ++i) + { + sortedKeys.emplace_back("key" + boost::lexical_cast(i)); + } + + std::sort(sortedKeys.begin(), sortedKeys.end()); + + BOOST_CHECK_EQUAL_COLLECTIONS( + sortedKeys.begin(), sortedKeys.end(), keys.begin(), keys.end()); + }); + + rocksDBStorage->asyncGetRow(tableInfo->name(), + "newkey" + boost::lexical_cast(1050), + [&](Error::UniquePtr error, std::optional entry) { + // SetRow doesn't need create table + + BOOST_CHECK(!error); + BOOST_CHECK(entry); + }); + + rocksDBStorage->asyncGetRow(tableInfo->name(), + "newkey" + boost::lexical_cast(2000), + [&](Error::UniquePtr error, std::optional entry) { + // SetRow doesn't need create table + + BOOST_CHECK(!error); + BOOST_CHECK(!entry); + }); + + // clean new data + for (size_t i = 0; i < 1000; ++i) + { + std::string key = "newkey" + boost::lexical_cast(i + 1000); + auto entry = Entry(); + entry.setStatus(Entry::DELETED); + + rocksDBStorage->asyncSetRow(tableInfo->name(), key, entry, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + } + + rocksDBStorage->asyncGetRow(tableInfo->name(), + "newkey" + boost::lexical_cast(1050), + [&](Error::UniquePtr error, std::optional entry) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entry.has_value(), false); + }); + + // check if the data is deleted + rocksDBStorage->asyncGetPrimaryKeys(tableInfo->name(), + std::optional(), + [](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 0); + }); + + cleanupTestTableData(); +} + +BOOST_AUTO_TEST_CASE(asyncGetRows) +{ + prepareTestTableData(); + + std::vector keys; + for (size_t i = 0; i < 1050; ++i) + { + std::string key = "key" + boost::lexical_cast(i); + keys.push_back(key); + } + rocksDBStorage->asyncGetRows(testTableName, keys, + [&](Error::UniquePtr error, std::vector> entries) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entries.size(), 1050); + + for (size_t i = 0; i < 1050; ++i) + { + auto& entry = entries[i]; + if (i < 1000) + { + BOOST_CHECK_NE(entry.has_value(), false); + auto data = entry->get(); + BOOST_CHECK_EQUAL(data, "value_" + boost::lexical_cast(i)); + } + else + { + BOOST_CHECK_EQUAL(entry.has_value(), false); + } + } + }); + + cleanupTestTableData(); +} + +BOOST_AUTO_TEST_CASE(asyncPrepare) +{ + prepareTestTableData(); + + auto hashImpl = std::make_shared(); + auto storage = std::make_shared(rocksDBStorage); + BOOST_CHECK(storage->createTable("table1", "value1,value2,value3")); + BOOST_CHECK(storage->createTable("table2", "value1,value2,value3,value4,value5")); + + auto table1 = storage->openTable("table1"); + auto table2 = storage->openTable("table2"); + + BOOST_CHECK_NE(table1.has_value(), false); + BOOST_CHECK_NE(table2.has_value(), false); + + std::vector table1Keys; + std::vector table2Keys; + + for (size_t i = 0; i < 10; ++i) + { + auto entry = table1->newEntry(); + auto key1 = "key" + boost::lexical_cast(i); + entry.setField(0, "hello world!" + boost::lexical_cast(i)); + table1->setRow(key1, entry); + table1Keys.push_back(key1); + + auto entry2 = table2->newEntry(); + auto key2 = "key" + boost::lexical_cast(i); + entry2.setField(0, "hello world!abc" + boost::lexical_cast(i)); + table2->setRow(key2, entry2); + table2Keys.push_back(key2); + } + + rocksDBStorage->asyncPrepare( + bcos::protocol::TwoPCParams(), *storage, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(ts, 0); + }); + // TODO: asyncPrepare can't be query + rocksDBStorage->asyncCommit(bcos::protocol::TwoPCParams(), + [&](Error::Ptr error, uint64_t) { BOOST_CHECK_EQUAL(error, nullptr); }); + + rocksDBStorage->asyncGetPrimaryKeys(table1->tableInfo()->name(), + std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 10); + + std::sort(table1Keys.begin(), table1Keys.end()); + BOOST_CHECK_EQUAL_COLLECTIONS( + table1Keys.begin(), table1Keys.end(), keys.begin(), keys.end()); + + rocksDBStorage->asyncGetRows(table1->tableInfo()->name(), table1Keys, + [&](Error::UniquePtr error, std::vector> entries) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + if (error) + { + std::cout << boost::diagnostic_information(*error) << std::endl; + } + + BOOST_CHECK_EQUAL(entries.size(), 10); + + for (size_t i = 0; i < 10; ++i) + { + BOOST_CHECK_EQUAL(entries[i]->getField(0), + std::string("hello world!") + table1Keys[i][3]); + } + }); + }); + + rocksDBStorage->asyncGetPrimaryKeys(table2->tableInfo()->name(), + std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 10); + + std::sort(table2Keys.begin(), table2Keys.end()); + BOOST_CHECK_EQUAL_COLLECTIONS( + table2Keys.begin(), table2Keys.end(), keys.begin(), keys.end()); + + rocksDBStorage->asyncGetRows(table2->tableInfo()->name(), table2Keys, + [&](Error::UniquePtr error, std::vector> entries) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entries.size(), 10); + + for (size_t i = 0; i < 10; ++i) + { + BOOST_CHECK_EQUAL(entries[i]->getField(0), + std::string("hello world!abc") + table2Keys[i][3]); + } + }); + }); + + cleanupTestTableData(); +} + +BOOST_AUTO_TEST_CASE(boostSerialize) +{ + // encode the vector + std::vector forEncode(5); + forEncode[3] = "hello world!"; + + std::string buffer; + boost::iostreams::stream> outputStream( + buffer); + boost::archive::binary_oarchive archive(outputStream, + boost::archive::no_header | boost::archive::no_codecvt | boost::archive::no_tracking); + + archive << forEncode; + outputStream.flush(); + + std::cout << forEncode << std::endl; + + // decode the vector + boost::iostreams::stream inputStream( + buffer.data(), buffer.size()); + boost::archive::binary_iarchive archive2(inputStream, + boost::archive::no_header | boost::archive::no_codecvt | boost::archive::no_tracking); + + std::vector forDecode; + archive2 >> forDecode; + + std::cout << forDecode; + + BOOST_CHECK_EQUAL_COLLECTIONS( + forEncode.begin(), forEncode.end(), forDecode.begin(), forDecode.end()); +} + +BOOST_AUTO_TEST_CASE(rocksDBiter) +{ + std::string testPath = "./iterDBTest"; + + rocksdb::DB* db; + rocksdb::Options options; + // Optimize RocksDB. This is the easiest way to get RocksDB to perform well + options.IncreaseParallelism(); + options.OptimizeLevelStyleCompaction(); + // create the DB if it's not already present + options.create_if_missing = true; + + // open DB + rocksdb::Status s = rocksdb::DB::Open(options, testPath, &db); + BOOST_CHECK_EQUAL(s.ok(), true); + + for (uint32_t i = 0; i < 10; ++i) + { + rocksdb::WriteBatch writeBatch; + + for (size_t j = 0; j != 1000; ++j) + { + std::string key = *(bcos::toHexString(std::string((char*)&i, sizeof(i)))) + "_key_" + + boost::lexical_cast(j); + std::string value = "hello world!"; + + writeBatch.Put(key, value); + } + + db->Write(rocksdb::WriteOptions(), &writeBatch); + + rocksdb::ReadOptions read_options; + read_options.total_order_seek = true; + auto iter = db->NewIterator(read_options); + + size_t total = 0; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) + { + ++total; + } + delete iter; + + BOOST_CHECK_EQUAL(total, 1000 * (i + 1)); + } + + if (boost::filesystem::exists(testPath)) + { + boost::filesystem::remove_all(testPath); + } + + delete db; +} + +BOOST_AUTO_TEST_CASE(writeReadDelete_1Table) +{ + writeReadDeleteSingleTable(1000); + writeReadDeleteSingleTable(5000); + writeReadDeleteSingleTable(10000); + writeReadDeleteSingleTable(20000); + writeReadDeleteSingleTable(50000); +} + +BOOST_AUTO_TEST_CASE(commitAndCheck) +{ + auto initState = std::make_shared(rocksDBStorage); + + initState->asyncCreateTable( + "test_table1", "value", [](Error::UniquePtr error, std::optional
table) { + BOOST_CHECK(!error); + if (error) + { + std::cout << boost::diagnostic_information(*error) << std::endl; + } + BOOST_CHECK(table); + }); + + initState->asyncCreateTable( + "test_table2", "value", [](Error::UniquePtr error, std::optional
table) { + BOOST_CHECK(!error); + if (error) + { + std::cout << boost::diagnostic_information(*error) << std::endl; + } + BOOST_CHECK(table); + }); + + for (size_t keyIndex = 0; keyIndex < 100; ++keyIndex) + { + Entry entry; + entry.importFields({boost::lexical_cast(100)}); + + auto key = (boost::format("key_%d") % keyIndex).str(); + initState->asyncSetRow("test_table1", key, std::move(entry), + [](Error::UniquePtr error) { BOOST_CHECK(!error); }); + } + + bcos::protocol::TwoPCParams params; + params.number = 1; + rocksDBStorage->asyncPrepare( + params, *initState, [](Error::Ptr error, uint64_t, const std::string&) { BOOST_CHECK(!error); }); + rocksDBStorage->asyncCommit(params, [](Error::Ptr error, uint64_t) { BOOST_CHECK(!error); }); + + STORAGE_LOG(INFO) << "Init state finished"; + + for (size_t i = 100; i < 1000; i += 100) + { + auto state = std::make_shared(rocksDBStorage); + + STORAGE_LOG(INFO) << "Expected: " << i; + for (size_t keyIndex = 0; keyIndex < 100; ++keyIndex) + { + auto key = (boost::format("key_%d") % keyIndex).str(); + + size_t num = 0; + state->asyncGetRow( + "test_table1", key, [&num](Error::UniquePtr error, std::optional entry) { + BOOST_CHECK(!error); + num = boost::lexical_cast(entry->getField(0)); + }); + + BOOST_CHECK_EQUAL(num, i); + + num += 100; + + Entry newEntry; + newEntry.importFields({boost::lexical_cast(num)}); + + state->asyncSetRow("test_table1", key, std::move(newEntry), + [](Error::UniquePtr error) { BOOST_CHECK(!error); }); + } + + tbb::concurrent_vector> checks; + auto keySet = std::make_shared>(); + state->parallelTraverse(true, [&checks, i, keySet](const std::string_view& table, + const std::string_view& key, const Entry& entry) { + checks.push_back([tableName = std::string(table), key = std::string(key), entry = entry, + i, keySet]() { + BOOST_CHECK_EQUAL(tableName, "test_table1"); + auto [it, inserted] = keySet->emplace(key); + boost::ignore_unused(it); + BOOST_CHECK(inserted); + auto num = boost::lexical_cast(entry.getField(0)); + BOOST_CHECK_EQUAL(i + 100, num); + }); + return true; + }); + + BOOST_CHECK_EQUAL(checks.size(), 100); + for (auto& it : checks) + { + it(); + } + + bcos::protocol::TwoPCParams params; + params.number = i; + rocksDBStorage->asyncPrepare( + params, *state, [](Error::Ptr error, uint64_t, const std::string&) { BOOST_CHECK(!error); }); + rocksDBStorage->asyncCommit( + params, [](Error::Ptr error, uint64_t) { BOOST_CHECK(!error); }); + } +} +BOOST_AUTO_TEST_SUITE_END() + +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-storage/test/unittest/TestTiKVStorage.cpp" "b/BFPL\345\243\271/bcos-storage/test/unittest/TestTiKVStorage.cpp" new file mode 100644 index 00000000..b689fc9f --- /dev/null +++ "b/BFPL\345\243\271/bcos-storage/test/unittest/TestTiKVStorage.cpp" @@ -0,0 +1,1384 @@ +#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 +#include +#include +#include + +using namespace bcos::storage; +using namespace std; + +namespace bcos::test +{ +size_t total = 500; + +class Header256Hash : public bcos::crypto::Hash +{ +public: + typedef std::shared_ptr Ptr; + Header256Hash() = default; + virtual ~Header256Hash(){}; + bcos::crypto::HashType hash(bytesConstRef _data) override + { + std::hash hash; + return bcos::crypto::HashType( + hash(std::string_view((const char*)_data.data(), _data.size()))); + } + bcos::crypto::hasher::AnyHasher hasher() override + { + return bcos::crypto::hasher::AnyHasher{bcos::crypto::hasher::openssl::OpenSSL_SM3_Hasher{}}; + } +}; + +struct TestTiKVStorageFixture +{ + TestTiKVStorageFixture() + { + // boost::log::core::get()->set_logging_enabled(false); + std::vector pd_addrs{"127.0.0.1:2379"}; + m_cluster = newTiKVClient(pd_addrs, "./"); + + storage = std::make_shared(m_cluster); + storage->asyncOpenTable(testTableName, [&](auto error, auto table) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + if (table) + { + testTableInfo = table->tableInfo(); + } + }); + if (!testTableInfo) + { + std::promise> prom; + storage->asyncCreateTable(testTableName, "v1,v2,v3,v4,v5,v6", + [&prom](Error::UniquePtr error, std::optional
table) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(table.has_value(), true); + prom.set_value(table); + }); + auto table = prom.get_future().get(); + testTableInfo = table->tableInfo(); + } + } + + void prepareTestTableData() + { + for (size_t i = 0; i < total; ++i) + { + std::string key = "key" + boost::lexical_cast(i); + Entry entry(testTableInfo); + entry.importFields({"value_" + boost::lexical_cast(i)}); + storage->asyncSetRow(testTableName, key, entry, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + } + } + + void cleanupTestTableData() + { + for (size_t i = 0; i < total; ++i) + { + std::string key = "key" + boost::lexical_cast(i); + Entry entry(testTableInfo); + entry.setStatus(Entry::DELETED); + + storage->asyncSetRow(testTableName, key, entry, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + } + } + void writeReadDeleteSingleTable(size_t count) + { + size_t tableEntries = count; + auto hashImpl = std::make_shared(); + auto stateStorage = std::make_shared(storage); + auto testTable = stateStorage->openTable(testTableName); + BOOST_CHECK_EQUAL(testTable.has_value(), true); + for (size_t i = 0; i < tableEntries; ++i) + { + std::string key = "key" + boost::lexical_cast(i); + Entry entry(testTableInfo); + entry.importFields({"value_" + boost::lexical_cast(i)}); + testTable->setRow(key, std::move(entry)); + } + + auto params1 = bcos::protocol::TwoPCParams(); + params1.number = 100; + params1.primaryKey = testTableName + ":key0"; + auto start = std::chrono::system_clock::now(); + // prewrite + storage->asyncPrepare( + params1, *stateStorage, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_NE(ts, 0); + params1.timestamp = ts; + }); + + // commit + storage->asyncCommit(bcos::protocol::TwoPCParams(), + [&](Error::Ptr error, uint64_t) { BOOST_CHECK_EQUAL(error, nullptr); }); + auto commitEnd = std::chrono::system_clock::now(); + // check commit success + storage->asyncGetPrimaryKeys(testTableName, std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), tableEntries); + storage->asyncGetRows(testTableName, keys, + [&](Error::UniquePtr error, std::vector> entries) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entries.size(), tableEntries); + for (size_t i = 0; i < tableEntries; ++i) + { + // BOOST_CHECK_EQUAL(entries[i]->getField(0), std::string("value3")); + BOOST_CHECK_EQUAL( + entries[i]->getField(0), std::string("value_") + keys[i].substr(3)); + } + }); + }); + auto getEnd = std::chrono::system_clock::now(); + // clean data + for (size_t i = 0; i < tableEntries; ++i) + { + auto entry = testTable->newEntry(); + auto key = "key" + boost::lexical_cast(i); + entry.setStatus(Entry::DELETED); + testTable->setRow(key, std::move(entry)); + } + params1.timestamp = 0; + storage->asyncPrepare( + params1, *stateStorage, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_NE(ts, 0); + params1.timestamp = ts; + }); + // commit + storage->asyncCommit(bcos::protocol::TwoPCParams(), + [&](Error::Ptr error, uint64_t) { BOOST_CHECK_EQUAL(error, nullptr); }); + auto deleteEnd = std::chrono::system_clock::now(); + // check if the data is deleted + storage->asyncGetPrimaryKeys(testTableName, std::optional(), + [](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 0); + }); + cerr << "entries count=" << tableEntries << "|>>>>>>>>> commit=" + << std::chrono::duration_cast(commitEnd - start).count() + << "ms|getAll=" + << std::chrono::duration_cast(getEnd - commitEnd).count() + << "ms|deleteAll=" + << std::chrono::duration_cast(deleteEnd - getEnd).count() << "ms" + << endl + << endl; + } + ~TestTiKVStorageFixture() + { + if (boost::filesystem::exists(path)) + { + boost::filesystem::remove_all(path); + } + boost::log::core::get()->set_logging_enabled(true); + } + + std::string path = "./unittestdb"; + TransactionalStorageInterface::Ptr storage; + std::string testTableName = "TestTable"; + TableInfo::ConstPtr testTableInfo = nullptr; + std::shared_ptr m_cluster; +}; + +BOOST_FIXTURE_TEST_SUITE(TestTiKVStorage, TestTiKVStorageFixture) + +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) + { + BOOST_CHECK_EQUAL(entry.has_value(), true); + } + else + { + BOOST_CHECK_EQUAL(entry.has_value(), false); + } + if (i < total) + { + BOOST_CHECK_NE(entry.has_value(), false); + auto data = entry->get(); + auto fields = std::string("value_" + boost::lexical_cast(i)); + + BOOST_CHECK_EQUAL(data, fields); + } + else + { + BOOST_CHECK_EQUAL(entry.has_value(), false); + } + }); + } + + cleanupTestTableData(); +} + +BOOST_AUTO_TEST_CASE(asyncGetPrimaryKeys) +{ + prepareTestTableData(); + storage->asyncGetPrimaryKeys(testTableName, std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), total); + + std::vector sortedKeys; + + for (size_t i = 0; i < total; ++i) + { + sortedKeys.emplace_back("key" + boost::lexical_cast(i)); + } + + std::sort(sortedKeys.begin(), sortedKeys.end()); + + BOOST_CHECK_EQUAL_COLLECTIONS( + sortedKeys.begin(), sortedKeys.end(), keys.begin(), keys.end()); + }); + + auto tableInfo = std::make_shared("new_table", vector{"value"}); + + for (size_t i = 1000; i < 1000 + total; ++i) + { + std::string key = "newkey" + boost::lexical_cast(i); + auto entry = Entry(tableInfo); + entry.importFields({"value12345"}); + + storage->asyncSetRow(tableInfo->name(), key, entry, + [&](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + } + + // query old data + auto condition = std::optional(); + BOOST_CHECK_EQUAL(condition.has_value(), false); + storage->asyncGetPrimaryKeys( + tableInfo->name(), condition, [](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), total); + + std::vector sortedKeys; + + for (size_t i = 0; i < total; ++i) + { + sortedKeys.emplace_back("newkey" + boost::lexical_cast(i + 1000)); + } + std::sort(sortedKeys.begin(), sortedKeys.end()); + + BOOST_CHECK_EQUAL_COLLECTIONS( + sortedKeys.begin(), sortedKeys.end(), keys.begin(), keys.end()); + }); + + // re-query non table data + storage->asyncGetPrimaryKeys( + testTableName, condition, [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), total); + + std::vector sortedKeys; + + for (size_t i = 0; i < total; ++i) + { + sortedKeys.emplace_back("key" + boost::lexical_cast(i)); + } + + std::sort(sortedKeys.begin(), sortedKeys.end()); + + BOOST_CHECK_EQUAL_COLLECTIONS( + sortedKeys.begin(), sortedKeys.end(), keys.begin(), keys.end()); + }); + + storage->asyncGetRow(tableInfo->name(), "newkey" + boost::lexical_cast(1050), + [&](Error::UniquePtr error, std::optional entry) { + BOOST_CHECK(!error.get()); + BOOST_CHECK(entry.has_value()); + }); + + // clean new data + for (size_t i = 0; i < total; ++i) + { + std::string key = "newkey" + boost::lexical_cast(i + 1000); + auto entry = Entry(); + entry.setStatus(Entry::DELETED); + + storage->asyncSetRow(tableInfo->name(), key, entry, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + } + + storage->asyncGetRow(tableInfo->name(), "newkey" + boost::lexical_cast(1000), + [&](Error::UniquePtr error, std::optional entry) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entry.has_value(), false); + }); + + // check if the data is deleted + storage->asyncGetPrimaryKeys(tableInfo->name(), std::optional(), + [](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 0); + }); + + cleanupTestTableData(); +} + +BOOST_AUTO_TEST_CASE(asyncGetRows) +{ + prepareTestTableData(); + + std::vector keys; + for (size_t i = 0; i < 1050; ++i) + { + std::string key = "key" + boost::lexical_cast(i); + keys.push_back(key); + } + storage->asyncGetRows(testTableName, keys, + [&](Error::UniquePtr error, std::vector> entries) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entries.size(), 1050); + + for (size_t i = 0; i < 1050; ++i) + { + auto& entry = entries[i]; + if (i < total) + { + BOOST_CHECK_EQUAL(entry.has_value(), true); + + auto data = entry->get(); + BOOST_CHECK_EQUAL( + data, std::string("value_" + boost::lexical_cast(i))); + + // BOOST_CHECK_EQUAL_COLLECTIONS( + // data.begin(), data.end(), fields.begin(), fields.end()); + } + else + { + BOOST_CHECK_EQUAL(entry.has_value(), false); + } + } + }); + + cleanupTestTableData(); +} + +BOOST_AUTO_TEST_CASE(asyncPrepare) +{ + prepareTestTableData(); + + auto hashImpl = std::make_shared(); + auto stateStorage = std::make_shared(storage); + auto table1Name = "table1"; + auto table2Name = "table2"; + BOOST_CHECK_EQUAL( + stateStorage->createTable(table1Name, "value1,value2,value3").has_value(), true); + BOOST_CHECK_EQUAL( + stateStorage->createTable(table2Name, "value1,value2,value3,value4,value5").has_value(), + true); + auto table1 = stateStorage->openTable(table1Name); + auto table2 = stateStorage->openTable(table2Name); + + BOOST_CHECK_NE(table1.has_value(), false); + BOOST_CHECK_NE(table2.has_value(), false); + + std::vector table1Keys; + std::vector table2Keys; + + for (size_t i = 0; i < 10; ++i) + { + auto entry = table1->newEntry(); + auto key1 = "key" + boost::lexical_cast(i); + entry.setField(0, "hello world!" + boost::lexical_cast(i)); + table1->setRow(key1, entry); + table1Keys.push_back(key1); + + auto entry2 = table2->newEntry(); + auto key2 = "key" + boost::lexical_cast(i); + entry2.setField(0, "hello world!" + boost::lexical_cast(i)); + table2->setRow(key2, entry2); + table2Keys.push_back(key2); + } + + storage->asyncPrepare(bcos::protocol::TwoPCParams(), *stateStorage, + [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_NE(ts, 0); + }); + storage->asyncCommit(bcos::protocol::TwoPCParams(), + [&](Error::Ptr error, uint64_t) { BOOST_CHECK_EQUAL(error, nullptr); }); + + storage->asyncGetPrimaryKeys(table1->tableInfo()->name(), + std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 10); + + std::sort(table1Keys.begin(), table1Keys.end()); + BOOST_CHECK_EQUAL_COLLECTIONS( + table1Keys.begin(), table1Keys.end(), keys.begin(), keys.end()); + + storage->asyncGetRows(table1->tableInfo()->name(), table1Keys, + [&](Error::UniquePtr error, std::vector> entries) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entries.size(), 10); + + for (size_t i = 0; i < 10; ++i) + { + BOOST_CHECK_EQUAL(entries[i]->getField(0), + std::string("hello world!") + table1Keys[i][3]); + } + }); + }); + + storage->asyncGetPrimaryKeys(table2->tableInfo()->name(), + std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 10); + + std::sort(table2Keys.begin(), table2Keys.end()); + BOOST_CHECK_EQUAL_COLLECTIONS( + table2Keys.begin(), table2Keys.end(), keys.begin(), keys.end()); + + storage->asyncGetRows(table2->tableInfo()->name(), table2Keys, + [&](Error::UniquePtr error, std::vector> entries) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entries.size(), 10); + + for (size_t i = 0; i < 10; ++i) + { + BOOST_CHECK_EQUAL(entries[i]->getField(0), + std::string("hello world!") + table2Keys[i][3]); + } + }); + }); + + auto entry1 = Entry(); + entry1.setStatus(Entry::DELETED); + storage->asyncSetRow(storage::StateStorage::SYS_TABLES, table1Name, entry1, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + auto entry2 = Entry(); + entry2.setStatus(Entry::DELETED); + storage->asyncSetRow(storage::StateStorage::SYS_TABLES, table2Name, entry2, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + + cleanupTestTableData(); +} + + +BOOST_AUTO_TEST_CASE(asyncPrepareTimeout) +{ + prepareTestTableData(); + + auto hashImpl = std::make_shared(); + auto stateStorage = std::make_shared(storage); + auto table1Name = "table1"; + auto table2Name = "table2"; + BOOST_CHECK_EQUAL( + stateStorage->createTable(table1Name, "value1,value2,value3").has_value(), true); + BOOST_CHECK_EQUAL( + stateStorage->createTable(table2Name, "value1,value2,value3,value4,value5").has_value(), + true); + auto table1 = stateStorage->openTable(table1Name); + auto table2 = stateStorage->openTable(table2Name); + + BOOST_CHECK_NE(table1.has_value(), false); + BOOST_CHECK_NE(table2.has_value(), false); + + std::vector table1Keys; + std::vector table2Keys; + + for (size_t i = 0; i < 10; ++i) + { + auto entry = table1->newEntry(); + auto key1 = "key" + boost::lexical_cast(i); + entry.setField(0, "hello world!" + boost::lexical_cast(i)); + table1->setRow(key1, entry); + table1Keys.push_back(key1); + + auto entry2 = table2->newEntry(); + auto key2 = "key" + boost::lexical_cast(i); + entry2.setField(0, "hello world!" + boost::lexical_cast(i)); + table2->setRow(key2, entry2); + table2Keys.push_back(key2); + } + + storage->asyncPrepare(bcos::protocol::TwoPCParams(), *stateStorage, + [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_NE(ts, 0); + }); + auto now = std::chrono::system_clock::now(); + storage->asyncPrepare(bcos::protocol::TwoPCParams(), *stateStorage, + [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_NE(ts, 0); + }); + auto end = std::chrono::system_clock::now(); + BOOST_CHECK_GE(std::chrono::duration_cast(end - now).count(), 3000); + cleanupTestTableData(); +} + +BOOST_AUTO_TEST_CASE(multiStorageCommit) +{ + // FIXME: this test case will crash, because tikv-rust client only resolve timeout lock + size_t tableEntries = 101; + 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 testTable = stateStorage->openTable(testTableName); + BOOST_CHECK_EQUAL(testTable.has_value(), true); + for (size_t i = 0; i < total; ++i) + { + std::string key = "key" + boost::lexical_cast(i); + Entry entry(testTableInfo); + 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 table1Name = "table1"; + auto table2Name = "table2"; + BOOST_CHECK_EQUAL( + stateStorage2->createTable(table1Name, "value1,value2,value3").has_value(), true); + BOOST_CHECK_EQUAL( + stateStorage3->createTable(table2Name, "value1,value2,value3,value4,value5").has_value(), + true); + auto table1 = stateStorage2->openTable(table1Name); + auto table2 = stateStorage3->openTable(table2Name); + + BOOST_CHECK_EQUAL(table1.has_value(), true); + BOOST_CHECK_EQUAL(table2.has_value(), true); + + std::vector table1Keys; + std::vector table2Keys; + + for (size_t i = 0; i < tableEntries; ++i) + { + auto entry = table1->newEntry(); + auto key1 = "key" + boost::lexical_cast(i); + entry.setField(0, "hello world!" + boost::lexical_cast(i)); + table1->setRow(key1, entry); + table1Keys.push_back(key1); + + auto entry2 = table2->newEntry(); + auto key2 = "key" + boost::lexical_cast(i); + entry2.setField(0, "hello world!" + boost::lexical_cast(i)); + table2->setRow(key2, entry2); + table2Keys.push_back(key2); + } + auto params1 = bcos::protocol::TwoPCParams(); + params1.number = 100; + params1.primaryKey = testTableName + ":key0"; + auto stateStorage0 = std::make_shared(storage); + // check empty storage error + storage->asyncPrepare( + params1, *stateStorage0, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_NE(error.get(), nullptr); + BOOST_CHECK_EQUAL(ts, 0); + }); + // prewrite + BOOST_CHECK_EQUAL(params1.timestamp, 0); + storage->asyncPrepare( + params1, *stateStorage, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_NE(ts, 0); + params1.timestamp = ts; + storage2->asyncPrepare( + params1, *stateStorage2, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(ts, 0); + }); + storage3->asyncPrepare( + params1, *stateStorage3, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(ts, 0); + }); + }); + // only storage call asyncCommit + storage->asyncCommit(bcos::protocol::TwoPCParams(), + [&](Error::Ptr error, uint64_t) { BOOST_CHECK_EQUAL(error, nullptr); }); + // check commit success + + // this_thread::sleep_for(chrono::seconds(3)); + storage->asyncGetPrimaryKeys(table1->tableInfo()->name(), + std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_REQUIRE_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), tableEntries); + + std::sort(table1Keys.begin(), table1Keys.end()); + BOOST_CHECK_EQUAL_COLLECTIONS( + table1Keys.begin(), table1Keys.end(), keys.begin(), keys.end()); + + storage->asyncGetRows(table1->tableInfo()->name(), table1Keys, + [&](Error::UniquePtr error, std::vector> entries) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entries.size(), tableEntries); + + for (size_t i = 0; i < tableEntries; ++i) + { + BOOST_CHECK_EQUAL(entries[i]->getField(0), + std::string("hello world!") + table1Keys[i].substr(3)); + } + }); + }); + + storage->asyncGetPrimaryKeys(table2->tableInfo()->name(), + std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), tableEntries); + + std::sort(table2Keys.begin(), table2Keys.end()); + BOOST_CHECK_EQUAL_COLLECTIONS( + table2Keys.begin(), table2Keys.end(), keys.begin(), keys.end()); + + storage->asyncGetRows(table2->tableInfo()->name(), table2Keys, + [&](Error::UniquePtr error, std::vector> entries) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entries.size(), tableEntries); + + for (size_t i = 0; i < tableEntries; ++i) + { + BOOST_CHECK_EQUAL(entries[i]->getField(0), + std::string("hello world!") + table2Keys[i].substr(3)); + } + }); + }); + // clean data + auto entry1 = Entry(); + entry1.setStatus(Entry::DELETED); + storage->asyncSetRow(storage::StateStorage::SYS_TABLES, table1Name, entry1, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + auto entry2 = Entry(); + entry2.setStatus(Entry::DELETED); + storage->asyncSetRow(storage::StateStorage::SYS_TABLES, table2Name, entry2, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + + for (size_t i = 0; i < tableEntries; ++i) + { + auto entry1 = table1->newEntry(); + auto key1 = "key" + boost::lexical_cast(i); + entry1.setStatus(Entry::DELETED); + storage2->asyncSetRow(table1Name, key1, entry1, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + + auto entry2 = table2->newEntry(); + auto key2 = "key" + boost::lexical_cast(i); + entry2.setStatus(Entry::DELETED); + storage3->asyncSetRow(table2Name, key2, entry2, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + } + dynamic_pointer_cast(storage)->reset(); + // check if the data is deleted + storage->asyncGetPrimaryKeys(table1Name, std::optional(), + [](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 0); + }); + // check if the data is deleted + storage->asyncGetPrimaryKeys(table2Name, std::optional(), + [](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 0); + }); + + cleanupTestTableData(); +} + +BOOST_AUTO_TEST_CASE(singleStorageRollback) +{ + size_t tableEntries = 101; + std::string table1Name = "table1"; + storage->asyncGetPrimaryKeys(table1Name, std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 0); + }); + auto stateStorage = std::make_shared(storage); + BOOST_CHECK_EQUAL( + stateStorage->createTable(table1Name, "value1,value2,value3").has_value(), true); + auto table1 = stateStorage->openTable(table1Name); + BOOST_CHECK_EQUAL(table1.has_value(), true); + for (size_t i = 0; i < tableEntries; ++i) + { + auto entry = table1->newEntry(); + auto key1 = "key" + boost::lexical_cast(i); + entry.setField(0, "hello world!" + boost::lexical_cast(i)); + table1->setRow(key1, entry); + } + auto params1 = bcos::protocol::TwoPCParams(); + params1.number = 100; + params1.primaryKey = table1Name + ":key0"; + params1.timestamp = 0; + storage->asyncPrepare( + params1, *stateStorage, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_NE(ts, 0); + params1.timestamp = ts; + }); + storage->asyncRollback( + params1, [&](Error::Ptr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + storage->asyncGetPrimaryKeys(table1Name, std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 0); + }); +} + +BOOST_AUTO_TEST_CASE(multiStorageRollback) +{ + size_t tableEntries = 101; + 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 testTable = stateStorage->openTable(testTableName); + BOOST_CHECK_EQUAL(testTable.has_value(), true); + for (size_t i = 0; i < total; ++i) + { + std::string key = "key" + boost::lexical_cast(i); + Entry entry(testTableInfo); + 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 table1Name = "table1"; + auto table2Name = "table2"; + BOOST_CHECK_EQUAL( + stateStorage2->createTable(table1Name, "value1,value2,value3").has_value(), true); + BOOST_CHECK_EQUAL( + stateStorage3->createTable(table2Name, "value1,value2,value3,value4,value5").has_value(), + true); + auto table1 = stateStorage2->openTable(table1Name); + auto table2 = stateStorage3->openTable(table2Name); + + BOOST_CHECK_EQUAL(table1.has_value(), true); + BOOST_CHECK_EQUAL(table2.has_value(), true); + + std::vector table1Keys; + std::vector table2Keys; + + for (size_t i = 0; i < tableEntries; ++i) + { + auto entry = table1->newEntry(); + auto key1 = "key" + boost::lexical_cast(i); + entry.setField(0, "hello world!" + boost::lexical_cast(i)); + table1->setRow(key1, entry); + table1Keys.push_back(key1); + + auto entry2 = table2->newEntry(); + auto key2 = "key" + boost::lexical_cast(i); + entry2.setField(0, "hello world!" + boost::lexical_cast(i)); + table2->setRow(key2, entry2); + table2Keys.push_back(key2); + } + auto params1 = bcos::protocol::TwoPCParams(); + params1.number = 100; + params1.primaryKey = testTableName + ":key0"; + auto stateStorage0 = std::make_shared(storage); + // check empty storage error + storage->asyncPrepare( + params1, *stateStorage0, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_NE(error.get(), nullptr); + BOOST_CHECK_EQUAL(ts, 0); + }); + // prewrite + storage->asyncPrepare( + params1, *stateStorage, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_NE(ts, 0); + params1.timestamp = ts; + storage2->asyncPrepare( + params1, *stateStorage2, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(ts, 0); + }); + }); + // all storage call asyncRollback + storage->asyncRollback( + params1, [&](Error::Ptr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + storage2->asyncRollback( + params1, [&](Error::Ptr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + + // check commit failed + storage->asyncGetPrimaryKeys(table1Name, std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 0); + }); + storage->asyncGetPrimaryKeys(table2Name, std::optional(), + [](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 0); + }); + storage->asyncGetPrimaryKeys(testTableName, std::optional(), + [](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 0); + }); +} + +BOOST_AUTO_TEST_CASE(secondaryRollbackAndPrimaryCommit) +{ + size_t tableEntries = 101; + 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 testTable = stateStorage->openTable(testTableName); + BOOST_CHECK_EQUAL(testTable.has_value(), true); + for (size_t i = 0; i < total; ++i) + { + std::string key = "key" + boost::lexical_cast(i); + Entry entry(testTableInfo); + entry.importFields({"value_" + boost::lexical_cast(i)}); + testTable->setRow(key, std::move(entry)); + } + auto stateStorage1 = std::make_shared(storage2); + auto table1Name = "table1"; + BOOST_CHECK_EQUAL( + stateStorage1->createTable(table1Name, "value1,value2,value3").has_value(), true); + auto table1 = stateStorage1->openTable(table1Name); + + BOOST_CHECK_EQUAL(table1.has_value(), true); + + std::vector table1Keys; + + for (size_t i = 0; i < tableEntries; ++i) + { + auto entry = table1->newEntry(); + auto key1 = "key" + boost::lexical_cast(i); + entry.setField(0, "hello world!" + boost::lexical_cast(i)); + table1->setRow(key1, entry); + table1Keys.push_back(key1); + } + auto params1 = bcos::protocol::TwoPCParams(); + params1.number = 100; + params1.primaryKey = testTableName + ":key0"; + auto stateStorage0 = std::make_shared(storage); + + // prewrite + storage->asyncPrepare( + params1, *stateStorage, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_NE(ts, 0); + params1.timestamp = ts; + storage2->asyncPrepare( + params1, *stateStorage1, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(ts, 0); + }); + }); + // storage2 rollback and storage commit + storage2->asyncRollback( + params1, [&](Error::Ptr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + params1.timestamp = 0; + storage->asyncCommit( + params1, [&](Error::Ptr error, uint64_t) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + + // check commit success bug storage2 rollback + storage->asyncGetPrimaryKeys(table1Name, std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 0); + }); + storage->asyncGetPrimaryKeys(testTableName, std::optional(), + [](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), total); + }); + cleanupTestTableData(); +} + +BOOST_AUTO_TEST_CASE(multiStorageScondaryCrash) +{ + size_t tableEntries = 101; + 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 testTable = stateStorage->openTable(testTableName); + BOOST_CHECK_EQUAL(testTable.has_value(), true); + for (size_t i = 0; i < total; ++i) + { + std::string key = "key" + boost::lexical_cast(i); + Entry entry(testTableInfo); + 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 table1Name = "table1"; + auto table2Name = "table2"; + BOOST_CHECK_EQUAL( + stateStorage2->createTable(table1Name, "value1,value2,value3").has_value(), true); + BOOST_CHECK_EQUAL( + stateStorage3->createTable(table2Name, "value1,value2,value3,value4,value5").has_value(), + true); + auto table1 = stateStorage2->openTable(table1Name); + auto table2 = stateStorage3->openTable(table2Name); + + BOOST_CHECK_EQUAL(table1.has_value(), true); + BOOST_CHECK_EQUAL(table2.has_value(), true); + + std::vector table1Keys; + std::vector table2Keys; + + for (size_t i = 0; i < tableEntries; ++i) + { + auto entry = table1->newEntry(); + auto key1 = "key" + boost::lexical_cast(i); + entry.setField(0, "hello world!" + boost::lexical_cast(i)); + table1->setRow(key1, entry); + table1Keys.push_back(key1); + + auto entry2 = table2->newEntry(); + auto key2 = "key" + boost::lexical_cast(i); + entry2.setField(0, "hello world!" + boost::lexical_cast(i)); + table2->setRow(key2, entry2); + table2Keys.push_back(key2); + } + auto params1 = bcos::protocol::TwoPCParams(); + params1.number = 100; + params1.primaryKey = testTableName + ":key0"; + auto stateStorage0 = std::make_shared(storage); + // check empty storage error + storage->asyncPrepare( + params1, *stateStorage0, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_NE(error.get(), nullptr); + BOOST_CHECK_EQUAL(ts, 0); + }); + // prewrite + storage->asyncPrepare( + params1, *stateStorage, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_NE(ts, 0); + params1.timestamp = ts; + storage2->asyncPrepare( + params1, *stateStorage2, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(ts, 0); + }); + }); + // all storage call asyncRollback + storage->asyncRollback( + params1, [&](Error::Ptr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + storage2->asyncRollback( + params1, [&](Error::Ptr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + + // this sleep_for is to wait lock timeout + // this_thread::sleep_for(chrono::seconds(3)); + + // check commit failed + dynamic_pointer_cast(storage)->reset(); + storage->asyncGetPrimaryKeys(table1Name, std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 0); + }); + storage->asyncGetPrimaryKeys(table2Name, std::optional(), + [](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 0); + }); + storage->asyncGetPrimaryKeys(testTableName, std::optional(), + [](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 0); + }); + + // recall prewrite + params1.timestamp = 0; + storage->asyncPrepare( + params1, *stateStorage, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_NE(ts, 0); + params1.timestamp = ts; + storage2->asyncPrepare( + params1, *stateStorage2, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(ts, 0); + }); + storage3->asyncPrepare( + params1, *stateStorage3, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(ts, 0); + }); + }); + // only storage call asyncCommit + storage->asyncCommit(bcos::protocol::TwoPCParams(), + [&](Error::Ptr error, uint64_t) { BOOST_CHECK_EQUAL(error, nullptr); }); + + // this sleep_for is to wait lock timeout + // this_thread::sleep_for(chrono::seconds(3)); + + // check commit success + dynamic_pointer_cast(storage)->reset(); + storage->asyncGetPrimaryKeys(table1->tableInfo()->name(), + std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), tableEntries); + + std::sort(table1Keys.begin(), table1Keys.end()); + BOOST_CHECK_EQUAL_COLLECTIONS( + table1Keys.begin(), table1Keys.end(), keys.begin(), keys.end()); + + storage->asyncGetRows(table1->tableInfo()->name(), table1Keys, + [&](Error::UniquePtr error, std::vector> entries) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entries.size(), tableEntries); + + for (size_t i = 0; i < tableEntries; ++i) + { + BOOST_CHECK_EQUAL(entries[i]->getField(0), + std::string("hello world!") + table1Keys[i].substr(3)); + } + }); + }); + + storage->asyncGetPrimaryKeys(table2->tableInfo()->name(), + std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), tableEntries); + + std::sort(table2Keys.begin(), table2Keys.end()); + BOOST_CHECK_EQUAL_COLLECTIONS( + table2Keys.begin(), table2Keys.end(), keys.begin(), keys.end()); + + storage->asyncGetRows(table2->tableInfo()->name(), table2Keys, + [&](Error::UniquePtr error, std::vector> entries) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entries.size(), tableEntries); + + for (size_t i = 0; i < tableEntries; ++i) + { + BOOST_CHECK_EQUAL(entries[i]->getField(0), + std::string("hello world!") + table2Keys[i].substr(3)); + } + }); + }); + + storage->asyncGetPrimaryKeys(testTableName, std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), total); + std::sort(keys.begin(), keys.end()); + storage->asyncGetRows(testTableName, keys, + [&](Error::UniquePtr error, std::vector> entries) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entries.size(), total); + for (size_t i = 0; i < total; ++i) + { + auto value = "value_" + keys[i].substr(3); + BOOST_CHECK_EQUAL(entries[i]->getField(0), value); + } + }); + }); + + // clean data + auto entry1 = Entry(); + entry1.setStatus(Entry::DELETED); + storage->asyncSetRow(storage::StateStorage::SYS_TABLES, table1Name, entry1, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + auto entry2 = Entry(); + entry2.setStatus(Entry::DELETED); + storage->asyncSetRow(storage::StateStorage::SYS_TABLES, table2Name, entry2, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + + for (size_t i = 0; i < tableEntries; ++i) + { + auto entry1 = table1->newEntry(); + auto key1 = "key" + boost::lexical_cast(i); + entry1.setStatus(Entry::DELETED); + storage2->asyncSetRow(table1Name, key1, entry1, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + + auto entry2 = table2->newEntry(); + auto key2 = "key" + boost::lexical_cast(i); + entry2.setStatus(Entry::DELETED); + storage3->asyncSetRow(table2Name, key2, entry2, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + } + // check if the data is deleted + dynamic_pointer_cast(storage)->reset(); + storage->asyncGetPrimaryKeys(table1Name, std::optional(), + [](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 0); + }); + // check if the data is deleted + storage->asyncGetPrimaryKeys(table2Name, std::optional(), + [](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 0); + }); + + cleanupTestTableData(); +} + +BOOST_AUTO_TEST_CASE(multiStoragePrimaryCrash) +{ + size_t tableEntries = 101; + 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 testTable = stateStorage->openTable(testTableName); + BOOST_CHECK_EQUAL(testTable.has_value(), true); + for (size_t i = 0; i < total; ++i) + { + std::string key = "key" + boost::lexical_cast(i); + Entry entry(testTableInfo); + 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 table1Name = "table1"; + auto table2Name = "table2"; + BOOST_CHECK_EQUAL( + stateStorage2->createTable(table1Name, "value1,value2,value3").has_value(), true); + BOOST_CHECK_EQUAL( + stateStorage3->createTable(table2Name, "value1,value2,value3,value4,value5").has_value(), + true); + auto table1 = stateStorage2->openTable(table1Name); + auto table2 = stateStorage3->openTable(table2Name); + + BOOST_CHECK_EQUAL(table1.has_value(), true); + BOOST_CHECK_EQUAL(table2.has_value(), true); + + std::vector table1Keys; + std::vector table2Keys; + + for (size_t i = 0; i < tableEntries; ++i) + { + auto entry = table1->newEntry(); + auto key1 = "key" + boost::lexical_cast(i); + entry.setField(0, "hello world!" + boost::lexical_cast(i)); + table1->setRow(key1, entry); + table1Keys.push_back(key1); + + auto entry2 = table2->newEntry(); + auto key2 = "key" + boost::lexical_cast(i); + entry2.setField(0, "hello world!" + boost::lexical_cast(i)); + table2->setRow(key2, entry2); + table2Keys.push_back(key2); + } + auto params1 = bcos::protocol::TwoPCParams(); + params1.number = 100; + params1.primaryKey = testTableName + ":key0"; + auto stateStorage0 = std::make_shared(storage); + // check empty storage error + storage->asyncPrepare( + params1, *stateStorage0, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_NE(error.get(), nullptr); + BOOST_CHECK_EQUAL(ts, 0); + }); + // prewrite + storage->asyncPrepare( + params1, *stateStorage, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_NE(ts, 0); + params1.timestamp = ts; + storage2->asyncPrepare( + params1, *stateStorage2, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(ts, 0); + }); + }); + + // this sleep_for is to wait lock timeout + this_thread::sleep_for(chrono::seconds(3)); + + // just recommit prewrite + storage = std::make_shared(m_cluster); + auto storage4 = std::make_shared(m_cluster); + auto stateStorage4 = std::make_shared(storage3); + params1.timestamp = 0; + storage->asyncPrepare( + params1, *stateStorage, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_NE(ts, 0); + params1.timestamp = ts; + storage2->asyncPrepare( + params1, *stateStorage2, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(ts, 0); + }); + storage3->asyncPrepare( + params1, *stateStorage3, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(ts, 0); + }); + // secondary storage accept empty stateStorage + storage4->asyncPrepare( + params1, *stateStorage4, [&](Error::Ptr error, uint64_t ts, const std::string&) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(ts, 0); + }); + }); + // only storage call asyncCommit + storage->asyncCommit(bcos::protocol::TwoPCParams(), + [&](Error::Ptr error, uint64_t) { BOOST_CHECK_EQUAL(error, nullptr); }); + + // this sleep_for is to wait lock timeout + // this_thread::sleep_for(chrono::seconds(3)); + + // check commit success + storage->asyncGetPrimaryKeys(table1->tableInfo()->name(), + std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), tableEntries); + + std::sort(table1Keys.begin(), table1Keys.end()); + BOOST_CHECK_EQUAL_COLLECTIONS( + table1Keys.begin(), table1Keys.end(), keys.begin(), keys.end()); + + storage->asyncGetRows(table1->tableInfo()->name(), table1Keys, + [&](Error::UniquePtr error, std::vector> entries) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entries.size(), tableEntries); + + for (size_t i = 0; i < tableEntries; ++i) + { + BOOST_CHECK_EQUAL(entries[i]->getField(0), + std::string("hello world!") + table1Keys[i].substr(3)); + } + }); + }); + + storage->asyncGetPrimaryKeys(table2->tableInfo()->name(), + std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), tableEntries); + + std::sort(table2Keys.begin(), table2Keys.end()); + BOOST_CHECK_EQUAL_COLLECTIONS( + table2Keys.begin(), table2Keys.end(), keys.begin(), keys.end()); + + storage->asyncGetRows(table2->tableInfo()->name(), table2Keys, + [&](Error::UniquePtr error, std::vector> entries) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entries.size(), tableEntries); + + for (size_t i = 0; i < tableEntries; ++i) + { + BOOST_CHECK_EQUAL(entries[i]->getField(0), + std::string("hello world!") + table2Keys[i].substr(3)); + } + }); + }); + + storage->asyncGetPrimaryKeys(testTableName, std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), total); + storage->asyncGetRows(testTableName, keys, + [&](Error::UniquePtr error, std::vector> entries) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entries.size(), total); + for (size_t i = 0; i < total; ++i) + { + auto value = "value_" + keys[i].substr(3); + BOOST_CHECK_EQUAL(entries[i]->getField(0), value); + } + }); + }); + + // clean data + auto entry1 = Entry(); + entry1.setStatus(Entry::DELETED); + storage->asyncSetRow(storage::StateStorage::SYS_TABLES, table1Name, entry1, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + storage->asyncGetRow(storage::StateStorage::SYS_TABLES, table1Name, + [](Error::UniquePtr error, std::optional entry) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entry.has_value(), false); + }); + + auto entry2 = Entry(); + entry2.setStatus(Entry::DELETED); + storage->asyncSetRow(storage::StateStorage::SYS_TABLES, table2Name, entry2, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + storage->asyncGetRow(storage::StateStorage::SYS_TABLES, table2Name, + [](Error::UniquePtr error, std::optional entry) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entry.has_value(), false); + }); + for (size_t i = 0; i < tableEntries; ++i) + { + auto entry1 = table1->newEntry(); + auto key1 = "key" + boost::lexical_cast(i); + entry1.setStatus(Entry::DELETED); + storage2->asyncSetRow(table1Name, key1, entry1, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + + auto entry2 = table2->newEntry(); + auto key2 = "key" + boost::lexical_cast(i); + entry2.setStatus(Entry::DELETED); + storage3->asyncSetRow(table2Name, key2, entry2, + [](Error::UniquePtr error) { BOOST_CHECK_EQUAL(error.get(), nullptr); }); + } + // check if the data is deleted + dynamic_pointer_cast(storage)->reset(); + storage->asyncGetPrimaryKeys(table1Name, std::optional(), + [&](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + // BOOST_CHECK_EQUAL(keys.size(), 0); + storage->asyncGetRows(table1Name, keys, + [&](Error::UniquePtr error, std::vector> entries) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(entries.size(), 0); + // for (size_t i = 0; i < tableEntries; ++i) + // { + // BOOST_CHECK_EQUAL(keys[i], ""); + // BOOST_CHECK_EQUAL(entries[i]->getField(0), ""); + // } + }); + }); + // check if the data is deleted + storage->asyncGetPrimaryKeys(table2Name, std::optional(), + [](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK_EQUAL(error.get(), nullptr); + BOOST_CHECK_EQUAL(keys.size(), 0); + }); + + cleanupTestTableData(); +} + +BOOST_AUTO_TEST_CASE(writeReadDelete_1Table) +{ + writeReadDeleteSingleTable(1000); + writeReadDeleteSingleTable(5000); + writeReadDeleteSingleTable(10000); + writeReadDeleteSingleTable(20000); + writeReadDeleteSingleTable(50000); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-storage/test/unittest/main.cpp" "b/BFPL\345\243\271/bcos-storage/test/unittest/main.cpp" new file mode 100644 index 00000000..5029377e --- /dev/null +++ "b/BFPL\345\243\271/bcos-storage/test/unittest/main.cpp" @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 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 main.cpp + * @author: yujiechen, jimmyshi + * @date 2021-02-24 + */ +#define BOOST_TEST_MODULE FISCO_BCOS_Tests +#define BOOST_TEST_MAIN + +#include +#include diff --git "a/BFPL\345\243\271/bcos-storage/tools/CMakeLists.txt" "b/BFPL\345\243\271/bcos-storage/tools/CMakeLists.txt" new file mode 100644 index 00000000..1825e0e0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-storage/tools/CMakeLists.txt" @@ -0,0 +1,12 @@ +file(GLOB SRC_LIST "*.cpp") + +find_package(Boost REQUIRED program_options) + +foreach(source ${SRC_LIST}) + get_filename_component(filename ${source} NAME) + string(REPLACE ".cpp" "" target_name ${filename}) + add_executable(${target_name} ${source}) + target_link_libraries(${target_name} ${STORAGE_TARGET} ${SECURITY_TARGET} Boost::program_options zstd::libzstd_static) + target_include_directories(${target_name} PUBLIC ../bcos-storage) + target_compile_options(${target_name} PRIVATE -Wno-unused-variable) +endforeach() diff --git "a/BFPL\345\243\271/bcos-storage/tools/reader.cpp" "b/BFPL\345\243\271/bcos-storage/tools/reader.cpp" new file mode 100644 index 00000000..9ebd332e --- /dev/null +++ "b/BFPL\345\243\271/bcos-storage/tools/reader.cpp" @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2021 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 reader.cpp + * @author: xingqiangbai + * @date 2020-06-29 + * @file reader.cpp + * @author: ancelmo + * @date 2021-11-05 + */ + +#include "bcos-framework/storage/StorageInterface.h" +#include "boost/filesystem.hpp" +#include "rocksdb/db.h" +#include "rocksdb/options.h" +#include "rocksdb/slice.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace rocksdb; +using namespace bcos; +using namespace bcos::storage; + +namespace fs = boost::filesystem; +namespace po = boost::program_options; + +po::options_description main_options("Main for Table benchmark"); + +po::variables_map initCommandLine(int argc, const char* argv[]) +{ + main_options.add_options()("help,h", "help of Table benchmark")( + "path,p", po::value()->default_value(""), "[RocksDB path]")("name,n", + po::value()->default_value(""), "[RocksDB name]")("table,t", po::value(), + "table name ")("key,k", po::value()->default_value(""), "table key")( + "iterate,i", po::value()->default_value(false), "traverse table")("config,c", + boost::program_options::value()->default_value("./config.ini"), + "config file path, eg. config.ini")("genesis,g", + boost::program_options::value()->default_value("./config.genesis"), + "genesis config file path, eg. genesis.ini"); + po::variables_map vm; + try + { + po::store(po::parse_command_line(argc, argv, main_options), vm); + po::notify(vm); + } + catch (...) + { + std::cout << "invalid input" << std::endl; + exit(0); + } + if (vm.count("help") || vm.count("h")) + { + std::cout << main_options << std::endl; + exit(0); + } + return vm; +} + +int main(int argc, const char* argv[]) +{ + boost::property_tree::ptree pt; + auto params = initCommandLine(argc, argv); + auto storagePath = params["path"].as(); + auto storageName = params["name"].as(); + if (!fs::exists(storagePath)) + { + cout << "the path is empty:" << storagePath << endl; + return 0; + } + auto iterate = params["iterate"].as(); + auto tableName = params["table"].as(); + auto key = params["key"].as(); + + cout << "rocksdb path : " << storagePath << endl; + cout << "tableName : " << tableName << endl; + // auto factory = make_shared(storagePath); + + rocksdb::DB* db; + rocksdb::Options options; + options.IncreaseParallelism(); + options.OptimizeLevelStyleCompaction(); + options.create_if_missing = false; + rocksdb::Status s = rocksdb::DB::Open(options, storagePath, &db); + + std::string configPath("./config.ini"); + if (params.count("config")) + { + configPath = params["config"].as(); + } + if (params.count("c")) + { + configPath = params["c"].as(); + } + + std::string genesisFilePath("./config.genesis"); + if (params.count("genesis")) + { + genesisFilePath = params["genesis"].as(); + } + if (params.count("g")) + { + genesisFilePath = params["g"].as(); + } + + if (!boost::filesystem::exists(configPath)) + { + std::cout << "config \'" << configPath << "\' not found!"; + exit(0); + } + + auto keyFactory = std::make_shared(); + auto nodeConfig = std::make_shared(keyFactory); + nodeConfig->loadConfig(configPath); + if (true == boost::filesystem::exists(genesisFilePath)) + nodeConfig->loadGenesisConfig(genesisFilePath); + + bcos::security::DataEncryption::Ptr dataEncryption = + std::make_shared(nodeConfig); + dataEncryption->init(); + + auto adapter = + std::make_shared(std::unique_ptr(db), dataEncryption); + + if (iterate) + { + cout << "iterator " << tableName << endl; + + std::vector keys; + adapter->asyncGetPrimaryKeys( + tableName, {}, [&](Error::Ptr error, std::vector _keys) { + if (error) + { + BOOST_THROW_EXCEPTION(*error); + } + + keys = std::move(_keys); + }); + + if (keys.empty()) + { + cout << tableName << " is empty" << endl; + return 0; + } + + // cout << "keys=" << boost::algorithm::join(keys, "\t") << endl; + for (auto& k : keys) + { + std::string hex; + hex.reserve(k.size() * 2); + boost::algorithm::hex_lower(k.begin(), k.end(), std::back_inserter(hex)); + cout << "key=" << hex << "|"; + + std::optional row; + adapter->asyncGetRow(tableName, k, [&](Error::Ptr error, std::optional entry) { + if (error) + { + BOOST_THROW_EXCEPTION(*error); + } + + row = std::move(entry); + }); + + auto view = row->get(); + std::string hexData; + hexData.reserve(view.size() * 2); + boost::algorithm::hex_lower(view.begin(), view.end(), std::back_inserter(hexData)); + cout << " [" << hex << "] "; + + cout << " [status=" << row->status() << "]"; + // << " [num=" << row->num() << "]"; + cout << endl; + } + return 0; + } + + std::optional row; + adapter->asyncGetRow(tableName, key, [&](Error::Ptr error, std::optional entry) { + if (error) + { + BOOST_THROW_EXCEPTION(*error); + } + + row = std::move(entry); + }); + + auto view = row->get(); + std::string hexData; + hexData.reserve(view.size() * 2); + boost::algorithm::hex_lower(view.begin(), view.end(), std::back_inserter(hexData)); + cout << " [" << hex << "] "; + + cout << " [status=" << row->status() << "]"; + return 0; +} diff --git "a/BFPL\345\243\271/bcos-storage/tools/storageTool.cpp" "b/BFPL\345\243\271/bcos-storage/tools/storageTool.cpp" new file mode 100644 index 00000000..4aab8bc9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-storage/tools/storageTool.cpp" @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2021 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. + * + * @brief the tool to read and modify data of storage + * @file storageTool.cpp + * @author: xingqiangbai + * @date 2022-07-13 + */ + +#include "bcos-framework/ledger/LedgerTypeDef.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-ledger/src/libledger/utilities/Common.h" +#include "bcos-utilities/BoostLogInitializer.h" +#include "boost/filesystem.hpp" +#include "rocksdb/db.h" +#include "rocksdb/options.h" +#include "rocksdb/slice.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace rocksdb; +using namespace bcos; +using namespace bcos::storage; + +namespace fs = boost::filesystem; +namespace po = boost::program_options; + +po::options_description main_options("storage tool used to read/write the data of storage"); + +po::variables_map initCommandLine(int argc, const char* argv[]) +{ + main_options.add_options()("help,h", "help of storage tool")( + "statistic,s", "statistic the data usage of the storage")( + "stateSize,S", "statistic the data usage of the contracts state")( + "read,r", po::value>()->multitoken(), "[TableName] [Key]")("write,w", + po::value>()->multitoken(), + "[TableName] [Key] [Value]")("iterate,i", po::value(), "[TableName]")("hex,H", + po::value()->default_value(false), + "if read display value use hex, if write decode hex value")("config,c", + boost::program_options::value()->default_value("./config.ini"), + "config file path")("genesis,g", + boost::program_options::value()->default_value("./config.genesis"), + "genesis config file path"); + po::variables_map vm; + try + { + po::store(po::parse_command_line(argc, argv, main_options), vm); + po::notify(vm); + } + catch (...) + { + std::cout << "parse_command_line failed" << std::endl; + std::cout << main_options << std::endl; + exit(0); + } + if (vm.count("help") || vm.count("h")) + { + std::cout << main_options << std::endl; + exit(0); + } + return vm; +} + +std::shared_ptr>> getKeyPageIgnoreTables() +{ + return std::make_shared>>( + std::initializer_list>::value_type>{ + std::string(ledger::SYS_CONFIG), + std::string(ledger::SYS_CONSENSUS), + std::string(ledger::SYS_CURRENT_STATE), + std::string(ledger::SYS_HASH_2_NUMBER), + std::string(ledger::SYS_NUMBER_2_HASH), + std::string(ledger::SYS_BLOCK_NUMBER_2_NONCES), + std::string(ledger::SYS_NUMBER_2_BLOCK_HEADER), + std::string(ledger::SYS_NUMBER_2_TXS), + std::string(ledger::SYS_HASH_2_TX), + std::string(ledger::SYS_HASH_2_RECEIPT), + std::string(ledger::FS_ROOT), + std::string(ledger::FS_APPS), + std::string(ledger::FS_USER), + std::string(ledger::FS_SYS_BIN), + std::string(ledger::FS_USER_TABLE), + storage::StorageInterface::SYS_TABLES, + }); +} + +StateStorageInterface::Ptr createKeyPageStorage( + StorageInterface::Ptr backend, size_t keyPageSize, uint32_t blockVersion) +{ + auto keyPageIgnoreTables = getKeyPageIgnoreTables(); + return std::make_shared( + backend, keyPageSize, blockVersion, keyPageIgnoreTables); +} + +void print( + std::string_view tableName, std::string_view key, std::string_view value, bool hex = false) +{ + cout << "[tableName=" << tableName << "]" + << " [key=" << key << "] [value=" << (hex ? toHex(value) : value) << "]" << endl; +} + +void writeKV(std::ofstream& output, std::string_view key, std::string_view value, bool hex = false) +{ + output << "[key=" << key << "] [value=" << (hex ? toHex(value) : value) << "]" << endl; +} + +DB* createSecondaryRocksDB( + const std::string& path, const std::string& secondaryPath = "./rocksdb_secondary/") +{ + Options options; + options.create_if_missing = false; + options.max_open_files = -1; + DB* db_secondary = nullptr; + Status s = DB::OpenAsSecondary(options, path, secondaryPath, &db_secondary); + if (!s.ok()) + { + std::cout << "open rocksDB failed: " << s.ToString() << std::endl; + exit(1); + } + s = db_secondary->TryCatchUpWithPrimary(); + if (!s.ok()) + { + std::cout << "TryCatchUpWithPrimary failed: " << s.ToString() << std::endl; + exit(1); + } + return db_secondary; +} + +void getTableSize(DB* db, const string_view& table) +{ + std::string tableName(table); + double size = 0; + rocksdb::Iterator* it = db->NewIterator(rocksdb::ReadOptions()); + it->Seek(tableName); + while (it->Valid()) + { + if (it->key().starts_with(tableName)) + { + size += it->value().size(); + size += it->key().size(); + } + else + { + break; + } + it->Next(); + } + delete it; + auto setw = 30; + if (size < 1024) + { // < 1MB + cout << std::left << std::setw(setw) << tableName << " size is " << size << "B" << endl; + } + else if (size < 1024 * 1024) + { // < 1MB + cout << std::left << std::setw(setw) << tableName << " size is " << (size / 1024) << "KB" + << endl; + } + else if (size < 1024 * 1024 * 1024) + { // < 1GB + cout << std::left << std::setw(setw) << tableName << " size is " << (size / 1024 / 1024) + << "MB" << endl; + } + else + { + cout << std::left << std::setw(setw) << tableName << " size is " + << (size / 1024 / 1024 / 1024) << "GB" << endl; + } +} + +int main(int argc, const char* argv[]) +{ + boost::property_tree::ptree pt; + auto params = initCommandLine(argc, argv); + // TODO: parse config file + std::string configPath("./config.ini"); + if (params.count("config")) + { + configPath = params["config"].as(); + } + if (!fs::exists(configPath)) + { + cout << "config file not found:" << configPath << endl; + return 1; + } + + std::string genesisFilePath("./config.genesis"); + if (params.count("genesis")) + { + genesisFilePath = params["genesis"].as(); + } + + auto hexEncoded = params["hex"].as(); + + cout << "config file : " << configPath << endl; + cout << "genesis file : " << genesisFilePath << endl; + boost::property_tree::read_ini(configPath, pt); + auto logInitializer = std::make_shared(); + logInitializer->initLog(pt); + // load node config + auto keyFactory = std::make_shared(); + auto nodeConfig = std::make_shared(keyFactory); + if (fs::exists(genesisFilePath)) + { + nodeConfig->loadGenesisConfig(genesisFilePath); + } + nodeConfig->loadConfig(configPath); + bcos::security::DataEncryption::Ptr dataEncryption = nullptr; + if (nodeConfig->storageSecurityEnable()) + { + dataEncryption = std::make_shared(nodeConfig); + dataEncryption->init(); + } + + auto keyPageSize = nodeConfig->keyPageSize(); + auto keyPageIgnoreTables = getKeyPageIgnoreTables(); + std::string secondaryPath = "./rocksdb_secondary/"; + if (params.count("read")) + { // read + auto readParameters = params["read"].as>(); + if (readParameters.empty()) + { + cerr << "invalid tableName" << endl; + return -1; + } + auto tableName = readParameters[0]; + string key = ""; + if (readParameters.size() >= 2) + { + key = readParameters[1]; + } + cout << "read " << tableName << ", key is " << key << endl; + if (hexEncoded) + { + auto keyBytes = fromHexString(key); + key = std::string((char*)keyBytes->data(), keyBytes->size()); + } + // create secondary instance + auto db = createSecondaryRocksDB(nodeConfig->storagePath(), secondaryPath); + auto rocksdbStorage = + std::make_shared(std::unique_ptr(db), dataEncryption); + StorageInterface::Ptr storage = rocksdbStorage; + if (keyPageSize > 0 && !keyPageIgnoreTables->count(tableName)) + { + auto keyPageStorage = createKeyPageStorage( + rocksdbStorage, keyPageSize, nodeConfig->compatibilityVersion()); + keyPageStorage->setReadOnly(true); + storage = keyPageStorage; + } + std::promise>> getPromise; + storage->asyncGetRow( + tableName, key, [&](Error::UniquePtr err, std::optional opEntry) { + getPromise.set_value(std::make_pair(std::move(err), opEntry)); + }); + auto ret = getPromise.get_future().get(); + if (ret.first) + { + cerr << "get row failed, err:" << ret.first->errorMessage() << endl; + return -1; + } + // print result + if (!ret.second.has_value()) + { + cerr << "get row not found," << LOG_KV("table", tableName) << LOG_KV("key", key) + << endl; + return -1; + } + if (key.empty()) + { // print table meta + KeyPageStorage::TableMeta meta(ret.second->get()); + cout << meta << std::endl; + } + else + { + print(tableName, key, ret.second->get(), hexEncoded); + } + } + else if (params.count("write")) + { // write + auto writeParameters = params["write"].as>(); + if (writeParameters.empty() || writeParameters.size() < 3) + { + cerr << "invalid parameters, should include [TableName] [Key] [Value]" << endl; + return -1; + } + auto tableName = writeParameters[0]; + auto key = writeParameters[1]; + std::string value; + if (hexEncoded) + { + auto tempBytes = fromHex(writeParameters[2]); + value = std::string((char*)tempBytes.data(), tempBytes.size()); + } + else + { + value = writeParameters[2]; + } + + rocksdb::DB* db; + rocksdb::Options options; + options.create_if_missing = false; + options.enable_blob_files = keyPageSize > 1 ? true : false; + options.compression = rocksdb::kZSTD; + rocksdb::Status s = rocksdb::DB::Open(options, nodeConfig->storagePath(), &db); + if (!s.ok()) + { + cerr << "open rocksDB to write failed, err:" << s.ToString() << endl; + return -1; + } + auto rocksdbStorage = + std::make_shared(std::unique_ptr(db), dataEncryption); + StorageInterface::Ptr storage = rocksdbStorage; + if (keyPageSize > 0 && !keyPageIgnoreTables->count(tableName)) + { + storage = createKeyPageStorage( + rocksdbStorage, keyPageSize, nodeConfig->compatibilityVersion()); + } + // std::promise>> getPromise; + // storage->asyncGetRow( + // tableName, key, [&](Error::UniquePtr err, std::optional opEntry) { + // getPromise.set_value(std::make_pair(std::move(err), opEntry)); + // }); + // auto ret = getPromise.get_future().get(); + // if (ret.first) + // { + // cerr << "get row failed, err:" << ret.first->errorMessage() << endl; + // return -1; + // } + + // async set row, check if need hex decode and write + std::promise setPromise; + Entry e; + if (value.empty()) + { + e.setStatus(Entry::Status::DELETED); + } + else + { + e.set(std::move(value)); + } + storage->asyncSetRow( + tableName, key, e, [&](Error::UniquePtr err) { setPromise.set_value(std::move(err)); }); + auto err = setPromise.get_future().get(); + if (err) + { + cerr << "set row failed, err:" << err->errorMessage() << endl; + return -1; + } + // if use key page need commit use rocksDB + if (keyPageSize > 0 && !keyPageIgnoreTables->count(tableName)) + { + auto keyPageStorage = dynamic_cast(storage.get()); + bcos::protocol::TwoPCParams param; + rocksdbStorage->asyncPrepare( + param, *keyPageStorage, [&](Error::Ptr err, uint64_t, const std::string&) { + if (err) + { + cerr << "asyncPrepare failed, err:" << err->errorMessage() << endl; + exit(1); + } + }); + rocksdbStorage->asyncCommit(param, [](Error::Ptr err, uint64_t) { + if (err) + { + cerr << "asyncCommit failed, err:" << err->errorMessage() << endl; + exit(1); + } + }); + } + cout << "set successfully" << endl; + } + else if (params.count("iterate")) + { // iterate + auto tableName = params["iterate"].as(); + if (tableName.empty()) + { + cerr << "empty table name" << endl; + return -1; + } + auto db = createSecondaryRocksDB(nodeConfig->storagePath(), secondaryPath); + auto rocksdbStorage = + std::make_shared(std::unique_ptr(db), dataEncryption); + StorageInterface::Ptr storage = rocksdbStorage; + if (keyPageSize > 0 && !keyPageIgnoreTables->count(tableName)) + { + storage = createKeyPageStorage( + rocksdbStorage, keyPageSize, nodeConfig->compatibilityVersion()); + } + auto outputFileName = tableName + ".txt"; + boost::replace_all(outputFileName, "/", "_"); + ofstream outfile("./" + outputFileName); + outfile << "db path : " << nodeConfig->storagePath() << ", table : " << tableName << endl; + if (keyPageSize > 0 && !keyPageIgnoreTables->count(tableName)) + { // keypage + size_t batchSize = 1000; + size_t gotKeys = 1000; + size_t total = 0; + cout << "iterate use key page" << endl; + while (gotKeys == batchSize) + { + storage::Condition condition; + condition.limit(total, 1000); + storage->asyncGetPrimaryKeys( + tableName, condition, [&](Error::UniquePtr err, std::vector keys) { + if (err) + { + cerr << "asyncGetPrimaryKeys failed, err:" << err->errorMessage() + << endl; + exit(1); + } + gotKeys = keys.size(); + total += gotKeys; + for (auto& key : keys) + { + storage->asyncGetRow( + tableName, key, [&](Error::UniquePtr err, std::optional e) { + if (err) + { + cerr << "asyncGetRow failed, err:" << err->errorMessage() + << endl; + exit(1); + } + writeKV(outfile, key, e ? e->get() : "", hexEncoded); + }); + } + }); + } + } + else + { // rocksdb + rocksdb::Iterator* it = db->NewIterator(rocksdb::ReadOptions()); + it->Seek(tableName); + while (it->Valid()) + { + if (it->key().starts_with(tableName)) + { + // outfile << "[" << it->key().ToString() << "][" << it->value().ToString() << + // "]" << endl; + writeKV(outfile, it->key().ToString(), it->value().ToString(), hexEncoded); + } + else + { + break; + } + it->Next(); + } + delete it; + } + cout << "result in ./" << outputFileName << endl; + outfile.close(); + } + else if (params.count("statistic") || params.count("s")) + { // statistics + auto db = createSecondaryRocksDB(nodeConfig->storagePath(), secondaryPath); + // auto rocksdbStorage = + // std::make_shared(std::unique_ptr(db), dataEncryption); + // StorageInterface::Ptr storage = rocksdbStorage; + // if (keyPageSize > 0 && !keyPageIgnoreTables->count(tableName)) + // { + // auto keyPageStorage = createKeyPageStorage(rocksdbStorage, keyPageSize); + // keyPageStorage->setReadOnly(true); + // storage = keyPageStorage; + // } + getTableSize(db, storage::StorageInterface::SYS_TABLES); + getTableSize(db, ledger::SYS_CONSENSUS); + getTableSize(db, ledger::SYS_CONFIG); + getTableSize(db, ledger::SYS_CURRENT_STATE); + getTableSize(db, ledger::SYS_HASH_2_NUMBER); + getTableSize(db, ledger::SYS_NUMBER_2_HASH); + getTableSize(db, ledger::SYS_BLOCK_NUMBER_2_NONCES); + getTableSize(db, ledger::SYS_NUMBER_2_TXS); + getTableSize(db, ledger::SYS_NUMBER_2_BLOCK_HEADER); + // calculate transactions data size + getTableSize(db, ledger::SYS_HASH_2_TX); + + // calculate receipts data size + getTableSize(db, ledger::SYS_HASH_2_RECEIPT); + } + else if (params.count("stateSize") || params.count("S")) + { // calculate contract data size + auto db = createSecondaryRocksDB(nodeConfig->storagePath(), secondaryPath); + getTableSize(db, ledger::FS_APPS); + } + else + { + std::cout << "invalid parameters" << std::endl; + std::cout << main_options << std::endl; + return 1; + } + if (fs::exists(secondaryPath)) + { + fs::remove_all(secondaryPath); + } + return 0; +} diff --git "a/BFPL\345\243\271/bcos-sync/CMakeLists.txt" "b/BFPL\345\243\271/bcos-sync/CMakeLists.txt" new file mode 100644 index 00000000..519d0e20 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/CMakeLists.txt" @@ -0,0 +1,72 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for bcos-sync +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 bcos-sync +# 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. +#------------------------------------------------------------------------------ + +cmake_minimum_required(VERSION 3.10) +set(CMAKE_OSX_DEPLOYMENT_TARGET "11.3" CACHE STRING "Minimum OS X deployment version") + +include(Version) +project(bcos-sync VERSION ${VERSION}) + +# proto generation +set(PROTO_INPUT_PATH ${CMAKE_SOURCE_DIR}/bcos-sync) +set(PROTO_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/) + +set(SYNC_PROTOS bcos-sync/protocol/proto/BlockSync.proto) +foreach(proto_file ${SYNC_PROTOS}) + get_filename_component(bcos_proto_abs "${PROTO_INPUT_PATH}" ABSOLUTE) + set(proto_file_abs ${bcos_proto_abs}/${proto_file}) + get_filename_component(rel_dir ${proto_file} DIRECTORY) + get_filename_component(basename ${proto_file} NAME_WE) + set(generated_files ${PROTO_OUTPUT_PATH}/${rel_dir}/${basename}.pb.cc) + + list(APPEND SYNC_SRCS ${generated_files}) + + message("Command: protoc --cpp_out ${PROTO_OUTPUT_PATH} -I ${PROTO_INPUT_PATH} ${proto_file}") + add_custom_command( + OUTPUT ${generated_files} + COMMAND protobuf::protoc --cpp_out ${PROTO_OUTPUT_PATH} -I ${PROTO_INPUT_PATH} ${proto_file} + COMMENT "Generating ${generated_files} from ${proto_file_abs}" + VERBATIM + ) +endforeach() + +find_package(Protobuf CONFIG REQUIRED) +find_package(jsoncpp CONFIG REQUIRED) + +include_directories(${PROTO_OUTPUT_PATH}) + +file(GLOB_RECURSE SRCS bcos-sync/*.cpp) +add_library(${SYNC_TARGET} ${SRCS} ${SYNC_SRCS}) +target_link_libraries(${SYNC_TARGET} PUBLIC ${STORAGE_TARGET} bcos-framework ${UTILITIES_TARGET} ${CODEC_TARGET} ${TOOL_TARGET} jsoncpp_static) + + +if (TESTS) + enable_testing() + set(CTEST_OUTPUT_ON_FAILURE TRUE) + add_subdirectory(test) +endif() + +# for doxygen +# include(BuildDocs) +# buildDoc(bcos-sync-doc) + +# for code coverage +if (COVERAGE) + include(Coverage) + config_coverage("sync-cov" "'/usr*' '${CMAKE_CURRENT_SOURCE_DIR}/bcos-cmake-scripts*' '${CMAKE_CURRENT_SOURCE_DIR}/test/bcos-test*'") +endif () diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/BlockSync.cpp" "b/BFPL\345\243\271/bcos-sync/bcos-sync/BlockSync.cpp" new file mode 100644 index 00000000..0f55bb36 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/BlockSync.cpp" @@ -0,0 +1,833 @@ +/** + * Copyright (C) 2021 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. + * + * @brief block sync implementation + * @file BlockSync.cpp + * @author: yujiechen + * @date 2021-05-24 + */ +#include "bcos-sync/BlockSync.h" +#include +#include +#include + +using namespace bcos; +using namespace bcos::sync; +using namespace bcos::protocol; +using namespace bcos::crypto; +using namespace bcos::ledger; +using namespace bcos::tool; + +BlockSync::BlockSync(BlockSyncConfig::Ptr _config, unsigned _idleWaitMs) + : Worker("syncWorker", _idleWaitMs), + m_config(_config), + m_syncStatus(std::make_shared(_config)), + m_downloadingQueue(std::make_shared(_config)) +{ + m_downloadBlockProcessor = std::make_shared("Download", 1); + m_sendBlockProcessor = std::make_shared("SyncSend", 1); + m_downloadingTimer = std::make_shared(m_config->downloadTimeout(), "downloadTimer"); + m_downloadingTimer->registerTimeoutHandler(boost::bind(&BlockSync::onDownloadTimeout, this)); + m_downloadingQueue->registerNewBlockHandler( + boost::bind(&BlockSync::onNewBlock, this, boost::placeholders::_1)); + initSendResponseHandler(); +} + +void BlockSync::start() +{ + if (m_running) + { + BLKSYNC_LOG(INFO) << LOG_DESC("BlockSync has already been started"); + return; + } + startWorking(); + m_running = true; + BLKSYNC_LOG(INFO) << LOG_DESC("Start BlockSync"); +} + +void BlockSync::init() +{ + auto fetcher = std::make_shared(m_config->ledger()); + BLKSYNC_LOG(INFO) << LOG_DESC("start fetch the ledger config for block sync module"); + fetcher->fetchBlockNumberAndHash(); + fetcher->fetchConsensusNodeList(); + fetcher->fetchObserverNodeList(); + fetcher->fetchGenesisHash(); + // set the syncConfig + auto genesisHash = fetcher->genesisHash(); + BLKSYNC_LOG(INFO) << LOG_DESC("fetch the ledger config for block sync module success") + << LOG_KV("number", fetcher->ledgerConfig()->blockNumber()) + << LOG_KV("latestHash", fetcher->ledgerConfig()->hash().abridged()) + << LOG_KV("genesisHash", genesisHash); + m_config->setGenesisHash(genesisHash); + m_config->resetConfig(fetcher->ledgerConfig()); + BLKSYNC_LOG(INFO) << LOG_DESC("init block sync success"); +} + +void BlockSync::enableAsMaster(bool _masterNode) +{ + BLKSYNC_LOG(INFO) << LOG_DESC("enableAsMaster:") << _masterNode; + if (m_masterNode == _masterNode) + { + BLKSYNC_LOG(INFO) << LOG_DESC("enableAsMasterNode: The masterNodeState is not changed") + << LOG_KV("master", _masterNode); + return; + } + m_config->setMasterNode(_masterNode); + if (!_masterNode) + { + m_masterNode = _masterNode; + return; + } + init(); + // only set m_masterNode to be true when init success + m_masterNode = _masterNode; +} + +void BlockSync::initSendResponseHandler() +{ + // set the sendResponse callback + std::weak_ptr weakFrontService = m_config->frontService(); + m_sendResponseHandler = [weakFrontService](std::string const& _id, int _moduleID, + NodeIDPtr _dstNode, bytesConstRef _data) { + try + { + auto frontService = weakFrontService.lock(); + if (!frontService) + { + return; + } + frontService->asyncSendResponse( + _id, _moduleID, _dstNode, _data, [_id, _moduleID, _dstNode](Error::Ptr _error) { + if (_error) + { + BLKSYNC_LOG(WARNING) + << LOG_DESC("sendResponse failed") << LOG_KV("uuid", _id) + << LOG_KV("module", std::to_string(_moduleID)) + << LOG_KV("dst", _dstNode->shortHex()) + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + } + }); + } + catch (std::exception const& e) + { + BLKSYNC_LOG(WARNING) << LOG_DESC("sendResponse exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }; +} + +void BlockSync::stop() +{ + if (!m_running) + { + BLKSYNC_LOG(INFO) << LOG_DESC("BlockSync has already been stopped"); + return; + } + BLKSYNC_LOG(INFO) << LOG_DESC("Stop BlockSync"); + if (m_downloadBlockProcessor) + { + m_downloadBlockProcessor->stop(); + } + if (m_sendBlockProcessor) + { + m_sendBlockProcessor->stop(); + } + if (m_downloadingTimer) + { + m_downloadingTimer->destroy(); + } + m_running = false; + finishWorker(); + if (isWorking()) + { + // stop the worker thread + stopWorking(); + terminate(); + } +} + +void BlockSync::printSyncInfo() +{ + auto peers = m_syncStatus->peers(); + std::string peer_str; + for (auto const& peer : *peers) + { + peer_str += peer->shortHex() + "/"; + } + BLKSYNC_LOG(TRACE) << "\n[Sync Info] --------------------------------------------\n" + << " IsSyncing: " << isSyncing() << "\n" + << " Block number: " << m_config->blockNumber() << "\n" + << " Block hash: " << m_config->hash().abridged() << "\n" + << " Genesis hash: " << m_config->genesisHash().abridged() << "\n" + << " Peers size: " << peers->size() << "\n" + << "[Peer Info] --------------------------------------------\n" + << " Host: " << m_config->nodeID()->shortHex() << "\n" + << " Peer: " << peer_str << "\n" + << " --------------------------------------------"; +} + +void BlockSync::executeWorker() +{ + if (!m_masterNode) + { + return; + } + if (isSyncing()) + { + printSyncInfo(); + } + // maintain the connections between observers/sealers + maintainPeersConnection(); + m_downloadBlockProcessor->enqueue([this]() { + try + { + // flush downloaded buffer into downloading queue + maintainDownloadingBuffer(); + maintainDownloadingQueue(); + + // send block-download-request to peers if this node is behind others + tryToRequestBlocks(); + } + catch (std::exception const& e) + { + BLKSYNC_LOG(ERROR) << LOG_DESC( + "maintainDownloadingQueue or maintainPeersStatus exception") + << LOG_KV("errorInfo", boost::diagnostic_information(e)); + } + }); + // send block to other nodes + m_sendBlockProcessor->enqueue([this]() { + try + { + maintainBlockRequest(); + } + catch (std::exception const& e) + { + BLKSYNC_LOG(ERROR) << LOG_DESC("maintainBlockRequest exception") + << LOG_KV("errorInfo", boost::diagnostic_information(e)); + } + }); +} + +void BlockSync::workerProcessLoop() +{ + while (workerState() == WorkerState::Started) + { + try + { + executeWorker(); + if (idleWaitMs()) + { + boost::unique_lock l(x_signalled); + m_signalled.wait_for(l, boost::chrono::milliseconds(idleWaitMs())); + } + } + catch (std::exception const& e) + { + BLKSYNC_LOG(ERROR) << LOG_DESC("BlockSync executeWorker exception") + << LOG_KV("errorInfo", boost::diagnostic_information(e)); + } + } +} + +bool BlockSync::shouldSyncing() +{ + if (m_config->blockNumber() >= m_config->knownHighestNumber()) + { + return false; + } + // the node is reaching consensus the block + if (m_config->committedProposalNumber() >= m_config->knownHighestNumber()) + { + return false; + } + if (m_config->executedBlock() >= m_config->knownHighestNumber()) + { + return false; + } + return true; +} + +bool BlockSync::isSyncing() +{ + return (m_state == SyncState::Downloading); +} + +void BlockSync::maintainDownloadingBuffer() +{ + if (m_downloadingQueue->size() == 0) + { + return; + } + if (!shouldSyncing()) + { + m_downloadingQueue->clear(); + return; + } + m_downloadingQueue->clearFullQueueIfNotHas(m_config->nextBlock()); + m_downloadingQueue->flushBufferToQueue(); +} + + +void BlockSync::asyncNotifyBlockSyncMessage(Error::Ptr _error, std::string const& _uuid, + NodeIDPtr _nodeID, bytesConstRef _data, std::function _onRecv) +{ + if (!m_masterNode) + { + return; + } + auto self = weak_from_this(); + asyncNotifyBlockSyncMessage( + _error, _nodeID, _data, + [_uuid, _nodeID, self](bytesConstRef _respData) { + try + { + auto sync = self.lock(); + if (!sync) + { + return; + } + sync->m_sendResponseHandler(_uuid, ModuleID::BlockSync, _nodeID, _respData); + } + catch (std::exception const& e) + { + BLKSYNC_LOG(WARNING) << LOG_DESC("asyncNotifyBlockSyncMessage sendResponse failed") + << LOG_KV("error", boost::diagnostic_information(e)) + << LOG_KV("id", _uuid) << LOG_KV("dst", _nodeID->shortHex()); + } + }, + _onRecv); +} + +void BlockSync::asyncNotifyBlockSyncMessage(Error::Ptr _error, NodeIDPtr _nodeID, + bytesConstRef _data, std::function, + std::function _onRecv) +{ + if (_onRecv) + { + _onRecv(nullptr); + } + if (_error != nullptr) + { + BLKSYNC_LOG(WARNING) << LOG_DESC("asyncNotifyBlockSyncMessage error") + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + return; + } + try + { + auto syncMsg = m_config->msgFactory()->createBlockSyncMsg(_data); + switch (syncMsg->packetType()) + { + case BlockSyncPacketType::BlockStatusPacket: { + onPeerStatus(_nodeID, syncMsg); + break; + } + case BlockSyncPacketType::BlockRequestPacket: { + onPeerBlocksRequest(_nodeID, syncMsg); + break; + } + case BlockSyncPacketType::BlockResponsePacket: { + onPeerBlocks(_nodeID, syncMsg); + break; + } + default: { + BLKSYNC_LOG(WARNING) << LOG_DESC( + "asyncNotifyBlockSyncMessage: unknown block sync message") + << LOG_KV("type", syncMsg->packetType()) + << LOG_KV("peer", _nodeID->shortHex()); + break; + } + } + } + catch (std::exception const& e) + { + BLKSYNC_LOG(WARNING) << LOG_DESC("asyncNotifyBlockSyncMessage exception") + << LOG_KV("error", boost::diagnostic_information(e)) + << LOG_KV("peer", _nodeID->shortHex()); + } +} + +void BlockSync::asyncNotifyNewBlock( + LedgerConfig::Ptr _ledgerConfig, std::function _onRecv) +{ + if (_onRecv) + { + _onRecv(nullptr); + } + BLKSYNC_LOG(DEBUG) << LOG_DESC("asyncNotifyNewBlock: receive new block info") + << LOG_KV("number", _ledgerConfig->blockNumber()) + << LOG_KV("hash", _ledgerConfig->hash().abridged()) + << LOG_KV("consNodeSize", _ledgerConfig->consensusNodeList().size()) + << LOG_KV("observerNodeSize", _ledgerConfig->observerNodeList().size()); + if (_ledgerConfig->blockNumber() > m_config->blockNumber()) + { + onNewBlock(_ledgerConfig); + // try to commitBlock to ledger when receive new block notification + m_downloadingQueue->tryToCommitBlockToLedger(); + } +} + +void BlockSync::onNewBlock(bcos::ledger::LedgerConfig::Ptr _ledgerConfig) +{ + m_config->resetConfig(_ledgerConfig); + broadcastSyncStatus(); + m_downloadingQueue->clearExpiredQueueCache(); +} + +void BlockSync::onPeerStatus(NodeIDPtr _nodeID, BlockSyncMsgInterface::Ptr _syncMsg) +{ + // receive peer not exist in the group + // Note: only should reject syncStatus from the node whose blockNumber falling behind of this + // node + if (!m_config->existsInGroup(_nodeID) && _syncMsg->number() <= m_config->blockNumber()) + { + return; + } + auto statusMsg = m_config->msgFactory()->createBlockSyncStatusMsg(_syncMsg); + m_syncStatus->updatePeerStatus(_nodeID, statusMsg); + + if (_syncMsg->version() > static_cast(BlockSyncMsgVersion::v0)) + { + m_config->nodeTimeMaintenance()->tryToUpdatePeerTimeInfo(_nodeID, statusMsg->time()); + } +} + +void BlockSync::onPeerBlocks(NodeIDPtr _nodeID, BlockSyncMsgInterface::Ptr _syncMsg) +{ + auto blockMsg = m_config->msgFactory()->createBlocksMsg(_syncMsg); + BLKSYNC_LOG(DEBUG) << LOG_BADGE("Download") << LOG_BADGE("BlockSync") + << LOG_DESC("Receive peer block packet") + << LOG_KV("peer", _nodeID->shortHex()); + m_downloadingQueue->push(blockMsg); + m_signalled.notify_all(); +} + +void BlockSync::onPeerBlocksRequest(NodeIDPtr _nodeID, BlockSyncMsgInterface::Ptr _syncMsg) +{ + auto blockRequest = m_config->msgFactory()->createBlockRequest(_syncMsg); + BLKSYNC_LOG(INFO) << LOG_BADGE("Download") << LOG_BADGE("onPeerBlocksRequest") + << LOG_DESC("Receive block request") << LOG_KV("peer", _nodeID->shortHex()) + << LOG_KV("from", blockRequest->number()) + << LOG_KV("size", blockRequest->size()); + auto peerStatus = m_syncStatus->peerStatus(_nodeID); + if (!peerStatus && m_config->existsInGroup(_nodeID)) + { + BLKSYNC_LOG(INFO) << LOG_BADGE("Download") << LOG_BADGE("onPeerBlocksRequest") + << LOG_DESC( + "Receive block request from the node belongs to the group but " + "with no peer status, create status now") + << LOG_KV("peer", _nodeID ? _nodeID->shortHex() : "unknown") + << LOG_KV("curNum", m_config->blockNumber()) + << LOG_KV("from", blockRequest->number()) + << LOG_KV("size", blockRequest->size()); + // the node belongs to the group, insert the status into the peer + peerStatus = m_syncStatus->insertEmptyPeer(_nodeID); + } + if (peerStatus) + { + peerStatus->downloadRequests()->push(blockRequest->number(), blockRequest->size()); + m_signalled.notify_all(); + return; + } + BLKSYNC_LOG(WARNING) << LOG_BADGE("Download") << LOG_BADGE("onPeerBlocksRequest") + << LOG_DESC("Receive block request from the unknown peer, drop directly") + << LOG_KV("peer", _nodeID ? _nodeID->shortHex() : "unknown") + << LOG_KV("from", blockRequest->number()) + << LOG_KV("size", blockRequest->size()); +} + +void BlockSync::onDownloadTimeout() +{ + // stop the timer and reset the state to idle + m_downloadingTimer->stop(); + m_state = SyncState::Idle; +} + +void BlockSync::downloadFinish() +{ + m_downloadingTimer->stop(); + m_state = SyncState::Idle; +} + +void BlockSync::tryToRequestBlocks() +{ + // wait the downloaded block commit to the ledger, and enable the next batch requests + if (m_config->blockNumber() < m_config->executedBlock() && + m_downloadingQueue->commitQueueSize() > 0) + { + return; + } + if (m_maxRequestNumber <= m_config->blockNumber() || + m_maxRequestNumber <= m_config->executedBlock()) + { + downloadFinish(); + } + if (!shouldSyncing() || isSyncing()) + { + return; + } + auto requestToNumber = m_config->knownHighestNumber(); + m_config->consensus()->notifyHighestSyncingNumber(requestToNumber); + auto topBlock = m_downloadingQueue->top(); + // The block in BlockQueue is not nextBlock(the BlockQueue missing some block) + if (topBlock) + { + auto topBlockHeader = topBlock->blockHeader(); + if (topBlockHeader && topBlockHeader->number() > m_config->nextBlock()) + { + requestToNumber = + std::min(m_config->knownHighestNumber(), (topBlockHeader->number() - 1)); + } + } + auto currentNumber = m_config->blockNumber(); + // no need to request blocks + if (currentNumber >= requestToNumber) + { + return; + } + requestBlocks(currentNumber, requestToNumber); +} + +void BlockSync::requestBlocks(BlockNumber _from, BlockNumber _to) +{ + BLKSYNC_LOG(INFO) << LOG_BADGE("Download") << LOG_BADGE("requestBlocks") + << LOG_KV("from", _from) << LOG_KV("to", _to); + m_state = SyncState::Downloading; + m_downloadingTimer->start(); + + auto blockSizePerShard = m_config->maxRequestBlocks(); + auto shardNumber = (_to - _from + blockSizePerShard - 1) / blockSizePerShard; + size_t shard = 0; + // at most request `maxShardPerPeer` shards every time + for (size_t loop = 0; loop < m_config->maxShardPerPeer() && shard < shardNumber; loop++) + { + bool findPeer = false; + m_syncStatus->foreachPeerRandom([&](PeerStatus::Ptr _p) { + if (_p->number() < m_config->knownHighestNumber()) + { + // Only send request to nodes which are not syncing(has max number) + return true; + } + // shard: [from, to] + BlockNumber from = _from + 1 + shard * blockSizePerShard; + BlockNumber to = std::min((BlockNumber)(from + blockSizePerShard - 1), _to); + if (_p->number() < to) + { + return true; // to next peer + } + // found a peer + findPeer = true; + auto blockRequest = m_config->msgFactory()->createBlockRequest(); + blockRequest->setNumber(from); + blockRequest->setSize(to - from + 1); + auto encodedData = blockRequest->encode(); + m_config->frontService()->asyncSendMessageByNodeID( + ModuleID::BlockSync, _p->nodeId(), ref(*encodedData), 0, nullptr); + + m_maxRequestNumber = std::max(m_maxRequestNumber.load(), to); + + BLKSYNC_LOG(INFO) << LOG_BADGE("Download") << LOG_BADGE("Request") + << LOG_DESC("Request blocks") << LOG_KV("from", from) + << LOG_KV("to", to) << LOG_KV("curNum", m_config->blockNumber()) + << LOG_KV("peer", _p->nodeId()->shortHex()) + << LOG_KV("maxRequestNumber", m_maxRequestNumber) + << LOG_KV("node", m_config->nodeID()->shortHex()); + + ++shard; // shard move + return shard < shardNumber; + }); + if (!findPeer) + { + BlockNumber from = _from + shard * blockSizePerShard; + BlockNumber to = std::min((BlockNumber)(from + blockSizePerShard - 1), _to); + BLKSYNC_LOG(WARNING) << LOG_BADGE("Download") << LOG_BADGE("Request") + << LOG_DESC("Couldn't find any peers to request blocks") + << LOG_KV("from", from) << LOG_KV("to", to); + break; + } + } +} + +void BlockSync::maintainDownloadingQueue() +{ + if (!shouldSyncing()) + { + m_downloadingQueue->clear(); + downloadFinish(); + return; + } + m_downloadingQueue->tryToCommitBlockToLedger(); + auto executedBlock = m_config->executedBlock(); + // remove the expired block + auto topBlock = m_downloadingQueue->top(); + while (topBlock && topBlock->blockHeader()->number() <= executedBlock) + { + m_downloadingQueue->pop(); + topBlock = m_downloadingQueue->top(); + } + topBlock = m_downloadingQueue->top(); + if (!topBlock) + { + return; + } + + // limit the executed blockNumber + if (executedBlock >= (m_config->blockNumber() + m_waterMark)) + { + BLKSYNC_LOG(WARNING) + << LOG_DESC("too many executed blocks have not been committed, stop execute new block") + << LOG_KV("curNumber", m_config->blockNumber()) + << LOG_KV("executedBlock", executedBlock); + return; + } + + auto expectedBlock = executedBlock + 1; + auto topNumber = topBlock->blockHeader()->number(); + if (topNumber > (expectedBlock)) + { + BLKSYNC_LOG(WARNING) << LOG_DESC("Discontinuous block") << LOG_KV("topNumber", topNumber) + << LOG_KV("curNumber", m_config->blockNumber()) + << LOG_KV("expectedBlock", expectedBlock) + << LOG_KV("commitQueueSize", m_downloadingQueue->commitQueueSize()) + << LOG_KV("isSyncing", isSyncing()) + << LOG_KV("knownHighestNumber", m_config->knownHighestNumber()) + << LOG_KV("node", m_config->nodeID()->shortHex()); + return; + } + // execute the expected block + if (topBlock->blockHeader()->number() == (executedBlock + 1)) + { + auto block = m_downloadingQueue->top(); + // Note: the block maybe cleared here + if (!block) + { + return; + } + m_downloadingQueue->pop(); + auto blockHeader = block->blockHeader(); + auto header = block->blockHeader(); + auto signature = header->signatureList(); + BLKSYNC_LOG(INFO) << LOG_BADGE("Download") << LOG_DESC("BlockSync: applyBlock") + << LOG_KV("execNum", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("node", m_config->nodeID()->shortHex()) + << LOG_KV("signatureSize", signature.size()) + << LOG_KV("txsSize", block->transactionsSize()); + m_downloadingQueue->applyBlock(block); + } +} + +void BlockSync::maintainBlockRequest() +{ + m_syncStatus->foreachPeerRandom([&](PeerStatus::Ptr _p) { + auto reqQueue = _p->downloadRequests(); + // no need to respond + if (reqQueue->empty()) + { + return true; + } + while (!reqQueue->empty()) + { + auto blocksReq = reqQueue->topAndPop(); + BlockNumber numberLimit = blocksReq->fromNumber() + blocksReq->size(); + BLKSYNC_LOG(DEBUG) << LOG_BADGE("Download Request: response blocks") + << LOG_KV("from", blocksReq->fromNumber()) + << LOG_KV("size", blocksReq->size()) << LOG_KV("to", numberLimit - 1) + << LOG_KV("peer", _p->nodeId()->shortHex()); + for (BlockNumber number = blocksReq->fromNumber(); number < numberLimit; number++) + { + fetchAndSendBlock(reqQueue, _p->nodeId(), number); + } + } + return true; + }); +} + +void BlockSync::fetchAndSendBlock( + DownloadRequestQueue::Ptr _reqQueue, PublicPtr _peer, BlockNumber _number) +{ + // only fetch blockHeader and transactions + auto blockFlag = HEADER | TRANSACTIONS; + auto self = weak_from_this(); + m_config->ledger()->asyncGetBlockDataByNumber(_number, blockFlag, + [self, _reqQueue, _peer, _number](Error::Ptr _error, Block::Ptr _block) { + if (_error != nullptr) + { + BLKSYNC_LOG(WARNING) + << LOG_DESC("fetchAndSendBlock failed for asyncGetBlockDataByNumber failed") + << LOG_KV("number", _number) << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()); + _reqQueue->push(_number, 1); + return; + } + try + { + auto sync = self.lock(); + if (!sync) + { + return; + } + auto blockHeader = _block->blockHeader(); + auto signature = blockHeader->signatureList(); + auto config = sync->m_config; + auto blocksReq = config->msgFactory()->createBlocksMsg(); + bytesPointer blockData = std::make_shared(); + _block->encode(*blockData); + blocksReq->appendBlockData(std::move(*blockData)); + blocksReq->setNumber(_number); + config->frontService()->asyncSendMessageByNodeID( + ModuleID::BlockSync, _peer, ref(*(blocksReq->encode())), 0, nullptr); + BLKSYNC_LOG(DEBUG) + << LOG_DESC("fetchAndSendBlock: response block") + << LOG_KV("toPeer", _peer->shortHex()) << LOG_KV("number", _number) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("signatureSize", signature.size()) + << LOG_KV("transactionsSize", _block->transactionsSize()); + } + catch (std::exception const& e) + { + BLKSYNC_LOG(WARNING) + << LOG_DESC("fetchAndSendBlock exception") << LOG_KV("number", _number) + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +void BlockSync::maintainPeersConnection() +{ + if (!m_config->existsInGroup()) + { + return; + } + // Delete uncorrelated peers + NodeIDs peersToDelete; + m_syncStatus->foreachPeer([&](PeerStatus::Ptr _p) { + if (_p->nodeId() == m_config->nodeID()) + { + return true; + } + if (!m_config->connected(_p->nodeId())) + { + peersToDelete.emplace_back(_p->nodeId()); + return true; + } + if (!m_config->existsInGroup(_p->nodeId()) && m_config->blockNumber() >= _p->number()) + { + // Only delete outsider whose number is smaller than myself + peersToDelete.emplace_back(_p->nodeId()); + } + return true; + }); + // delete the invalid peer + for (auto node : peersToDelete) + { + m_syncStatus->deletePeer(node); + } + // Add new peers + auto groupNodeList = m_config->groupNodeList(); + for (auto node : groupNodeList) + { + // skip the node-self + if (node->data() == m_config->nodeID()->data()) + { + continue; + } + // not send request to the nodes disconnected + if (!m_config->connected(node)) + { + continue; + } + // create a peer + auto newPeerStatus = m_config->msgFactory()->createBlockSyncStatusMsg( + m_config->blockNumber(), m_config->hash(), m_config->genesisHash(), + static_cast(BlockSyncMsgVersion::v1)); + m_syncStatus->updatePeerStatus(m_config->nodeID(), newPeerStatus); + BLKSYNC_LOG(TRACE) << LOG_BADGE("Status") << LOG_DESC("Send current status to peer") + << LOG_KV("number", newPeerStatus->number()) + << LOG_KV("genesisHash", newPeerStatus->genesisHash().abridged()) + << LOG_KV("currentHash", newPeerStatus->hash().abridged()) + << LOG_KV("peer", node->shortHex()) + << LOG_KV("node", m_config->nodeID()->shortHex()); + auto encodedData = newPeerStatus->encode(); + m_config->frontService()->asyncSendMessageByNodeID( + ModuleID::BlockSync, node, ref(*encodedData), 0, nullptr); + } +} + +void BlockSync::broadcastSyncStatus() +{ + auto statusMsg = m_config->msgFactory()->createBlockSyncStatusMsg( + m_config->blockNumber(), m_config->hash(), m_config->genesisHash()); + auto encodedData = statusMsg->encode(); + BLKSYNC_LOG(TRACE) << LOG_BADGE("BlockSync") << LOG_DESC("broadcastSyncStatus") + << LOG_KV("number", statusMsg->number()) + << LOG_KV("genesisHash", statusMsg->genesisHash().abridged()) + << LOG_KV("currentHash", statusMsg->hash().abridged()); + m_config->frontService()->asyncSendBroadcastMessage( + bcos::protocol::NodeType::CONSENSUS_NODE | bcos::protocol::NodeType::OBSERVER_NODE, + ModuleID::BlockSync, ref(*encodedData)); +} + +bool BlockSync::faultyNode(bcos::crypto::NodeIDPtr _nodeID) +{ + if (!m_syncStatus->hasPeer(_nodeID)) + { + return true; + } + auto nodeStatus = m_syncStatus->peerStatus(_nodeID); + if ((nodeStatus->number() + c_FaultyNodeBlockDelta) < m_config->blockNumber()) + { + return true; + } + return false; +} + +void BlockSync::asyncGetSyncInfo(std::function _onGetSyncInfo) +{ + Json::Value syncInfo; + syncInfo["isSyncing"] = isSyncing(); + syncInfo["genesisHash"] = *toHexString(m_config->genesisHash()); + syncInfo["nodeID"] = *toHexString(m_config->nodeID()->data()); + + int64_t currentNumber = m_config->blockNumber(); + syncInfo["blockNumber"] = currentNumber; + syncInfo["latestHash"] = *toHexString(m_config->hash()); + syncInfo["knownHighestNumber"] = m_config->knownHighestNumber(); + syncInfo["knownLatestHash"] = *toHexString(m_config->knownLatestHash()); + + Json::Value peersInfo(Json::arrayValue); + m_syncStatus->foreachPeer([&](PeerStatus::Ptr _p) { + // not print the status of the node-self + if (_p->nodeId() == m_config->nodeID()) + { + return true; + } + Json::Value info; + info["nodeID"] = *toHexString(_p->nodeId()->data()); + info["genesisHash"] = *toHexString(_p->genesisHash()); + info["blockNumber"] = Json::UInt64(_p->number()); + info["latestHash"] = *toHexString(_p->hash()); + peersInfo.append(info); + return true; + }); + + syncInfo["peers"] = peersInfo; + Json::FastWriter fastWriter; + std::string statusStr = fastWriter.write(syncInfo); + _onGetSyncInfo(nullptr, statusStr); +} diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/BlockSync.h" "b/BFPL\345\243\271/bcos-sync/bcos-sync/BlockSync.h" new file mode 100644 index 00000000..2e8e6481 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/BlockSync.h" @@ -0,0 +1,143 @@ +/** + * Copyright (C) 2021 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. + * + * @brief block sync implementation + * @file BlockSync.h + * @author: yujiechen + * @date 2021-05-24 + */ +#pragma once +#include "bcos-sync/BlockSyncConfig.h" +#include "bcos-sync/state/DownloadingQueue.h" +#include "bcos-sync/state/SyncPeerStatus.h" +#include "bcos-tool/NodeTimeMaintenance.h" +#include +#include +#include +#include +namespace bcos +{ +namespace sync +{ +class BlockSync : public BlockSyncInterface, + public Worker, + public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + BlockSync(BlockSyncConfig::Ptr _config, unsigned _idleWaitMs = 200); + ~BlockSync() override {} + + void start() override; + void stop() override; + + // called by the frontService to dispatch message + void asyncNotifyBlockSyncMessage(Error::Ptr _error, std::string const& _uuid, + bcos::crypto::NodeIDPtr _nodeID, bytesConstRef _data, + std::function _onRecv) override; + + void asyncNotifyNewBlock(bcos::ledger::LedgerConfig::Ptr _ledgerConfig, + std::function _onRecv) override; + void asyncGetSyncInfo(std::function _onGetSyncInfo) override; + + void asyncNotifyCommittedIndex(bcos::protocol::BlockNumber _number, + std::function _onRecv) override + { + m_config->setCommittedProposalNumber(_number); + if (_onRecv) + { + _onRecv(nullptr); + } + } + + virtual void init(); + BlockSyncConfig::Ptr config() { return m_config; } + + void notifyConnectedNodes(bcos::crypto::NodeIDSet const& _connectedNodes, + std::function _onResponse) override + { + m_config->notifyConnectedNodes(_connectedNodes, _onResponse); + } + + // determine the specified node is faulty or not + // used to optimize consensus + bool faultyNode(bcos::crypto::NodeIDPtr _nodeID) override; + + void enableAsMaster(bool _masterNode); + +protected: + virtual void asyncNotifyBlockSyncMessage(Error::Ptr _error, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, std::function _sendResponse, + std::function _onRecv); + + void initSendResponseHandler(); + void executeWorker() override; + void workerProcessLoop() override; + // for message handle + virtual void onPeerStatus(bcos::crypto::NodeIDPtr _nodeID, BlockSyncMsgInterface::Ptr _syncMsg); + virtual void onPeerBlocks(bcos::crypto::NodeIDPtr _nodeID, BlockSyncMsgInterface::Ptr _syncMsg); + virtual void onPeerBlocksRequest( + bcos::crypto::NodeIDPtr _nodeID, BlockSyncMsgInterface::Ptr _syncMsg); + + virtual bool shouldSyncing(); + virtual bool isSyncing(); + virtual void tryToRequestBlocks(); + virtual void onDownloadTimeout(); + // block execute and submit + virtual void maintainDownloadingQueue(); + virtual void maintainDownloadingBuffer(); + // maintain connections + virtual void maintainPeersConnection(); + // block requests + virtual void maintainBlockRequest(); + // broadcast sync status + virtual void broadcastSyncStatus(); + + virtual void onNewBlock(bcos::ledger::LedgerConfig::Ptr _ledgerConfig); + + virtual void downloadFinish(); + +protected: + void requestBlocks(bcos::protocol::BlockNumber _from, bcos::protocol::BlockNumber _to); + void fetchAndSendBlock(DownloadRequestQueue::Ptr _reqQueue, bcos::crypto::PublicPtr _peer, + bcos::protocol::BlockNumber _number); + void printSyncInfo(); + +protected: + BlockSyncConfig::Ptr m_config; + SyncPeerStatus::Ptr m_syncStatus; + DownloadingQueue::Ptr m_downloadingQueue; + + std::function + m_sendResponseHandler; + + bcos::ThreadPool::Ptr m_downloadBlockProcessor = nullptr; + bcos::ThreadPool::Ptr m_sendBlockProcessor = nullptr; + std::shared_ptr m_downloadingTimer; + + std::atomic_bool m_running = {false}; + std::atomic m_state = {SyncState::Idle}; + std::atomic m_maxRequestNumber = {0}; + + boost::condition_variable m_signalled; + boost::mutex x_signalled; + bcos::protocol::BlockNumber m_waterMark = 10; + bcos::protocol::BlockNumber c_FaultyNodeBlockDelta = 50; + + std::atomic_bool m_masterNode = {false}; +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/BlockSyncConfig.cpp" "b/BFPL\345\243\271/bcos-sync/bcos-sync/BlockSyncConfig.cpp" new file mode 100644 index 00000000..b9d34026 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/BlockSyncConfig.cpp" @@ -0,0 +1,174 @@ +/** + * Copyright (C) 2021 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. + * + * @brief config for the block sync + * @file BlockSyncConfig.cpp + * @author: yujiechen + * @date 2021-05-25 + */ +#include "BlockSyncConfig.h" +#include "bcos-sync/utilities/Common.h" +using namespace bcos; +using namespace bcos::sync; +using namespace bcos::crypto; +using namespace bcos::protocol; +using namespace bcos::ledger; + +void BlockSyncConfig::resetConfig(LedgerConfig::Ptr _ledgerConfig) +{ + if (_ledgerConfig->blockNumber() <= m_blockNumber && m_blockNumber > 0) + { + return; + } + // must resetConfig for the consensus module firstly for the following block check depends on + // the consensus config + m_consensus->asyncNotifyNewBlock(_ledgerConfig, [_ledgerConfig](Error::Ptr _error) { + if (!_error) + { + return; + } + BLKSYNC_LOG(WARNING) << LOG_DESC("asyncNotifyNewBlock to consensus failed") + << LOG_KV("number", _ledgerConfig->blockNumber()) + << LOG_KV("hash", _ledgerConfig->hash().abridged()) + << LOG_KV("error", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + }); + + // Note: can't add lock before asyncNotifyNewBlock in case of deadlock + Guard l(m_mutex); + if (_ledgerConfig->blockNumber() <= m_blockNumber && m_blockNumber > 0) + { + return; + } + resetBlockInfo(_ledgerConfig->blockNumber(), _ledgerConfig->hash()); + setConsensusNodeList(_ledgerConfig->consensusNodeList()); + setObserverList(_ledgerConfig->observerNodeList()); + auto type = determineNodeType(); + if (type != m_nodeType) + { + m_nodeType = type; + } + if (m_nodeTypeChanged && m_masterNode && (m_notifiedNodeType != m_nodeType)) + { + m_nodeTypeChanged(type); + m_notifiedNodeType = m_nodeType; + } + BLKSYNC_LOG(INFO) << LOG_DESC("#### BlockSyncConfig resetConfig") + << LOG_KV("number", m_blockNumber) + << LOG_KV("consNodeSize", consensusNodeList().size()) + << LOG_KV("observerNodeSize", observerNodeList().size()) + << LOG_KV("type", m_nodeType); +} + +void BlockSyncConfig::setGenesisHash(HashType const& _hash) +{ + m_genesisHash = _hash; + if (knownLatestHash() == HashType()) + { + setKnownLatestHash(m_genesisHash); + } +} + +void BlockSyncConfig::resetBlockInfo(BlockNumber _blockNumber, bcos::crypto::HashType const& _hash) +{ + m_blockNumber = _blockNumber; + setHash(_hash); + m_nextBlock = m_blockNumber + 1; + if (m_knownHighestNumber < _blockNumber) + { + m_knownHighestNumber = _blockNumber; + setKnownLatestHash(_hash); + } + if (_blockNumber > m_executedBlock) + { + m_executedBlock = _blockNumber; + } +} + +HashType const& BlockSyncConfig::hash() const +{ + ReadGuard l(x_hash); + return m_hash; +} + +void BlockSyncConfig::setHash(HashType const& _hash) +{ + WriteGuard l(x_hash); + m_hash = _hash; +} + +void BlockSyncConfig::setKnownHighestNumber(BlockNumber _highestNumber) +{ + m_knownHighestNumber = _highestNumber; +} + +void BlockSyncConfig::setKnownLatestHash(HashType const& _hash) +{ + WriteGuard l(x_knownLatestHash); + m_knownLatestHash = _hash; +} + +HashType const& BlockSyncConfig::knownLatestHash() +{ + ReadGuard l(x_knownLatestHash); + return m_knownLatestHash; +} + +void BlockSyncConfig::setMaxDownloadingBlockQueueSize(size_t _maxDownloadingBlockQueueSize) +{ + m_maxDownloadingBlockQueueSize = _maxDownloadingBlockQueueSize; +} + +void BlockSyncConfig::setMaxDownloadRequestQueueSize(size_t _maxDownloadRequestQueueSize) +{ + m_maxDownloadRequestQueueSize = _maxDownloadRequestQueueSize; +} + +void BlockSyncConfig::setExecutedBlock(BlockNumber _executedBlock) +{ + if (m_blockNumber <= _executedBlock) + { + m_executedBlock = _executedBlock; + return; + } + m_executedBlock.store(m_blockNumber); +} + +bcos::protocol::NodeType BlockSyncConfig::determineNodeType() +{ + if (existNode(m_consensusNodeList, x_consensusNodeList, m_nodeId)) + { + return bcos::protocol::NodeType::CONSENSUS_NODE; + } + if (existNode(m_observerNodeList, x_observerNodeList, m_nodeId)) + { + return bcos::protocol::NodeType::OBSERVER_NODE; + } + return bcos::protocol::NodeType::NODE_OUTSIDE_GROUP; +} + +bool BlockSyncConfig::existNode(bcos::consensus::ConsensusNodeListPtr const& _nodeList, + SharedMutex& _lock, bcos::crypto::NodeIDPtr _nodeID) +{ + ReadGuard l(_lock); + for (auto const& it : *_nodeList) + { + if (it->nodeID()->data() == _nodeID->data()) + { + return true; + } + } + return false; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/BlockSyncConfig.h" "b/BFPL\345\243\271/bcos-sync/bcos-sync/BlockSyncConfig.h" new file mode 100644 index 00000000..6ecaac26 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/BlockSyncConfig.h" @@ -0,0 +1,191 @@ +/** + * Copyright (C) 2021 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. + * + * @brief config for the block sync + * @file BlockSyncConfig.h + * @author: yujiechen + * @date 2021-05-24 + */ +#pragma once +#include "bcos-sync/interfaces/BlockSyncMsgFactory.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace sync +{ +class BlockSyncConfig : public SyncConfig +{ +public: + using Ptr = std::shared_ptr; + BlockSyncConfig(bcos::crypto::PublicPtr _nodeId, bcos::ledger::LedgerInterface::Ptr _ledger, + bcos::txpool::TxPoolInterface::Ptr _txpool, bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::protocol::TransactionSubmitResultFactory::Ptr _txResultFactory, + bcos::front::FrontServiceInterface::Ptr _frontService, + bcos::scheduler::SchedulerInterface::Ptr _scheduler, + bcos::consensus::ConsensusInterface::Ptr _consensus, BlockSyncMsgFactory::Ptr _msgFactory, + bcos::tool::NodeTimeMaintenance::Ptr _nodeTimeMaintenance) + : SyncConfig(_nodeId), + m_ledger(_ledger), + m_txpool(_txpool), + m_blockFactory(_blockFactory), + m_txResultFactory(_txResultFactory), + m_frontService(_frontService), + m_scheduler(_scheduler), + m_consensus(_consensus), + m_msgFactory(_msgFactory), + m_nodeTimeMaintenance(_nodeTimeMaintenance) + {} + ~BlockSyncConfig() override {} + + bcos::ledger::LedgerInterface::Ptr ledger() { return m_ledger; } + bcos::protocol::BlockFactory::Ptr blockFactory() { return m_blockFactory; } + bcos::front::FrontServiceInterface::Ptr frontService() { return m_frontService; } + bcos::scheduler::SchedulerInterface::Ptr scheduler() { return m_scheduler; } + bcos::consensus::ConsensusInterface::Ptr consensus() { return m_consensus; } + bcos::tool::NodeTimeMaintenance::Ptr nodeTimeMaintenance() { return m_nodeTimeMaintenance; } + + BlockSyncMsgFactory::Ptr msgFactory() { return m_msgFactory; } + virtual void resetConfig(bcos::ledger::LedgerConfig::Ptr _ledgerConfig); + + bcos::crypto::HashType const& genesisHash() const { return m_genesisHash; } + void setGenesisHash(bcos::crypto::HashType const& _hash); + + bcos::protocol::BlockNumber blockNumber() const { return m_blockNumber; } + bcos::crypto::HashType const& hash() const; + + bcos::protocol::BlockNumber nextBlock() const { return m_nextBlock; } + void resetBlockInfo( + bcos::protocol::BlockNumber _blockNumber, bcos::crypto::HashType const& _hash); + + void setKnownHighestNumber(bcos::protocol::BlockNumber _highestNumber); + bcos::protocol::BlockNumber knownHighestNumber() { return m_knownHighestNumber; } + + void setKnownLatestHash(bcos::crypto::HashType const& _hash); + + bcos::crypto::HashType const& knownLatestHash(); + + size_t maxDownloadingBlockQueueSize() const { return m_maxDownloadingBlockQueueSize; } + void setMaxDownloadingBlockQueueSize(size_t _maxDownloadingBlockQueueSize); + + void setMaxDownloadRequestQueueSize(size_t _maxDownloadRequestQueueSize); + + size_t maxDownloadRequestQueueSize() const { return m_maxDownloadRequestQueueSize; } + + size_t downloadTimeout() const { return m_downloadTimeout; } + + size_t maxRequestBlocks() const { return m_maxRequestBlocks; } + size_t maxShardPerPeer() const { return m_maxShardPerPeer; } + + void setExecutedBlock(bcos::protocol::BlockNumber _executedBlock); + bcos::protocol::BlockNumber executedBlock() { return m_executedBlock; } + + bcos::txpool::TxPoolInterface::Ptr txpool() { return m_txpool; } + bcos::protocol::TransactionSubmitResultFactory::Ptr txResultFactory() + { + return m_txResultFactory; + } + + void setCommittedProposalNumber(bcos::protocol::BlockNumber _committedProposalNumber) + { + m_committedProposalNumber = _committedProposalNumber; + } + + bcos::protocol::BlockNumber committedProposalNumber() const + { + return m_committedProposalNumber; + } + + bcos::protocol::NodeType nodeType() const { return m_nodeType; } + + void registerOnNodeTypeChanged(std::function _onNodeTypeChanged) + { + m_nodeTypeChanged = _onNodeTypeChanged; + } + + void setMasterNode(bool _masterNode) + { + Guard l(m_mutex); + m_masterNode = _masterNode; + // notify nodeType to the gateway + if (m_nodeTypeChanged) + { + m_nodeTypeChanged(nodeType()); + } + } + + bool masterNode() const { return m_masterNode; } + +protected: + void setHash(bcos::crypto::HashType const& _hash); + + // Note: this only be called after block on-chain successfully + virtual bcos::protocol::NodeType determineNodeType(); + bool existNode(bcos::consensus::ConsensusNodeListPtr const& _nodeList, SharedMutex& _lock, + bcos::crypto::NodeIDPtr _nodeID); + +private: + bcos::ledger::LedgerInterface::Ptr m_ledger; + bcos::txpool::TxPoolInterface::Ptr m_txpool; + bcos::protocol::BlockFactory::Ptr m_blockFactory; + bcos::protocol::TransactionSubmitResultFactory::Ptr m_txResultFactory; + bcos::front::FrontServiceInterface::Ptr m_frontService; + bcos::scheduler::SchedulerInterface::Ptr m_scheduler; + bcos::consensus::ConsensusInterface::Ptr m_consensus; + BlockSyncMsgFactory::Ptr m_msgFactory; + bcos::tool::NodeTimeMaintenance::Ptr m_nodeTimeMaintenance; + + bcos::crypto::HashType m_genesisHash; + std::atomic m_blockNumber = {0}; + std::atomic m_nextBlock = {0}; + std::atomic m_executedBlock = {0}; + bcos::crypto::HashType m_hash; + mutable SharedMutex x_hash; + + std::atomic m_knownHighestNumber = {0}; + bcos::crypto::HashType m_knownLatestHash; + mutable SharedMutex x_knownLatestHash; + mutable Mutex m_mutex; + + std::atomic m_maxDownloadingBlockQueueSize = 256; + std::atomic m_maxDownloadRequestQueueSize = 1000; + std::atomic m_downloadTimeout = (200 * m_maxDownloadingBlockQueueSize); + // the max number of blocks this node can requested to + std::atomic m_maxRequestBlocks = {8}; + + std::atomic m_maxShardPerPeer = {2}; + + std::atomic m_committedProposalNumber = {0}; + + // TODO: ensure thread-safe + bcos::protocol::NodeType m_nodeType = bcos::protocol::NodeType::None; + bcos::protocol::NodeType m_notifiedNodeType = bcos::protocol::NodeType::None; + + std::function m_nodeTypeChanged; + + std::atomic_bool m_masterNode = {false}; +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/BlockSyncFactory.cpp" "b/BFPL\345\243\271/bcos-sync/bcos-sync/BlockSyncFactory.cpp" new file mode 100644 index 00000000..b424388d --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/BlockSyncFactory.cpp" @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2021 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. + * + * @brief factory to create BlockSync + * @file BlockSyncFactory.cpp + * @author: yujiechen + * @date 2021-05-28 + */ +#include "BlockSyncFactory.h" +#include "protocol/PB/BlockSyncMsgFactoryImpl.h" + +using namespace bcos; +using namespace bcos::sync; + +BlockSyncFactory::BlockSyncFactory(bcos::crypto::PublicPtr _nodeId, + bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::protocol::TransactionSubmitResultFactory::Ptr _txResultFactory, + bcos::ledger::LedgerInterface::Ptr _ledger, bcos::txpool::TxPoolInterface::Ptr _txpool, + bcos::front::FrontServiceInterface::Ptr _frontService, + bcos::scheduler::SchedulerInterface::Ptr _scheduler, + bcos::consensus::ConsensusInterface::Ptr _consensus, + bcos::tool::NodeTimeMaintenance::Ptr _nodeTimeMaintenance) + : m_nodeId(_nodeId), + m_blockFactory(_blockFactory), + m_txResultFactory(_txResultFactory), + m_ledger(_ledger), + m_txpool(_txpool), + m_frontService(_frontService), + m_scheduler(_scheduler), + m_consensus(_consensus), + m_nodeTimeMaintenance(_nodeTimeMaintenance) +{} + +BlockSync::Ptr BlockSyncFactory::createBlockSync() +{ + auto msgFactory = std::make_shared(); + auto syncConfig = std::make_shared(m_nodeId, m_ledger, m_txpool, + m_blockFactory, m_txResultFactory, m_frontService, m_scheduler, m_consensus, msgFactory, m_nodeTimeMaintenance); + return std::make_shared(syncConfig); +} diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/BlockSyncFactory.h" "b/BFPL\345\243\271/bcos-sync/bcos-sync/BlockSyncFactory.h" new file mode 100644 index 00000000..2b9b5b84 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/BlockSyncFactory.h" @@ -0,0 +1,58 @@ +/** + * Copyright (C) 2021 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. + * + * @brief factory to create BlockSync + * @file BlockSyncFactory.h + * @author: yujiechen + * @date 2021-05-28 + */ +#pragma once +#include "bcos-sync/BlockSync.h" +#include "bcos-sync/BlockSyncConfig.h" +#include "bcos-tool/NodeTimeMaintenance.h" + +namespace bcos +{ +namespace sync +{ +class BlockSyncFactory +{ +public: + using Ptr = std::shared_ptr; + BlockSyncFactory(bcos::crypto::PublicPtr _nodeId, + bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::protocol::TransactionSubmitResultFactory::Ptr _txResultFactory, + bcos::ledger::LedgerInterface::Ptr _ledger, bcos::txpool::TxPoolInterface::Ptr _txpool, + bcos::front::FrontServiceInterface::Ptr _frontService, + bcos::scheduler::SchedulerInterface::Ptr _scheduler, + bcos::consensus::ConsensusInterface::Ptr _consensus, + bcos::tool::NodeTimeMaintenance::Ptr _nodeTimeMaintenance); + virtual ~BlockSyncFactory() {} + + virtual BlockSync::Ptr createBlockSync(); + +protected: + bcos::crypto::PublicPtr m_nodeId; + bcos::protocol::BlockFactory::Ptr m_blockFactory; + bcos::protocol::TransactionSubmitResultFactory::Ptr m_txResultFactory; + bcos::ledger::LedgerInterface::Ptr m_ledger; + bcos::txpool::TxPoolInterface::Ptr m_txpool; + bcos::front::FrontServiceInterface::Ptr m_frontService; + bcos::scheduler::SchedulerInterface::Ptr m_scheduler; + bcos::consensus::ConsensusInterface::Ptr m_consensus; + bcos::tool::NodeTimeMaintenance::Ptr m_nodeTimeMaintenance; +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlockRequestInterface.h" "b/BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlockRequestInterface.h" new file mode 100644 index 00000000..6129ff31 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlockRequestInterface.h" @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interfaces for block request packet + * @file BlockRequestInterface.h + * @author: yujiechen + * @date 2021-05-24 + */ + +#pragma once +#include "bcos-sync/interfaces/BlockSyncMsgInterface.h" +namespace bcos +{ +namespace sync +{ +class BlockRequestInterface : virtual public BlockSyncMsgInterface +{ +public: + using Ptr = std::shared_ptr; + BlockRequestInterface() = default; + virtual ~BlockRequestInterface() {} + + virtual size_t size() const = 0; + virtual void setSize(size_t _size) = 0; +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlockSyncMsgFactory.h" "b/BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlockSyncMsgFactory.h" new file mode 100644 index 00000000..feef9392 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlockSyncMsgFactory.h" @@ -0,0 +1,64 @@ +/** + * Copyright (C) 2021 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. + * + * @brief factory to create block sync message + * @file BlockSyncMsgFactory.h + * @author: yujiechen + * @date 2021-05-23 + */ +#pragma once +#include "bcos-sync/utilities/Common.h" +#include "bcos-sync/interfaces/BlockRequestInterface.h" +#include "bcos-sync/interfaces/BlockSyncStatusInterface.h" +#include "bcos-sync/interfaces/BlocksMsgInterface.h" +namespace bcos +{ +namespace sync +{ +class BlockSyncMsgFactory +{ +public: + using Ptr = std::shared_ptr; + BlockSyncMsgFactory() = default; + virtual ~BlockSyncMsgFactory() {} + + virtual BlockSyncMsgInterface::Ptr createBlockSyncMsg(bytesConstRef _data) = 0; + virtual BlockSyncStatusInterface::Ptr createBlockSyncStatusMsg(int32_t version = 0) = 0; + virtual BlockSyncStatusInterface::Ptr createBlockSyncStatusMsg(bytesConstRef _data) = 0; + virtual BlockSyncStatusInterface::Ptr createBlockSyncStatusMsg( + BlockSyncMsgInterface::Ptr _msg) = 0; + virtual BlockSyncStatusInterface::Ptr createBlockSyncStatusMsg( + bcos::protocol::BlockNumber _number, bcos::crypto::HashType const& _hash, + bcos::crypto::HashType const& _gensisHash,int32_t _version = 0, + int64_t const time = utcTime()) + { + auto statusMsg = createBlockSyncStatusMsg(_version); + statusMsg->setNumber(_number); + statusMsg->setHash(_hash); + statusMsg->setGenesisHash(_gensisHash); + statusMsg->setTime(time); + return statusMsg; + } + + virtual BlocksMsgInterface::Ptr createBlocksMsg() = 0; + virtual BlocksMsgInterface::Ptr createBlocksMsg(bytesConstRef _data) = 0; + virtual BlocksMsgInterface::Ptr createBlocksMsg(BlockSyncMsgInterface::Ptr _msg) = 0; + + virtual BlockRequestInterface::Ptr createBlockRequest() = 0; + virtual BlockRequestInterface::Ptr createBlockRequest(bytesConstRef _data) = 0; + virtual BlockRequestInterface::Ptr createBlockRequest(BlockSyncMsgInterface::Ptr _msg) = 0; +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlockSyncMsgInterface.h" "b/BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlockSyncMsgInterface.h" new file mode 100644 index 00000000..4c3702c0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlockSyncMsgInterface.h" @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for the basic block syncMsg + * @file BlockSyncMsgInterface.h + * @author: yujiechen + * @date 2021-05-24 + */ +#pragma once +#include +#include +namespace bcos +{ +namespace sync +{ +class BlockSyncMsgInterface +{ +public: + using Ptr = std::shared_ptr; + BlockSyncMsgInterface() = default; + virtual ~BlockSyncMsgInterface() {} + + virtual bytesPointer encode() const = 0; + virtual void decode(bytesConstRef _data) = 0; + + virtual bcos::protocol::BlockNumber number() const = 0; + virtual int32_t packetType() const = 0; + virtual int32_t version() const = 0; + + virtual void setNumber(bcos::protocol::BlockNumber _number) = 0; + virtual void setPacketType(int32_t packetType) = 0; + virtual void setVersion(int32_t _version) = 0; +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlockSyncStatusInterface.h" "b/BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlockSyncStatusInterface.h" new file mode 100644 index 00000000..da6bb273 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlockSyncStatusInterface.h" @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2021 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. + * + * @brief status for the block sync + * @file BlockSyncStatueInterface.h + * @author: yujiechen + * @date 2021-05-23 + */ + +#pragma once +#include "bcos-sync/interfaces/BlockSyncMsgInterface.h" +#include +namespace bcos +{ +namespace sync +{ +class BlockSyncStatusInterface : virtual public BlockSyncMsgInterface +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + BlockSyncStatusInterface() = default; + virtual ~BlockSyncStatusInterface() {} + + virtual bcos::crypto::HashType const& hash() const = 0; + virtual bcos::crypto::HashType const& genesisHash() const = 0; + virtual std::int64_t time() const = 0; + + virtual void setHash(bcos::crypto::HashType const& _hash) = 0; + virtual void setGenesisHash(bcos::crypto::HashType const& _gensisHash) = 0; + virtual void setTime(std::int64_t const time) = 0; +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlocksMsgInterface.h" "b/BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlocksMsgInterface.h" new file mode 100644 index 00000000..69cf574f --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/interfaces/BlocksMsgInterface.h" @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for the message contains blockData + * @file BlocksMsgInterface.h + * @author: yujiechen + * @date 2021-05-24 + */ +#pragma once +#include "bcos-sync/interfaces/BlockSyncMsgInterface.h" +namespace bcos +{ +namespace sync +{ +class BlocksMsgInterface : virtual public BlockSyncMsgInterface +{ +public: + using Ptr = std::shared_ptr; + BlocksMsgInterface() = default; + virtual ~BlocksMsgInterface() {} + + virtual size_t blocksSize() const = 0; + virtual bytesConstRef blockData(size_t _index) const = 0; + + virtual void appendBlockData(bytes&& _blockData) = 0; + virtual void appendBlockData(bytes const& _blockData) = 0; +}; +using BlocksMsgList = std::vector; +using BlocksMsgListPtr = std::shared_ptr; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockRequestImpl.h" "b/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockRequestImpl.h" new file mode 100644 index 00000000..ec1e39f6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockRequestImpl.h" @@ -0,0 +1,54 @@ +/** + * Copyright (C) 2021 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. + * + * @brief PB implementation for BlockRequestInterface + * @file BlockRequestImpl.h + * @author: yujiechen + * @date 2021-05-24 + */ +#pragma once +#include "bcos-sync/interfaces/BlockRequestInterface.h" +#include "bcos-sync/protocol/PB/BlockSyncMsgImpl.h" +#include "bcos-sync/utilities/Common.h" +namespace bcos +{ +namespace sync +{ +class BlockRequestImpl : public BlockRequestInterface, public BlockSyncMsgImpl +{ +public: + BlockRequestImpl() : BlockSyncMsgImpl() + { + setPacketType(BlockSyncPacketType::BlockRequestPacket); + } + explicit BlockRequestImpl(BlockSyncMsgImpl::Ptr _blockSyncMsg) + : BlockRequestImpl(_blockSyncMsg->syncMessage()) + {} + + explicit BlockRequestImpl(bytesConstRef _data) : BlockRequestImpl() { decode(_data); } + ~BlockRequestImpl() override {} + + size_t size() const override { return m_syncMessage->size(); } + void setSize(size_t _size) override { m_syncMessage->set_size(_size); } + +protected: + explicit BlockRequestImpl(std::shared_ptr _syncMessage) + { + setPacketType(BlockSyncPacketType::BlockRequestPacket); + m_syncMessage = _syncMessage; + } +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockSyncMsgFactoryImpl.h" "b/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockSyncMsgFactoryImpl.h" new file mode 100644 index 00000000..3750bb43 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockSyncMsgFactoryImpl.h" @@ -0,0 +1,85 @@ +/** + * Copyright (C) 2021 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. + * + * @brief factory to create block sync message + * @file BlockSyncMsgFactoryImpl.h + * @author: yujiechen + * @date 2021-05-23 + */ +#pragma once +#include "bcos-sync/interfaces/BlockSyncMsgFactory.h" +#include "bcos-sync/protocol/PB/BlockRequestImpl.h" +#include "bcos-sync/protocol/PB/BlockSyncStatusImpl.h" +#include "bcos-sync/protocol/PB/BlocksMsgImpl.h" +namespace bcos +{ +namespace sync +{ +class BlockSyncMsgFactoryImpl : public BlockSyncMsgFactory +{ +public: + BlockSyncMsgFactoryImpl() = default; + ~BlockSyncMsgFactoryImpl() override {} + + BlockSyncMsgInterface::Ptr createBlockSyncMsg(bytesConstRef _data) override + { + return std::make_shared(_data); + } + + BlockSyncStatusInterface::Ptr createBlockSyncStatusMsg(int32_t version = 0) override + { + BlockSyncStatusInterface::Ptr status = std::make_shared(); + status->setVersion(version); + + return status; + } + + BlockSyncStatusInterface::Ptr createBlockSyncStatusMsg(bytesConstRef _data) override + { + return std::make_shared(_data); + } + BlockSyncStatusInterface::Ptr createBlockSyncStatusMsg(BlockSyncMsgInterface::Ptr _msg) override + { + auto syncMsg = std::dynamic_pointer_cast(_msg); + return std::make_shared(syncMsg); + } + + BlocksMsgInterface::Ptr createBlocksMsg() override { return std::make_shared(); } + BlocksMsgInterface::Ptr createBlocksMsg(bytesConstRef _data) override + { + return std::make_shared(_data); + } + BlocksMsgInterface::Ptr createBlocksMsg(BlockSyncMsgInterface::Ptr _msg) override + { + auto syncMsg = std::dynamic_pointer_cast(_msg); + return std::make_shared(syncMsg); + } + + BlockRequestInterface::Ptr createBlockRequest() override + { + return std::make_shared(); + } + BlockRequestInterface::Ptr createBlockRequest(bytesConstRef _data) override + { + return std::make_shared(_data); + } + BlockRequestInterface::Ptr createBlockRequest(BlockSyncMsgInterface::Ptr _msg) override + { + auto syncMsg = std::dynamic_pointer_cast(_msg); + return std::make_shared(syncMsg); + } +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockSyncMsgImpl.h" "b/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockSyncMsgImpl.h" new file mode 100644 index 00000000..c8a2bc2f --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockSyncMsgImpl.h" @@ -0,0 +1,64 @@ +/** + * Copyright (C) 2021 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. + * + * @brief PB implement for BlockSyncMsgInterface + * @file BlockSyncMsgImpl.h + * @author: yujiechen + * @date 2021-05-24 + */ +#pragma once +#include "bcos-sync/utilities/Common.h" +#include "bcos-sync/interfaces/BlockSyncMsgInterface.h" +#include "bcos-sync/protocol/proto/BlockSync.pb.h" +#include +namespace bcos +{ +namespace sync +{ +class BlockSyncMsgImpl : virtual public BlockSyncMsgInterface +{ +public: + using Ptr = std::shared_ptr; + BlockSyncMsgImpl() : m_syncMessage(std::make_shared()) {} + explicit BlockSyncMsgImpl(bytesConstRef _data) : BlockSyncMsgImpl() { decode(_data); } + + ~BlockSyncMsgImpl() override {} + + bytesPointer encode() const override { return bcos::protocol::encodePBObject(m_syncMessage); } + void decode(bytesConstRef _data) override + { + bcos::protocol::decodePBObject(m_syncMessage, _data); + } + + int32_t version() const override { return m_syncMessage->version(); } + bcos::protocol::BlockNumber number() const override { return m_syncMessage->number(); } + int32_t packetType() const override { return m_syncMessage->packettype(); } + + + void setVersion(int32_t _version) override { m_syncMessage->set_version(_version); } + void setNumber(bcos::protocol::BlockNumber _number) override + { + m_syncMessage->set_number(_number); + } + + void setPacketType(int32_t packetType) override { m_syncMessage->set_packettype(packetType); } + + std::shared_ptr syncMessage() { return m_syncMessage; } + +protected: + std::shared_ptr m_syncMessage; +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockSyncStatusImpl.cpp" "b/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockSyncStatusImpl.cpp" new file mode 100644 index 00000000..5db617d9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockSyncStatusImpl.cpp" @@ -0,0 +1,64 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for the block sync status packet + * @file BlockSyncStatusImpl.cpp + * @author: yujiechen + * @date 2021-05-23 + */ +#include "BlockSyncStatusImpl.h" + +using namespace bcos; +using namespace bcos::sync; +using namespace bcos::protocol; +using namespace bcos::crypto; + +void BlockSyncStatusImpl::decode(bytesConstRef _data) +{ + BlockSyncMsgImpl::decode(_data); + deserializeObject(); +} + +void BlockSyncStatusImpl::deserializeObject() +{ + auto const& hashData = m_syncMessage->hash(); + if (hashData.size() >= HashType::SIZE) + { + m_hash = HashType((byte const*)hashData.data(), HashType::SIZE); + } + auto const& genesisHashData = m_syncMessage->genesishash(); + if (genesisHashData.size() >= HashType::SIZE) + { + m_genesisHash = HashType((byte const*)genesisHashData.data(), HashType::SIZE); + } + m_time = m_syncMessage->time(); +} +void BlockSyncStatusImpl::setHash(HashType const& _hash) +{ + m_hash = _hash; + m_syncMessage->set_hash(_hash.data(), HashType::SIZE); +} + +void BlockSyncStatusImpl::setGenesisHash(HashType const& _gensisHash) +{ + m_genesisHash = _gensisHash; + m_syncMessage->set_genesishash(_gensisHash.data(), HashType::SIZE); +} + +void BlockSyncStatusImpl::setTime(std::int64_t const time) +{ + m_time = time; + m_syncMessage->set_time(time); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockSyncStatusImpl.h" "b/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockSyncStatusImpl.h" new file mode 100644 index 00000000..857aa413 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlockSyncStatusImpl.h" @@ -0,0 +1,66 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for the block sync status packet + * @file BlockSyncStatusImpl.h + * @author: yujiechen + * @date 2021-05-23 + */ +#pragma once +#include "bcos-sync/interfaces/BlockSyncStatusInterface.h" +#include "bcos-sync/protocol/PB/BlockSyncMsgImpl.h" + +namespace bcos +{ +namespace sync +{ +class BlockSyncStatusImpl : public BlockSyncStatusInterface, public BlockSyncMsgImpl +{ +public: + using Ptr = std::shared_ptr; + BlockSyncStatusImpl() : BlockSyncMsgImpl() + { + setPacketType(BlockSyncPacketType::BlockStatusPacket); + } + explicit BlockSyncStatusImpl(BlockSyncMsgImpl::Ptr _blockSyncMsg) + { + setPacketType(BlockSyncPacketType::BlockStatusPacket); + m_syncMessage = _blockSyncMsg->syncMessage(); + deserializeObject(); + } + + explicit BlockSyncStatusImpl(bytesConstRef _data) : BlockSyncStatusImpl() { decode(_data); } + + ~BlockSyncStatusImpl() override {} + + void decode(bytesConstRef _data) override; + bcos::crypto::HashType const& hash() const override { return m_hash; } + bcos::crypto::HashType const& genesisHash() const override { return m_genesisHash; } + std::int64_t time() const override { return m_time; } + + void setHash(bcos::crypto::HashType const& _hash) override; + void setGenesisHash(bcos::crypto::HashType const& _gensisHash) override; + void setTime(std::int64_t const time) override; + +protected: + virtual void deserializeObject(); + +private: + bcos::crypto::HashType m_hash; + bcos::crypto::HashType m_genesisHash; + std::int64_t m_time; +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlocksMsgImpl.h" "b/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlocksMsgImpl.h" new file mode 100644 index 00000000..e3ed498f --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/PB/BlocksMsgImpl.h" @@ -0,0 +1,75 @@ +/** + * Copyright (C) 2021 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. + * + * @brief PB implementation for BlocksMsgInterface + * @file BlocksMsgImpl.h + * @author: yujiechen + * @date 2021-05-24 + */ +#pragma once +#include "bcos-sync/interfaces/BlocksMsgInterface.h" +#include "bcos-sync/protocol/PB/BlockSyncMsgImpl.h" +#include "bcos-sync/utilities/Common.h" +namespace bcos +{ +namespace sync +{ +class BlocksMsgImpl : public BlocksMsgInterface, public BlockSyncMsgImpl +{ +public: + using Ptr = std::shared_ptr; + BlocksMsgImpl() : BlockSyncMsgImpl() + { + setPacketType(BlockSyncPacketType::BlockResponsePacket); + } + explicit BlocksMsgImpl(BlockSyncMsgImpl::Ptr _blockSyncMsg) + : BlocksMsgImpl(_blockSyncMsg->syncMessage()) + {} + + explicit BlocksMsgImpl(bytesConstRef _data) : BlocksMsgImpl() { decode(_data); } + ~BlocksMsgImpl() override {} + + size_t blocksSize() const override { return m_syncMessage->blocksdata_size(); } + bytesConstRef blockData(size_t _index) const override + { + auto const& blockData = m_syncMessage->blocksdata(_index); + return bytesConstRef((byte const*)blockData.data(), blockData.size()); + } + + void appendBlockData(bytes&& _blockData) override + { + auto index = blocksSize(); + auto blockSize = _blockData.size(); + m_syncMessage->add_blocksdata(); + m_syncMessage->set_blocksdata(index, (std::move(_blockData)).data(), blockSize); + } + + void appendBlockData(bytes const& _blockData) override + { + auto index = blocksSize(); + auto blockSize = _blockData.size(); + m_syncMessage->add_blocksdata(); + m_syncMessage->set_blocksdata(index, _blockData.data(), blockSize); + } + +protected: + explicit BlocksMsgImpl(std::shared_ptr _syncMessage) + { + setPacketType(BlockSyncPacketType::BlockResponsePacket); + m_syncMessage = _syncMessage; + } +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/proto/BlockSync.proto" "b/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/proto/BlockSync.proto" new file mode 100644 index 00000000..a0ea717b --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/protocol/proto/BlockSync.proto" @@ -0,0 +1,21 @@ +syntax = "proto3"; +package bcos.sync; + +message BlockSyncMessage +{ + // the basic fields + int32 version = 1; + int32 packetType = 2; + int64 number = 3; + + // for sync status + bytes hash = 4; + bytes genesisHash = 5; + + // for blocks sync + int64 size = 6; + repeated bytes blocksData = 7; + + //for time sync + int64 time = 8; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/state/DownloadRequestQueue.cpp" "b/BFPL\345\243\271/bcos-sync/bcos-sync/state/DownloadRequestQueue.cpp" new file mode 100644 index 00000000..dec82750 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/state/DownloadRequestQueue.cpp" @@ -0,0 +1,88 @@ +/** + * Copyright (C) 2021 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. + * + * @brief queue to maintain the download request + * @file DownloadRequestQueue.cpp + * @author: jimmyshi + * @date 2021-05-24 + */ +#include "DownloadRequestQueue.h" +#include "bcos-sync/utilities/Common.h" + +using namespace bcos; +using namespace bcos::sync; +using namespace bcos::protocol; + +void DownloadRequestQueue::push(BlockNumber _fromNumber, size_t _size) +{ + UpgradableGuard l(x_reqQueue); + // Note: the requester must has retry logic + if (m_reqQueue.size() >= m_config->maxDownloadRequestQueueSize()) + { + BLKSYNC_LOG(DEBUG) << LOG_BADGE("Download") << LOG_BADGE("Request") + << LOG_DESC("Drop request for reqQueue full") + << LOG_KV("reqQueueSize", m_reqQueue.size()) + << LOG_KV("fromNumber", _fromNumber) << LOG_KV("size", _size) + << LOG_KV("nodeId", m_config->nodeID()->shortHex()); + return; + } + UpgradeGuard ul(l); + m_reqQueue.push(std::make_shared(_fromNumber, _size)); + BLKSYNC_LOG(DEBUG) << LOG_BADGE("Download") << LOG_BADGE("Request") + << LOG_DESC("Push request in reqQueue req") << LOG_KV("from", _fromNumber) + << LOG_KV("to", _fromNumber + _size - 1) + << LOG_KV("currentNumber", m_config->blockNumber()) + << LOG_KV("queueSize", m_reqQueue.size()) + << LOG_KV("peer", m_nodeId->shortHex()) + << LOG_KV("nodeId", m_config->nodeID()->shortHex()); +} + +DownloadRequest::Ptr DownloadRequestQueue::topAndPop() +{ + WriteGuard l(x_reqQueue); + if (m_reqQueue.empty()) + { + return nullptr; + } + // "Tops" means that the merge result of all tops can merge at one turn + // Example: + // top[x] (fromNumber, size) range merged range merged tops(fromNumber, size) + // top[0] (1, 3) [1, 4) [1, 4) (1, 3) + // top[1] (1, 4) [1, 5) [1, 5) (1, 4) + // top[2] (2, 1) [2, 3) [1, 5) (1, 4) + // top[3] (2, 4) [2, 6) [1, 6) (1, 5) + // top[4] (6, 2) [6, 8) [1, 8) (1, 7) + // top[5] (10, 2) [10, 12] can not merge into (1, 7) leave it for next turn + size_t fromNumber = m_reqQueue.top()->fromNumber(); + size_t size = 0; + while (!m_reqQueue.empty() && (fromNumber + size) >= (size_t)(m_reqQueue.top()->fromNumber())) + { + auto topReq = m_reqQueue.top(); + // m_queue is increasing by fromNumber, so fromNumber must no more than + // merged tops + size = std::max(size, (size_t)(topReq->fromNumber() + topReq->size() - fromNumber)); + m_reqQueue.pop(); + } + BLKSYNC_LOG(TRACE) << LOG_BADGE("Download") << LOG_BADGE("Request") + << LOG_DESC("Pop reqQueue top req") << LOG_KV("from", fromNumber) + << LOG_KV("to", fromNumber + size - 1); + return std::make_shared(fromNumber, size); +} + +bool DownloadRequestQueue::empty() +{ + ReadGuard l(x_reqQueue); + return m_reqQueue.empty(); +} diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/state/DownloadRequestQueue.h" "b/BFPL\345\243\271/bcos-sync/bcos-sync/state/DownloadRequestQueue.h" new file mode 100644 index 00000000..e34ea50a --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/state/DownloadRequestQueue.h" @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2021 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. + * + * @brief queue to maintain the download request + * @file DownloadRequestQueue.h + * @author: jimmyshi + * @date 2021-05-24 + */ +#pragma once +#include "bcos-sync/BlockSyncConfig.h" +namespace bcos +{ +namespace sync +{ +class DownloadRequest +{ +public: + using Ptr = std::shared_ptr; + DownloadRequest(bcos::protocol::BlockNumber _fromNumber, size_t _size) + : m_fromNumber(_fromNumber), m_size(_size) + {} + + bcos::protocol::BlockNumber fromNumber() { return m_fromNumber; } + size_t size() { return m_size; } + +private: + bcos::protocol::BlockNumber m_fromNumber; + size_t m_size; +}; + +struct DownloadRequestCmp +{ + bool operator()(DownloadRequest::Ptr const& _first, DownloadRequest::Ptr const& _second) + { + if (_first->fromNumber() == _second->fromNumber()) + { + return _first->size() < _second->size(); + } + return _first->fromNumber() > _second->fromNumber(); + } +}; + +class DownloadRequestQueue +{ +public: + using Ptr = std::shared_ptr; + explicit DownloadRequestQueue(BlockSyncConfig::Ptr _config, bcos::crypto::NodeIDPtr _nodeId) + : m_config(_config), m_nodeId(_nodeId) + {} + virtual ~DownloadRequestQueue() {} + + virtual void push(bcos::protocol::BlockNumber _fromNumber, size_t _size); + virtual DownloadRequest::Ptr topAndPop(); // Must call use disablePush() before + virtual bool empty(); + +private: + BlockSyncConfig::Ptr m_config; + bcos::crypto::NodeIDPtr m_nodeId; + using RequestQueue = std::priority_queue, DownloadRequestCmp>; + RequestQueue m_reqQueue; + mutable SharedMutex x_reqQueue; +}; +} // namespace sync +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/state/DownloadingQueue.cpp" "b/BFPL\345\243\271/bcos-sync/bcos-sync/state/DownloadingQueue.cpp" new file mode 100644 index 00000000..e75cef80 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/state/DownloadingQueue.cpp" @@ -0,0 +1,762 @@ +/** + * Copyright (C) 2021 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. + * + * @brief queue to store the downloading blocks + * @file DownloadingQueue.cpp + * @author: jimmyshi + * @date 2021-05-24 + */ +#include "DownloadingQueue.h" +#include "bcos-sync/utilities/Common.h" +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::protocol; +using namespace bcos::sync; +using namespace bcos::ledger; + +void DownloadingQueue::push(BlocksMsgInterface::Ptr _blocksData) +{ + // push to the blockBuffer firstly + UpgradableGuard l(x_blockBuffer); + if (m_blockBuffer->size() >= m_config->maxDownloadingBlockQueueSize()) + { + BLKSYNC_LOG(WARNING) << LOG_BADGE("Download") << LOG_BADGE("BlockSync") + << LOG_DESC("DownloadingBlockQueueBuffer is full") + << LOG_KV("queueSize", m_blockBuffer->size()); + return; + } + UpgradeGuard ul(l); + m_blockBuffer->emplace_back(_blocksData); +} + +bool DownloadingQueue::empty() +{ + ReadGuard l1(x_blockBuffer); + ReadGuard l2(x_blocks); + return (m_blocks.empty() && (!m_blockBuffer || m_blockBuffer->empty())); +} + +size_t DownloadingQueue::size() +{ + ReadGuard l1(x_blockBuffer); + ReadGuard l2(x_blocks); + size_t s = (!m_blockBuffer ? 0 : m_blockBuffer->size()) + m_blocks.size(); + return s; +} + +void DownloadingQueue::pop() +{ + WriteGuard l(x_blocks); + if (!m_blocks.empty()) + { + m_blocks.pop(); + } +} + +Block::Ptr DownloadingQueue::top(bool isFlushBuffer) +{ + if (isFlushBuffer) + { + flushBufferToQueue(); + } + ReadGuard l(x_blocks); + if (!m_blocks.empty()) + { + return m_blocks.top(); + } + return nullptr; +} + +void DownloadingQueue::clear() +{ + { + WriteGuard l(x_blockBuffer); + m_blockBuffer->clear(); + } + clearQueue(); +} + +void DownloadingQueue::clearQueue() +{ + WriteGuard l(x_blocks); + BlockQueue emptyQueue; + swap(m_blocks, emptyQueue); // Does memory leak here ? +} + +void DownloadingQueue::flushBufferToQueue() +{ + WriteGuard l(x_blockBuffer); + bool ret = true; + while (m_blockBuffer->size() > 0 && ret) + { + auto blocksShard = m_blockBuffer->front(); + m_blockBuffer->pop_front(); + ret = flushOneShard(blocksShard); + } +} + +bool DownloadingQueue::flushOneShard(BlocksMsgInterface::Ptr _blocksData) +{ + // pop buffer into queue + WriteGuard l(x_blocks); + if (m_blocks.size() >= m_config->maxDownloadingBlockQueueSize()) + { + BLKSYNC_LOG(DEBUG) << LOG_BADGE("Download") << LOG_BADGE("BlockSync") + << LOG_DESC("DownloadingBlockQueueBuffer is full") + << LOG_KV("queueSize", m_blocks.size()); + + return false; + } + BLKSYNC_LOG(TRACE) << LOG_BADGE("Download") << LOG_BADGE("BlockSync") + << LOG_DESC("Decoding block buffer") + << LOG_KV("blocksShardSize", _blocksData->blocksSize()); + size_t blocksSize = _blocksData->blocksSize(); + for (size_t i = 0; i < blocksSize; i++) + { + try + { + auto block = + m_config->blockFactory()->createBlock(_blocksData->blockData(i), true, true); + auto blockHeader = block->blockHeader(); + if (isNewerBlock(block)) + { + m_blocks.push(block); + BLKSYNC_LOG(DEBUG) << LOG_BADGE("Download") << LOG_BADGE("BlockSync") + << LOG_DESC("Flush block to the queue") + << LOG_KV("number", blockHeader->number()) + << LOG_KV("nodeId", m_config->nodeID()->shortHex()); + } + } + catch (std::exception const& e) + { + BLKSYNC_LOG(WARNING) << LOG_BADGE("Download") << LOG_BADGE("BlockSync") + << LOG_DESC("Invalid block data") + << LOG_KV("reason", boost::diagnostic_information(e)) + << LOG_KV("blockDataSize", _blocksData->blockData(i).size()); + continue; + } + } + if (m_blocks.size() == 0) + { + return true; + } + BLKSYNC_LOG(DEBUG) << LOG_BADGE("Download") << LOG_BADGE("BlockSync") + << LOG_DESC("Flush buffer to block queue") << LOG_KV("rcv", blocksSize) + << LOG_KV("top", m_blocks.top()->blockHeader()->number()) + << LOG_KV("downloadBlockQueue", m_blocks.size()) + << LOG_KV("nodeId", m_config->nodeID()->shortHex()); + return true; +} + +bool DownloadingQueue::isNewerBlock(Block::Ptr _block) +{ + // Note: must holder blockHeader here to ensure the life cycle of blockHeader + auto blockHeader = _block->blockHeader(); + if (blockHeader->number() <= m_config->blockNumber()) + { + return false; + } + return true; +} + +void DownloadingQueue::clearFullQueueIfNotHas(BlockNumber _blockNumber) +{ + bool needClear = false; + { + ReadGuard l(x_blocks); + if (m_blocks.size() == m_config->maxDownloadingBlockQueueSize() && + m_blocks.top()->blockHeader()->number() > _blockNumber) + { + needClear = true; + } + } + if (needClear) + { + clearQueue(); + } +} + +bool DownloadingQueue::verifyExecutedBlock( + bcos::protocol::Block::Ptr _block, bcos::protocol::BlockHeader::Ptr _blockHeader) +{ + // check blockHash(Note: since the ledger check the parentHash before commit, here no need to + // check the parentHash) + auto orgBlockHeader = _block->blockHeader(); + if (orgBlockHeader->hash() != _blockHeader->hash()) + { + BLKSYNC_LOG(ERROR) << LOG_DESC("verifyExecutedBlock failed for inconsistent hash") + << LOG_KV("orgHeader", printBlockHeader(orgBlockHeader)) << "\n" + << LOG_KV("executedHeader", printBlockHeader(_blockHeader)); + + return false; + } + return true; +} + +std::string DownloadingQueue::printBlockHeader(BlockHeader::Ptr _header) +{ + std::stringstream oss; + std::stringstream sealerListStr; + std::stringstream signatureListStr; + std::stringstream weightsStr; + + sealerListStr << "size: " << _header->sealerList().size(); + signatureListStr << "size: " << _header->signatureList().size(); + if (c_fileLogLevel <= TRACE) + { + auto sealerList = _header->sealerList(); + sealerListStr << ", sealer list: "; + for (auto const& sealer : sealerList) + { + sealerListStr << *toHexString(sealer) << ", "; + } + auto signatureList = _header->signatureList(); + signatureListStr << ", sign list: "; + for (auto const& signatureData : signatureList) + { + signatureListStr << (*toHexString(signatureData.signature)) << ":" + << signatureData.index << ", "; + } + } + auto weightList = _header->consensusWeights(); + for (auto const& weight : weightList) + { + weightsStr << weight << ", "; + } + + auto parentInfo = _header->parentInfo(); + std::stringstream parentInfoStr; + for (auto const& parent : parentInfo) + { + parentInfoStr << parent.blockNumber << ":" << parent.blockHash << ", "; + } + oss << LOG_KV("hash", _header->hash()) << LOG_KV("version", _header->version()) + << LOG_KV("txsRoot", _header->txsRoot()) << LOG_KV("receiptsRoot", _header->receiptsRoot()) + << LOG_KV("dbHash", _header->stateRoot()) << LOG_KV("number", _header->number()) + << LOG_KV("gasUsed", _header->gasUsed()) << LOG_KV("timestamp", _header->timestamp()) + << LOG_KV("sealer", _header->sealer()) << LOG_KV("sealerList", sealerListStr.str()) + << LOG_KV("signatureList", signatureListStr.str()) + << LOG_KV("consensusWeights", weightsStr.str()) << LOG_KV("parents", parentInfoStr.str()) + << LOG_KV("extraData", *toHexString(_header->extraData())); + return oss.str(); +} + + +void DownloadingQueue::applyBlock(Block::Ptr _block) +{ + auto blockHeader = _block->blockHeader(); + // check the block number + if (blockHeader->number() <= m_config->blockNumber()) + { + BLKSYNC_LOG(WARNING) << LOG_BADGE("Download") + << LOG_BADGE("BlockSync: checkBlock before apply") + << LOG_DESC("Ignore illegal block") + << LOG_KV("reason", "number illegal") + << LOG_KV("thisNumber", blockHeader->number()) + << LOG_KV("currentNumber", m_config->blockNumber()); + m_config->setExecutedBlock(m_config->blockNumber()); + return; + } + if (blockHeader->number() <= m_config->executedBlock()) + { + return; + } + auto startT = utcTime(); + auto self = weak_from_this(); + m_config->scheduler()->executeBlock(_block, true, + [self, startT, _block]( + Error::Ptr&& _error, protocol::BlockHeader::Ptr&& _blockHeader, bool _sysBlock) { + auto orgBlockHeader = _block->blockHeader(); + try + { + auto downloadQueue = self.lock(); + if (!downloadQueue) + { + return; + } + auto config = downloadQueue->m_config; + // execute/verify exception + if (_error != nullptr) + { + // reset the executed number + BLKSYNC_LOG(WARNING) + << LOG_DESC("applyBlock: executing the downloaded block failed") + << LOG_KV("number", orgBlockHeader->number()) + << LOG_KV("hash", orgBlockHeader->hash().abridged()) + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMessage", _error->errorMessage()); + if (_error->errorCode() == bcos::scheduler::SchedulerError::InvalidBlocks) + { + BLKSYNC_LOG(INFO) + << LOG_DESC("fetchAndUpdateLedgerConfig for InvalidBlocks"); + downloadQueue->fetchAndUpdateLedgerConfig(); + return; + } + if (!config->masterNode()) + { + BLKSYNC_LOG(INFO) << LOG_DESC( + "applyBlock error: but do nothing for the node is not the master node"); + return; + } + { + // re-push the block into blockQueue to retry later + if (_block->blockHeader()->number() > config->blockNumber()) + { + BLKSYNC_LOG(INFO) + << LOG_DESC( + "applyBlock: executing the downloaded block failed, re-push " + "the block into executing queue") + << LOG_KV("number", orgBlockHeader->number()) + << LOG_KV("hash", orgBlockHeader->hash().abridged()); + WriteGuard l(downloadQueue->x_blocks); + downloadQueue->m_blocks.push(_block); + } + } + config->setExecutedBlock(config->blockNumber()); + return; + } + if (!downloadQueue->verifyExecutedBlock(_block, _blockHeader)) + { + config->setExecutedBlock(config->blockNumber()); + return; + } + auto executedBlock = config->executedBlock(); + if (orgBlockHeader->number() > executedBlock + 1) + { + { + WriteGuard lock(downloadQueue->x_blocks); + downloadQueue->m_blocks.push(_block); + } + BLKSYNC_LOG(WARNING) + << LOG_BADGE("Download") + << LOG_DESC("BlockSync: re-push the appliedBlock for discontinuous") + << LOG_KV("executedBlock", executedBlock) + << LOG_KV("nextBlock", downloadQueue->m_config->nextBlock()) + << LOG_KV("number", orgBlockHeader->number()) + << LOG_KV("hash", orgBlockHeader->hash().abridged()); + return; + } + // Note: continue to execute the next block only after sysBlock is submitted + if (!_sysBlock) + { + config->setExecutedBlock(orgBlockHeader->number()); + } + auto signature = orgBlockHeader->signatureList(); + BLKSYNC_LOG(INFO) << METRIC << LOG_BADGE("Download") + << LOG_DESC("BlockSync: applyBlock success") + << LOG_KV("number", orgBlockHeader->number()) + << LOG_KV("hash", orgBlockHeader->hash().abridged()) + << LOG_KV("signatureSize", signature.size()) + << LOG_KV("txsSize", _block->transactionsSize()) + << LOG_KV("nextBlock", downloadQueue->m_config->nextBlock()) + << LOG_KV( + "executedBlock", downloadQueue->m_config->executedBlock()) + << LOG_KV("timeCost", (utcTime() - startT)) + << LOG_KV("node", downloadQueue->m_config->nodeID()->shortHex()) + << LOG_KV("sysBlock", _sysBlock); + // verify and commit the block + downloadQueue->updateCommitQueue(_block); + } + catch (std::exception const& e) + { + BLKSYNC_LOG(WARNING) << LOG_DESC("applyBlock exception") + << LOG_KV("number", orgBlockHeader->number()) + << LOG_KV("hash", orgBlockHeader->hash().abridged()) + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +bool DownloadingQueue::checkAndCommitBlock(bcos::protocol::Block::Ptr _block) +{ + auto blockHeader = _block->blockHeader(); + // check the block number + if (blockHeader->number() != m_config->nextBlock()) + { + BLKSYNC_LOG(WARNING) << LOG_BADGE("Download") << LOG_BADGE("BlockSync: checkBlock") + << LOG_DESC("Ignore illegal block") + << LOG_KV("reason", "number illegal") + << LOG_KV("thisNumber", blockHeader->number()) + << LOG_KV("currentNumber", m_config->blockNumber()); + m_config->setExecutedBlock(m_config->blockNumber()); + return false; + } + auto signature = blockHeader->signatureList(); + BLKSYNC_LOG(INFO) << LOG_BADGE("Download") << LOG_BADGE("checkAndCommitBlock") + << LOG_KV("number", blockHeader->number()) + << LOG_KV("signatureSize", signature.size()) + << LOG_KV("currentNumber", m_config->blockNumber()) + << LOG_KV("hash", blockHeader->hash().abridged()); + + auto self = weak_from_this(); + m_config->consensus()->asyncCheckBlock(_block, [self, _block, blockHeader]( + Error::Ptr _error, bool _ret) { + try + { + auto downloadQueue = self.lock(); + if (!downloadQueue) + { + return; + } + if (_error) + { + BLKSYNC_LOG(WARNING) + << LOG_DESC("asyncCheckBlock error") + << LOG_KV("blockNumber", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("code", _error->errorCode()) << LOG_KV("msg", _error->errorMessage()); + downloadQueue->m_config->setExecutedBlock(blockHeader->number() - 1); + return; + } + if (_ret) + { + BLKSYNC_LOG(INFO) << LOG_DESC("asyncCheckBlock success, try to commit the block") + << LOG_KV("blockNumber", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()); + downloadQueue->commitBlock(_block); + return; + } + downloadQueue->m_config->setExecutedBlock(blockHeader->number() - 1); + BLKSYNC_LOG(WARNING) << LOG_DESC("asyncCheckBlock failed") + << LOG_KV("blockNumber", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()); + } + catch (std::exception const& e) + { + BLKSYNC_LOG(WARNING) << LOG_DESC("asyncCheckBlock exception") + << LOG_KV("blockNumber", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); + return true; +} + +void DownloadingQueue::updateCommitQueue(Block::Ptr _block) +{ + { + WriteGuard l(x_commitQueue); + m_commitQueue.push(_block); + } + tryToCommitBlockToLedger(); +} + +void DownloadingQueue::tryToCommitBlockToLedger() +{ + WriteGuard l(x_commitQueue); + if (m_commitQueue.empty()) + { + return; + } + // remove expired block + while (!m_commitQueue.empty() && + m_commitQueue.top()->blockHeader()->number() <= m_config->blockNumber()) + { + m_commitQueue.pop(); + } + // try to commit the block + if (!m_commitQueue.empty() && + m_commitQueue.top()->blockHeader()->number() == m_config->nextBlock()) + { + auto block = m_commitQueue.top(); + m_commitQueue.pop(); + checkAndCommitBlock(block); + } +} + + +void DownloadingQueue::commitBlock(bcos::protocol::Block::Ptr _block) +{ + auto blockHeader = _block->blockHeader(); + BLKSYNC_LOG(INFO) << LOG_DESC("commitBlock") << LOG_KV("number", blockHeader->number()) + << LOG_KV("txsNum", _block->transactionsSize()) + << LOG_KV("hash", blockHeader->hash().abridged()); + // empty block + if (_block->transactionsSize() == 0) + { + BLKSYNC_LOG(INFO) << LOG_DESC("commitBlock: receive empty block, commitBlockState directly") + << LOG_KV("number", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()); + commitBlockState(_block); + return; + } + // commit transaction firstly + auto txsData = std::make_shared>(); + auto txsSize = _block->transactionsSize(); + auto txsHashList = std::make_shared(); + + txsData->resize(txsSize); + txsHashList->resize(txsSize); + tbb::parallel_for( + tbb::blocked_range(0, txsSize), [&](const tbb::blocked_range& range) { + for (size_t i = range.begin(); i < range.end(); ++i) + { + // maintain lifetime for tx + auto tx = _block->transaction(i); + bcos::bytes encodeData; + tx->encode(encodeData); + (*txsData)[i] = std::make_shared(std::move(encodeData)); + (*txsHashList)[i] = tx->hash(); + } + }); + auto startT = utcTime(); + auto self = weak_from_this(); + m_config->ledger()->asyncStoreTransactions( + txsData, txsHashList, [self, startT, _block, blockHeader](Error::Ptr _error) { + try + { + auto downloadingQueue = self.lock(); + if (!downloadingQueue) + { + return; + } + // store transaction failed + if (_error) + { + BLKSYNC_LOG(WARNING) << LOG_DESC("commitBlock: store transactions failed") + << LOG_KV("number", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("txsSize", _block->transactionsSize()) + << LOG_KV("code", _error->errorCode()) + << LOG_KV("message", _error->errorMessage()); + downloadingQueue->onCommitFailed(_error, _block); + return; + } + BLKSYNC_LOG(INFO) << METRIC << LOG_DESC("commitBlock: store transactions success") + << LOG_KV("number", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("txsSize", _block->transactionsSize()) + << LOG_KV("storeTxsTimeCost", (utcTime() - startT)); + downloadingQueue->commitBlockState(_block); + } + catch (std::exception const& e) + { + BLKSYNC_LOG(WARNING) << LOG_DESC("commitBlock exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +void DownloadingQueue::commitBlockState(bcos::protocol::Block::Ptr _block) +{ + auto blockHeader = _block->blockHeader(); + BLKSYNC_LOG(INFO) << LOG_DESC("commitBlockState") << LOG_KV("number", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()); + auto startT = utcTime(); + auto self = weak_from_this(); + m_config->scheduler()->commitBlock(blockHeader, [self, startT, _block, blockHeader]( + Error::Ptr&& _error, + LedgerConfig::Ptr&& _ledgerConfig) { + try + { + auto downloadingQueue = self.lock(); + if (!downloadingQueue) + { + return; + } + if (_error != nullptr) + { + BLKSYNC_LOG(WARNING) + << LOG_DESC("commitBlockState failed") + << LOG_KV("executedBlock", downloadingQueue->m_config->executedBlock()) + << LOG_KV("number", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("code", _error->errorCode()) + << LOG_KV("message", _error->errorMessage()); + downloadingQueue->onCommitFailed(_error, _block); + return; + } + _ledgerConfig->setTxsSize(_block->transactionsSize()); + _ledgerConfig->setSealerId(blockHeader->sealer()); + // reset the blockNumber + _ledgerConfig->setBlockNumber(blockHeader->number()); + _ledgerConfig->setHash(blockHeader->hash()); + // notify the txpool the transaction result + // reset the config for the consensus and the blockSync module + // broadcast the status to all the peers + // clear the expired cache + downloadingQueue->finalizeBlock(_block, _ledgerConfig); + auto executedBlock = downloadingQueue->m_config->executedBlock(); + if (executedBlock < blockHeader->number()) + { + downloadingQueue->m_config->setExecutedBlock(blockHeader->number()); + } + BLKSYNC_LOG(INFO) << METRIC << LOG_DESC("commitBlockState success") + << LOG_KV("number", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV( + "executedBlock", downloadingQueue->m_config->executedBlock()) + << LOG_KV("commitBlockTimeCost", (utcTime() - startT)) + << LOG_KV("node", downloadingQueue->m_config->nodeID()->shortHex()) + << LOG_KV("txsSize", _block->transactionsSize()) + << LOG_KV("sealer", blockHeader->sealer()); + } + catch (std::exception const& e) + { + BLKSYNC_LOG(WARNING) << LOG_DESC("commitBlock exception") + << LOG_KV("number", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + + +void DownloadingQueue::finalizeBlock(bcos::protocol::Block::Ptr, LedgerConfig::Ptr _ledgerConfig) +{ + if (m_newBlockHandler) + { + m_newBlockHandler(_ledgerConfig); + } + // try to commit the next block + tryToCommitBlockToLedger(); +} + +void DownloadingQueue::clearExpiredQueueCache() +{ + clearExpiredCache(m_blocks, x_blocks); + clearExpiredCache(m_commitQueue, x_commitQueue); +} + +void DownloadingQueue::clearExpiredCache(BlockQueue& _queue, SharedMutex& _lock) +{ + WriteGuard l(_lock); + while (!_queue.empty() && _queue.top()->blockHeader()->number() <= m_config->blockNumber()) + { + _queue.pop(); + } +} + +void DownloadingQueue::onCommitFailed( + bcos::Error::Ptr _error, bcos::protocol::Block::Ptr _failedBlock) +{ + auto blockHeader = _failedBlock->blockHeader(); + // case invalidBlocks + if (_error->errorCode() == bcos::scheduler::SchedulerError::InvalidBlocks) + { + BLKSYNC_LOG(WARNING) << LOG_DESC( + "onCommitFailed: the block has already been committed, return " + "directly") + << LOG_KV("executedBlock", m_config->executedBlock()) + << LOG_KV("number", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("code", _error->errorCode()) + << LOG_KV("message", _error->errorMessage()); + fetchAndUpdateLedgerConfig(); + return; + } + if (blockHeader->number() <= m_config->blockNumber()) + { + BLKSYNC_LOG(INFO) << LOG_DESC("onCommitFailed: drop the expired block") + << LOG_KV("number", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("executedBlock", m_config->executedBlock()); + return; + } + BLKSYNC_LOG(INFO) << LOG_DESC("onCommitFailed") << LOG_KV("number", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("executedBlock", m_config->executedBlock()); + + // re-push failedBlock to commitQueue + { + WriteGuard l(x_commitQueue); + m_commitQueue.push(_failedBlock); + } + if (_error->errorCode() == bcos::scheduler::SchedulerError::BlockIsCommitting) + { + BLKSYNC_LOG(INFO) << LOG_DESC( + "onCommitFailed for BlockIsCommitting: re-push failed " + "block to commitQueue") + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("executedBlock", m_config->executedBlock()); + // retry after 20ms + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + tryToCommitBlockToLedger(); + return; + } + // fetchAndUpdateLedgerConfig in case of the blocks commit success while get-system-config + // failed + fetchAndUpdateLedgerConfig(); + m_config->setExecutedBlock(blockHeader->number() - 1); + auto topBlock = top(); + bcos::protocol::BlockNumber topNumber = std::numeric_limits::max(); + if (topBlock) + { + topNumber = topBlock->blockHeader()->number(); + } + size_t rePushedBlockCount = 0; + { + // re-push un-committed block into m_blocks + // Note: this operation is low performance and low frequency + WriteGuard l(x_commitQueue); + WriteGuard lock(x_blocks); + if (m_commitQueue.empty()) + { + return; + } + // Note: since the applied block will be re-pushed into m_commitQueue again, no-need to + // write-back the poped block into commitQueue here + while (!m_commitQueue.empty()) + { + auto topBlock = m_commitQueue.top(); + if (topBlock->blockHeader()->number() >= topNumber) + { + break; + } + rePushedBlockCount++; + m_blocks.push(topBlock); + m_commitQueue.pop(); + } + } + auto blocksTop = m_blocks.top(); + BLKSYNC_LOG(INFO) << LOG_DESC("onCommitFailed: update commitQueue and executingQueue") + << LOG_KV("commitQueueSize", m_commitQueue.size()) + << LOG_KV("blocksQueueSize", m_blocks.size()) + << LOG_KV("topNumber", topNumber) + << LOG_KV("topBlock", blocksTop ? (blocksTop->blockHeader()->number()) : -1) + << LOG_KV("rePushedBlockCount", rePushedBlockCount) + << LOG_KV("executedBlock", m_config->executedBlock()); +} + +void DownloadingQueue::fetchAndUpdateLedgerConfig() +{ + try + { + BLKSYNC_LOG(INFO) << LOG_DESC("fetchAndUpdateLedgerConfig"); + m_ledgerFetcher->fetchBlockNumberAndHash(); + m_ledgerFetcher->fetchConsensusNodeList(); + // Note: must fetchObserverNode here to notify the latest sealerList and observerList to + // txpool + m_ledgerFetcher->fetchObserverNodeList(); + m_ledgerFetcher->fetchBlockTxCountLimit(); + m_ledgerFetcher->fetchConsensusLeaderPeriod(); + m_ledgerFetcher->fetchCompatibilityVersion(); + auto ledgerConfig = m_ledgerFetcher->ledgerConfig(); + BLKSYNC_LOG(INFO) << LOG_DESC("fetchAndUpdateLedgerConfig success") + << LOG_KV("blockNumber", ledgerConfig->blockNumber()) + << LOG_KV("hash", ledgerConfig->hash().abridged()) + << LOG_KV("maxTxsPerBlock", ledgerConfig->blockTxCountLimit()) + << LOG_KV("consensusNodeList", ledgerConfig->consensusNodeList().size()); + m_config->resetConfig(ledgerConfig); + } + catch (std::exception const& e) + { + BLKSYNC_LOG(WARNING) << LOG_DESC("fetchAndUpdateLedgerConfig exception") + << LOG_KV("msg", boost::diagnostic_information(e)); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/state/DownloadingQueue.h" "b/BFPL\345\243\271/bcos-sync/bcos-sync/state/DownloadingQueue.h" new file mode 100644 index 00000000..8b9d97d2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/state/DownloadingQueue.h" @@ -0,0 +1,133 @@ +/** + * Copyright (C) 2021 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. + * + * @brief queue to store the downloading blocks + * @file DownloadingQueue.h + * @author: jimmyshi + * @date 2021-05-24 + */ +#pragma once +#include "bcos-sync/BlockSyncConfig.h" +#include "bcos-sync/interfaces/BlocksMsgInterface.h" +#include +#include +#include +namespace bcos +{ +namespace sync +{ +// increase order +struct BlockCmp +{ + bool operator()( + bcos::protocol::Block::Ptr const& _first, bcos::protocol::Block::Ptr const& _second) const + { + // increase order + return _first->blockHeader()->number() > _second->blockHeader()->number(); + } +}; +using BlockQueue = + std::priority_queue; +class DownloadingQueue : public std::enable_shared_from_this +{ +public: + using BlocksMessageQueue = std::list; + using BlocksMessageQueuePtr = std::shared_ptr; + + using Ptr = std::shared_ptr; + explicit DownloadingQueue(BlockSyncConfig::Ptr _config) + : m_config(_config), m_blockBuffer(std::make_shared()) + { + m_ledgerFetcher = std::make_shared(m_config->ledger()); + } + virtual ~DownloadingQueue() {} + + virtual void push(BlocksMsgInterface::Ptr _blocksData); + // Is the queue empty? + virtual bool empty(); + + // get the total size of th block queue + virtual size_t size(); + + // pop the top unit of the block queue + virtual void pop(); + + // get the top unit of the block queue + bcos::protocol::Block::Ptr top(bool isFlushBuffer = false); + + virtual void clearFullQueueIfNotHas(bcos::protocol::BlockNumber _blockNumber); + + virtual void applyBlock(bcos::protocol::Block::Ptr _block); + // clear queue and buffer + virtual void clear(); + + virtual void registerNewBlockHandler( + std::function _newBlockHandler) + { + m_newBlockHandler = _newBlockHandler; + } + + // flush m_buffer into queue + virtual void flushBufferToQueue(); + virtual void clearExpiredQueueCache(); + virtual void tryToCommitBlockToLedger(); + virtual size_t commitQueueSize() + { + ReadGuard l(x_commitQueue); + return m_commitQueue.size(); + } + + virtual void onCommitFailed(bcos::Error::Ptr _error, bcos::protocol::Block::Ptr _failedBlock); + +protected: + // clear queue + virtual void clearQueue(); + virtual void clearExpiredCache(BlockQueue& _queue, SharedMutex& _lock); + virtual bool flushOneShard(BlocksMsgInterface::Ptr _blocksData); + virtual bool isNewerBlock(bcos::protocol::Block::Ptr _block); + + virtual void commitBlock(bcos::protocol::Block::Ptr _block); + virtual void commitBlockState(bcos::protocol::Block::Ptr _block); + + virtual bool checkAndCommitBlock(bcos::protocol::Block::Ptr _block); + virtual void updateCommitQueue(bcos::protocol::Block::Ptr _block); + + virtual void finalizeBlock( + bcos::protocol::Block::Ptr _block, bcos::ledger::LedgerConfig::Ptr _ledgerConfig); + virtual bool verifyExecutedBlock( + bcos::protocol::Block::Ptr _block, bcos::protocol::BlockHeader::Ptr _blockHeader); + +private: + // Note: this function should not be called frequently + std::string printBlockHeader(bcos::protocol::BlockHeader::Ptr _header); + void fetchAndUpdateLedgerConfig(); + +private: + BlockSyncConfig::Ptr m_config; + BlockQueue m_blocks; + mutable SharedMutex x_blocks; + + BlocksMessageQueuePtr m_blockBuffer; + mutable SharedMutex x_blockBuffer; + + BlockQueue m_commitQueue; + mutable SharedMutex x_commitQueue; + + std::function m_newBlockHandler; + + std::shared_ptr m_ledgerFetcher; +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/state/SyncPeerStatus.cpp" "b/BFPL\345\243\271/bcos-sync/bcos-sync/state/SyncPeerStatus.cpp" new file mode 100644 index 00000000..db9c081a --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/state/SyncPeerStatus.cpp" @@ -0,0 +1,220 @@ +/** + * Copyright (C) 2021 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. + * + * @brief matain the sync status + * @file SyncPeerStatus.cpp + * @author: jimmyshi + * @date 2021-05-24 + */ +#include "SyncPeerStatus.h" + +using namespace bcos; +using namespace bcos::sync; +using namespace bcos::crypto; +using namespace bcos::protocol; + +PeerStatus::PeerStatus(BlockSyncConfig::Ptr _config, PublicPtr _nodeId, BlockNumber _number, + HashType const& _hash, HashType const& _gensisHash) + : m_nodeId(_nodeId), + m_number(_number), + m_hash(_hash), + m_genesisHash(_gensisHash), + m_downloadRequests(std::make_shared(_config, m_nodeId)) +{} + +PeerStatus::PeerStatus(BlockSyncConfig::Ptr _config, PublicPtr _nodeId) + : PeerStatus(_config, _nodeId, 0, HashType(), HashType()) +{} + +PeerStatus::PeerStatus( + BlockSyncConfig::Ptr _config, PublicPtr _nodeId, BlockSyncStatusInterface::ConstPtr _status) + : PeerStatus(_config, _nodeId, _status->number(), _status->hash(), _status->genesisHash()) +{} + +bool PeerStatus::update(BlockSyncStatusInterface::ConstPtr _status) +{ + UpgradableGuard l(x_mutex); + if (m_hash == _status->hash() && _status->number() == m_number) + { + return false; + } + if (m_genesisHash != HashType() && _status->genesisHash() != m_genesisHash) + { + BLKSYNC_LOG(WARNING) << LOG_BADGE("Status") + << LOG_DESC( + "Receive invalid status packet with different genesis hash") + << LOG_KV("peer", m_nodeId->shortHex()) + << LOG_KV("genesisHash", _status->genesisHash().abridged()) + << LOG_KV("storedGenesisHash", m_genesisHash.abridged()); + return false; + } + UpgradeGuard ul(l); + m_number = _status->number(); + m_hash = _status->hash(); + if (m_genesisHash == HashType()) + { + m_genesisHash = _status->genesisHash(); + } + BLKSYNC_LOG(DEBUG) << LOG_DESC("updatePeerStatus") << LOG_KV("peer", m_nodeId->shortHex()) + << LOG_KV("number", _status->number()) + << LOG_KV("hash", _status->hash().abridged()) + << LOG_KV("genesisHash", _status->genesisHash().abridged()); + return true; +} + +bool SyncPeerStatus::hasPeer(PublicPtr _peer) +{ + ReadGuard l(x_peersStatus); + return m_peersStatus.count(_peer); +} + +PeerStatus::Ptr SyncPeerStatus::peerStatus(bcos::crypto::PublicPtr _peer) +{ + ReadGuard l(x_peersStatus); + if (!m_peersStatus.count(_peer)) + { + return nullptr; + } + return m_peersStatus[_peer]; +} + +PeerStatus::Ptr SyncPeerStatus::insertEmptyPeer(PublicPtr _peer) +{ + WriteGuard l(x_peersStatus); + // create and insert the new peer status + auto peerStatus = std::make_shared(m_config, _peer); + m_peersStatus.insert(std::make_pair(_peer, peerStatus)); + return peerStatus; +} + +bool SyncPeerStatus::updatePeerStatus( + PublicPtr _peer, BlockSyncStatusInterface::ConstPtr _peerStatus) +{ + WriteGuard l(x_peersStatus); + // check the status + if (_peerStatus->genesisHash() != m_config->genesisHash()) + { + BLKSYNC_LOG(WARNING) << LOG_BADGE("updatePeerStatus") + << LOG_DESC( + "Receive invalid status packet with different genesis hash") + << LOG_KV("peer", _peer->shortHex()) + << LOG_KV("genesisHash", _peerStatus->genesisHash().abridged()) + << LOG_KV("expectedGenesisHash", m_config->genesisHash().abridged()); + return false; + } + // update the existed peer status + if (m_peersStatus.count(_peer)) + { + auto status = m_peersStatus[_peer]; + if (status->update(_peerStatus)) + { + updateKnownMaxBlockInfo(_peerStatus); + } + return true; + } + // create and insert the new peer status + auto peerStatus = std::make_shared(m_config, _peer, _peerStatus); + m_peersStatus.insert(std::make_pair(_peer, peerStatus)); + BLKSYNC_LOG(DEBUG) << LOG_DESC("updatePeerStatus: new peer") + << LOG_KV("peer", _peer->shortHex()) + << LOG_KV("number", _peerStatus->number()) + << LOG_KV("hash", _peerStatus->hash().abridged()) + << LOG_KV("genesisHash", _peerStatus->genesisHash().abridged()) + << LOG_KV("node", m_config->nodeID()->shortHex()); + updateKnownMaxBlockInfo(_peerStatus); + return true; +} + +void SyncPeerStatus::updateKnownMaxBlockInfo(BlockSyncStatusInterface::ConstPtr _peerStatus) +{ + if (_peerStatus->genesisHash() != m_config->genesisHash()) + { + return; + } + if (_peerStatus->number() <= m_config->knownHighestNumber()) + { + return; + } + m_config->setKnownHighestNumber(_peerStatus->number()); + m_config->setKnownLatestHash(_peerStatus->hash()); +} + +void SyncPeerStatus::deletePeer(PublicPtr _peer) +{ + WriteGuard l(x_peersStatus); + auto peer = m_peersStatus.find(_peer); + if (peer != m_peersStatus.end()) + { + m_peersStatus.erase(peer); + } +} + +void SyncPeerStatus::foreachPeerRandom(std::function const& _f) const +{ + ReadGuard l(x_peersStatus); + if (m_peersStatus.empty()) + { + return; + } + + // Get nodeid list + NodeIDs nodeIds; + for (auto& peer : m_peersStatus) + { + nodeIds.emplace_back(peer.first); + } + + // Random nodeid list + for (size_t i = nodeIds.size() - 1; i > 0; --i) + { + size_t select = rand() % (i + 1); + swap(nodeIds[i], nodeIds[select]); + } + + // access _f() according to the random list + for (auto nodeId : nodeIds) + { + auto const& peer = m_peersStatus.find(nodeId); + if (peer == m_peersStatus.end()) + { + continue; + } + if (peer->second && !_f(peer->second)) + { + break; + } + } +} + +void SyncPeerStatus::foreachPeer(std::function const& _f) const +{ + ReadGuard l(x_peersStatus); + for (auto peer : m_peersStatus) + { + if (peer.second && !_f(peer.second)) + { + break; + } + } +} + +std::shared_ptr SyncPeerStatus::peers() +{ + auto nodeIds = std::make_shared(); + ReadGuard l(x_peersStatus); + for (auto& peer : m_peersStatus) + nodeIds->emplace_back(peer.first); + return nodeIds; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/state/SyncPeerStatus.h" "b/BFPL\345\243\271/bcos-sync/bcos-sync/state/SyncPeerStatus.h" new file mode 100644 index 00000000..3717a8b7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/state/SyncPeerStatus.h" @@ -0,0 +1,106 @@ +/** + * Copyright (C) 2021 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. + * + * @brief matain the sync status + * @file SyncPeerStatus.h + * @author: jimmyshi + * @date 2021-05-24 + */ +#pragma once +#include "bcos-sync/BlockSyncConfig.h" +#include "bcos-sync/interfaces/BlockSyncStatusInterface.h" +#include "bcos-sync/state/DownloadRequestQueue.h" +#include "bcos-sync/utilities/Common.h" +namespace bcos +{ +namespace sync +{ +class PeerStatus +{ +public: + using Ptr = std::shared_ptr; + PeerStatus(BlockSyncConfig::Ptr _config, bcos::crypto::PublicPtr _nodeId, + bcos::protocol::BlockNumber _number, bcos::crypto::HashType const& _hash, + bcos::crypto::HashType const& _gensisHash); + PeerStatus(BlockSyncConfig::Ptr _config, bcos::crypto::PublicPtr _nodeId); + + PeerStatus(BlockSyncConfig::Ptr _config, bcos::crypto::PublicPtr _nodeId, + BlockSyncStatusInterface::ConstPtr _status); + + virtual ~PeerStatus() {} + + virtual bool update(BlockSyncStatusInterface::ConstPtr _status); + + bcos::crypto::PublicPtr nodeId() { return m_nodeId; } + + bcos::protocol::BlockNumber number() const + { + ReadGuard l(x_mutex); + return m_number; + } + + bcos::crypto::HashType const& hash() const + { + ReadGuard l(x_mutex); + return m_hash; + } + + bcos::crypto::HashType const& genesisHash() const + { + ReadGuard l(x_mutex); + return m_genesisHash; + } + + DownloadRequestQueue::Ptr downloadRequests() { return m_downloadRequests; } + +private: + bcos::crypto::PublicPtr m_nodeId; + bcos::protocol::BlockNumber m_number; + bcos::crypto::HashType m_hash; + bcos::crypto::HashType m_genesisHash; + + mutable SharedMutex x_mutex; + DownloadRequestQueue::Ptr m_downloadRequests; +}; + +class SyncPeerStatus +{ +public: + using Ptr = std::shared_ptr; + explicit SyncPeerStatus(BlockSyncConfig::Ptr _config) : m_config(_config) {} + virtual ~SyncPeerStatus() {} + + virtual bool hasPeer(bcos::crypto::PublicPtr _peer); + virtual PeerStatus::Ptr peerStatus(bcos::crypto::PublicPtr _peer); + virtual bool updatePeerStatus( + bcos::crypto::PublicPtr _peer, BlockSyncStatusInterface::ConstPtr _peerStatus); + virtual void deletePeer(bcos::crypto::PublicPtr _peer); + + void foreachPeerRandom(std::function const& _f) const; + void foreachPeer(std::function const& _f) const; + std::shared_ptr peers(); + PeerStatus::Ptr insertEmptyPeer(bcos::crypto::PublicPtr _peer); + +protected: + virtual void updateKnownMaxBlockInfo(BlockSyncStatusInterface::ConstPtr _peerStatus); + +private: + std::map m_peersStatus; + mutable SharedMutex x_peersStatus; + + BlockSyncConfig::Ptr m_config; +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/bcos-sync/utilities/Common.h" "b/BFPL\345\243\271/bcos-sync/bcos-sync/utilities/Common.h" new file mode 100644 index 00000000..fd3caa81 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/bcos-sync/utilities/Common.h" @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Common files for block sync + * @file Common.h + * @author: yujiechen + * @date 2021-05-23 + */ +#pragma once +#include +#include + +#define BLKSYNC_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("BLOCK SYNC") +namespace bcos +{ +namespace sync +{ +enum BlockSyncPacketType : int32_t +{ + BlockStatusPacket = 0x00, + BlockRequestPacket = 0x01, + BlockResponsePacket = 0x02, +}; +enum SyncState : int32_t +{ + Idle = 0x00, //< Initial chain sync complete. Waiting for new packets + Downloading = 0x01, //< Downloading blocks +}; +enum class BlockSyncMsgVersion +{ + v0, + v1 +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-sync/test/CMakeLists.txt" new file mode 100644 index 00000000..c28647b6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/test/CMakeLists.txt" @@ -0,0 +1,30 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of bcos-sync +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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(GLOB_RECURSE SOURCES "*.cpp" "*.h" "*.sol") + +# cmake settings +set(TEST_BINARY_NAME test-bcos-sync) +find_package(Protobuf REQUIRED) + +add_executable(${TEST_BINARY_NAME} ${SOURCES}) +target_include_directories(${TEST_BINARY_NAME} PRIVATE . ../src ${CMAKE_SOURCE_DIR}) + +find_package(Boost REQUIRED unit_test_framework) + +target_link_libraries(${TEST_BINARY_NAME} PUBLIC ${PBPROTOCOL_TARGET} ${SYNC_TARGET} ${CRYPTO_TARGET} ${TARS_PROTOCOL_TARGET} protobuf::libprotobuf Boost::unit_test_framework) +add_test(NAME test-sync WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) diff --git "a/BFPL\345\243\271/bcos-sync/test/unittests/faker/FakeConsensus.h" "b/BFPL\345\243\271/bcos-sync/test/unittests/faker/FakeConsensus.h" new file mode 100644 index 00000000..2495b3bb --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/test/unittests/faker/FakeConsensus.h" @@ -0,0 +1,94 @@ +/** + * Copyright (C) 2021 bcos-sync. + * 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. + * + * @brief consensus faker + * @file FakeConsensus.h + * @author: yujiechen + * @date 2021-06-08 + */ +#pragma once +#include +#include +#include +using namespace bcos; +using namespace bcos::consensus; +using namespace bcos::crypto; +using namespace bcos::protocol; +using namespace bcos::ledger; + +namespace bcos +{ +namespace test +{ +class FakeConsensus : public ConsensusInterface +{ +public: + using Ptr = std::shared_ptr; + FakeConsensus() { m_taskPool = std::make_shared("task", 1); } + ~FakeConsensus() override {} + + + void start() override {} + void stop() override {} + + // useless for bcos-sync + void asyncSubmitProposal( + bool, bytesConstRef, BlockNumber, HashType const&, std::function) override + {} + + // useless for bcos-sync + void asyncGetPBFTView(std::function) override {} + + // the sync module calls this interface to check block + void asyncCheckBlock(Block::Ptr, std::function _onVerifyFinish) override + { + m_taskPool->enqueue( + [_onVerifyFinish, this]() { _onVerifyFinish(nullptr, m_checkBlockResult); }); + } + + // the sync module calls this interface to notify new block + void asyncNotifyNewBlock( + LedgerConfig::Ptr _ledgerConfig, std::function _onRecv) override + { + m_ledgerConfig = _ledgerConfig; + m_taskPool->enqueue([_onRecv]() { _onRecv(nullptr); }); + } + + // useless for the sync module + void asyncNotifyConsensusMessage(bcos::Error::Ptr, std::string const&, NodeIDPtr, bytesConstRef, + std::function) override + {} + + bool checkBlockResult() const { return m_checkBlockResult; } + void setCheckBlockResult(bool _checkBlockResult) { m_checkBlockResult = _checkBlockResult; } + + LedgerConfig::Ptr ledgerConfig() { return m_ledgerConfig; } + + void notifyHighestSyncingNumber(bcos::protocol::BlockNumber) override {} + + void asyncNoteUnSealedTxsSize(uint64_t, std::function) override {} + + void asyncGetConsensusStatus(std::function) override {} + void notifyConnectedNodes( + bcos::crypto::NodeIDSet const&, std::function) override + {} + +private: + std::atomic_bool m_checkBlockResult = {true}; + LedgerConfig::Ptr m_ledgerConfig; + ThreadPool::Ptr m_taskPool; +}; +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-sync/test/unittests/main/main.cpp" "b/BFPL\345\243\271/bcos-sync/test/unittests/main/main.cpp" new file mode 100644 index 00000000..5029377e --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/test/unittests/main/main.cpp" @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 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 main.cpp + * @author: yujiechen, jimmyshi + * @date 2021-02-24 + */ +#define BOOST_TEST_MODULE FISCO_BCOS_Tests +#define BOOST_TEST_MAIN + +#include +#include diff --git "a/BFPL\345\243\271/bcos-sync/test/unittests/protocol/BlockSyncMsgTest.cpp" "b/BFPL\345\243\271/bcos-sync/test/unittests/protocol/BlockSyncMsgTest.cpp" new file mode 100644 index 00000000..332070f0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/test/unittests/protocol/BlockSyncMsgTest.cpp" @@ -0,0 +1,160 @@ +/** + * Copyright (C) 2021 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. + * + * @brief unit test for the BlockSyncMsg + * @file BlockSyncMsgTest.cpp + * @author: yujiechen + * @date 2021-06-08 + */ +#include "bcos-sync/protocol/PB/BlockSyncMsgFactoryImpl.h" +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::sync; +using namespace bcos::protocol; +using namespace bcos::crypto; + +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(BlockSyncMsgTest, TestPromptFixture) +inline void checkBasic(BlockSyncMsgInterface::Ptr _syncMsg, int32_t _packetType, + BlockNumber _blockNumber, int32_t _version) +{ + auto factory = std::make_shared(); + auto encodedData = _syncMsg->encode(); + auto decodedBasicMsg = factory->createBlockSyncMsg(ref(*encodedData)); + BOOST_CHECK(_syncMsg->version() == decodedBasicMsg->version()); + BOOST_CHECK(_syncMsg->packetType() == decodedBasicMsg->packetType()); + BOOST_CHECK(_syncMsg->number() == decodedBasicMsg->number()); + + BOOST_CHECK(decodedBasicMsg->version() == _version); + BOOST_CHECK(decodedBasicMsg->packetType() == _packetType); + BOOST_CHECK(decodedBasicMsg->number() == _blockNumber); +} +inline BlockSyncMsgInterface::Ptr testSyncMsg(int32_t _packetType, BlockNumber _blockNumber, + int32_t _version, HashType const& _hash, HashType const& _genesisHash, size_t _size, + std::vector _blockData) +{ + auto factory = std::make_shared(); + BlockSyncMsgInterface::Ptr syncMsg = nullptr; + switch (_packetType) + { + case BlockSyncPacketType::BlockStatusPacket: + { + auto statusMsg = factory->createBlockSyncStatusMsg(); + statusMsg->setHash(_hash); + statusMsg->setGenesisHash(_genesisHash); + syncMsg = statusMsg; + break; + } + case BlockSyncPacketType::BlockRequestPacket: + { + auto requestMsg = factory->createBlockRequest(); + requestMsg->setSize(_size); + syncMsg = requestMsg; + break; + } + case BlockSyncPacketType::BlockResponsePacket: + { + auto responseMsg = factory->createBlocksMsg(); + for (auto const& data : _blockData) + { + responseMsg->appendBlockData(data); + } + syncMsg = responseMsg; + break; + } + default: + { + return nullptr; + } + } + syncMsg->setPacketType(_packetType); + syncMsg->setNumber(_blockNumber); + syncMsg->setVersion(_version); + + // check basic + checkBasic(syncMsg, _packetType, _blockNumber, _version); + auto encodedData = syncMsg->encode(); + auto decodedBasicMsg = factory->createBlockSyncMsg(ref(*encodedData)); + + // check different packet + switch (_packetType) + { + case BlockSyncPacketType::BlockStatusPacket: + { + auto statusMsg = factory->createBlockSyncStatusMsg(decodedBasicMsg); + BOOST_CHECK(statusMsg->hash().asBytes() == _hash.asBytes()); + BOOST_CHECK(statusMsg->genesisHash().asBytes() == _genesisHash.asBytes()); + break; + } + case BlockSyncPacketType::BlockRequestPacket: + { + auto requestMsg = factory->createBlockRequest(decodedBasicMsg); + BOOST_CHECK(requestMsg->size() == _size); + break; + } + case BlockSyncPacketType::BlockResponsePacket: + { + auto responseMsg = factory->createBlocksMsg(decodedBasicMsg); + BOOST_CHECK(responseMsg->blocksSize() == _blockData.size()); + size_t i = 0; + for (auto const& data : _blockData) + { + auto decodedData = responseMsg->blockData(i++); + BOOST_CHECK(data == decodedData.toBytes()); + } + break; + } + default: + { + return nullptr; + } + } + return syncMsg; +} + +BOOST_AUTO_TEST_CASE(testBlockSyncMsg) +{ + BlockNumber blockNumber = 123214; + int32_t version = 10; + auto hashImpl = std::make_shared(); + HashType hash = hashImpl->hash(std::string("hash")); + HashType genesisHash = hashImpl->hash(std::string("genesisHash")); + size_t requestedSize = 1203; + std::vector blockData; + size_t blockDataSize = 5; + for (size_t i = 0; i < blockDataSize; i++) + { + std::string data = "blockData" + std::to_string(i); + blockData.push_back(bytes(data.begin(), data.end())); + } + testSyncMsg(BlockSyncPacketType::BlockStatusPacket, blockNumber, version, hash, genesisHash, + requestedSize, blockData); + + testSyncMsg(BlockSyncPacketType::BlockRequestPacket, blockNumber, version, hash, genesisHash, + requestedSize, blockData); + + testSyncMsg(BlockSyncPacketType::BlockResponsePacket, blockNumber, version, hash, genesisHash, + requestedSize, blockData); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/test/unittests/sync/BlockSyncTest.cpp" "b/BFPL\345\243\271/bcos-sync/test/unittests/sync/BlockSyncTest.cpp" new file mode 100644 index 00000000..7a1d6a82 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/test/unittests/sync/BlockSyncTest.cpp" @@ -0,0 +1,214 @@ +/** + * Copyright (C) 2021 bcos-sync. + * 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. + * + * @brief test for the BlockSync + * @file BlockSyncTest.h + * @author: yujiechen + * @date 2021-06-08 + */ + +#include +#include + +#include "SyncFixture.h" +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::sync; +using namespace bcos::crypto; + +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(BlockSyncTest, TestPromptFixture) +void testRequestAndDownloadBlock(CryptoSuite::Ptr _cryptoSuite) +{ + auto gateWay = std::make_shared(); + BlockNumber maxBlock = 10; + auto newerPeer = std::make_shared(_cryptoSuite, gateWay, (maxBlock + 1)); + auto ledgerData = newerPeer->ledger()->ledgerData(); + auto latestHash = (ledgerData[newerPeer->ledger()->blockNumber()])->blockHeader()->hash(); + + BlockNumber minBlock = 5; + auto lowerPeer = std::make_shared(_cryptoSuite, gateWay, (minBlock + 1)); + std::vector nodeList; + nodeList.push_back(newerPeer->nodeID()); + nodeList.push_back(lowerPeer->nodeID()); + newerPeer->setObservers(nodeList); + lowerPeer->setObservers(nodeList); + + newerPeer->init(); + lowerPeer->init(); + + // maintainPeersConnection + newerPeer->sync()->executeWorker(); + lowerPeer->sync()->executeWorker(); + while (!newerPeer->sync()->syncStatus()->hasPeer(lowerPeer->nodeID()) || + !lowerPeer->sync()->syncStatus()->hasPeer(newerPeer->nodeID())) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + + BOOST_CHECK(newerPeer->syncConfig()->knownHighestNumber() == maxBlock); + BOOST_CHECK(lowerPeer->syncConfig()->knownHighestNumber() == maxBlock); + BOOST_CHECK(lowerPeer->syncConfig()->knownLatestHash().asBytes() == latestHash.asBytes()); + BOOST_CHECK(newerPeer->syncConfig()->knownLatestHash().asBytes() == latestHash.asBytes()); + // check request/response blocks + while (lowerPeer->ledger()->blockNumber() != maxBlock) + { + newerPeer->sync()->executeWorker(); + lowerPeer->sync()->executeWorker(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + BOOST_CHECK(newerPeer->consensus()->ledgerConfig()->blockNumber() == maxBlock); + BOOST_CHECK(lowerPeer->consensus()->ledgerConfig()->blockNumber() == maxBlock); +} + +bool checkPeer(std::vector const& _peerList, size_t _expectedPeerSize) +{ + for (auto peer : _peerList) + { + if (peer->sync()->syncStatus()->peers()->size() < _expectedPeerSize) + { + return false; + } + } + return true; +} + +bool downloadFinish( + std::vector const& _peerList, BlockNumber _expectedBlockNumber) +{ + for (auto peer : _peerList) + { + if (peer->ledger()->blockNumber() != _expectedBlockNumber) + { + return false; + } + } + return true; +} + +void testComplicatedCase(CryptoSuite::Ptr _cryptoSuite) +{ + auto gateWay = std::make_shared(); + std::vector syncPeerList; + std::vector nodeList; + + BlockNumber maxBlockNumber = 50; + size_t maxBlockNumberPeerSize = 2; + + BlockNumber medianBlockNumber = 20; + size_t medianBlockNumberPeerSize = 2; + + BlockNumber minBlockNumber = 10; + size_t minBlockNumberPeerSize = 3; + + for (size_t i = 0; i < maxBlockNumberPeerSize; i++) + { + auto faker = std::make_shared(_cryptoSuite, gateWay, maxBlockNumber + 1); + nodeList.push_back(faker->nodeID()); + syncPeerList.push_back(faker); + } + for (size_t i = 0; i < medianBlockNumberPeerSize; i++) + { + auto faker = std::make_shared(_cryptoSuite, gateWay, medianBlockNumber + 1); + nodeList.push_back(faker->nodeID()); + syncPeerList.push_back(faker); + } + + for (size_t i = 0; i < minBlockNumberPeerSize; i++) + { + auto faker = std::make_shared(_cryptoSuite, gateWay, minBlockNumber + 1); + nodeList.push_back(faker->nodeID()); + syncPeerList.push_back(faker); + } + std::vector sealers; + sealers.push_back(_cryptoSuite->signatureImpl()->generateKeyPair()->publicKey()->data()); + // with different genesis hash with others + auto invalidFaker = std::make_shared(_cryptoSuite, gateWay, 0, sealers); + nodeList.push_back(invalidFaker->nodeID()); + invalidFaker->setObservers(nodeList); + invalidFaker->init(); + for (auto faker : syncPeerList) + { + faker->setObservers(nodeList); + faker->init(); + } + // maintainPeersConnection + for (auto faker : syncPeerList) + { + faker->sync()->executeWorker(); + } + invalidFaker->sync()->executeWorker(); + + size_t peerSize = maxBlockNumberPeerSize + medianBlockNumberPeerSize + minBlockNumberPeerSize; + // check peers + auto startT = utcTime(); + while (!checkPeer(syncPeerList, peerSize) && (utcTime() - startT <= 60 * 1000)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + auto ledgerData = (syncPeerList[0])->ledger()->ledgerData(); + auto latestBlock = ledgerData[(syncPeerList[0])->ledger()->blockNumber()]; + auto genesisBlock = ledgerData[0]; + // check the maxKnownBlockNumber + for (auto faker : syncPeerList) + { + auto genesisBlockHeader = genesisBlock->blockHeader(); + auto latestBlockHeader = latestBlock->blockHeader(); + BOOST_CHECK(faker->syncConfig()->genesisHash() == genesisBlockHeader->hash()); + BOOST_CHECK(faker->syncConfig()->knownLatestHash() == latestBlockHeader->hash()); + BOOST_CHECK(faker->syncConfig()->knownHighestNumber() == maxBlockNumber); + } + auto invalidLedgerData = invalidFaker->ledger()->ledgerData(); + auto invalidLatestBlock = invalidLedgerData[invalidFaker->ledger()->blockNumber()]; + auto invalidGenesisBlock = invalidLedgerData[0]; + BOOST_CHECK(invalidFaker->sync()->syncStatus()->peers()->size() == 1); + auto invalidGenesisBlockHeader = invalidGenesisBlock->blockHeader(); + BOOST_CHECK(invalidFaker->syncConfig()->genesisHash() == invalidGenesisBlockHeader->hash()); + auto invalidLatestBlockHeader = invalidLatestBlock->blockHeader(); + BOOST_CHECK(invalidFaker->syncConfig()->knownLatestHash() == invalidLatestBlockHeader->hash()); + BOOST_CHECK(invalidFaker->syncConfig()->knownHighestNumber() == 0); + + // wait the nodes to sync blocks + startT = utcTime(); + while (!downloadFinish(syncPeerList, maxBlockNumber) && (utcTime() - startT <= 60 * 1000)) + { + for (auto faker : syncPeerList) + { + faker->sync()->executeWorker(); + } + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } +} + + +BOOST_AUTO_TEST_CASE(testNonSMRequestAndDownloadBlock) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + testRequestAndDownloadBlock(cryptoSuite); + testComplicatedCase(cryptoSuite); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-sync/test/unittests/sync/SyncConfigTest.cpp" "b/BFPL\345\243\271/bcos-sync/test/unittests/sync/SyncConfigTest.cpp" new file mode 100644 index 00000000..395bf555 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/test/unittests/sync/SyncConfigTest.cpp" @@ -0,0 +1,83 @@ +/** + * Copyright (C) 2021 bcos-sync. + * 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. + * + * @brief test for the syncConfig + * @file SyncConfigTest.h + * @author: yujiechen + * @date 2021-06-08 + */ + +#include +#include + +#include "SyncFixture.h" +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::sync; +using namespace bcos::crypto; + +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(SyncConfigTest, TestPromptFixture) + +void testSyncConfig(CryptoSuite::Ptr _cryptoSuite) +{ + auto gateWay = std::make_shared(); + auto faker = std::make_shared(_cryptoSuite, gateWay); + faker->init(); + // check the config + auto config = faker->syncConfig(); + BOOST_CHECK(config->ledger()); + BOOST_CHECK(config->nodeID()->data() == faker->nodeID()->data()); + BOOST_CHECK(config->blockFactory()); + BOOST_CHECK(config->frontService()); + BOOST_CHECK(config->scheduler()); + BOOST_CHECK(config->consensus()); + BOOST_CHECK(config->msgFactory()); + + auto ledgerData = faker->ledger()->ledgerData(); + auto genesisBlock = (ledgerData[0]); + auto genesisBlockHeader = genesisBlock->blockHeader(); + BOOST_CHECK(config->genesisHash().asBytes() == genesisBlockHeader->hash().asBytes()); + BOOST_CHECK(config->blockNumber() == faker->ledger()->blockNumber()); + BOOST_CHECK(config->nextBlock() == faker->ledger()->blockNumber() + 1); + BOOST_CHECK(config->hash().asBytes() == faker->ledger()->ledgerConfig()->hash().asBytes()); +} + +BOOST_AUTO_TEST_CASE(testNonSMSyncConfig) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + testSyncConfig(cryptoSuite); +} + +BOOST_AUTO_TEST_CASE(testSMSyncConfig) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + testSyncConfig(cryptoSuite); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-sync/test/unittests/sync/SyncFixture.h" "b/BFPL\345\243\271/bcos-sync/test/unittests/sync/SyncFixture.h" new file mode 100644 index 00000000..8b403877 --- /dev/null +++ "b/BFPL\345\243\271/bcos-sync/test/unittests/sync/SyncFixture.h" @@ -0,0 +1,176 @@ +/** + * Copyright (C) 2021 bcos-sync. + * 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. + * + * @brief fixture for the BlockSync + * @file SyncFixture.h + * @author: yujiechen + * @date 2021-06-08 + */ +#pragma once +#include "../faker/FakeConsensus.h" +#include "bcos-sync/BlockSync.h" +#include "bcos-sync/BlockSyncFactory.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::sync; +using namespace bcos::crypto; +using namespace bcos::protocol; +using namespace bcos::scheduler; +using namespace bcos::tool; + +namespace bcos +{ +namespace test +{ +class FakeBlockSync : public BlockSync +{ +public: + using Ptr = std::shared_ptr; + FakeBlockSync(BlockSyncConfig::Ptr _config, unsigned _idleWaitMs = 200) + : BlockSync(_config, _idleWaitMs) + { + m_running = true; + enableAsMaster(true); + } + ~FakeBlockSync() override {} + + void executeWorker() override { BlockSync::executeWorker(); } + void maintainPeersConnection() override { BlockSync::maintainPeersConnection(); } + SyncPeerStatus::Ptr syncStatus() { return m_syncStatus; } +}; + +class FakeTxPoolForSync : public FakeTxPool +{ +public: + FakeTxPoolForSync() = default; + void asyncNotifyBlockResult(BlockNumber, TransactionSubmitResultsPtr, + std::function _callback) override + { + if (_callback) + { + _callback(nullptr); + } + } +}; + +class FakeBlockSyncFactory : public BlockSyncFactory +{ +public: + using Ptr = std::shared_ptr; + FakeBlockSyncFactory(PublicPtr _nodeId, BlockFactory::Ptr _blockFactory, + LedgerInterface::Ptr _ledger, FrontServiceInterface::Ptr _frontService, + SchedulerInterface::Ptr _dispatcher, ConsensusInterface::Ptr _consensus, + NodeTimeMaintenance::Ptr _nodeTimeMaintenance) + : BlockSyncFactory(_nodeId, _blockFactory, + std::make_shared(), _ledger, + std::make_shared(), _frontService, _dispatcher, _consensus, + _nodeTimeMaintenance) + {} + + BlockSync::Ptr createBlockSync() override + { + auto sync = BlockSyncFactory::createBlockSync(); + return std::make_shared(sync->config()); + } +}; + +class SyncFixture +{ +public: + using Ptr = std::shared_ptr; + SyncFixture(CryptoSuite::Ptr _cryptoSuite, FakeGateWay::Ptr _fakeGateWay, + size_t _blockNumber = 0, std::vector _sealerList = std::vector()) + : m_cryptoSuite(_cryptoSuite), m_gateWay(_fakeGateWay) + { + m_keyPair = _cryptoSuite->signatureImpl()->generateKeyPair(); + m_blockFactory = createBlockFactory(_cryptoSuite); + m_ledger = std::make_shared(m_blockFactory, _blockNumber, 10, 0, _sealerList); + m_frontService = std::make_shared(m_keyPair->publicKey()); + m_consensus = std::make_shared(); + m_nodeTimeMaintenance = std::make_shared(); + + // create FakeScheduler + m_scheduler = std::make_shared(m_ledger, m_blockFactory); + auto blockSyncFactory = std::make_shared(m_keyPair->publicKey(), + m_blockFactory, m_ledger, m_frontService, m_scheduler, m_consensus, m_nodeTimeMaintenance); + m_sync = std::dynamic_pointer_cast(blockSyncFactory->createBlockSync()); + if (_fakeGateWay) + { + _fakeGateWay->addSync(m_keyPair->publicKey(), m_sync); + } + m_frontService->setGateWay(_fakeGateWay); + } + + FakeFrontService::Ptr frontService() { return m_frontService; } + FakeScheduler::Ptr scheduler() { return m_scheduler; } + FakeConsensus::Ptr consensus() { return m_consensus; } + FakeLedger::Ptr ledger() { return m_ledger; } + + FakeGateWay::Ptr gateWay() { return m_gateWay; } + PublicPtr nodeID() { return m_keyPair->publicKey(); } + + FakeBlockSync::Ptr sync() { return m_sync; } + + void appendObserver(NodeIDPtr _nodeId) + { + auto node = std::make_shared(_nodeId); + m_ledger->ledgerConfig()->mutableObserverList()->emplace_back(node); + m_sync->config()->setObserverList(m_ledger->ledgerConfig()->observerNodeList()); + } + + void setObservers(std::vector _nodeIdList) + { + m_ledger->ledgerConfig()->mutableObserverList()->clear(); + for (auto const& node : _nodeIdList) + { + m_ledger->ledgerConfig()->mutableObserverList()->emplace_back( + std::make_shared(node)); + } + m_sync->config()->setObserverList(m_ledger->ledgerConfig()->observerNodeList()); + bcos::crypto::NodeIDSet nodeIdSet; + for (auto node : m_ledger->ledgerConfig()->observerNodeList()) + { + nodeIdSet.insert(node->nodeID()); + } + m_sync->config()->setConnectedNodeList(nodeIdSet); + } + + void init() { m_sync->init(); } + + BlockSyncConfig::Ptr syncConfig() { return m_sync->config(); } + +private: + CryptoSuite::Ptr m_cryptoSuite; + KeyPairInterface::Ptr m_keyPair; + BlockFactory::Ptr m_blockFactory; + FakeGateWay::Ptr m_gateWay; + + FakeFrontService::Ptr m_frontService; + FakeConsensus::Ptr m_consensus; + FakeLedger::Ptr m_ledger; + + FakeScheduler::Ptr m_scheduler; + FakeBlockSync::Ptr m_sync; + NodeTimeMaintenance::Ptr m_nodeTimeMaintenance; +}; +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-table/CMakeLists.txt" "b/BFPL\345\243\271/bcos-table/CMakeLists.txt" new file mode 100644 index 00000000..cfeaaf5b --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/CMakeLists.txt" @@ -0,0 +1,45 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for libtable +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 libtable +# 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. +#------------------------------------------------------------------------------ + +cmake_minimum_required(VERSION 3.10) +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum OS X deployment version") + +project(bcos-table VERSION ${VERSION}) + +file(GLOB_RECURSE SRCS src/*.cpp) + +find_package(Boost REQUIRED serialization) +find_package(TBB CONFIG REQUIRED) + +add_library(${TABLE_TARGET} ${SRCS}) +target_link_libraries(${TABLE_TARGET} PUBLIC ${UTILITIES_TARGET} bcos-framework Boost::serialization TBB::tbb) + +if (TESTS) + enable_testing() + set(CTEST_OUTPUT_ON_FAILURE TRUE) + add_subdirectory(test) +endif() + +# for code coverage +if (COVERAGE) + include(Coverage) + config_coverage("table-coverage" "'/usr*' '${CMAKE_CURRENT_SOURCE_DIR}/bcos-cmake-scripts*' '${CMAKE_CURRENT_SOURCE_DIR}/test/bcos-test*'") +endif () + +include(GNUInstallDirs) +install(TARGETS ${TABLE_TARGET} EXPORT fiscobcosTargets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") diff --git "a/BFPL\345\243\271/bcos-table/src/CacheStorageFactory.cpp" "b/BFPL\345\243\271/bcos-table/src/CacheStorageFactory.cpp" new file mode 100644 index 00000000..415bb20b --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/src/CacheStorageFactory.cpp" @@ -0,0 +1,13 @@ +#include "CacheStorageFactory.h" +#include "StateStorage.h" + +using namespace bcos::storage; + +MergeableStorageInterface::Ptr CacheStorageFactory::build() +{ + auto cache = std::make_shared(m_backendStorage); + cache->setMaxCapacity(m_cacheSize); + BCOS_LOG(INFO) << "Build CacheStorage: enableLRUCacheStorage, size: " << m_cacheSize; + + return cache; +} diff --git "a/BFPL\345\243\271/bcos-table/src/CacheStorageFactory.h" "b/BFPL\345\243\271/bcos-table/src/CacheStorageFactory.h" new file mode 100644 index 00000000..c90e5d98 --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/src/CacheStorageFactory.h" @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 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. + * + * @brief interface of Table + * @file CacheStorageFactory.h + * @author: jimmyshi + * @date: 2022-10-20 + */ +#pragma once + +#include "bcos-framework/storage/StorageInterface.h" + +namespace bcos::storage +{ +class CacheStorageFactory +{ +public: + using Ptr = std::shared_ptr; + CacheStorageFactory( + bcos::storage::TransactionalStorageInterface::Ptr backendStorage, ssize_t cacheSize) + : m_cacheSize(cacheSize), m_backendStorage(backendStorage) + {} + + virtual ~CacheStorageFactory() = default; + storage::MergeableStorageInterface::Ptr build(); + +private: + ssize_t m_cacheSize; + bcos::storage::TransactionalStorageInterface::Ptr m_backendStorage; +}; + +} // namespace bcos::storage \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-table/src/KeyPageStorage.cpp" "b/BFPL\345\243\271/bcos-table/src/KeyPageStorage.cpp" new file mode 100644 index 00000000..63f655e5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/src/KeyPageStorage.cpp" @@ -0,0 +1,1051 @@ +/* + * Copyright (C) 2021 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. + * + * @brief KeyPage implementation + * @file KeyPageStorage.cpp + * @author: xingqiangbai + * @date: 2022-04-19 + */ +#include "KeyPageStorage.h" +#include +#include +#include + +namespace bcos::storage +{ +void KeyPageStorage::asyncGetPrimaryKeys(std::string_view tableView, + const std::optional& _condition, + std::function)> _callback) +{ + // if SYS_TABLES is not supported + if (m_ignoreTables->find(tableView) != m_ignoreTables->end()) + { + _callback(BCOS_ERROR_UNIQUE_PTR(StorageError::ReadError, + std::string("scan ").append(tableView).append(" is not supported")), + std::vector()); + return; + } + if (!_condition) + { + _callback(BCOS_ERROR_UNIQUE_PTR( + StorageError::ReadError, "asyncGetPrimaryKeys must have condition"), + std::vector()); + return; + } + // page + auto [error, data] = getData(tableView, TABLE_META_KEY); + if (error) + { + _callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(StorageError::ReadError, + std::string("get table meta data failed, table:").append(tableView), *error), + std::vector()); + return; + } + std::vector ret; + auto* meta = &std::get<1>(data.value()->data); + auto readLock = meta->rLock(); + auto& pageInfo = meta->getAllPageInfoNoLock(); + auto [offset, total] = _condition->getLimit(); + ret.reserve(total); + size_t validCount = 0; + for (auto& info : pageInfo) + { + auto [error, data] = getData(tableView, info.getPageKey(), info.getCount() > 0); + boost::ignore_unused(error); + assert(!error); + auto* page = &std::get<0>(data.value()->data); + auto [entries, pageLock] = page->getEntries(); + boost::ignore_unused(pageLock); + for (auto& it : entries) + { + if (it.second.status() != Entry::DELETED && _condition->isValid(it.first)) + { + if (validCount >= offset && validCount < offset + total) + { + ret.emplace_back(it.first); + } + ++validCount; + if (validCount == offset + total) + { + break; + } + } + } + } + readLock.unlock(); + _callback(nullptr, std::move(ret)); +} +// TODO: add interface and cow to avoid page copy +void KeyPageStorage::asyncGetRow(std::string_view tableView, std::string_view keyView, + std::function)> _callback) +{ + if (!m_readOnly) + { + m_readLength += keyView.size(); + } + // if sys table, read cache and read from prev, return + if (m_ignoreTables->find(tableView) != m_ignoreTables->end()) + { + auto [error, entry] = getSysTableRawEntry(tableView, keyView); + if (error) + { + _callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR( + StorageError::ReadError, "Get row from storage failed!", *error), + {}); + return; + } + if (entry) + { // add cache, found + if (!m_readOnly) + { + m_readLength += entry->size(); + } + _callback(nullptr, std::move(*entry)); + } + else + { // not found + _callback(nullptr, std::nullopt); + } + return; + } + + auto [error, entry] = getEntryFromPage(tableView, keyView); + if (!m_readOnly && entry.has_value()) + { + m_readLength += entry->size(); + } + _callback(std::move(error), std::move(entry)); +} + +void KeyPageStorage::asyncGetRows(std::string_view tableView, + const std::variant, const gsl::span>& + _keys, + std::function>)> _callback) +{ + std::visit( + [this, &tableView, &_callback](auto&& _keys) { + std::vector> results(_keys.size()); + + if (m_ignoreTables->find(tableView) != m_ignoreTables->end()) + { + Error::UniquePtr err; + for (auto i = 0U; i < _keys.size(); ++i) + { + auto [error, entry] = getSysTableRawEntry(tableView, _keys[i]); + if (error) + { + err = BCOS_ERROR_WITH_PREV_UNIQUE_PTR( + StorageError::ReadError, "get s_tables rows failed!", *error); + break; + } + results[i] = std::make_optional(std::move(*entry)); + } + _callback(std::move(err), std::move(results)); + } + else + { // page + Error::UniquePtr err(nullptr); + // TODO: because of page and lock, maybe not parallel is better + for (auto i = 0U; i < _keys.size(); ++i) + { + asyncGetRow(tableView, _keys[i], + [i, &results, &err](Error::UniquePtr _error, std::optional _entry) { + if (_error) + { + err = BCOS_ERROR_WITH_PREV_UNIQUE_PTR( + StorageError::ReadError, "get rows failed!", *_error); + return; + } + results[i] = std::move(_entry); + }); + if (err) + { + break; + } + } + _callback(std::move(err), std::move(results)); + } + }, + _keys); +} + +void KeyPageStorage::asyncSetRow(std::string_view tableView, std::string_view keyView, Entry entry, + std::function callback) +{ // delete is the same process as insert + if (m_readOnly) + { + callback( + BCOS_ERROR_UNIQUE_PTR(StorageError::ReadOnly, "Try to operate a read-only storage")); + return; + } + m_writeLength += keyView.size(); + m_writeLength += entry.size(); + // if sys table, write cache and write to prev, return + if (m_ignoreTables->find(tableView) != m_ignoreTables->end()) + { + std::optional entryOld; + + auto [bucket, lock] = getMutBucket(tableView, keyView); + boost::ignore_unused(lock); + // entry.setStatus(Entry::Status::MODIFIED); + auto it = + bucket->container.find(std::make_pair(std::string(tableView), std::string(keyView))); + if (it != bucket->container.end()) + { // update + auto& existsEntry = it->second->entry; + entryOld.emplace(std::move(existsEntry)); + + it->second->entry = std::move(entry); + } + else + { // insert + auto tableKey = std::make_pair(std::string(tableView), std::string(keyView)); + bucket->container.emplace(std::make_pair(std::move(tableKey), + std::make_shared(std::string(tableView), std::string(keyView), + std::move(entry), Data::Type::NormalEntry))); + } + + if (m_recoder.local()) + { + m_recoder.local()->log( + Recoder::Change(std::string(tableView), std::string(keyView), std::move(entryOld))); + } + lock.unlock(); + callback(nullptr); + } + else + { + auto error = setEntryToPage(std::string(tableView), std::string(keyView), std::move(entry)); + callback(std::move(error)); + } +} + +void KeyPageStorage::parallelTraverse(bool onlyDirty, + std::function + callback) const +{ + tbb::parallel_for(tbb::blocked_range(0, m_buckets.size(), 32), + [this, &onlyDirty, &callback](const tbb::blocked_range& range) { + for (auto i = range.begin(); i != range.end(); ++i) + { + const auto& bucket = m_buckets[i]; + + for (const auto& it : bucket.container) + { + if (it.second->type == Data::Type::TableMeta) + { // if metadata + if (!onlyDirty || it.second->entry.dirty()) + { + auto* meta = &std::get<1>(it.second->data); + auto readLock = meta->rLock(); + Entry entry; + entry.setObject(*meta); + readLock.unlock(); + if (!m_readOnly) + { + if (meta->size() <= 10) + { // FIXME: this log is only for debug, comment it when release + KeyPage_LOG(DEBUG) + << LOG_DESC("TableMeta") << LOG_KV("table", it.first.first) + << LOG_KV("key", toHex(it.first.second)) + << LOG_KV("meta", *meta); + } + KeyPage_LOG(DEBUG) + << LOG_DESC("Traverse TableMeta") + << LOG_KV("table", it.first.first) + << LOG_KV("pageCount", meta->size()) + << LOG_KV("rowCount", meta->rowCount()) + << LOG_KV("size", entry.size()) + << LOG_KV("payloadRate", + sizeof(PageInfo) * meta->size() / (double)entry.size()) + << LOG_KV("predictHit", meta->hitRate()); + } + callback(it.first.first, it.first.second, entry); + } + } + else if (it.second->type == Data::Type::Page) + { // if page, encode and return + auto* page = &std::get<0>(it.second->data); + if (!onlyDirty || it.second->entry.dirty()) + { + Entry entry; + if (page->validCount() == 0) + { + if (!m_readOnly) + { + KeyPage_LOG(DEBUG) << LOG_DESC("Traverse deleted Page") + << LOG_KV("table", it.first.first) + << LOG_KV("key", toHex(it.first.second)) + << LOG_KV("validCount", page->validCount()); + } + entry.setStatus(Entry::Status::DELETED); + callback(it.first.first, it.first.second, entry); + } + else + { + entry.setObject(*page); + entry.setStatus(it.second->entry.status()); + if (!m_readOnly) + { + KeyPage_LOG(DEBUG) + << LOG_DESC("Traverse Page") + << LOG_KV("table", it.first.first) + << LOG_KV("pageKey", toHex(it.first.second)) + << LOG_KV("valid", page->validCount()) + << LOG_KV("count", page->count()) + << LOG_KV("status", (int)it.second->entry.status()) + << LOG_KV("pageSize", page->size()) + << LOG_KV("size", entry.size()); + } + if (it.first.second != page->endKey()) + { + KeyPage_LOG(FATAL) + << LOG_DESC("Traverse Page pageKey not equal to map key") + << LOG_KV("table", it.first.first) + << LOG_KV("pageKey", page->endKey()) + << LOG_KV("mapKey", it.first.second); + } + callback(it.first.first, it.first.second, entry); + } + auto invalidKeys = page->invalidKeySet(); + for (const auto& k : invalidKeys) + { + if (!m_readOnly) + { + KeyPage_LOG(DEBUG) + << LOG_DESC("Traverse Page delete invalid key") + << LOG_KV("currentKey", toHex(page->endKey())) + << LOG_KV("table", it.first.first) + << LOG_KV("key", toHex(k)); + } + Entry e; + e.setStatus(Entry::Status::DELETED); + callback(it.first.first, k, e); + } + } + } + else + { + if (!onlyDirty || it.second->entry.dirty()) + { + auto& entry = it.second->entry; + // assert(it.first.first == SYS_TABLES); + callback(it.first.first, it.first.second, entry); + } + } + } + } + }); +} + +auto KeyPageStorage::hash(const bcos::crypto::Hash::Ptr& hashImpl) const -> crypto::HashType +{ + bcos::crypto::HashType pagesHash(0); + bcos::crypto::HashType entriesHash(0); + std::atomic_int64_t pageCount = 0; + std::atomic_int64_t entrycount = 0; + std::vector allData; + for (size_t i = 0; i < m_buckets.size(); ++i) + { + const auto& bucket = m_buckets[i]; + for (const auto& it : bucket.container) + { + allData.push_back(it.second.get()); + } + } + std::mutex mutex; + tbb::parallel_for(tbb::blocked_range(0, allData.size()), + [&](const tbb::blocked_range& range) { + bcos::crypto::HashType localPagesHash; + bcos::crypto::HashType localEntriesHash; + for (size_t i = range.begin(); i != range.end(); ++i) + { + const auto* data = allData[i]; + const auto& entry = data->entry; + if (entry.dirty() && data->type != Data::Type::TableMeta) + { + if (data->type == Data::Type::Page) + { + const auto* page = &std::get<0>(data->data); + auto pageHash = page->hash(data->table, hashImpl, m_blockVersion); + localPagesHash ^= pageHash; + ++pageCount; + } + else + { // sys table + auto hash = hashImpl->hash(data->table); + hash ^= hashImpl->hash(data->key); + hash ^= entry.hash(data->table, data->key, hashImpl, m_blockVersion); + localEntriesHash ^= hash; + ++entrycount; + } + } + } + std::lock_guard lock(mutex); + pagesHash ^= localPagesHash; + entriesHash ^= localEntriesHash; + }); + bcos::crypto::HashType totalHash(0); + totalHash ^= pagesHash; + totalHash ^= entriesHash; + KeyPage_LOG(INFO) << LOG_DESC("hash") << LOG_KV("size", allData.size()) + << LOG_KV("readLength", m_readLength) << LOG_KV("writeLength", m_writeLength) + << LOG_KV("pageCount", pageCount) << LOG_KV("entrycount", entrycount) + << LOG_KV("entriesHash", entriesHash.hex()) + << LOG_KV("pagesHash", pagesHash.hex()) + << LOG_KV("totalHash", totalHash.hex()); + return totalHash; +} + +void KeyPageStorage::rollback(const Recoder& recoder) +{ + if (m_readOnly) + { + return; + } + + for (const auto& change : recoder) + { + if (m_ignoreTables->find(change.table) != m_ignoreTables->end()) + { + auto [bucket, lock] = getMutBucket(change.table, change.key); + boost::ignore_unused(lock); + + auto it = bucket->container.find(std::make_pair(change.table, change.key)); + if (change.entry) + { + if (it != bucket->container.end()) + { + if (c_fileLogLevel <= bcos::LogLevel::TRACE) + { + KeyPage_LOG(TRACE) + << "Revert exists: " << change.table << " | " << toHex(change.key) + << " | " << toHex(change.entry->get()); + } + const auto& rollbackEntry = change.entry; + it->second->entry = *rollbackEntry; + } + else + { + if (c_fileLogLevel <= bcos::LogLevel::TRACE) + { + KeyPage_LOG(TRACE) + << "Revert deleted: " << change.table << " | " << toHex(change.key) + << " | " << toHex(change.entry->get()); + } + auto tableKey = std::make_pair(change.table, change.key); + bucket->container.emplace(std::make_pair( + std::move(tableKey), std::make_shared(change.table, change.key, + *(change.entry), Data::Type::NormalEntry))); + } + } + else + { // nullopt means the key is not exist in m_cache + if (it != bucket->container.end()) + { + if (c_fileLogLevel <= bcos::LogLevel::TRACE) + { + KeyPage_LOG(TRACE) + << "Revert insert: " << change.table << " | " << toHex(change.key); + } + bucket->container.erase(it); + } + else + { + auto message = (boost::format("Not found rollback entry: %s:%s") % + change.table % change.key) + .str(); + + BOOST_THROW_EXCEPTION(BCOS_ERROR(StorageError::UnknownError, message)); + } + } + } + else + { // page entry + + auto [error, data] = getData(change.table, TABLE_META_KEY); + if (error) + { + BOOST_THROW_EXCEPTION(*error); + } + auto* meta = &std::get<1>(data.value()->data); + auto writeLock = meta->lock(); + auto pageInfoOp = meta->getPageInfoNoLock(change.key); + if (pageInfoOp) + { + auto pageKey = pageInfoOp.value()->getPageKey(); + auto* pageData = pageInfoOp.value()->getPageData(); + if (pageData == nullptr) + { + auto [error, pageDataOp] = getData(change.table, pageKey, true); + if (error || !pageDataOp) + { + BOOST_THROW_EXCEPTION(*error); + } + pageData = pageDataOp.value(); + } + auto* page = &std::get<0>(pageData->data); + if (page->validCount() != pageInfoOp.value()->getCount()) + { + KeyPage_LOG(FATAL) << LOG_DESC("page valid count mismatch") + << LOG_KV("count", pageInfoOp.value()->getCount()) + << LOG_KV("realCount", page->validCount()); + } + page->rollback(change); + if (page->count() == 0) + { // page is empty because of rollback, means it it first created + pageData->entry.setStatus(Entry::Status::EMPTY); + KeyPage_LOG(DEBUG) + << LOG_DESC("revert page to empty") << LOG_KV("table", change.table) + << LOG_KV("key", toHex(change.key)); + } + if (c_fileLogLevel <= TRACE) + { + KeyPage_LOG(TRACE) + << LOG_DESC("revert page entry") << LOG_KV("table", change.table) + << LOG_KV("key", toHex(change.key)) << LOG_KV("page", (uint64_t)page) + << LOG_KV("pageKey", toHex(pageKey)) + << LOG_KV("pageEndKey", toHex(page->endKey())) + << LOG_KV("count", page->count()) + << LOG_KV("validCount", page->validCount()); + } + // revert also need update pageInfo + auto oldStartKey = meta->updatePageInfoNoLock( + pageKey, page->endKey(), page->validCount(), page->size(), pageInfoOp); + if (oldStartKey) + { + changePageKey(change.table, oldStartKey.value(), page->endKey(), true); + } + } + else + { + auto message = + (boost::format("Not found rollback page: %s:%s") % change.table % change.key) + .str(); + BOOST_THROW_EXCEPTION(BCOS_ERROR(StorageError::ReadError, message)); + } + } + } +} + +// if data not exist, create an empty one +auto KeyPageStorage::getData(std::string_view tableView, std::string_view key, bool mustExist) + -> std::tuple> +{ + // find from cache + auto [bucket, lock] = getBucket(tableView, key); + boost::ignore_unused(lock); + auto keyPair = std::make_pair(std::string(tableView), std::string(key)); + decltype(bucket->container)::iterator it = bucket->container.find(keyPair); + if (it != bucket->container.end()) + { + // assert(it->first.second == key); + auto* data = it->second.get(); + lock.unlock(); + return std::make_tuple(std::unique_ptr(nullptr), std::make_optional(data)); + } + lock.unlock(); + // find from prev + auto d = std::make_shared(); + d->table = std::string(tableView); + d->key = std::string(key); + do + { + auto prevKeyPage = std::dynamic_pointer_cast(getPrev()); + if (prevKeyPage) + { + auto dataOption = prevKeyPage->copyData(tableView, key); + if (c_fileLogLevel <= TRACE) + { + KeyPage_LOG(TRACE) + << LOG_DESC("get data from KeyPageStorage") << LOG_KV("table", tableView) + << LOG_KV("key", toHex(key)) << LOG_KV("found", static_cast(dataOption)); + } + if (dataOption) + { + d = std::move(*dataOption); + if (!d->key.empty()) + { // set entry to clean + auto* page = &std::get<0>(d->data); + page->clean(d->key); + if (c_fileLogLevel <= TRACE) + { + KeyPage_LOG(TRACE) + << LOG_DESC("import page") << LOG_KV("table", tableView) + << LOG_KV("key", toHex(key)) << LOG_KV("validCount", page->validCount()) + << LOG_KV("count", page->count()); + } + } + else + { + auto* meta = &std::get<1>(d->data); + meta->clean(); + if (c_fileLogLevel <= TRACE) + { + KeyPage_LOG(TRACE) + << LOG_DESC("import TableMeta") << LOG_KV("table", tableView) + << LOG_KV("size", meta->size()); + } + } + d->entry.setStatus(Entry::Status::NORMAL); + break; + } + } + else + { + auto [error, entry] = getRawEntryFromStorage(tableView, key); + if (error) + { + KeyPage_LOG(ERROR) + << LOG_DESC("getData error") << LOG_KV("table", tableView) + << LOG_KV("key", toHex(key)) << LOG_KV("error", error->errorMessage()); + return std::make_tuple(std::move(error), std::nullopt); + } + if (c_fileLogLevel <= TRACE) + { + KeyPage_LOG(TRACE) + << LOG_DESC("get data from storage") << LOG_KV("table", tableView) + << LOG_KV("key", toHex(key)) << LOG_KV("found", static_cast(entry)); + } + if (entry) + { + entry->setStatus(Entry::Status::NORMAL); + d = std::make_shared(std::string(tableView), std::string(key), + std::move(*entry), key.empty() ? Data::Type::TableMeta : Data::Type::Page); + break; + } + } + if (mustExist && !m_ignoreNotExist) + { + KeyPage_LOG(FATAL) << LOG_DESC("data should exist") << LOG_KV("table", tableView) + << LOG_KV("key", toHex(key)); + } + if (m_ignoreNotExist) + { + KeyPage_LOG(INFO) << LOG_DESC("data should exist but ignore not exist") + << LOG_KV("table", tableView) << LOG_KV("key", toHex(key)); + } + if (c_fileLogLevel <= TRACE) + { + KeyPage_LOG(TRACE) << LOG_DESC("create empty data") << LOG_KV("table", tableView) + << LOG_KV("key", toHex(key)); + } + if (key.empty()) + { + d->data = KeyPageStorage::TableMeta(); + d->type = Data::Type::TableMeta; + } + else + { + d->data = KeyPageStorage::Page(); + d->type = Data::Type::Page; + } + d->entry.setStatus(Entry::Status::EMPTY); + } while (false); + + { // insert into cache + auto [bucket, writeLock] = getMutBucket(d->table, d->key); + boost::ignore_unused(writeLock); + auto* data = + bucket->container.emplace(std::make_pair(keyPair, std::move(d))).first->second.get(); + return std::make_tuple(nullptr, std::make_optional(data)); + } +} + +auto KeyPageStorage::getEntryFromPage(std::string_view table, std::string_view key) + -> std::pair> +{ + // key is empty means the data is TableMeta + auto [error, data] = getData(table, TABLE_META_KEY); + if (error) + { + return std::make_pair(std::move(error), std::nullopt); + } + auto* meta = &std::get<1>(data.value()->data); + auto readLock = meta->rLock(); + if (key.empty()) + { // table meta + if (meta->size() > 0) + { + if (c_fileLogLevel <= TRACE) + { + KeyPage_LOG(TRACE) << LOG_DESC("return meta entry") << LOG_KV("table", table) + << LOG_KV("size", meta->size()) + << LOG_KV("status", int(data.value()->entry.status())) + << LOG_KV("dirty", data.value()->entry.dirty()); + } + if (data.value()->entry.dirty()) + { + Entry entry; + entry.setObject(*meta); + entry.setStatus(data.value()->entry.status()); + return std::make_pair(nullptr, std::move(entry)); + } + return std::make_pair(nullptr, data.value()->entry); + } + return std::make_pair(nullptr, std::nullopt); + } + auto pageInfoOp = meta->getPageInfoNoLock(key); + if (pageInfoOp) + { + Data* pageData = pageInfoOp.value()->getPageData(); + if (pageData == nullptr) + { + auto [error, pageDataOp] = getData( + table, pageInfoOp.value()->getPageKey(), pageInfoOp.value()->getCount() > 0); + if (error) + { + return std::make_pair(std::move(error), std::nullopt); + } + pageData = pageDataOp.value(); + pageInfoOp.value()->setPageData(pageData); + } + if (pageData->entry.status() == Entry::Status::EMPTY) + { + return std::make_pair(nullptr, std::nullopt); + } + auto* page = &std::get<0>(pageData->data); + if (page->validCount() != pageInfoOp.value()->getCount()) + { + if (m_ignoreNotExist) + { + KeyPage_LOG(INFO) << LOG_DESC("getEntryFromPage page count mismatch ignore") + << LOG_KV("key", toHex(key)) + << LOG_KV("pageKey", toHex(pageInfoOp.value()->getPageKey())) + << LOG_KV("count", pageInfoOp.value()->getCount()) + << LOG_KV("realCount", page->validCount()); + } + else + { + KeyPage_LOG(FATAL) + << LOG_DESC("getEntryFromPage page valid count mismatch") + << LOG_KV("pageKey", toHex(pageInfoOp.value()->getPageKey())) + << LOG_KV("key", toHex(key)) << LOG_KV("count", pageInfoOp.value()->getCount()) + << LOG_KV("realCount", page->validCount()); + } + } + + if (m_readOnly) + { // TODO: check condition, if key is pageKey, return page + if (pageInfoOp.value()->getPageKey() != key) + { + KeyPage_LOG(FATAL) + << LOG_DESC("getEntryFromPage readonly try to read entry(should read page)") + << LOG_KV("table", table) + << LOG_KV("pageKey", toHex(pageInfoOp.value()->getPageKey())) + << LOG_KV("key", toHex(key)); + } + if (page->size() > 0) + { + auto pageReadLock = page->rLock(); + if (c_fileLogLevel <= TRACE) + { + KeyPage_LOG(TRACE) + << LOG_DESC("return page entry") << LOG_KV("table", table) + << LOG_KV("startKey", toHex(page->startKey())) + << LOG_KV("EndKey", toHex(page->endKey())) + << LOG_KV("pageSize", page->size()) << LOG_KV("valid", page->validCount()) + << LOG_KV("count", page->count()) + << LOG_KV("dirty", data.value()->entry.dirty()); + } + Entry entry; + entry.setObject(*page); + entry.setStatus(pageData->entry.status()); + return std::make_pair(nullptr, std::move(entry)); + } + return std::make_pair(nullptr, std::nullopt); + } + auto entry = page->getEntry(key); + // if (c_fileLogLevel <= TRACE) + // { // FIXME: this log is only for debug, comment it when release + // KeyPage_LOG(TRACE) << LOG_DESC("getEntry from page") << LOG_KV("table", table) + // << LOG_KV("pageKey", toHex(pageKey.value())) + // << LOG_KV("key", toHex(key)) + // << LOG_KV("value", entry ? toHex(entry->get()) : "Not found"); + // } + return std::make_pair(nullptr, std::move(entry)); + } + return std::make_pair(nullptr, std::nullopt); +} + +auto KeyPageStorage::setEntryToPage(std::string table, std::string key, Entry entry) + -> Error::UniquePtr +{ + auto [error, data] = getData(table, TABLE_META_KEY); + if (error) + { + return std::move(error); + } + auto* meta = &std::get<1>(data.value()->data); + auto metaWriteLock = meta->lock(); + // insert or update + auto pageInfoOption = meta->getPageInfoNoLock(key); + std::string pageKey = key; + Data* pageData = nullptr; + if (pageInfoOption) + { + pageKey = pageInfoOption.value()->getPageKey(); + pageData = pageInfoOption.value()->getPageData(); + } + std::optional entryOld; + if (pageData == nullptr) + { + bool shouldExist = + pageInfoOption.has_value() ? (pageInfoOption.value()->getCount() > 0) : false; + auto [e, pageDataOp] = getData(table, pageKey, shouldExist); + if (e) + { + return std::move(e); + } + pageData = pageDataOp.value(); + if (pageInfoOption) + { + pageInfoOption.value()->setPageData(pageData); + } + auto* page = &std::get<0>(pageData->data); + if (shouldExist && page->validCount() != pageInfoOption.value()->getCount()) + { + KeyPage_LOG(FATAL) << LOG_DESC("page valid count mismatch") << LOG_KV("key", toHex(key)) + << LOG_KV("count", pageInfoOption.value()->getCount()) + << LOG_KV("realCount", page->validCount()); + } + } + // if new entry is too big, it will trigger split + auto* page = &std::get<0>(pageData->data); + { + auto ret = page->setEntry(key, std::move(entry)); + entryOld = std::move(std::get<0>(ret)); + auto pageInfoChanged = std::get<1>(ret); + + if (pageInfoChanged) + { + if (pageData->entry.status() == Entry::Status::EMPTY) + { // new page insert, if entries is empty means page delete entry which not exist + meta->insertPageInfoNoLock(PageInfo(page->endKey(), (uint16_t)page->validCount(), + (uint16_t)page->size(), pageData)); + // pageData->entry.setStatus(Entry::Status::NORMAL); + pageData->entry.setStatus(Entry::Status::MODIFIED); + } + else + { + auto oldPageKey = meta->updatePageInfoNoLock( + pageKey, page->endKey(), page->validCount(), page->size(), pageInfoOption); + pageData->entry.setStatus(Entry::Status::MODIFIED); + if (oldPageKey) + { // the page key is changed, 1. delete the last key, 2. insert a bigger key + // the container need to be updated + pageData = changePageKey(table, oldPageKey.value(), page->endKey()); + page = &std::get<0>(pageData->data); + if (page->validCount() == 0) + { // page is empty because delete, not update startKey and mark as deleted + pageData->entry.setStatus(Entry::Status::DELETED); + } + } + } + } + else + { + pageData->entry.setStatus(Entry::Status::MODIFIED); + } + // page is modified, the meta maybe modified, mark meta as dirty + data.value()->entry.setStatus(Entry::Status::MODIFIED); + } + pageKey = page->endKey(); + if (page->size() > m_pageSize && page->validCount() > 1) + { // split page, TODO: if dag trigger split, it maybe split to different page? + if (c_fileLogLevel <= TRACE) + { + KeyPage_LOG(TRACE) << LOG_DESC("trigger split page") << LOG_KV("table", table) + << LOG_KV("pageKey", toHex(pageKey)) << LOG_KV("size", page->size()) + << LOG_KV("m_pageSize", m_pageSize) + << LOG_KV("validCount", page->validCount()) + << LOG_KV("count", page->count()); + } + auto newPage = page->split(m_splitSize); + // update old meta pageInfo + auto oldStartKey = meta->updatePageInfoNoLock( + pageKey, page->endKey(), page->validCount(), page->size(), pageInfoOption); + if (oldStartKey) + { // if the startKey of page changed, the container also need to be updated + pageData = changePageKey(table, oldStartKey.value(), page->endKey()); + page = &std::get<0>(pageData->data); + } + + KeyPage_LOG(DEBUG) << LOG_DESC("split page finished") << LOG_KV("table", table) + << LOG_KV("pageKey", toHex(page->endKey())) + << LOG_KV("newPageKey", toHex(newPage.endKey())) + << LOG_KV("validCount", page->validCount()) + << LOG_KV("newValidCount", newPage.validCount()) + << LOG_KV("count", page->count()) << LOG_KV("newCount", newPage.count()) + << LOG_KV("pageSize", page->size()) + << LOG_KV("newPageSize", newPage.size()); + // insert new page to container, newPageInfo to meta + insertNewPage(table, newPage.endKey(), meta, std::move(newPage)); + data.value()->entry.setStatus(Entry::Status::MODIFIED); + } + else if (page->size() < m_mergeSize) + { // merge operation + // get next page, check size and merge current into next + auto nextPageKey = meta->getNextPageKeyNoLock(page->endKey()); + if (nextPageKey) + { + auto [error, nextPageData] = getData(table, nextPageKey.value()); + boost::ignore_unused(error); + if (error) + { + KeyPage_LOG(FATAL) + << LOG_DESC("merge page getData error") << LOG_KV("table", table) + << LOG_KV("key", toHex(key)) << LOG_KV("pageKey", toHex(pageKey)); + } + auto* nextPage = &std::get<0>(nextPageData.value()->data); + if (nextPage->size() < m_splitSize && nextPage != page) + { + auto endKey = page->endKey(); + auto nextEndKey = nextPage->endKey(); + KeyPage_LOG(DEBUG) + << LOG_DESC("merge current page into next") << LOG_KV("table", table) + << LOG_KV("key", toHex(key)) << LOG_KV("pageKey", toHex(pageKey)) + << LOG_KV("pageEnd", toHex(endKey)) << LOG_KV("pageCount", page->validCount()) + << LOG_KV("pageSize", page->size()) << LOG_KV("nextPageKey", toHex(nextEndKey)) + << LOG_KV("nextPageCount", nextPage->validCount()) + << LOG_KV("nextPageSize", nextPage->size()); + nextPage->merge(*page); + // remove current page info and update next page info + auto nextPageInfoOp = meta->deletePageInfoNoLock(endKey, pageInfoOption); + auto oldStartKey = meta->updatePageInfoNoLock(nextEndKey, nextPage->endKey(), + nextPage->validCount(), nextPage->size(), nextPageInfoOp); + // old page also need write to disk to clean data, so not remove old page + // nextPageData.value()->entry.setDirty(true); + // pageData->entry.setDirty(true); + nextPageData.value()->entry.setStatus(Entry::Status::MODIFIED); + pageData->entry.setStatus(Entry::Status::DELETED); + if (oldStartKey) + { // if the startKey of nextPage changed, the container also need to be updated + changePageKey(table, oldStartKey.value(), nextPage->endKey()); + } + data.value()->entry.setStatus(Entry::Status::MODIFIED); + } + } + } + if (m_recoder.local()) + { + m_recoder.local()->log( + Recoder::Change(std::move(table), std::move(key), std::move(entryOld))); + } + return nullptr; +} + +auto KeyPageStorage::getRawEntryFromStorage(std::string_view table, std::string_view key) + -> std::pair> +{ + auto prev = getPrev(); // prev must not null + if (!prev) + { + KeyPage_LOG(FATAL) << LOG_DESC("previous stortage is null"); + return std::make_pair(nullptr, std::nullopt); + } + std::promise>> getPromise; + prev->asyncGetRow(table, key, [&](Error::UniquePtr error, std::optional entry) { + KeyPage_LOG(TRACE) << LOG_DESC("getEntry from previous") << LOG_KV("table", table) + << LOG_KV("key", toHex(key)) + << LOG_KV("size", entry ? entry->size() : 0); + getPromise.set_value({std::move(error), std::move(entry)}); + }); + return getPromise.get_future().get(); +} +auto KeyPageStorage::getSysTableRawEntry(std::string_view table, std::string_view key) + -> std::pair> +{ + auto [bucket, lock] = getBucket(table, key); + boost::ignore_unused(lock); + auto it = bucket->container.find(std::make_pair(std::string(table), std::string(key))); + if (it != bucket->container.end()) + { + auto& entry = it->second->entry; + lock.unlock(); + return std::make_pair(nullptr, std::make_optional(entry)); + } + lock.unlock(); + // find from prev + auto [error, entryOption] = getRawEntryFromStorage(table, key); + if (!error && entryOption) + { + auto entry = importExistingEntry(table, key, std::move(entryOption.value())); + return std::make_pair(nullptr, std::make_optional(std::move(entry))); + } + return std::make_pair(std::move(error), entryOption); +} + +auto KeyPageStorage::importExistingEntry(std::string_view table, std::string_view key, Entry entry) + -> Entry +{ + if (m_readOnly) + { + return entry; + } + + // entry.setDirty(false); + entry.setStatus(Entry::NORMAL); + KeyPage_LOG(DEBUG) << "import entry, " << table << " | " << key; + auto [bucket, lock] = getMutBucket(table, key); + boost::ignore_unused(lock); + auto it = bucket->container.find(std::make_pair(std::string(table), std::string(key))); + if (it == bucket->container.end()) + { + auto d = std::make_shared( + std::string(table), std::string(key), std::move(entry), Data::Type::NormalEntry); + auto tableKey = std::make_pair(std::string(table), std::string(key)); + it = bucket->container.emplace(std::make_pair(std::move(tableKey), std::move(d))).first; + } + else + { + KeyPage_LOG(DEBUG) << "Fail import existsing entry, " << table << " | " << toHex(key); + } + + return it->second->entry; +} + +auto KeyPageStorage::count(const std::string_view& table) -> std::pair +{ + // if SYS_TABLES is not supported + if (m_ignoreTables->find(table) != m_ignoreTables->end()) + { + return std::make_pair( + 0, BCOS_ERROR_PTR(StorageError::ReadError, + std::string("count ").append(table).append(" is not supported"))); + } + + // page + auto [error, data] = getData(table, TABLE_META_KEY); + if (error) + { + return std::make_pair( + 0, BCOS_ERROR_PTR(StorageError::ReadError, + std::string("get table meta data failed, table:").append(table))); + } + auto* meta = &std::get<1>(data.value()->data); + auto readLock = meta->rLock(); + auto& pageInfo = meta->getAllPageInfoNoLock(); + size_t count = 0; + for (auto& info : pageInfo) + { + count += info.getCount(); + } + readLock.unlock(); + return std::make_pair(count, nullptr); +} + +} // namespace bcos::storage diff --git "a/BFPL\345\243\271/bcos-table/src/KeyPageStorage.h" "b/BFPL\345\243\271/bcos-table/src/KeyPageStorage.h" new file mode 100644 index 00000000..6f74fc84 --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/src/KeyPageStorage.h" @@ -0,0 +1,1258 @@ +/* + * Copyright (C) 2021 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. + * + * @brief KeyPage implementation of StorageInterface + * @file KeyPageStorage.h + * @author: xingqiangbai + * @date: 2022-04-19 + */ +#pragma once + +#include "StateStorageInterface.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KeyPage_LOG(LEVEL) BCOS_LOG(LEVEL) << "[STORAGE-KeyPage]" +namespace std +{ +template <> +struct hash> +{ + auto operator()(const std::pair& p) const -> size_t + { + // calculate the hash result + auto hash_result = std::hash{}(p.first); + boost::hash_combine(hash_result, std::hash{}(p.second)); + return hash_result; + } +}; + +template <> +struct hash> +{ + auto operator()(const std::pair& p) const -> size_t + { + // calculate the hash result + auto hash_result = std::hash{}(p.first); + boost::hash_combine(hash_result, std::hash{}(p.second)); + return hash_result; + } +}; + +} // namespace std + +namespace bcos::storage +{ +constexpr static int32_t ARCHIVE_FLAG = + boost::archive::no_header | boost::archive::no_codecvt | boost::archive::no_tracking; + +const char* const TABLE_META_KEY = ""; +const size_t MIN_PAGE_SIZE = 2048; +class KeyPageStorage : public virtual storage::StateStorageInterface +{ +public: + using Ptr = std::shared_ptr; + + explicit KeyPageStorage(std::shared_ptr _prev, size_t _pageSize = 10240, + uint32_t _blockVersion = (uint32_t)bcos::protocol::BlockVersion::V3_0_VERSION, + std::shared_ptr>> _ignoreTables = nullptr, + bool _ignoreNotExist = false) + : storage::StateStorageInterface(std::move(_prev)), + m_blockVersion(_blockVersion), + m_pageSize(_pageSize > MIN_PAGE_SIZE ? _pageSize : MIN_PAGE_SIZE), + m_splitSize(m_pageSize / 3 * 2), + m_mergeSize(m_pageSize / 4), + m_buckets(std::thread::hardware_concurrency()), + m_ignoreTables(std::move(_ignoreTables)), + m_ignoreNotExist(_ignoreNotExist) + { + if (!m_ignoreTables) + { + auto ignore = std::make_shared>>(); + ignore->insert(std::string(SYS_TABLES)); + m_ignoreTables = ignore; + } + } + + KeyPageStorage(const KeyPageStorage&) = delete; + KeyPageStorage& operator=(const KeyPageStorage&) = delete; + + KeyPageStorage(KeyPageStorage&&) = delete; + KeyPageStorage& operator=(KeyPageStorage&&) = delete; + + ~KeyPageStorage() override + { + m_recoder.clear(); + m_buckets.clear(); + } + + void asyncGetPrimaryKeys(std::string_view table, + const std::optional& _condition, + std::function)> _callback) override; + + void asyncGetRow(std::string_view tableView, std::string_view keyView, + std::function)> _callback) override; + + void asyncGetRows(std::string_view tableView, + const std::variant, + const gsl::span>& _keys, + std::function>)> _callback) + override; + + void asyncSetRow(std::string_view tableView, std::string_view keyView, Entry entry, + std::function callback) override; + + void parallelTraverse(bool onlyDirty, std::function + callback) const override; + + crypto::HashType hash(const bcos::crypto::Hash::Ptr& hashImpl) const override; + + void rollback(const Recoder& recoder) override; + + struct Data; + class PageInfo + { // all methods is not thread safe + public: + auto operator<(const PageInfo& rhs) const -> bool + { + return m_data->pageKey < rhs.m_data->pageKey; + } + PageInfo() = default; + ~PageInfo() = default; + PageInfo(std::string _pageKey, uint16_t _count, uint16_t _size, Data* p) + : m_data(std::make_shared(std::move(_pageKey), _count, _size)), + m_pageData(p) + {} + PageInfo(PageInfo&&) = default; + auto operator=(PageInfo&&) -> PageInfo& = default; + PageInfo(const PageInfo& page) = default; + auto operator=(const PageInfo& page) -> PageInfo& = default; + + void setPageKey(std::string key) + { + prepareMyData(); + m_data->pageKey = std::move(key); + } + [[nodiscard]] auto getPageKey() const -> std::string { return m_data->pageKey; } + void setCount(uint16_t _count) + { + prepareMyData(); + m_data->count = _count; + } + [[nodiscard]] auto getCount() const -> uint16_t { return m_data->count; } + void setSize(uint16_t _size) + { + prepareMyData(); + m_data->size = _size; + } + [[nodiscard]] auto getSize() const -> uint16_t { return m_data->size; } + + [[nodiscard]] auto getPageData() const -> Data* { return m_pageData; } + void setPageData(Data* data) { m_pageData = data; } + + private: + void prepareMyData() + { + if (m_data.use_count() > 1) + { + m_data = + std::make_shared(m_data->pageKey, m_data->count, m_data->size); + } + } + struct PageInfoData + { + PageInfoData() = default; + PageInfoData(std::string _endKey, uint16_t _count, uint16_t _size) + : pageKey(std::move(_endKey)), count(_count), size(_size) + {} + // startKey is not involved in comparison + std::string pageKey; + uint16_t count = 0; + uint16_t size = 0; + }; + std::shared_ptr m_data = nullptr; + Data* m_pageData = nullptr; + friend class boost::serialization::access; + + template + void save(Archive& archive, const unsigned int version) const + { + std::ignore = version; + archive & m_data->pageKey; + archive & m_data->count; + archive & m_data->size; + } + template + void load(Archive& archive, const unsigned int version) + { + std::ignore = version; + m_data = std::make_shared(); + archive & m_data->pageKey; + archive & m_data->count; + archive & m_data->size; + } + BOOST_SERIALIZATION_SPLIT_MEMBER() + }; + + struct TableMeta + { + TableMeta() : pages(std::make_unique>()) {} + ~TableMeta() = default; + TableMeta(const std::string_view value) + { + if (value.empty()) + { + return; + } + boost::iostreams::stream inputStream( + value.data(), value.size()); + boost::archive::binary_iarchive archive(inputStream, ARCHIVE_FLAG); + archive >> *this; + } + TableMeta(const TableMeta& meta) + { + pages = std::make_unique>(); + *pages = *meta.pages; + } + TableMeta& operator=(const TableMeta& t) + { + if (this != &t) + { + pages = std::make_unique>(); + *pages = *t.pages; + } + return *this; + } + TableMeta(TableMeta&& t) { pages = std::move(t.pages); } + TableMeta& operator=(TableMeta&& t) + { + if (this != &t) + { + pages = std::move(t.pages); + } + return *this; + } + auto lower_bound(std::string_view key) + { + return std::lower_bound(pages->begin(), pages->end(), key, + [](const PageInfo& lhs, const std::string_view& rhs) { + return lhs.getPageKey() < rhs; + }); + } + auto upper_bound(std::string_view key) + { + return std::upper_bound(pages->begin(), pages->end(), key, + [](const std::string_view& lhs, const PageInfo& rhs) { + return lhs < rhs.getPageKey(); + }); + } + inline std::optional getPageInfoNoLock(std::string_view key) + { + ++getPageInfoCount; + if (pages->empty()) + { // if pages is empty + return std::nullopt; + } + if (lastPageInfoIndex < pages->size()) + { + auto* lastPageInfo = &pages->at(lastPageInfoIndex); + if (lastPageInfo->getPageData()) + { + auto page = &std::get<0>(lastPageInfo->getPageData()->data); + if (!page->startKey().empty() && page->startKey() <= key && + key <= page->endKey()) + { + hit += 1; + return lastPageInfo; + } + } + } + auto it = lower_bound(key); + if (it != pages->end()) + { + lastPageInfoIndex = it - pages->begin(); + return &*it; + } + if (pages->rbegin()->getPageKey().empty()) + { // page is empty because of rollback + return std::nullopt; + } + lastPageInfoIndex = pages->size() - 1; + return &pages->back(); + } + + auto getNextPageKeyNoLock(std::string_view key) -> std::optional + { + if (key.empty()) + { + return std::nullopt; + } + auto it = upper_bound(key); + if (it != pages->end()) + { + return it->getPageKey(); + } + return std::nullopt; + } + + std::vector& getAllPageInfoNoLock() { return std::ref(*pages); } + void insertPageInfo(PageInfo&& pageInfo) + { + std::unique_lock lock(mutex); + insertPageInfoNoLock(std::move(pageInfo)); + } + void insertPageInfoNoLock(PageInfo&& pageInfo) + { + if (pageInfo.getPageKey().empty()) + { + KeyPage_LOG(FATAL) << LOG_DESC("insert empty pageInfo"); + return; + } + auto it = lower_bound(pageInfo.getPageKey()); + auto newIt = pages->insert(it, std::move(pageInfo)); + KeyPage_LOG(DEBUG) << LOG_DESC("insert new pageInfo") + << LOG_KV("pageKey", toHex(newIt->getPageKey())) + << LOG_KV("valid", newIt->getCount()) + << LOG_KV("size", newIt->getSize()); + } + + std::optional updatePageInfoNoLock(std::string_view oldEndKey, + const std::string& pageKey, size_t count, size_t size, + std::optional pageInfo) + { + std::optional oldPageKey; + auto updateInfo = [&](PageInfo* p) { + if (p->getPageKey() != pageKey) + { + oldPageKey = p->getPageKey(); + p->setPageKey(pageKey); + if (pageKey.empty()) + { + p->setPageData(nullptr); + } + } + if (c_fileLogLevel <= TRACE) + { + KeyPage_LOG(TRACE) + << LOG_DESC("updatePageInfo") + << LOG_KV("oldPageKey", + oldPageKey.has_value() ? toHex(oldPageKey.value()) : "not changed") + << LOG_KV("oldEndKey", toHex(oldEndKey)) + << LOG_KV("newPageKey", toHex(pageKey)) << LOG_KV("count", count) + << LOG_KV("size", size); + } + p->setCount(count); + p->setSize(size); + }; + if (pageInfo.has_value()) + { + updateInfo(pageInfo.value()); + } + else + { + auto it = lower_bound(oldEndKey); + if (it != pages->end()) + { + updateInfo(&*it); + } + else + { + KeyPage_LOG(FATAL) + << LOG_DESC("updatePageInfo not found") + << LOG_KV("oldEndKey", toHex(oldEndKey)) << LOG_KV("endKey", toHex(pageKey)) + << LOG_KV("valid", count) << LOG_KV("size", size); + } + } + return oldPageKey; + } + + std::optional deletePageInfoNoLock( + std::string_view endkey, std::optional pageInfo) + { // remove current page info and update next page start key + std::vector::iterator it; + if (pageInfo) + { + auto offset = pageInfo.value() - &*pages->begin(); + it = pages->begin() + offset; + } + else + { + it = lower_bound(endkey); + } + if (it != pages->end() && it->getPageKey() == endkey) + { + return &*pages->erase(it); + } + return std::nullopt; + } + size_t size() const + { + std::shared_lock lock(mutex); + return pages->size(); + } + + void clean() + { + for (auto it = pages->begin(); it != pages->end();) + { + it->setPageData(nullptr); + if (it->getCount() == 0 || it->getPageKey().empty()) + { + KeyPage_LOG(DEBUG) << LOG_DESC("TableMeta clean empty page") + << LOG_KV("pageKey", toHex(it->getPageKey())); + it = pages->erase(it); + } + else + { + ++it; + } + } + } + std::unique_lock lock() const { return std::unique_lock(mutex); } + std::shared_lock rLock() const { return std::shared_lock(mutex); } + + friend std::ostream& operator<<( + std::ostream& os, const bcos::storage::KeyPageStorage::TableMeta& meta) + { + auto lock = std::shared_lock(meta.mutex); + os << "["; + for (auto& pageInfo : *meta.pages) + { + os << "{" + // << "startKey=" << toHex(pageInfo.getStartKey()) + << ",endKey=" << toHex(pageInfo.getPageKey()) << ",count=" << pageInfo.getCount() + << ",size=" << pageInfo.getSize() << "}"; + } + os << "]"; + return os; + } + double hitRate() { return hit / (double)getPageInfoCount; } + uint64_t rowCount() { return m_rows; } + + private: + uint32_t getPageInfoCount = 0; + uint32_t hit = 0; + mutable uint64_t m_rows = 0; + mutable std::shared_mutex mutex; + std::unique_ptr> pages = nullptr; + friend class boost::serialization::access; + size_t lastPageInfoIndex = 0; + template + void save(Archive& ar, const unsigned int version) const + { + std::ignore = version; + // auto len = (uint32_t)pages->size(); + // ar& len; + // for (size_t i = 0; i < pages->size(); ++i) + // { + // if (pages->at(i).getCount() == 0) + // { + // continue; + // } + // ar & pages->at(i); + // } + int invalid = 0; + m_rows = 0; + for (auto it = pages->begin(); it != pages->end();) + { + if (it->getCount() == 0 || it->getPageKey().empty()) + { + KeyPage_LOG(DEBUG) + << LOG_DESC("TableMeta empty page") + << LOG_KV("pageKey", toHex(it->getPageKey())) + << LOG_KV("count", it->getCount()) << LOG_KV("size", it->getSize()); + it = pages->erase(it); + ++invalid; + } + else + { + m_rows += it->getCount(); + ++it; + } + } + ar << *pages; + KeyPage_LOG(DEBUG) << LOG_DESC("Serialize meta") << LOG_KV("valid", pages->size()) + << LOG_KV("invalid", invalid); + } + template + void load(Archive& ar, const unsigned int version) + { + std::ignore = version; + // uint32_t s = 0; + // ar& s; + // pages = std::make_unique>(s, PageInfo()); + // for (size_t i = 0; i < pages->size(); ++i) + // { + // ar & pages->at(i); + // } + pages = std::make_unique>(); + ar >> *pages; + } + BOOST_SERIALIZATION_SPLIT_MEMBER() + }; + + struct Page + { + Page() : m_size(0) {} + ~Page() = default; + Page(const std::string_view& value, const std::string_view& pageKey) + { + if (value.empty()) + { + return; + } + boost::iostreams::stream inputStream( + value.data(), value.size()); + boost::archive::binary_iarchive archive(inputStream, ARCHIVE_FLAG); + archive >> *this; + if (pageKey != entries.rbegin()->first) + { + KeyPage_LOG(INFO) << LOG_DESC("load page with invalid pageKey") + << LOG_KV("pageKey", toHex(pageKey)) + << LOG_KV("validPageKey", toHex(entries.rbegin()->first)) + << LOG_KV("valid", m_validCount) + << LOG_KV("count", entries.size()); + m_invalidPageKeys.insert(std::string(pageKey)); + } + } + Page(const Page& page) + : entries(page.entries), + m_size(page.m_size), + m_validCount(page.m_validCount), + m_invalidPageKeys(page.m_invalidPageKeys) + {} + Page& operator=(const Page& p) + { + if (this != &p) + { + entries = p.entries; + m_size = p.m_size; + m_validCount = p.m_validCount; + m_invalidPageKeys = p.m_invalidPageKeys; + } + return *this; + } + Page(Page&& p) noexcept + { + entries = std::move(p.entries); + m_size = p.m_size; + m_validCount = p.m_validCount; + m_invalidPageKeys = std::move(p.m_invalidPageKeys); + } + Page& operator=(Page&& p) + { + if (this != &p) + { + entries = std::move(p.entries); + m_size = p.m_size; + m_validCount = p.m_validCount; + m_invalidPageKeys = std::move(p.m_invalidPageKeys); + } + return *this; + } + + std::optional getEntry(std::string_view key) + { + std::shared_lock lock(mutex); + auto it = entries.find(key); + if (it != entries.end()) + { + // if (c_fileLogLevel <= bcos::LogLevel::TRACE) + // { // FIXME: this log is only for debug, comment it when release + // KeyPage_LOG(TRACE) + // << LOG_DESC("getEntry") << LOG_KV("pageKey", + // toHex(entries.begin()->first)) + // << LOG_KV("valid", m_validCount) << LOG_KV("count", entries.size()) + // << LOG_KV("key", toHex(key)) << LOG_KV("status", + // (int)it->second.status()); + // } + if (it->second.status() != Entry::Status::DELETED && + it->second.status() != Entry::EMPTY) + { + return std::make_optional(it->second); + } + } + else + { + // if (c_fileLogLevel <= bcos::LogLevel::TRACE) + // { // FIXME: this log is only for debug, comment it when release + // KeyPage_LOG(TRACE) + // << LOG_DESC("getEntry not found") + // << LOG_KV( + // "pageKey", entries.empty() ? "empty" : + // toHex(entries.begin()->first)) + // << LOG_KV("valid", m_validCount) << LOG_KV("count", entries.size()) + // << LOG_KV("key", toHex(key)); + // } + } + return std::nullopt; + } + std::pair>&, std::unique_lock> + getEntries() + { + std::unique_lock lock(mutex); + return std::make_pair(std::ref(entries), std::move(lock)); + } + inline std::tuple, bool> setEntry( + const std::string_view& key, Entry&& entry) + { // TODO: do not exist entry optimization: insert a empty entry to cache, then + // entry status none should return null optional + bool pageInfoChanged = false; + std::optional ret; + std::unique_lock lock(mutex); + auto it = entries.lower_bound(key); + m_size += entry.size(); + if (it != entries.end() && it->first == key) + { // delete exist entry + m_size -= it->second.size(); + if (entry.status() != Entry::Status::DELETED) + { + if (it->second.status() == Entry::Status::DELETED) + { + ++m_validCount; + pageInfoChanged = true; + } + } + else + { + if (it->second.status() != Entry::Status::DELETED) + { + --m_validCount; + pageInfoChanged = true; + } + } + if (m_invalidPageKeys.size() == 1) + { + pageInfoChanged = true; + } + ret = std::move(it->second); + it->second = std::move(entry); + // if (c_fileLogLevel <= bcos::LogLevel::TRACE) + // { // FIXME: this log is only for debug, comment it when release + // KeyPage_LOG(TRACE) + // << LOG_DESC("setEntry update") + // << LOG_KV("pageKey", toHex(entries.rbegin()->first)) + // << LOG_KV("valid", m_validCount) << LOG_KV("count", entries.size()) + // << LOG_KV("key", toHex(key)) << LOG_KV("status", + // (int)it->second.status()) + // << LOG_KV("pageInfoChanged", pageInfoChanged); + // } + } + else + { + pageInfoChanged = true; + m_size += key.size(); + if (entry.status() != Entry::Status::DELETED) + { + ++m_validCount; + } + if (entries.empty() || key > entries.rbegin()->first) + { + if (!entries.empty()) + { // means key > entries.rbegin()->first is true + m_invalidPageKeys.insert(entries.rbegin()->first); + } + auto it = m_invalidPageKeys.find(std::string(key)); + if (it != m_invalidPageKeys.end()) + { + m_invalidPageKeys.erase(it); + KeyPage_LOG(DEBUG) << LOG_DESC("invalid pageKey become valid") + << LOG_KV("pageKey", toHex(key)); + } + } + entries.insert(it, std::make_pair(std::string(key), std::move(entry))); + // if (c_fileLogLevel <= bcos::LogLevel::TRACE) + // { // FIXME: this log is only for debug, comment it when release + // KeyPage_LOG(TRACE) << LOG_DESC("setEntry insert") + // << LOG_KV("pageKey", toHex(entries.rbegin()->first)) + // << LOG_KV("key", toHex(key)) << LOG_KV("valid", + // m_validCount) + // << LOG_KV("count", entries.size()) + // << LOG_KV("pageInfoChanged", pageInfoChanged); + // } + } + return std::make_tuple(std::move(ret), pageInfoChanged); + } + auto size() const -> size_t + { + std::shared_lock lock(mutex); + return m_size; + } + auto validCount() const -> size_t + { + std::shared_lock lock(mutex); + return m_validCount; + } + auto count() const -> size_t + { + std::shared_lock lock(mutex); + return entries.size(); + } + auto invalidKeySet() const -> const std::set& + { + std::shared_lock lock(mutex); + return m_invalidPageKeys; + } + auto invalidKeyCount() const -> size_t { return m_invalidPageKeys.size(); } + auto startKey() const -> std::string + { + std::shared_lock lock(mutex); + if (entries.empty()) + { + return ""; + } + return entries.begin()->first; + } + std::string endKey() const + { + std::shared_lock lock(mutex); + if (entries.empty()) + { + return ""; + } + return entries.rbegin()->first; + } + auto split(size_t threshold) + { + auto page = Page(); + std::unique_lock lock(mutex); + // split this page to two pages + auto iter = entries.begin(); + while (iter != entries.end()) + { + m_size -= iter->second.size(); + m_size -= iter->first.size(); + page.m_size += iter->second.size(); + page.m_size += iter->first.size(); + if (iter->second.status() != Entry::Status::DELETED) + { + --m_validCount; + ++page.m_validCount; + } + if (!m_invalidPageKeys.empty() && iter->first >= *m_invalidPageKeys.begin()) + { + page.m_invalidPageKeys.insert( + m_invalidPageKeys.extract(m_invalidPageKeys.begin())); + } + auto ret = page.entries.insert(entries.extract(iter)); + assert(ret.inserted); + iter = entries.begin(); + if (m_size - iter->second.size() < threshold || entries.size() == 1) + { + break; + } + } + // if new pageKey has been pageKey, remove it from m_invalidPageKeys + auto it = m_invalidPageKeys.find(page.entries.rbegin()->first); + if (it != m_invalidPageKeys.end()) + { + m_invalidPageKeys.erase(it); + KeyPage_LOG(DEBUG) << LOG_DESC("invalid pageKey become valid because of split") + << LOG_KV("pageKey", toHex(page.entries.rbegin()->first)); + } + it = page.m_invalidPageKeys.find(page.entries.rbegin()->first); + if (it != page.m_invalidPageKeys.end()) + { + page.m_invalidPageKeys.erase(it); + KeyPage_LOG(DEBUG) << LOG_DESC("invalid pageKey become valid because of split") + << LOG_KV("pageKey", toHex(page.entries.rbegin()->first)); + } + return page; + } + + void merge(Page& p) + { + if (this != &p) + { + std::unique_lock lock(mutex); + for (auto iter = p.entries.begin(); iter != p.entries.end();) + { + m_size += iter->second.size(); + m_size += iter->first.size(); + p.m_size -= iter->second.size(); + p.m_size -= iter->first.size(); + if (iter->second.status() != Entry::Status::DELETED) + { + ++m_validCount; + --p.m_validCount; + } + entries.insert(p.entries.extract(iter)); + iter = p.entries.begin(); + } + // Merges two sorted lists into one + m_invalidPageKeys.merge(p.m_invalidPageKeys); + } + else + { + KeyPage_LOG(WARNING) + << LOG_DESC("merge self") << LOG_KV("startKey", toHex(entries.begin()->first)) + << LOG_KV("endKey", toHex(entries.rbegin()->first)) + << LOG_KV("valid", m_validCount) << LOG_KV("count", entries.size()); + } + } + void clean(const std::string_view& pageKey) + { + std::unique_lock lock(mutex); + for (auto iter = entries.begin(); iter != entries.end();) + { + if (iter->second.status() != Entry::Status::DELETED) + { + iter->second.setStatus(Entry::Status::NORMAL); + ++iter; + } + else + { + iter = entries.erase(iter); + } + } + m_invalidPageKeys.clear(); + if (!entries.empty() && pageKey != entries.rbegin()->first) + { + KeyPage_LOG(WARNING) << LOG_DESC("import page with invalid pageKey") + << LOG_KV("pageKey", toHex(pageKey)) + << LOG_KV("validPageKey", toHex(entries.rbegin()->first)) + << LOG_KV("count", entries.size()); + m_invalidPageKeys.insert(std::string(pageKey)); + } + if (entries.empty()) + { + KeyPage_LOG(DEBUG) + << LOG_DESC("import empty page") << LOG_KV("pageKey", toHex(pageKey)) + << LOG_KV("count", entries.size()); + } + } + auto hash(const std::string& table, const bcos::crypto::Hash::Ptr& hashImpl, + uint32_t blockVersion) const -> crypto::HashType + { + bcos::crypto::HashType pageHash(0); + auto hash = hashImpl->hash(table); + // std::shared_lock lock(mutex); + for (const auto& entry : entries) + { + if (entry.second.dirty()) + { + bcos::crypto::HashType entryHash(0); + if (blockVersion >= (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION) + { + entryHash = entry.second.hash(table, entry.first, hashImpl, blockVersion); + } + else + { // 3.0.0 + entryHash = hash ^ hashImpl->hash(entry.first) ^ + entry.second.hash(table, entry.first, hashImpl, blockVersion); + } + // if (c_fileLogLevel <= TRACE) + // { + // KeyPage_LOG(TRACE) + // << "Storage hash: " << LOG_KV("table", table) + // << LOG_KV("key", toHex(iter->first)) << LOG_KV("hash", + // entryHash.hex()); + // } + pageHash ^= entryHash; + } + } + return pageHash; + } + + void rollback(const Recoder::Change& change) + { + std::unique_lock lock(mutex); + auto it = entries.find(change.key); + if (change.entry) + { + if (it != entries.end()) + { // update + if (c_fileLogLevel <= bcos::LogLevel::TRACE) + { + KeyPage_LOG(TRACE) + << "Revert update: " << change.table << " | " << toHex(change.key) + << " | " << toHex(change.entry->get()); + } + if (it->second.status() == Entry::Status::DELETED && + change.entry->status() != Entry::Status::DELETED) + { + ++m_validCount; + } + else if (it->second.status() != Entry::Status::DELETED && + change.entry->status() == Entry::Status::DELETED) + { + --m_validCount; + } + m_size -= it->second.size(); + it->second = *change.entry; + m_size += it->second.size(); + } + else + { // delete, should not happen? + if (c_fileLogLevel <= bcos::LogLevel::TRACE) + { + KeyPage_LOG(TRACE) + << "Revert delete: " << change.table << " | " << toHex(change.key) + << " | " << toHex(change.entry->get()); + } + if (change.entry->status() != Entry::Status::DELETED) + { + ++m_validCount; + } + m_size -= change.entry->size(); + entries[change.key] = std::move(change.entry.value()); + } + } + else + { // rollback insert + if (it != entries.end()) + { // insert or update + if (c_fileLogLevel <= bcos::LogLevel::TRACE) + { + KeyPage_LOG(TRACE) + << "Revert insert: " << change.table << " | " << toHex(change.key); + } + m_size -= it->second.size(); + m_size -= it->first.size(); + if (it->second.status() != Entry::Status::DELETED) + { + --m_validCount; + } + entries.erase(it); + if (!m_invalidPageKeys.empty() && !entries.empty() && + entries.rbegin()->first == *m_invalidPageKeys.rbegin()) + { // if new pageKey has been pageKey, remove it from m_invalidPageKeys + m_invalidPageKeys.erase(std::prev(m_invalidPageKeys.end())); + } + } + else + { // delete a key not exist + KeyPage_LOG(DEBUG) + << "Revert invalid delete: " << change.table << " | " << toHex(change.key); + auto message = (boost::format("Not found rollback entry: %s:%s") % + change.table % change.key) + .str(); + BOOST_THROW_EXCEPTION(BCOS_ERROR(StorageError::UnknownError, message)); + } + } + } + auto lock() -> std::unique_lock { return std::unique_lock(mutex); } + auto rLock() -> std::shared_lock { return std::shared_lock(mutex); } + + private: + // PageInfo* pageInfo; + mutable std::shared_mutex mutex; + std::map> entries; + uint32_t m_size = 0; // page real size + uint32_t m_validCount = 0; // valid entry count + friend class boost::serialization::access; + // if startKey changed the old startKey need keep to delete old page + std::set m_invalidPageKeys; + template + void save(Archive& ar, const unsigned int version) const + { + std::ignore = version; + ar&(uint32_t)m_validCount; + size_t count = 0; + for (const auto& i : entries) + { + if (i.second.status() == Entry::Status::DELETED) + { // skip deleted entry + continue; + } + ++count; + ar& i.first; + auto value = i.second.get(); + ar&(uint32_t)value.size(); + ar.save_binary(value.data(), value.size()); + } + assert(count == m_validCount); + } + template + void load(Archive& ar, const unsigned int version) + { + std::ignore = version; + uint32_t count = 0; + ar& count; + m_validCount = count; + auto iter = entries.begin(); + for (size_t i = 0; i < m_validCount; ++i) + { + std::string key; + ar& key; + uint32_t len = 0; + ar& len; + m_size += len; + m_size += key.size(); + auto value = std::make_shared>(len, 0); + ar.load_binary(value->data(), value->size()); + Entry e; + e.setPointer(std::move(value)); + e.setStatus(Entry::Status::NORMAL); + iter = entries.emplace_hint(iter, std::move(key), std::move(e)); + } + } + BOOST_SERIALIZATION_SPLIT_MEMBER() + }; + + struct Data + { + enum Type : int8_t + { + Page = 0, + TableMeta = 1, + NormalEntry = 2, + }; + Data() = default; + ~Data() = default; + Data(std::string _table, std::string _key, Entry _entry, Type _type) + : table(std::move(_table)), key(std::move(_key)), type(_type), entry(std::move(_entry)) + { + if (type == Type::TableMeta) + { + auto meta = KeyPageStorage::TableMeta(entry.get()); + if (c_fileLogLevel <= TRACE) + { + KeyPage_LOG(TRACE) << LOG_DESC("Data TableMeta") << LOG_KV("table", table) + << LOG_KV("len", entry.size()) << LOG_KV("size", meta.size()) + << LOG_KV("meta", meta); + } + data = std::move(meta); + } + else if (type == Type::Page) + { + auto page = KeyPageStorage::Page(entry.get(), key); + if (c_fileLogLevel <= TRACE) + { + KeyPage_LOG(TRACE) + << LOG_DESC("Data Page") << LOG_KV("table", table) + << LOG_KV("key", toHex(key)) << LOG_KV("startKey", toHex(page.startKey())) + << LOG_KV("endKey", toHex(page.endKey())) << LOG_KV("len", entry.size()) + << LOG_KV("valid", page.validCount()) << LOG_KV("count", page.count()) + << LOG_KV("pageSize", page.size()); + } + data = std::move(page); + } + else + { // sys table entry + } + }; + Data(const Data& d) = default; + Data& operator=(const Data& d) = default; + Data(Data&& d) = default; + Data& operator=(Data&& d) = default; + std::string table; + std::string key; + Type type = Type::NormalEntry; + Entry entry; + std::variant data; + // std::pair view() const + // { + // return std::make_pair(std::string_view(table), std::string_view(key)); + // } + }; + + struct Bucket + { + Bucket() = default; + std::unordered_map, std::shared_ptr> container; + std::shared_mutex mutex; + std::optional find(std::string_view table, std::string_view key) + { + std::shared_lock lock(mutex); + auto it = container.find(std::make_pair(std::string(table), std::string(key))); + if (it != container.end()) + { + return it->second.get(); + } + return std::nullopt; + } + std::unique_lock lock() { return std::unique_lock(mutex); } + std::shared_lock rLock() { return std::shared_lock(mutex); } + }; + + std::optional> copyData(std::string_view table, std::string_view key) + { + auto [bucket, lock] = getBucket(table, key); + boost::ignore_unused(lock); + auto it = bucket->container.find(std::make_pair(std::string(table), std::string(key))); + if (it != bucket->container.end()) + { + return std::make_optional(std::make_shared(*it->second)); + } + auto prevKeyPage = std::dynamic_pointer_cast(getPrev()); + if (prevKeyPage) + { + return prevKeyPage->copyData(table, key); + } + auto [error, entry] = getRawEntryFromStorage(table, key); + if (error) + { + KeyPage_LOG(ERROR) << LOG_DESC("getData error") << LOG_KV("table", table) + << LOG_KV("key", toHex(key)) + << LOG_KV("error", error->errorMessage()); + return std::nullopt; + } + if (c_fileLogLevel <= TRACE) + { + KeyPage_LOG(TRACE) << LOG_DESC("get data from storage") << LOG_KV("table", table) + << LOG_KV("key", toHex(key)) + << LOG_KV("found", entry ? true : false); + } + if (entry) + { + entry->setStatus(Entry::Status::NORMAL); + return std::make_optional(std::make_shared(std::string(table), std::string(key), + std::move(*entry), key.empty() ? Data::Type::TableMeta : Data::Type::Page)); + } + return std::nullopt; + } + virtual std::pair count(const std::string_view& table) override; + +private: + auto getPrev() -> std::shared_ptr + { + std::shared_lock lock(m_prevMutex); + return m_prev; + } + auto getBucketIndex(std::string_view table, std::string_view key) const -> size_t + { + auto hash = std::hash{}(table); + std::ignore = key; + // the table must in one bucket + // boost::hash_combine(hash, std::hash{}(key)); + return hash % m_buckets.size(); + } + + auto changePageKey(std::string table, const std::string& oldPageKey, + const std::string& newPageKey, bool isRevert = false) -> Data* + { + if (newPageKey.empty() && !isRevert) + { + KeyPage_LOG(FATAL) << LOG_DESC("changePageKey to empty") << LOG_KV("table", table) + << LOG_KV("oldPageKey", toHex(oldPageKey)) + << LOG_KV("newPageKey", toHex(newPageKey)); + return nullptr; + } + + auto [bucket, lock] = getMutBucket(table, oldPageKey); + boost::ignore_unused(lock); + auto node = bucket->container.extract(std::make_pair(table, oldPageKey)); + auto* page = &std::get<0>(node.mapped()->data); + KeyPage_LOG(DEBUG) << LOG_DESC("changePageKey") << LOG_KV("table", table) + << LOG_KV("oldPageKey", toHex(oldPageKey)) + << LOG_KV("newPageKey", toHex(newPageKey)) + << LOG_KV("validCount", page->validCount()); + node.key().second = newPageKey; + node.mapped()->key = newPageKey; + if (newPageKey.empty()) + { + return nullptr; + } + auto it = bucket->container.find(std::make_pair(table, newPageKey)); + if (it != bucket->container.end()) + { // erase old page to update data + bucket->container.erase(it); + } + auto ret = bucket->container.insert(std::move(node)); + assert(ret.inserted); + return ret.position->second.get(); + // the bucket also need to be updated + } + + std::pair> getBucket( + std::string_view table, std::string_view key) + { + auto index = getBucketIndex(table, key); + auto& bucket = m_buckets[index]; + return std::make_pair(&bucket, std::shared_lock(bucket.mutex)); + } + + std::pair> getMutBucket( + std::string_view table, std::string_view key) + { + auto index = getBucketIndex(table, key); + auto& bucket = m_buckets[index]; + return std::make_pair(&bucket, std::unique_lock(bucket.mutex)); + } + + void insertNewPage(std::string_view tableView, std::string_view keyView, + KeyPageStorage::TableMeta* meta, Page&& page) + { + // insert page + auto d = std::make_shared(); + d->table = std::string(tableView); + d->key = std::string(keyView); + d->type = Data::Type::Page; + // prepare page info + PageInfo info(page.endKey(), page.validCount(), page.size(), d.get()); + + d->data = std::move(page); + + // d.entry.setDirty(true); + d->entry.setStatus(Entry::Status::MODIFIED); + auto [bucket, lock] = getMutBucket(tableView, keyView); + boost::ignore_unused(lock); + bucket->container.insert_or_assign( + std::make_pair(std::string(tableView), std::string(keyView)), std::move(d)); + lock.unlock(); + // update table meta + meta->insertPageInfoNoLock(std::move(info)); + } + + std::pair> getSysTableRawEntry( + std::string_view table, std::string_view key); + std::pair> getRawEntryFromStorage( + std::string_view table, std::string_view key); + Entry importExistingEntry(std::string_view table, std::string_view key, Entry entry); + + // if data not exist, create an empty one + std::tuple> getData( + std::string_view tableView, std::string_view key, bool mustExist = false); + std::pair> getEntryFromPage( + std::string_view table, std::string_view key); + Error::UniquePtr setEntryToPage(std::string table, std::string key, Entry entry); + uint32_t m_blockVersion = 0; + size_t m_pageSize = 8 * 1024; + size_t m_splitSize; + size_t m_mergeSize; + std::atomic_uint64_t m_readLength{0}; + std::atomic_uint64_t m_writeLength{0}; + std::vector m_buckets; + std::shared_ptr>> m_ignoreTables; + bool m_ignoreNotExist = false; +}; + +} // namespace bcos::storage diff --git "a/BFPL\345\243\271/bcos-table/src/StateStorage.h" "b/BFPL\345\243\271/bcos-table/src/StateStorage.h" new file mode 100644 index 00000000..4bbbcafa --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/src/StateStorage.h" @@ -0,0 +1,685 @@ +/* + * Copyright (C) 2021 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. + * + * @brief interface of Table + * @file Table.h + * @author: xingqiangbai + * @date: 2021-04-07 + * @brief interface of Table + * @file StateStorage.h + * @author: ancelmo + * @date: 2021-09-01 + */ +#pragma once + +#include "StateStorageInterface.h" +#include "bcos-framework/storage/Table.h" +#include "tbb/enumerable_thread_specific.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::storage +{ +template +class BaseStorage : public virtual storage::StateStorageInterface, + public virtual storage::MergeableStorageInterface +{ +private: +#define STORAGE_REPORT_GET(table, key, entry, desc) \ + if (c_fileLogLevel <= bcos::LogLevel::TRACE) \ + { \ + } + // STORAGE_LOG(TRACE) << LOG_DESC("GET") << LOG_KV("table", table) + // << LOG_KV("key", toHex(key)) << LOG_KV("desc", desc);} + + +#define STORAGE_REPORT_SET(table, key, entry, desc) \ + if (c_fileLogLevel <= bcos::LogLevel::TRACE) \ + { \ + } \ + // log("SET", (table), (key), (entry), (desc)) + + // for debug + void log(const std::string_view& op, const std::string_view& table, const std::string_view& key, + const std::optional& entry, const std::string_view& desc = "") + { + if (!m_readOnly) + { + if (entry) + { + STORAGE_LOG(TRACE) + << op << "|" << table << "|" << toHex(key) << "|[" << toHex(entry->getField(0)) + << "]|" << (int32_t)entry->status() << "|" << desc; + } + else + { + STORAGE_LOG(TRACE) << op << "|" << table << "|" << toHex(key) << "|" + << "[]" + << "|" + << "NO ENTRY" + << "|" << desc; + } + } + } + +public: + using Ptr = std::shared_ptr>; + + explicit BaseStorage(std::shared_ptr prev, + uint32_t _blockVersion = (uint32_t)bcos::protocol::BlockVersion::V3_0_VERSION) + : storage::StateStorageInterface(prev), + m_blockVersion(_blockVersion), + m_buckets(std::thread::hardware_concurrency()) + {} + + BaseStorage(const BaseStorage&) = delete; + BaseStorage& operator=(const BaseStorage&) = delete; + + BaseStorage(BaseStorage&&) = delete; + BaseStorage& operator=(BaseStorage&&) = delete; + + ~BaseStorage() override { m_recoder.clear(); } + + void asyncGetPrimaryKeys(std::string_view table, + const std::optional& _condition, + std::function)> _callback) override + { + std::map localKeys; + + if (m_enableTraverse) + { + std::mutex mergeMutex; + tbb::parallel_for(tbb::blocked_range(0U, m_buckets.size()), + [this, &mergeMutex, &localKeys, &table, &_condition](auto const& range) { + for (auto i = range.begin(); i < range.end(); ++i) + { + auto& bucket = m_buckets[i]; + std::unique_lock lock(bucket.mutex); + + decltype(localKeys) bucketKeys; + for (auto& it : bucket.container) + { + if (it.table == table && (!_condition || _condition->isValid(it.key))) + { + bucketKeys.emplace(it.key, it.entry.status()); + } + } + + std::unique_lock mergeLock(mergeMutex); + localKeys.merge(std::move(bucketKeys)); + } + }); + } + + auto prev = getPrev(); + if (!prev) + { + std::vector resultKeys; + for (auto& localIt : localKeys) + { + if (localIt.second == Entry::NORMAL || localIt.second == Entry::MODIFIED) + { + resultKeys.push_back(std::string(localIt.first)); + } + } + + _callback(nullptr, std::move(resultKeys)); + return; + } + + prev->asyncGetPrimaryKeys(table, _condition, + [localKeys = std::move(localKeys), callback = std::move(_callback)]( + auto&& error, auto&& remoteKeys) mutable { + if (error) + { + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(StorageError::ReadError, + "Get primary keys from prev failed!", *error), + std::vector()); + return; + } + + for (auto it = remoteKeys.begin(); it != remoteKeys.end();) + { + bool deleted = false; + + auto localIt = localKeys.find(*it); + if (localIt != localKeys.end()) + { + if (localIt->second == Entry::DELETED) + { + it = remoteKeys.erase(it); + deleted = true; + } + + localKeys.erase(localIt); + } + + if (!deleted) + { + ++it; + } + } + + for (auto& localIt : localKeys) + { + if (localIt.second == Entry::NORMAL || localIt.second == Entry::MODIFIED) + { + remoteKeys.push_back(std::string(localIt.first)); + } + } + + callback(nullptr, std::forward(remoteKeys)); + }); + } + + void asyncGetRow(std::string_view tableView, std::string_view keyView, + std::function)> _callback) override + { + auto [bucket, lock] = getBucket(tableView, keyView); + boost::ignore_unused(lock); + + auto it = bucket->container.template get<0>().find(std::make_tuple(tableView, keyView)); + if (it != bucket->container.template get<0>().end()) + { + auto& entry = it->entry; + + if (entry.status() == Entry::DELETED) + { + lock.unlock(); + + STORAGE_REPORT_GET(tableView, keyView, std::nullopt, "DELETED"); + _callback(nullptr, std::nullopt); + } + else + { + auto optionalEntry = std::make_optional(entry); + if constexpr (enableLRU) + { + updateMRUAndCheck(*bucket, it); + } + + lock.unlock(); + + STORAGE_REPORT_GET(tableView, keyView, optionalEntry, "FOUND"); + _callback(nullptr, std::move(optionalEntry)); + } + return; + } + else + { + STORAGE_REPORT_GET(tableView, keyView, std::nullopt, "NO ENTRY"); + } + lock.unlock(); + + auto prev = getPrev(); + if (prev) + { + prev->asyncGetRow(tableView, keyView, + [this, prev, table = std::string(tableView), key = std::string(keyView), _callback]( + Error::UniquePtr error, std::optional entry) { + if (error) + { + _callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(StorageError::ReadError, + "Get row from storage failed!", *error), + {}); + return; + } + + if (entry) + { + STORAGE_REPORT_GET(table, key, entry, "PREV FOUND"); + + _callback(nullptr, + std::make_optional(importExistingEntry(table, key, std::move(*entry)))); + } + else + { + STORAGE_REPORT_GET(table, key, std::nullopt, "PREV NOT FOUND"); + + _callback(nullptr, std::nullopt); + } + }); + } + else + { + _callback(nullptr, std::nullopt); + } + } + + void asyncGetRows(std::string_view tableView, + const std::variant, + const gsl::span>& _keys, + std::function>)> _callback) override + { + std::visit( + [this, &tableView, &_callback](auto&& _keys) { + std::vector> results(_keys.size()); + auto missinges = std::tuple, + std::vector>>(); + + std::atomic_ulong existsCount = 0; + +#pragma omp parallel for + for (auto i = 0u; i < _keys.size(); ++i) + { + auto [bucket, lock] = getBucket(tableView, _keys[i]); + boost::ignore_unused(lock); + + auto it = bucket->container.find( + std::make_tuple(tableView, std::string_view(_keys[i]))); + if (it != bucket->container.end()) + { + auto& entry = it->entry; + if (entry.status() == Entry::NORMAL || entry.status() == Entry::MODIFIED) + { + results[i].emplace(entry); + + if constexpr (enableLRU) + { + updateMRUAndCheck(*bucket, it); + } + } + else + { + results[i] = std::nullopt; + } + ++existsCount; + } + else + { +#pragma omp critical + { + std::get<1>(missinges).emplace_back(std::string(_keys[i]), i); + std::get<0>(missinges).emplace_back(_keys[i]); + } + } + } + + auto prev = getPrev(); + if (existsCount < _keys.size() && prev) + { + prev->asyncGetRows(tableView, std::get<0>(missinges), + [this, table = std::string(tableView), callback = std::move(_callback), + missingIndexes = std::move(std::get<1>(missinges)), + results = std::move(results)]( + auto&& error, std::vector>&& entries) mutable { + if (error) + { + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(StorageError::ReadError, + "async get perv rows failed!", *error), + std::vector>()); + return; + } + +#pragma omp parallel for + for (size_t i = 0; i < entries.size(); ++i) + { + auto& entry = entries[i]; + + if (entry) + { + results[std::get<1>(missingIndexes[i])].emplace( + importExistingEntry(table, + std::move(std::get<0>(missingIndexes[i])), + std::move(*entry))); + } + } + + callback(nullptr, std::move(results)); + }); + } + else + { + _callback(nullptr, std::move(results)); + } + }, + _keys); + } + + void asyncSetRow(std::string_view tableView, std::string_view keyView, Entry entry, + std::function callback) override + { + if (m_readOnly) + { + callback(BCOS_ERROR_UNIQUE_PTR( + StorageError::ReadOnly, "Try to operate a read-only storage")); + return; + } + + ssize_t updatedCapacity = entry.size(); + std::optional entryOld; + + 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()) + { + auto& existsEntry = it->entry; + entryOld.emplace(std::move(existsEntry)); + + updatedCapacity -= entryOld->size(); + + STORAGE_REPORT_SET(tableView, keyView, entry, "UPDATE"); + bucket->container.modify(it, [&entry](Data& data) { data.entry = std::move(entry); }); + + if constexpr (enableLRU) + { + updateMRUAndCheck(*bucket, it); + } + } + else + { + bucket->container.emplace( + Data{std::string(tableView), std::string(keyView), std::move(entry)}); + + STORAGE_REPORT_SET(tableView, keyView, std::nullopt, "INSERT"); + } + + if (m_recoder.local()) + { + m_recoder.local()->log( + Recoder::Change(std::string(tableView), std::string(keyView), std::move(entryOld))); + } + + bucket->capacity += updatedCapacity; + + lock.unlock(); + callback(nullptr); + } + + void parallelTraverse(bool onlyDirty, std::function + callback) const override + { + tbb::parallel_for(tbb::blocked_range(0, m_buckets.size()), + [this, &onlyDirty, &callback](auto const& range) { + for (auto i = range.begin(); i < range.end(); ++i) + { + auto& bucket = m_buckets[i]; + + for (auto& it : bucket.container) + { + auto& entry = it.entry; + if (!onlyDirty || entry.dirty()) + { + callback(it.table, it.key, entry); + } + } + } + }); + } + + void merge(bool onlyDirty, const TraverseStorageInterface& source) override + { + if (&source == this) + { + STORAGE_LOG(ERROR) << "Can't merge from self!"; + BOOST_THROW_EXCEPTION(BCOS_ERROR(-1, "Can't merge from self!")); + } + + std::atomic_size_t count = 0; + source.parallelTraverse( + onlyDirty, [this, &count](const std::string_view& table, const std::string_view& key, + const storage::Entry& entry) { + asyncSetRow(table, key, entry, [](Error::UniquePtr) {}); + ++count; + return true; + }); + + STORAGE_LOG(INFO) << "Successful merged records" << LOG_KV("count", count); + } + + crypto::HashType hash(const bcos::crypto::Hash::Ptr& hashImpl) const override + { + bcos::crypto::HashType totalHash(0); + + std::mutex totalMutex; + tbb::parallel_for(tbb::blocked_range(0U, m_buckets.size()), + [this, &hashImpl, &totalHash, &totalMutex](auto const& range) { + for (auto i = range.begin(); i < range.end(); ++i) + { + auto& bucket = m_buckets[i]; + + bcos::crypto::HashType bucketHash(0); + for (auto& it : bucket.container) + { + auto& entry = it.entry; + if (entry.dirty()) + { + auto entryHash = hashImpl->hash(it.table) ^ hashImpl->hash(it.key) ^ + entry.hash(it.table, it.key, hashImpl, m_blockVersion); + bucketHash ^= entryHash; + } + } + + std::unique_lock lock(totalMutex); + totalHash ^= bucketHash; + } + }); + + + return totalHash; + } + + + void rollback(const Recoder& recoder) override + { + if (m_readOnly) + { + return; + } + + for (auto& change : recoder) + { + ssize_t updateCapacity = 0; + auto [bucket, lock] = getBucket(change.table, change.key); + boost::ignore_unused(lock); + + auto it = bucket->container.find( + std::make_tuple(std::string_view(change.table), std::string_view(change.key))); + if (change.entry) + { + if (it != bucket->container.end()) + { + if (c_fileLogLevel <= bcos::LogLevel::TRACE) + { + STORAGE_LOG(TRACE) + << "Revert exists: " << change.table << " | " << toHex(change.key) + << " | " << toHex(change.entry->get()); + } + + updateCapacity = change.entry->size() - it->entry.size(); + + auto& rollbackEntry = change.entry; + bucket->container.modify(it, + [&rollbackEntry](Data& data) { data.entry = std::move(*rollbackEntry); }); + } + else + { + if (c_fileLogLevel <= bcos::LogLevel::TRACE) + { + STORAGE_LOG(TRACE) + << "Revert deleted: " << change.table << " | " << toHex(change.key) + << " | " << toHex(change.entry->get()); + } + updateCapacity = change.entry->size(); + bucket->container.emplace( + Data{change.table, change.key, std::move(*(change.entry))}); + } + } + else + { // nullopt means the key is not exist in m_cache + if (it != bucket->container.end()) + { + if (c_fileLogLevel <= bcos::LogLevel::TRACE) + { + STORAGE_LOG(TRACE) + << "Revert insert: " << change.table << " | " << toHex(change.key); + } + + updateCapacity = 0 - it->entry.size(); + bucket->container.erase(it); + } + else + { + auto message = (boost::format("Not found rollback entry: %s:%s") % + change.table % change.key) + .str(); + + BOOST_THROW_EXCEPTION(BCOS_ERROR(StorageError::UnknownError, message)); + } + } + + bucket->capacity += updateCapacity; + } + } + + void setEnableTraverse(bool enableTraverse) { m_enableTraverse = enableTraverse; } + + void setMaxCapacity(ssize_t capacity) { m_maxCapacity = capacity; } + +private: + Entry importExistingEntry(std::string_view table, std::string_view key, Entry entry) + { + if (m_readOnly) + { + return entry; + } + + // entry.setDirty(false); + entry.setStatus(Entry::NORMAL); + + auto updateCapacity = entry.size(); + + auto [bucket, lock] = getBucket(table, key); + boost::ignore_unused(lock); + auto it = bucket->container.find(std::make_tuple(table, key)); + + if (it == bucket->container.end()) + { + STORAGE_REPORT_SET( + std::get<0>(entryIt->first), key, std::make_optional(entryIt->second), "IMPORT"); + it = bucket->container + .emplace(Data{std::string(table), std::string(key), std::move(entry)}) + .first; + + bucket->capacity += updateCapacity; + } + else + { + STORAGE_REPORT_SET( + std::get<0>(entryIt->first), key, entryIt->second, "IMPORT EXISTS FAILED"); + + STORAGE_LOG(DEBUG) << "Fail import existsing entry, " << table << " | " << toHex(key); + } + + return it->entry; + } + + std::shared_ptr getPrev() + { + std::shared_lock lock(m_prevMutex); + auto prev = m_prev; + return prev; + } + + bool m_enableTraverse = false; + + ssize_t m_maxCapacity = 32 * 1024 * 1024; + + struct Data + { + std::string table; + std::string key; + Entry entry; + + std::tuple view() const + { + return std::make_tuple(std::string_view(table), std::string_view(key)); + } + }; + + using HashContainer = boost::multi_index_container, &Data::view>>>>; + using LRUHashContainer = boost::multi_index_container, &Data::view>>, + boost::multi_index::sequenced<>>>; + using Container = std::conditional_t; + + struct Bucket + { + Container container; + std::mutex mutex; + ssize_t capacity = 0; + }; + uint32_t m_blockVersion = 0; + std::vector m_buckets; + + std::tuple> getBucket( + std::string_view table, std::string_view key) + { + auto hash = std::hash{}(table); + boost::hash_combine(hash, std::hash{}(key)); + auto index = hash % m_buckets.size(); + + auto& bucket = m_buckets[index]; + return std::make_tuple(&bucket, std::unique_lock(bucket.mutex)); + } + + void updateMRUAndCheck( + Bucket& bucket, typename Container::template nth_index<0>::type::iterator it) + { + auto seqIt = bucket.container.template get<1>().iterator_to(*it); + bucket.container.template get<1>().relocate( + bucket.container.template get<1>().end(), seqIt); + + size_t clearCount = 0; + while (bucket.capacity > m_maxCapacity && !bucket.container.empty()) + { + auto& item = bucket.container.template get<1>().front(); + bucket.capacity -= item.entry.size(); + + bucket.container.template get<1>().pop_front(); + ++clearCount; + } + + if (clearCount > 0) + { + STORAGE_LOG(TRACE) << "LRUStorage cleared:" << clearCount + << ", current size: " << bucket.container.size(); + } + } +}; + +using StateStorage = BaseStorage; +using LRUStateStorage = BaseStorage; + +} // namespace bcos::storage diff --git "a/BFPL\345\243\271/bcos-table/src/StateStorageInterface.h" "b/BFPL\345\243\271/bcos-table/src/StateStorageInterface.h" new file mode 100644 index 00000000..8fc76de9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/src/StateStorageInterface.h" @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2021 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. + * + * @brief interface of StateStorage, TableFactory in FISCO BCOS 2.0 + * @file Table.h + * @author: xingqiangbai + * @date: 2022-04-19 + */ +#pragma once + +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-framework/storage/Table.h" +#include "tbb/enumerable_thread_specific.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::storage +{ +class Recoder +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + struct Change + { + Change(std::string _table, std::string _key, std::optional _entry) + : table(std::move(_table)), key(std::move(_key)), entry(std::move(_entry)) + {} + Change(const Change&) = delete; + Change& operator=(const Change&) = delete; + Change(Change&&) noexcept = default; + Change& operator=(Change&&) noexcept = default; + + std::string table; + std::string key; + std::optional entry; + }; + + void log(Change&& change) { m_changes.emplace_front(std::move(change)); } + auto begin() const { return m_changes.cbegin(); } + auto end() const { return m_changes.cend(); } + void clear() { m_changes.clear(); } + +private: + std::list m_changes; +}; + +class StateStorageInterface : public virtual storage::TraverseStorageInterface + +{ +public: + using Ptr = std::shared_ptr; + StateStorageInterface(std::shared_ptr prev) + : storage::TraverseStorageInterface(), m_prev(std::move(prev)){}; + virtual std::optional
openTable(const std::string_view& tableView) + { + std::promise>> openPromise; + asyncOpenTable(tableView, [&](auto&& error, auto&& table) { + openPromise.set_value({std::move(error), std::move(table)}); + }); + + auto [error, table] = openPromise.get_future().get(); + if (error) + { + BOOST_THROW_EXCEPTION(*error); + } + return table; + } + + virtual std::optional
createTable(std::string _tableName, std::string _valueFields) + { + std::promise>> createPromise; + asyncCreateTable( + _tableName, _valueFields, [&](Error::UniquePtr&& error, std::optional
&& table) { + createPromise.set_value({std::move(error), std::move(table)}); + }); + auto [error, table] = createPromise.get_future().get(); + if (error) + { + BOOST_THROW_EXCEPTION(*error); + } + return table; + } + + virtual std::pair count(const std::string_view& _table [[maybe_unused]]) + { + BOOST_THROW_EXCEPTION(BCOS_ERROR(-1, "Called interface count method")); + } + + virtual crypto::HashType hash(const bcos::crypto::Hash::Ptr& hashImpl) const = 0; + virtual void setPrev(std::shared_ptr prev) + { + std::unique_lock lock(m_prevMutex); + m_prev = std::move(prev); + } + virtual void rollback(const Recoder& recoder) = 0; + virtual void setRecoder(typename Recoder::Ptr recoder) { m_recoder.local().swap(recoder); } + virtual void setReadOnly(bool readOnly) { m_readOnly = readOnly; } + +protected: + bool m_readOnly = false; + tbb::enumerable_thread_specific m_recoder; + std::shared_ptr m_prev; + std::shared_mutex m_prevMutex; +}; + + +} // namespace bcos::storage diff --git "a/BFPL\345\243\271/bcos-table/src/StorageInterface.cpp" "b/BFPL\345\243\271/bcos-table/src/StorageInterface.cpp" new file mode 100644 index 00000000..2966479f --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/src/StorageInterface.cpp" @@ -0,0 +1,161 @@ +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-framework/storage/Table.h" +#include + +using namespace bcos::storage; + +TableInfo::ConstPtr StorageInterface::getSysTableInfo(std::string_view tableName) +{ + struct SystemTables + { + SystemTables() + { + tables.push_back(std::make_shared( + std::string(SYS_TABLES), std::vector{SYS_TABLE_VALUE_FIELDS})); + } + + std::vector tables; + } static m_systemTables; + + if (tableName == SYS_TABLES) + { + return m_systemTables.tables[0]; + } + + return nullptr; +} + +void StorageInterface::asyncCreateTable(std::string _tableName, std::string _valueFields, + std::function)> callback) +{ + asyncOpenTable(SYS_TABLES, [this, tableName = std::move(_tableName), + callback = std::move(callback), + valueFields = std::move(_valueFields)]( + auto&& error, auto&& sysTable) { + if (error) + { + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR(-1, "Open sys_tables failed!", *error), {}); + return; + } + + sysTable->asyncGetRow(tableName, [this, tableName, callback = std::move(callback), + &sysTable, valueFields = std::move(valueFields)]( + auto&& error, auto&& entry) { + if (error) + { + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR( + StorageError::ReadError, "Get table info row failed!", *error), + {}); + return; + } + + if (entry) + { + callback(BCOS_ERROR_UNIQUE_PTR( + StorageError::TableExists, "Table: " + tableName + " already exists"), + {}); + return; + } + + auto tableEntry = sysTable->newEntry(); + tableEntry.setField(0, std::string(valueFields)); + + sysTable->asyncSetRow(tableName, tableEntry, + [this, callback, tableName, valueFields = std::move(valueFields)](auto&& error) { + if (error) + { + callback(BCOS_ERROR_WITH_PREV_UNIQUE_PTR( + -1, "Put table info into sys_tables failed!", *error), + {}); + return; + } + + std::vector fields; + boost::split(fields, valueFields, boost::is_any_of(",")); + + auto tableInfo = std::make_shared( + std::move(tableName), std::move(fields)); + auto table = Table(this, tableInfo); + + callback(nullptr, std::make_optional(std::move(table))); + }); + }); + }); +} + +void StorageInterface::asyncOpenTable(std::string_view tableName, + std::function)> callback) +{ + asyncGetTableInfo(tableName, [this, callback = std::move(callback)]( + Error::UniquePtr error, TableInfo::ConstPtr tableInfo) { + if (error) + { + callback(std::move(error), std::nullopt); + return; + } + + if (tableInfo) + { + callback(nullptr, Table(this, tableInfo)); + } + else + { + callback(nullptr, std::nullopt); + } + }); +} + +void StorageInterface::asyncGetTableInfo( + std::string_view tableName, std::function callback) +{ + auto sysTableInfo = getSysTableInfo(tableName); + if (sysTableInfo) + { + callback(nullptr, std::move(sysTableInfo)); + + return; + } + else + { + asyncOpenTable( + SYS_TABLES, [callback = std::move(callback), tableName = std::string(tableName)]( + Error::UniquePtr&& error, std::optional
&& sysTable) { + if (error) + { + callback(std::move(error), {}); + return; + } + + if (!sysTable) + { + callback(BCOS_ERROR_UNIQUE_PTR(StorageError::SystemTableNotExists, + "System table: " + std::string(SYS_TABLES) + " not found!"), + {}); + return; + } + + sysTable->asyncGetRow(tableName, + [tableName, callback = std::move(callback)](auto&& error, auto&& entry) { + if (error) + { + callback(std::move(error), {}); + return; + } + + if (!entry) + { + callback(nullptr, {}); + return; + } + + std::vector fields; + boost::split(fields, entry->getField(0), boost::is_any_of(",")); + + auto tableInfo = std::make_shared( + std::move(tableName), std::move(fields)); + + callback(nullptr, std::move(tableInfo)); + }); + }); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-table/src/StorageWrapper.h" "b/BFPL\345\243\271/bcos-table/src/StorageWrapper.h" new file mode 100644 index 00000000..738280b7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/src/StorageWrapper.h" @@ -0,0 +1,167 @@ +#pragma once + +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-framework/storage/Table.h" +#include "bcos-table/src/StateStorage.h" +#include +#include +#include +#include +#include + +namespace bcos::storage +{ +using GetPrimaryKeysReponse = std::tuple>; +using GetRowResponse = std::tuple>; +using GetRowsResponse = std::tuple>>; +using SetRowResponse = std::tuple; +using OpenTableResponse = std::tuple>; + + +class StorageWrapper +{ +public: + StorageWrapper(storage::StateStorageInterface::Ptr storage, bcos::storage::Recoder::Ptr recoder) + : m_storage(std::move(storage)), m_recoder(recoder) + {} + + StorageWrapper(const StorageWrapper&) = delete; + StorageWrapper(StorageWrapper&&) = delete; + StorageWrapper& operator=(const StorageWrapper&) = delete; + StorageWrapper& operator=(StorageWrapper&&) = delete; + + virtual ~StorageWrapper() {} + + std::vector getPrimaryKeys( + const std::string_view& table, const std::optional& _condition) + { + GetPrimaryKeysReponse value; + m_storage->asyncGetPrimaryKeys( + table, _condition, [&value](auto&& error, auto&& keys) mutable { + value = {std::move(error), std::move(keys)}; + }); + + // After coroutine switch, set the recoder + setRecoder(m_recoder); + + auto& [error, keys] = value; + + if (error) + { + BOOST_THROW_EXCEPTION(*error); + } + + return std::move(keys); + } + + virtual std::optional getRow( + const std::string_view& table, const std::string_view& _key) + { + GetRowResponse value; + m_storage->asyncGetRow(table, _key, [&value](auto&& error, auto&& entry) mutable { + value = {std::move(error), std::move(entry)}; + }); + + auto& [error, entry] = value; + + if (error) + { + BOOST_THROW_EXCEPTION(*error); + } + + return std::move(entry); + } + + virtual std::vector> getRows( + const std::string_view& table, const std::variant, + const gsl::span>& _keys) + { + GetRowsResponse value; + m_storage->asyncGetRows(table, _keys, [&value](auto&& error, auto&& entries) mutable { + value = {std::move(error), std::move(entries)}; + }); + + + auto& [error, entries] = value; + + if (error) + { + BOOST_THROW_EXCEPTION(*error); + } + + return std::move(entries); + } + + virtual void setRow( + const std::string_view& table, const std::string_view& key, storage::Entry entry) + { + SetRowResponse value; + + m_storage->asyncSetRow(table, key, std::move(entry), + [&value](auto&& error) mutable { value = std::tuple{std::move(error)}; }); + + auto& [error] = value; + + if (error) + { + BOOST_THROW_EXCEPTION(*error); + } + } + + std::optional createTable(std::string _tableName, std::string _valueFields) + { + auto ret = createTableWithoutException(_tableName, _valueFields); + if (std::get<0>(ret)) + { + BOOST_THROW_EXCEPTION(*(std::get<0>(ret))); + } + + return std::get<1>(ret); + } + + std::tuple> createTableWithoutException( + std::string _tableName, std::string _valueFields) + { + std::promise createPromise; + m_storage->asyncCreateTable(std::move(_tableName), std::move(_valueFields), + [&](Error::UniquePtr&& error, auto&& table) mutable { + createPromise.set_value({std::move(error), std::move(table)}); + }); + auto value = createPromise.get_future().get(); + return value; + } + + std::optional openTable(std::string_view tableName) + { + auto ret = openTableWithoutException(tableName); + if (std::get<0>(ret)) + { + BOOST_THROW_EXCEPTION(*(std::get<0>(ret))); + } + + return std::get<1>(ret); + } + + std::pair count(const std::string_view& _table) + { + return m_storage->count(_table); + } + + std::tuple> openTableWithoutException( + std::string_view tableName) + { + std::promise openPromise; + m_storage->asyncOpenTable(tableName, [&](auto&& error, auto&& table) mutable { + openPromise.set_value({std::move(error), std::move(table)}); + }); + auto value = openPromise.get_future().get(); + return value; + } + + void setRecoder(storage::Recoder::Ptr recoder) { m_storage->setRecoder(std::move(recoder)); } + +private: + storage::StateStorageInterface::Ptr m_storage; + bcos::storage::Recoder::Ptr m_recoder; +}; +} // namespace bcos::storage \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-table/src/Table.cpp" "b/BFPL\345\243\271/bcos-table/src/Table.cpp" new file mode 100644 index 00000000..63a81497 --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/src/Table.cpp" @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2021 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. + * + * @brief interface of Table + * @file Table.h + * @author: xingqiangbai + * @date: 2021-04-07 + */ +#include "bcos-framework/storage/Table.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "tbb/concurrent_vector.h" +#include "tbb/enumerable_thread_specific.h" +#include "tbb/parallel_for.h" +#include "tbb/parallel_invoke.h" +#include +#include + +using namespace std; + +namespace bcos +{ +namespace storage +{ +std::optional Table::getRow(std::string_view _key) +{ + std::promise>> promise; + + asyncGetRow(_key, [&promise](auto error, auto entry) { + promise.set_value({std::move(error), std::move(entry)}); + }); + + auto result = promise.get_future().get(); + + if (std::get<0>(result)) + { + BOOST_THROW_EXCEPTION(*(std::get<0>(result))); + } + + return std::get<1>(result); +} + +std::vector> Table::getRows( + const std::variant, const gsl::span>& + _keys) +{ + std::promise>>> promise; + asyncGetRows(_keys, [&promise](auto error, auto entries) { + promise.set_value(std::tuple{std::move(error), std::move(entries)}); + }); + + auto result = promise.get_future().get(); + + if (std::get<0>(result)) + { + BOOST_THROW_EXCEPTION(*(std::get<0>(result))); + } + + return std::get<1>(result); +} + +std::vector Table::getPrimaryKeys(std::optional const& _condition) +{ + std::promise>> promise; + asyncGetPrimaryKeys(_condition, [&promise](auto&& error, auto&& keys) { + promise.set_value(std::tuple{std::move(error), std::move(keys)}); + }); + auto result = promise.get_future().get(); + + if (std::get<0>(result)) + { + BOOST_THROW_EXCEPTION(*(std::get<0>(result))); + } + + return std::get<1>(result); +} + +void Table::setRow(std::string_view _key, Entry _entry) +{ + std::promise promise; + m_storage->asyncSetRow(m_tableInfo->name(), _key, std::move(_entry), + [&promise](auto&& error) { promise.set_value(std::move(error)); }); + auto result = promise.get_future().get(); + + if (result) + { + BOOST_THROW_EXCEPTION(*result); + } +} + +void Table::asyncGetPrimaryKeys(std::optional const& _condition, + std::function)> _callback) noexcept +{ + m_storage->asyncGetPrimaryKeys(m_tableInfo->name(), _condition, _callback); +} + +void Table::asyncGetRow(std::string_view _key, + std::function)> _callback) noexcept +{ + m_storage->asyncGetRow(m_tableInfo->name(), _key, + [callback = std::move(_callback)](Error::UniquePtr error, std::optional entry) { + callback(std::move(error), std::move(entry)); + }); +} + +void Table::asyncGetRows( + const std::variant, const gsl::span>& + _keys, + std::function>)> _callback) noexcept +{ + m_storage->asyncGetRows(m_tableInfo->name(), _keys, + [callback = std::move(_callback)]( + Error::UniquePtr error, std::vector> entries) { + callback(std::move(error), std::move(entries)); + }); +} + +void Table::asyncSetRow( + std::string_view key, Entry entry, std::function callback) noexcept +{ + m_storage->asyncSetRow(m_tableInfo->name(), key, std::move(entry), callback); +} + +} // namespace storage +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-table/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-table/test/CMakeLists.txt" new file mode 100644 index 00000000..867b1ce0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/test/CMakeLists.txt" @@ -0,0 +1,31 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of libtable +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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(GLOB_RECURSE SOURCES "*.cpp" "*.h" "*.sol") + +# cmake settings +set(TEST_BINARY_NAME test-table) + +add_executable(${TEST_BINARY_NAME} ${SOURCES}) +target_include_directories(${TEST_BINARY_NAME} PRIVATE . ${CMAKE_SOURCE_DIR}) + +find_package(Boost REQUIRED unit_test_framework) + +target_link_libraries(${TEST_BINARY_NAME} ${TABLE_TARGET} Boost::unit_test_framework) +include(SearchTestCases) +config_test_cases("" "${SOURCES}" ${TEST_BINARY_NAME} "${EXCLUDE_SUITES}") +# add_test(NAME test-table WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) diff --git "a/BFPL\345\243\271/bcos-table/test/unittests/libtable/Entry.cpp" "b/BFPL\345\243\271/bcos-table/test/unittests/libtable/Entry.cpp" new file mode 100644 index 00000000..71c205ef --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/test/unittests/libtable/Entry.cpp" @@ -0,0 +1,223 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Unit tests for the Entry + * @file Entry.cpp + */ + +#include "bcos-framework/protocol/Protocol.h" +#include "bcos-framework/storage/Table.h" +#include "bcos-table/src/StateStorage.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::storage; + +namespace bcos +{ +namespace test +{ +struct EntryFixture +{ + EntryFixture() + { + tableInfo = std::make_shared("testTable", std::vector{"key2"}); + } + + ~EntryFixture() {} + + std::shared_ptr tableInfo; +}; +BOOST_FIXTURE_TEST_SUITE(EntryTest, EntryFixture) + +BOOST_AUTO_TEST_CASE(viewEqual) +{ + std::string a = "value"; + + BOOST_CHECK_EQUAL(a, "value"); + BOOST_CHECK_EQUAL(std::string_view(a), "value"); +} + +BOOST_AUTO_TEST_CASE(copyFrom) +{ + auto entry1 = std::make_shared(tableInfo); + auto entry2 = std::make_shared(tableInfo); + BOOST_CHECK_EQUAL(entry1->dirty(), false); + entry1->setField(0, "value"); + BOOST_TEST(entry1->dirty() == true); + BOOST_TEST(entry1->size() == 5); + + *entry2 = *entry1; + + { + auto entry3 = Entry(*entry1); + + entry3.setField(0, "i am key2"); + + auto entry4(std::move(entry3)); + + auto entry5(*entry2); + + auto entry6(std::move(entry5)); + } + + BOOST_CHECK_EQUAL(entry2->getField(0), "value"sv); + + entry2->setField(0, "value2"); + + BOOST_CHECK_EQUAL(entry2->getField(0), "value2"); + BOOST_CHECK_EQUAL(entry1->getField(0), "value"); + + entry2->setField(0, "value3"); + BOOST_TEST(entry2->size() == 6); + BOOST_TEST(entry2->getField(0) == "value3"); + *entry2 = *entry2; + BOOST_TEST(entry2->dirty() == true); + // entry2->setDirty(false); + entry2->setStatus(Entry::Status::NORMAL); + BOOST_TEST(entry2->dirty() == false); + // test setField lValue and rValue + entry2->setField(0, string("value2")); + BOOST_TEST(entry2->dirty() == true); + BOOST_TEST(entry2->size() == 6); + auto value2 = "value2"; + entry2->setField(0, value2); +} + +BOOST_AUTO_TEST_CASE(functions) +{ + auto entry = std::make_shared(tableInfo); + BOOST_TEST(entry->dirty() == false); + BOOST_TEST(entry->status() == Entry::Status::EMPTY); + entry->setStatus(Entry::Status::DELETED); + BOOST_TEST(entry->status() == Entry::Status::DELETED); + BOOST_TEST(entry->dirty() == true); +} + +BOOST_AUTO_TEST_CASE(BytesField) +{ + Entry entry; + + std::string value = "abcdefghijklmn"; + std::vector data; + data.assign(value.begin(), value.end()); + + entry.importFields({std::string(value)}); + + BOOST_CHECK_EQUAL(entry.getField(0), value); + + Entry entry2; + entry2.importFields({data}); + + BOOST_CHECK_EQUAL(entry2.getField(0), value); +} + +BOOST_AUTO_TEST_CASE(capacity) +{ + Entry entry; + + entry.importFields({std::string("abc")}); + + entry.setField( + 0, std::string("abdflsakdjflkasjdfoiqwueroi!!!!sdlkfjsldfbclsadflaksjdfpqweioruaaa")); + + BOOST_CHECK_LT(entry.size(), 100); + BOOST_CHECK_GT(entry.size(), 0); +} + +BOOST_AUTO_TEST_CASE(object) +{ + std::tuple value = std::make_tuple(100, "hello", "world"); + + Entry entry; + entry.setObject(value); + + auto out = entry.getObject>(); + + BOOST_CHECK(out == value); +} + +BOOST_AUTO_TEST_CASE(largeObject) +{ + Entry entry; + entry.setField(0, std::string(1024, 'a')); + + BOOST_CHECK_EQUAL(entry.getField(0), std::string(1024, 'a')); +} + +BOOST_AUTO_TEST_CASE(entryHash) +{ + auto data = "Hello world!"s; + auto table = "table!"s; + auto key = "key!"s; + + Entry entry; + entry.setStatus(Entry::MODIFIED); + entry.setField(0, data); + + auto sm3 = std::make_shared(); + auto oldHash = entry.hash(table, key, sm3, 0); + auto oldExpect = sm3->hash(bytesConstRef((bcos::byte*)data.data(), data.size())); + BOOST_CHECK_EQUAL(oldHash, oldExpect); + + entry.setStatus(Entry::DELETED); + auto deletedHash = entry.hash(table, key, sm3, (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION); + + auto anyHasher = sm3->hasher(); + auto deletedExpect = std::visit( + [&](auto& hasher) { + hasher.update(table); + hasher.update(key); + + bcos::crypto::HashType hash; + hasher.final(hash); + return hash; + }, + anyHasher); + BOOST_CHECK_EQUAL(deletedHash, deletedExpect); + + entry.setStatus(Entry::MODIFIED); + entry.setField(0, data); + auto modifyHash = entry.hash(table, key, sm3, (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION); + anyHasher = sm3->hasher(); + auto modifyExpect = std::visit( + [&](auto& hasher) { + hasher.update(table); + hasher.update(key); + hasher.update(data); + + bcos::crypto::HashType hash; + hasher.final(hash); + return hash; + }, + anyHasher); + BOOST_CHECK_EQUAL(modifyHash, modifyExpect); + + entry.setStatus(Entry::NORMAL); + auto normalHash = entry.hash(table, key, sm3, (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION); + BOOST_CHECK_EQUAL(normalHash, bcos::crypto::HashType{}); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-table/test/unittests/libtable/Hash.h" "b/BFPL\345\243\271/bcos-table/test/unittests/libtable/Hash.h" new file mode 100644 index 00000000..be45c596 --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/test/unittests/libtable/Hash.h" @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2021 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. + * + * @brief the Header256Hash return first 32 byte as hash result + * @file Header256Hash.h + */ + +#include +#include + +namespace bcos +{ +namespace crypto +{ +class Header256Hash : public Hash +{ +public: + typedef std::shared_ptr Ptr; + Header256Hash() = default; + virtual ~Header256Hash(){}; + HashType hash(bytesConstRef _data) override + { + std::hash hash; + auto h = hash(std::string_view((const char*)_data.data(), _data.size())); + uint8_t hash_result[32] = {0}; + memcpy(hash_result, &h, sizeof(h)); + return HashType(hash_result, 32); + } + bcos::crypto::hasher::AnyHasher hasher() override + { + return bcos::crypto::hasher::AnyHasher{bcos::crypto::hasher::openssl::OpenSSL_SM3_Hasher{}}; + }; +}; + +} // namespace crypto + +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-table/test/unittests/libtable/Table.cpp" "b/BFPL\345\243\271/bcos-table/test/unittests/libtable/Table.cpp" new file mode 100644 index 00000000..c77d6b7f --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/test/unittests/libtable/Table.cpp" @@ -0,0 +1,244 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Unit tests for the Table + * @file Table.cpp + */ + +#include "bcos-framework/storage/Table.h" +#include "Hash.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-table/src/StateStorage.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::storage; +using namespace bcos::crypto; + +namespace std +{ +inline ostream& operator<<(ostream& os, const tuple& item) +{ + os << get<0>(item) << " " << get<1>(item); + return os; +} + +inline ostream& operator<<(ostream& os, const std::optional
& table) +{ + os << table.has_value(); + return os; +} + +inline ostream& operator<<(ostream& os, const std::unique_ptr& error) +{ + os << error->what(); + return os; +} +} // namespace std + +namespace bcos +{ +namespace test +{ +struct TableFixture +{ + TableFixture() + { + hashImpl = make_shared(); + memoryStorage = make_shared(nullptr); + tableFactory = make_shared(memoryStorage); + } + + ~TableFixture() {} + std::shared_ptr hashImpl = nullptr; + std::shared_ptr memoryStorage = nullptr; + protocol::BlockNumber m_blockNumber = 0; + std::shared_ptr tableFactory = nullptr; +}; +BOOST_FIXTURE_TEST_SUITE(TableTest, TableFixture) + +BOOST_AUTO_TEST_CASE(constructor) +{ + auto threadPool = ThreadPool("a", 1); + auto table = std::make_shared
(nullptr, nullptr); + auto tableFactory = std::make_shared(memoryStorage); +} + +BOOST_AUTO_TEST_CASE(tableInfo) +{ + std::vector fields = {"value9", "value8", "value7", "value6"}; + TableInfo tableInfo("test-table", fields); + + BOOST_CHECK_EQUAL_COLLECTIONS( + fields.begin(), fields.end(), tableInfo.fields().begin(), tableInfo.fields().end()); + + BOOST_CHECK_EQUAL(tableInfo.fieldIndex("value9"), 0); + BOOST_CHECK_EQUAL(tableInfo.fieldIndex("value8"), 1); + BOOST_CHECK_EQUAL(tableInfo.fieldIndex("value7"), 2); + BOOST_CHECK_EQUAL(tableInfo.fieldIndex("value6"), 3); +} + +BOOST_AUTO_TEST_CASE(dump_hash) +{ + std::string tableName("t_test"); + std::string keyField("key"); + std::string valueField("value"); + + std::promise> createPromise; + tableFactory->asyncCreateTable( + tableName, valueField, [&](auto&& error, std::optional
&& table) { + BOOST_CHECK(!error); + createPromise.set_value(table); + }); + + BOOST_CHECK(createPromise.get_future().get()); + + std::promise> tablePromise; + tableFactory->asyncOpenTable("t_test", [&](auto&& error, auto&& table) { + BOOST_CHECK(!error); + tablePromise.set_value(std::move(table)); + }); + auto table = tablePromise.get_future().get(); + BOOST_TEST(table); + + // BOOST_TEST(table->dirty() == false); + auto entry = std::make_optional(table->newEntry()); + // entry->setField("key", "name"); + entry->setField(0, "Lili"); + table->setRow("name", *entry); + auto tableinfo = table->tableInfo(); + BOOST_CHECK_EQUAL(tableinfo->name(), tableName); + + // BOOST_CHECK_EQUAL_COLLECTIONS( + // valueField.begin(), valueField.end(), tableinfo->fields.begin(), + // tableinfo->fields.end()); + + // auto hash = tableFactory->hash(hashImpl); + // BOOST_CHECK_EQUAL(hash.size, 32); + + // BOOST_CHECK_EQUAL(tableFactory.ex) + + // auto data = table->dump(m_blockNumber); + // auto hash = table->hash(); + // BOOST_TEST(data->size() == 1); + entry = table->newEntry(); + // entry->setField("key", "name2"); + entry->setField(0, "WW"); + BOOST_CHECK_NO_THROW(table->setRow("name2", *entry)); + + // data = table->dump(m_blockNumber); + // BOOST_TEST(data->size() == 2); + // hash = table->hash(); + // BOOST_TEST(table->dirty() == true); +} + +BOOST_AUTO_TEST_CASE(setRow) +{ + std::string tableName("t_test"); + std::string keyField("key"); + std::string valueField("value1,value2"); + + std::promise> createPromise; + tableFactory->asyncCreateTable( + tableName, valueField, [&](auto&& error, std::optional
&& table) { + BOOST_CHECK(!error); + createPromise.set_value(std::move(table)); + }); + BOOST_CHECK(createPromise.get_future().get()); + + std::promise> tablePromise; + tableFactory->asyncOpenTable("t_test", [&](auto&& error, auto&& table) { + BOOST_CHECK(!error); + tablePromise.set_value(std::move(table)); + }); + auto table = tablePromise.get_future().get(); + BOOST_TEST(table); + + // check fields order of t_test + BOOST_TEST(table->tableInfo()->fields().size() == 2); + BOOST_TEST(table->tableInfo()->fields()[0] == "value1"); + BOOST_TEST(table->tableInfo()->fields()[1] == "value2"); + // BOOST_TEST(table->tableInfo()->key == keyField); + auto entry = std::make_optional(table->newEntry()); + // entry->setField("key", "name"); + // BOOST_CHECK_THROW(entry->setField(0, "Lili"), bcos::Error); + BOOST_CHECK_NO_THROW(table->setRow("name", *entry)); + + // check fields order of SYS_TABLE + std::promise> sysTablePromise; + tableFactory->asyncOpenTable(StorageInterface::SYS_TABLES, [&](auto&& error, auto&& table) { + BOOST_CHECK(!error); + BOOST_TEST(table); + sysTablePromise.set_value(std::move(table)); + }); + auto sysTable = sysTablePromise.get_future().get(); + BOOST_CHECK(sysTable); + + BOOST_TEST(sysTable->tableInfo()->fields().size() == 1); + BOOST_TEST(sysTable->tableInfo()->fields()[0] == StateStorage::SYS_TABLE_VALUE_FIELDS); + // BOOST_TEST(sysTable->tableInfo()->key == StateStorage::SYS_TABLE_KEY); +} + +BOOST_AUTO_TEST_CASE(removeFromCache) +{ + std::string tableName("t_test"); + std::string keyField("key"); + std::string valueField("value1,value2"); + + auto ret = tableFactory->createTable(tableName, valueField); + BOOST_TEST(ret); + auto table = tableFactory->openTable("t_test"); + BOOST_TEST(table); + // check fields order of t_test + BOOST_TEST(table->tableInfo()->fields().size() == 2); + BOOST_TEST(table->tableInfo()->fields()[0] == "value1"); + BOOST_TEST(table->tableInfo()->fields()[1] == "value2"); + // BOOST_TEST(table->tableInfo()->key == keyField); + auto entry = std::make_optional(table->newEntry()); + // entry->setField("key", "name"); + entry->setField(0, "hello world!"); + // BOOST_CHECK_THROW(entry->setField(0, "Lili"), bcos::Error); + BOOST_CHECK_NO_THROW(table->setRow("name", *entry)); + + auto deleteEntry = std::make_optional(table->newEntry()); + deleteEntry->setStatus(Entry::DELETED); + BOOST_CHECK_NO_THROW(table->setRow("name", *deleteEntry)); + + auto hashs = tableFactory->hash(hashImpl); + + auto tableFactory2 = std::make_shared(nullptr); + BOOST_CHECK(tableFactory2->createTable(tableName, valueField)); + auto table2 = tableFactory2->openTable(tableName); + BOOST_TEST(table2); + + auto deleteEntry2 = std::make_optional(table2->newEntry()); + deleteEntry2->setStatus(Entry::DELETED); + BOOST_CHECK_NO_THROW(table2->setRow("name", *deleteEntry2)); + auto hashs2 = tableFactory2->hash(hashImpl); + + BOOST_CHECK_EQUAL_COLLECTIONS(hashs.begin(), hashs.end(), hashs2.begin(), hashs2.end()); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-table/test/unittests/libtable/TablePerf.cpp" "b/BFPL\345\243\271/bcos-table/test/unittests/libtable/TablePerf.cpp" new file mode 100644 index 00000000..52c36370 --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/test/unittests/libtable/TablePerf.cpp" @@ -0,0 +1,133 @@ +#include "Hash.h" +#include "bcos-table/src/StateStorage.h" +#include +#include +#include +#include +#include + +namespace bcos::test +{ +using namespace bcos::storage; + +struct TablePerfFixture +{ + TablePerfFixture() + { + // auto hashImpl = std::make_shared(); + auto memoryStorage = std::make_shared(nullptr); + BOOST_TEST(memoryStorage != nullptr); + tableFactory = std::make_shared(memoryStorage); + BOOST_TEST(tableFactory != nullptr); + } + + std::vector> createTestData(Table table) + { + std::vector> entries; + entries.reserve(count); + for (size_t i = 0; i < count; ++i) + { + auto entry = table.newEntry(); + entry.setField(0, "value1"); + + entries.emplace_back("key_" + boost::lexical_cast(i), std::move(entry)); + } + + return entries; + } + + std::shared_ptr tableFactory; + size_t count = 100 * 1000; +}; + +BOOST_FIXTURE_TEST_SUITE(TablePerf, TablePerfFixture) + +BOOST_AUTO_TEST_CASE(syncGet) +{ + tableFactory->createTable("test_table", "field1"); + auto table = tableFactory->openTable("test_table"); + + auto entries = createTestData(*table); + + for (auto& [key, entry] : entries) + { + table->setRow(key, entry); + } + + auto now = bcos::utcSteadyTime(); + for (size_t i = 0; i < count; ++i) + { + std::string key = "key_" + boost::lexical_cast(i); + auto entry = table->getRow(key); + + BOOST_CHECK_EQUAL(entry->getField(0), "value1"); + } + + std::cout << "sync cost: " << bcos::utcSteadyTime() - now << std::endl; +} + +BOOST_AUTO_TEST_CASE(asyncGet) +{ + tableFactory->createTable("test_table", "field1,field2,field3"); + auto table = tableFactory->openTable("test_table"); + + auto entries = createTestData(*table); + + for (auto& [key, entry] : entries) + { + table->setRow(key, entry); + } + + auto total = count; + + auto now = bcos::utcSteadyTime(); + std::promise finished; + std::atomic done(0); + for (size_t i = 0; i < count; ++i) + { + std::string key = "key_" + boost::lexical_cast(i); + table->asyncGetRow(key, [&total, &finished, &done](auto&&, auto&& entry) { + BOOST_CHECK_EQUAL(entry->getField(0), "value1"); + + auto current = done.fetch_add(1); + if (current + 1 >= total) + { + finished.set_value(true); + } + }); + } + finished.get_future().get(); + + std::cout << "async cost: " << bcos::utcSteadyTime() - now << std::endl; +} + +BOOST_AUTO_TEST_CASE(asyncToSyncGet) +{ + tableFactory->createTable("test_table", "field1,field2,field3"); + auto table = tableFactory->openTable("test_table"); + + auto entries = createTestData(*table); + + for (auto& [key, entry] : entries) + { + table->setRow(key, entry); + } + + auto now = bcos::utcSteadyTime(); + for (size_t i = 0; i < count; ++i) + { + std::string key = "key_" + boost::lexical_cast(i); + std::promise finished; + table->asyncGetRow(key, [&finished](auto&&, auto&& entry) { + BOOST_CHECK_EQUAL(entry->getField(0), "value1"); + finished.set_value(true); + }); + finished.get_future().get(); + } + + std::cout << "asyncToSync cost: " << bcos::utcSteadyTime() - now << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-table/test/unittests/libtable/TestKeyPageStorage.cpp" "b/BFPL\345\243\271/bcos-table/test/unittests/libtable/TestKeyPageStorage.cpp" new file mode 100644 index 00000000..ab56ef10 --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/test/unittests/libtable/TestKeyPageStorage.cpp" @@ -0,0 +1,2871 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Unit tests for KeyPageStorage + * @file TestKeyPageStorage.cpp + */ + +#include "Hash.h" +#include "bcos-crypto/hash/Keccak256.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-table/src/KeyPageStorage.h" +#include "bcos-table/src/StateStorage.h" +#include "bcos-table/src/StateStorageInterface.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::storage; +using namespace bcos::crypto; + +#if defined(__APPLE__) +#undef __APPLE__ +#endif + +namespace std +{ +inline ostream& operator<<(ostream& os, const std::optional& entry) +{ + os << entry.has_value(); + return os; +} + +inline ostream& operator<<(ostream& os, const std::optional
& table) +{ + os << table.has_value(); + return os; +} + +inline ostream& operator<<(ostream& os, const std::unique_ptr& error) +{ + os << error->what(); + return os; +} + +inline ostream& operator<<(ostream& os, const std::tuple& pair) +{ + os << std::get<0>(pair) << " " << std::get<1>(pair).hex(); + return os; +} +} // namespace std + +namespace bcos::test +{ +struct KeyPageStorageFixture +{ + KeyPageStorageFixture() + { + boost::log::core::get()->set_logging_enabled(false); + hashImpl = make_shared(); + auto stateStorage = make_shared(nullptr); + stateStorage->setEnableTraverse(true); + memoryStorage = stateStorage; + BOOST_REQUIRE(memoryStorage != nullptr); + tableFactory = make_shared(memoryStorage); + BOOST_REQUIRE(tableFactory != nullptr); + c.limit(0, 100); + } + + ~KeyPageStorageFixture() { boost::log::core::get()->set_logging_enabled(true); } + std::optional
createDefaultTable() + { + std::promise> createPromise; + tableFactory->asyncCreateTable( + testTableName, valueField, [&](auto&& error, std::optional
table) { + BOOST_REQUIRE(!error); + createPromise.set_value(table); + }); + return createPromise.get_future().get(); + } + + std::shared_ptr hashImpl = nullptr; + std::shared_ptr memoryStorage = nullptr; + protocol::BlockNumber m_blockNumber = 0; + std::shared_ptr tableFactory = nullptr; + std::string testTableName = "t_test"; + std::string keyField = "key"; + std::string valueField = "value"; + Condition c; +}; +BOOST_FIXTURE_TEST_SUITE(KeyPageStorageTest, KeyPageStorageFixture) + +BOOST_AUTO_TEST_CASE(constructor) +{ + auto threadPool = ThreadPool("a", 1); + auto tf = std::make_shared(memoryStorage); +} + +BOOST_AUTO_TEST_CASE(create_Table) +{ + std::string tableName("t_test1"); + auto table = tableFactory->openTable(tableName); + + BOOST_REQUIRE(!table); + auto ret = tableFactory->createTable(tableName, valueField); + BOOST_REQUIRE(ret); + + table = tableFactory->openTable(tableName); + BOOST_REQUIRE(table); + + BOOST_REQUIRE_THROW(tableFactory->createTable(tableName, valueField), bcos::Error); +} + + +BOOST_AUTO_TEST_CASE(count_empty_Table) +{ + std::string tableName("t_test1"); + auto countRet = tableFactory->count(tableName); + BOOST_REQUIRE_EQUAL(countRet.first, 0); + auto table = tableFactory->openTable(tableName); + + BOOST_REQUIRE(!table); + auto ret = tableFactory->createTable(tableName, valueField); + BOOST_REQUIRE(ret); + countRet = tableFactory->count(tableName); + BOOST_REQUIRE_EQUAL(countRet.first, 0); +} + +BOOST_AUTO_TEST_CASE(rollback) +{ + auto ret = createDefaultTable(); + BOOST_REQUIRE(ret); + auto table = tableFactory->openTable(testTableName); + + auto deleteEntry = table->newEntry(); + deleteEntry.setStatus(Entry::DELETED); + BOOST_REQUIRE_NO_THROW(table->setRow("name", deleteEntry)); + + auto hash = tableFactory->hash(hashImpl); + auto countRet = tableFactory->count(testTableName); + BOOST_REQUIRE_EQUAL(countRet.first, 0); + +#ifdef __APPLE__ +#undef __APPLE__ +#endif + // delete not exist entry will cause hash mismatch +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("ab98649ca506b076000000000000000000000000000000000000000000000001").hex()); +#endif + auto entry = std::make_optional(table->newEntry()); + BOOST_REQUIRE_NO_THROW(entry->setField(0, "Lili")); + BOOST_REQUIRE_NO_THROW(table->setRow("name", *entry)); + + countRet = tableFactory->count(testTableName); + BOOST_REQUIRE_EQUAL(countRet.first, 1); + + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + BOOST_REQUIRE(entry->dirty() == true); + BOOST_REQUIRE(entry->getField(0) == "Lili"); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + auto savePoint = std::make_shared(); + tableFactory->setRecoder(savePoint); + + entry = table->newEntry(); + entry->setField(0, "12345"); + table->setRow("id", *entry); + countRet = tableFactory->count(testTableName); + BOOST_REQUIRE_EQUAL(countRet.first, 2); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("d26dbc9a92ed28b1000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("id"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("d26dbc9a92ed28b1000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("d26dbc9a92ed28b1000000000000000000000000000000000000000000000000").hex()); +#endif + auto savePoint1 = std::make_shared(); + tableFactory->setRecoder(savePoint1); + + entry = table->newEntry(); + entry->setField(0, "500"); + table->setRow("balance", *entry); + countRet = tableFactory->count(testTableName); + BOOST_REQUIRE_EQUAL(countRet.first, 3); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("2b7be3797d97dcf7000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("balance"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("2b7be3797d97dcf7000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("2b7be3797d97dcf7000000000000000000000000000000000000000000000000").hex()); +#endif + auto savePoint2 = std::make_shared(); + tableFactory->setRecoder(savePoint2); + + auto deleteEntry2 = std::make_optional(table->newDeletedEntry()); + table->setRow("name", *deleteEntry2); + countRet = tableFactory->count(testTableName); + BOOST_REQUIRE_EQUAL(countRet.first, 2); + hash = tableFactory->hash(hashImpl); + +// delete entry will cause hash mismatch +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("4160d337ddd671e0000000000000000000000000000000000000000000000001").hex()); +#endif + entry = table->getRow("name"); + BOOST_REQUIRE(!entry); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("4160d337ddd671e0000000000000000000000000000000000000000000000001").hex()); +#endif + std::cout << "Try remove balance" << std::endl; + tableFactory->rollback(*savePoint2); + countRet = tableFactory->count(testTableName); + BOOST_REQUIRE_EQUAL(countRet.first, 3); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("2b7be3797d97dcf7000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("name"); + BOOST_REQUIRE_NE(entry->status(), Entry::DELETED); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("2b7be3797d97dcf7000000000000000000000000000000000000000000000000").hex()); +#endif + tableFactory->rollback(*savePoint1); + countRet = tableFactory->count(testTableName); + BOOST_REQUIRE_EQUAL(countRet.first, 2); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("d26dbc9a92ed28b1000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("d26dbc9a92ed28b1000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("balance"); + BOOST_REQUIRE(!entry); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("d26dbc9a92ed28b1000000000000000000000000000000000000000000000000").hex()); +#endif + + tableFactory->rollback(*savePoint); + countRet = tableFactory->count(testTableName); + BOOST_REQUIRE_EQUAL(countRet.first, 1); + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("balance"); + BOOST_REQUIRE(!entry); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("id"); + BOOST_REQUIRE(!entry); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + + // insert without version + entry = table->newEntry(); + entry->setField(0, "new record"); + BOOST_REQUIRE_NO_THROW(table->setRow("id", *entry)); + countRet = tableFactory->count(testTableName); + BOOST_REQUIRE_EQUAL(countRet.first, 2); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("2c14904fc33bbbae000000000000000000000000000000000000000000000000").hex()); +#endif + + entry = table->newDeletedEntry(); + BOOST_REQUIRE_NO_THROW(table->setRow("id", *entry)); + countRet = tableFactory->count(testTableName); + BOOST_REQUIRE_EQUAL(countRet.first, 1); + hash = tableFactory->hash(hashImpl); + // delete entry will cause hash mismatch +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("159ca8eb7641c2c1000000000000000000000000000000000000000000000001").hex()); +#endif +} + +BOOST_AUTO_TEST_CASE(rollback2) +{ + auto hash0 = tableFactory->hash(hashImpl); + // auto savePoint0 = tableFactory->savepoint(); + auto savePoint0 = std::make_shared(); + tableFactory->setRecoder(savePoint0); + BOOST_REQUIRE(hash0 == crypto::HashType(0)); + auto ret = createDefaultTable(); + BOOST_REQUIRE(ret); + auto table = tableFactory->openTable(testTableName); + + auto deleteEntry = table->newDeletedEntry(); + table->setRow("name", deleteEntry); + auto hash = tableFactory->hash(hashImpl); +// delete not exist entry will cause hash mismatch +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("ab98649ca506b076000000000000000000000000000000000000000000000001").hex()); +#endif + auto entry = std::make_optional(table->newEntry()); + // entry->setField("key", "name"); + entry->setField(0, "Lili"); + table->setRow("name", *entry); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + // BOOST_REQUIRE(table->dirty() == true); + BOOST_REQUIRE(entry->dirty() == true); + BOOST_REQUIRE(entry->getField(0) == "Lili"); + // auto savePoint = tableFactory->savepoint(); + auto savePoint = std::make_shared(); + tableFactory->setRecoder(savePoint); + + entry = table->newEntry(); + // entry->setField("key", "id"); + entry->setField(0, "12345"); + table->setRow("id", *entry); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("d26dbc9a92ed28b1000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("id"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("d26dbc9a92ed28b1000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("d26dbc9a92ed28b1000000000000000000000000000000000000000000000000").hex()); +#endif + // BOOST_REQUIRE(table->dirty() == true); + + tableFactory->rollback(*savePoint); + + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("balance"); + BOOST_REQUIRE(!entry); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("id"); + BOOST_REQUIRE(!entry); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + + // BOOST_REQUIRE(table->dirty() == true); + tableFactory->rollback(*savePoint0); + hash = tableFactory->hash(hashImpl); + BOOST_REQUIRE(hash.hex() == crypto::HashType("").hex()); + entry = table->getRow("name"); + BOOST_REQUIRE(!entry); + + auto hash00 = tableFactory->hash(hashImpl); + BOOST_REQUIRE(hash00 == crypto::HashType(0)); + + BOOST_REQUIRE_EQUAL_COLLECTIONS(hash0.begin(), hash0.end(), hash00.begin(), hash00.end()); + BOOST_REQUIRE(hash00 == hash0); + table = tableFactory->openTable(testTableName); + BOOST_REQUIRE(!table); +} + +BOOST_AUTO_TEST_CASE(rollback3) +{ + auto hash0 = tableFactory->hash(hashImpl); + // auto savePoint0 = tableFactory->savepoint(); + auto savePoint0 = std::make_shared(); + tableFactory->setRecoder(savePoint0); + BOOST_REQUIRE(hash0 == crypto::HashType(0)); + auto ret = createDefaultTable(); + BOOST_REQUIRE(ret); + auto table = tableFactory->openTable(testTableName); + auto entry = table->newEntry(); + entry.set("value"); + table->setRow("name", entry); + tableFactory->hash(hashImpl); + // first rollback + tableFactory->rollback(*savePoint0); + + savePoint0 = std::make_shared(); + tableFactory->setRecoder(savePoint0); + ret = createDefaultTable(); + BOOST_REQUIRE(ret); + table = tableFactory->openTable(testTableName); + entry = table->newEntry(); + entry.set("value"); + table->setRow("name", entry); + // second rollback + tableFactory->rollback(*savePoint0); + + tableFactory->setReadOnly(true); + std::promise getRow; + tableFactory->asyncGetRow( + testTableName, "", [&](Error::UniquePtr error, std::optional e) { + BOOST_REQUIRE(!error); + getRow.set_value(e.value()); + }); + KeyPageStorage::TableMeta meta(getRow.get_future().get().get()); + auto pageInfo = meta.getAllPageInfoNoLock(); + BOOST_REQUIRE(pageInfo.empty()); + tableFactory->setReadOnly(false); + + ret = createDefaultTable(); + BOOST_REQUIRE(ret); + table = tableFactory->openTable(testTableName); + entry = table->newEntry(); + entry.set("value"); + table->setRow("name", entry); +} + +BOOST_AUTO_TEST_CASE(hash) +{ + auto ret = createDefaultTable(); + BOOST_REQUIRE(ret); + + auto table = tableFactory->openTable(testTableName); + auto entry = std::make_optional(table->newEntry()); + // entry->setField("key", "name"); + entry->setField(0, "Lili"); + BOOST_REQUIRE_NO_THROW(table->setRow("name", *entry)); + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + auto tableFactory0 = make_shared(tableFactory); + + entry = std::make_optional(table->newEntry()); + // entry->setField("key", "id"); + entry->setField(0, "12345"); + BOOST_REQUIRE_NO_THROW(table->setRow("id", *entry)); + entry = table->getRow("id"); + BOOST_REQUIRE(entry.has_value()); + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + // BOOST_REQUIRE(table->dirty() == true); + auto keys = table->getPrimaryKeys({c}); + BOOST_REQUIRE(keys.size() == 2); + + auto entries = table->getRows(keys); + BOOST_REQUIRE(entries.size() == 2); + + auto dbHash1 = tableFactory->hash(hashImpl); + + auto savePoint = std::make_shared(); + tableFactory->setRecoder(savePoint); + auto idEntry = table->getRow("id"); + + auto deletedEntry = std::make_optional(table->newDeletedEntry()); + BOOST_REQUIRE_NO_THROW(table->setRow("id", *deletedEntry)); + entry = table->getRow("id"); + BOOST_REQUIRE(!entry); + + tableFactory->rollback(*savePoint); + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + entry = table->getRow("balance"); + BOOST_REQUIRE(!entry); + // BOOST_REQUIRE(table->dirty() == true); + + auto dbHash2 = tableFactory->hash(hashImpl); + BOOST_REQUIRE_EQUAL(dbHash1.hex(), dbHash2.hex()); + + // getPrimaryKeys and getRows + entry = table->newEntry(); + // entry->setField("key", "id"); + entry->setField(0, "12345"); + BOOST_REQUIRE_NO_THROW(table->setRow("id", *entry)); + entry = table->getRow("name"); + entry->setField(0, "Wang"); + BOOST_REQUIRE_NO_THROW(table->setRow("name", *entry)); + entry = table->newEntry(); + // entry->setField("key", "balance"); + entry->setField(0, "12345"); + BOOST_REQUIRE_NO_THROW(table->setRow("balance", *entry)); + BOOST_REQUIRE(entry.has_value()); + keys = table->getPrimaryKeys({c}); + BOOST_REQUIRE(keys.size() == 3); + + entries = table->getRows(keys); + BOOST_REQUIRE(entries.size() == 3); + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + entry = table->getRow("balance"); + BOOST_REQUIRE(entry.has_value()); + entry = table->getRow("balance1"); + BOOST_REQUIRE(!entry); + + auto nameEntry = table->getRow("name"); + auto deletedEntry2 = std::make_optional(table->newDeletedEntry()); + BOOST_REQUIRE_NO_THROW(table->setRow("name", *deletedEntry2)); + entry = table->getRow("name"); + BOOST_REQUIRE(!entry); + // BOOST_REQUIRE_EQUAL(entry->status(), Entry::DELETED); + keys = table->getPrimaryKeys({c}); + BOOST_REQUIRE(keys.size() == 2); + + entries = table->getRows(keys); + BOOST_REQUIRE(entries.size() == 2); + + auto idEntry2 = table->getRow("id"); + auto deletedEntry3 = std::make_optional(table->newDeletedEntry()); + BOOST_REQUIRE_NO_THROW(table->setRow("id", *deletedEntry3)); + entry = table->getRow("id"); + BOOST_REQUIRE(!entry); + // BOOST_REQUIRE_EQUAL(entry->status(), Entry::DELETED); + keys = table->getPrimaryKeys({c}); + BOOST_REQUIRE(keys.size() == 1); + + entries = table->getRows(keys); + BOOST_REQUIRE(entries.size() == 1); + // tableFactory->asyncCommit([](Error::Ptr, size_t) {}); +} + + +BOOST_AUTO_TEST_CASE(hash_V3_1_0) +{ + auto hashImpl2 = make_shared(); + auto memoryStorage2 = make_shared(nullptr); + auto tableFactory2 = make_shared( + memoryStorage2, 10240, (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION); + auto tableFactory1 = make_shared( + memoryStorage2, 10240, (uint32_t)bcos::protocol::BlockVersion::V3_0_VERSION); + + for (int i = 10; i < 20; ++i) + { + BOOST_REQUIRE(tableFactory1 != nullptr); + + std::string tableName = "testTable" + boost::lexical_cast(i); + auto key = "testKey" + boost::lexical_cast(i); + tableFactory1->createTable(tableName, "value"); + auto table = tableFactory1->openTable(tableName); + + auto entry = std::make_optional(table->newEntry()); + entry->setField(0, "hello world!"); + table->setRow(key, *entry); + + std::promise getRow; + table->asyncGetRow(key, [&](auto&& error, auto&& result) { + BOOST_REQUIRE(!error); + BOOST_REQUIRE_EQUAL(result->getField(0), "hello world!"); + + getRow.set_value(true); + }); + + getRow.get_future().get(); + } + + for (int i = 10; i < 20; ++i) + { + BOOST_REQUIRE(tableFactory2 != nullptr); + + std::string tableName = "testTable" + boost::lexical_cast(i); + auto key = "testKey" + boost::lexical_cast(i); + tableFactory2->createTable(tableName, "value"); + auto table = tableFactory2->openTable(tableName); + + auto entry = std::make_optional(table->newEntry()); + entry->setField(0, "hello world!"); + table->setRow(key, *entry); + + std::promise getRow; + table->asyncGetRow(key, [&](auto&& error, auto&& result) { + BOOST_REQUIRE(!error); + BOOST_REQUIRE_EQUAL(result->getField(0), "hello world!"); + + getRow.set_value(true); + }); + + getRow.get_future().get(); + } + + auto dbHash1 = tableFactory1->hash(hashImpl); + auto dbHash2 = tableFactory2->hash(hashImpl); + BOOST_REQUIRE_NE(dbHash1.hex(), dbHash2.hex()); +} + + +BOOST_AUTO_TEST_CASE(hash_different_table_same_data) +{ + auto hashImpl2 = std::make_shared(); + auto memoryStorage2 = make_shared(nullptr); + + auto tableFactory1 = make_shared( + memoryStorage2, 10240, (uint32_t)bcos::protocol::BlockVersion::V3_0_VERSION); + auto tableFactory2 = make_shared( + memoryStorage2, 10240, (uint32_t)bcos::protocol::BlockVersion::V3_0_VERSION); + BOOST_REQUIRE(tableFactory1 != nullptr); + BOOST_REQUIRE(tableFactory2 != nullptr); + + auto setData1 = [&](auto&& tableFactory) { + std::string tableName = "testTable1"; + auto key = "testKey1"; + tableFactory->createTable(tableName, "value"); + auto table = tableFactory->openTable(tableName); + auto entry = std::make_optional(table->newEntry()); + entry->setField(0, "hello world!"); + table->setRow(key, *entry); + tableName = "testTable2"; + tableFactory->createTable(tableName, "value"); + key = "testKey2"; + table = tableFactory->openTable(tableName); + entry = std::make_optional(table->newEntry()); + entry->setField(0, "hello world!"); + table->setRow(key, *entry); + }; + auto setData2 = [&](auto&& tableFactory) { + std::string tableName = "testTable2"; + auto key = "testKey1"; + tableFactory->createTable(tableName, "value"); + auto table = tableFactory->openTable(tableName); + auto entry = std::make_optional(table->newEntry()); + entry->setField(0, "hello world!"); + table->setRow(key, *entry); + tableName = "testTable1"; + tableFactory->createTable(tableName, "value"); + key = "testKey2"; + table = tableFactory->openTable(tableName); + entry = std::make_optional(table->newEntry()); + entry->setField(0, "hello world!"); + table->setRow(key, *entry); + }; + setData1(tableFactory1); + setData2(tableFactory2); + auto dbHash1 = tableFactory1->hash(hashImpl2); + auto dbHash2 = tableFactory2->hash(hashImpl2); + BOOST_REQUIRE_EQUAL(dbHash1.hex(), dbHash2.hex()); + + auto tableFactory3 = make_shared( + memoryStorage2, 10240, (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION); + auto tableFactory4 = make_shared( + memoryStorage2, 10240, (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION); + + setData1(tableFactory3); + setData2(tableFactory4); + auto dbHash3 = tableFactory3->hash(hashImpl2); + auto dbHash4 = tableFactory4->hash(hashImpl2); + BOOST_REQUIRE_NE(dbHash3.hex(), dbHash4.hex()); +} + +BOOST_AUTO_TEST_CASE(open_sysTables) +{ + auto table = tableFactory->openTable(StorageInterface::SYS_TABLES); + BOOST_REQUIRE(table); +} + +BOOST_AUTO_TEST_CASE(openAndCommit) +{ + auto hashImpl2 = make_shared(); + auto memoryStorage2 = make_shared(nullptr); + auto tableFactory2 = make_shared(memoryStorage2); + + for (int i = 10; i < 20; ++i) + { + BOOST_REQUIRE(tableFactory2 != nullptr); + + std::string tableName = "testTable" + boost::lexical_cast(i); + auto key = "testKey" + boost::lexical_cast(i); + tableFactory2->createTable(tableName, "value"); + auto table = tableFactory2->openTable(tableName); + + auto entry = std::make_optional(table->newEntry()); + entry->setField(0, "hello world!"); + table->setRow(key, *entry); + + std::promise getRow; + table->asyncGetRow(key, [&](auto&& error, auto&& result) { + BOOST_REQUIRE(!error); + BOOST_REQUIRE_EQUAL(result->getField(0), "hello world!"); + + getRow.set_value(true); + }); + + getRow.get_future().get(); + } +} + +BOOST_AUTO_TEST_CASE(checkInvalidKeys) +{ + auto hashImpl2 = make_shared(); + auto memoryStorage2 = make_shared(nullptr); + auto tableFactory2 = make_shared(memoryStorage2); + BOOST_REQUIRE(tableFactory2 != nullptr); + + std::string tableName = "testTable"; + tableFactory2->createTable(tableName, "value"); + auto table = tableFactory2->openTable(tableName); + for (int i = 0; i < 10; ++i) + { + auto key = "testKey" + boost::lexical_cast(i); + auto entry = std::make_optional(table->newEntry()); + entry->setField(0, "hello world!"); + table->setRow(key, *entry); + } + std::atomic invalid = 0; + tableFactory2->parallelTraverse(false, [&](auto&, auto&, auto& entry) { + if (entry.status() == Entry::Status::DELETED) + { + ++invalid; + } + return true; + }); + BOOST_TEST(invalid == 9); + auto key = "testKey" + boost::lexical_cast(9); + auto entry = table->newDeletedEntry(); + table->setRow(key, entry); + tableFactory2->setReadOnly(true); + auto tableFactory3 = make_shared(tableFactory2); + table = tableFactory3->openTable(tableName); + key = "testKey" + boost::lexical_cast(8); + entry = table->newEntry(); + entry.setField(0, "hello world!sss"); + table->setRow(key, entry); + invalid = 0; + tableFactory3->parallelTraverse(false, [&](auto&, auto&, auto& entry) { + if (entry.status() == Entry::Status::DELETED) + { + ++invalid; + } + return true; + }); + BOOST_TEST(invalid == 1); +} + +BOOST_AUTO_TEST_CASE(chainLink) +{ + std::vector storages; + auto valueFields = "value1"; + + auto stateStorage = make_shared(nullptr); + StateStorageInterface::Ptr prev = stateStorage; + for (int i = 0; i < 10; ++i) + { + prev->setReadOnly(true); + auto tableStorage = std::make_shared(prev); + for (int j = 0; j < 10; ++j) + { + auto tableName = "table_" + boost::lexical_cast(i) + "_" + + boost::lexical_cast(j); + BOOST_REQUIRE(tableStorage->createTable(tableName, valueFields)); + + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); + + for (int k = 0; k < 10; ++k) + { + auto entry = std::make_optional(table->newEntry()); + auto key = + boost::lexical_cast(i) + boost::lexical_cast(k); + entry->setField(0, boost::lexical_cast(i)); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + } + } + + prev = tableStorage; + storages.push_back(tableStorage); + } + + for (int index = 0; index < 10; ++index) + { + auto storage = storages[index]; + storage->setReadOnly(false); + // Data count must be 10 * 10 + 10 + std::atomic totalCount = 0; + storage->parallelTraverse(false, [&](auto&&, auto&&, auto&&) { + ++totalCount; + // std::string message = std::string("table=") + std::string(table) + ",key=" + + // std::string(key); cout << message << endl; + return true; + }); + BOOST_REQUIRE_EQUAL(totalCount, (9 + 1 + 1) * 10 + 10); // 10 for s_tables, every table has + // 1 page(10 entry) and 1 meta and + // 9 invalid key + + // Dirty data count must be 10 * 10 + 10 + std::atomic dirtyCount = 0; + storage->parallelTraverse(true, [&](auto&&, auto&&, auto&&) { + ++dirtyCount; + return true; + }); + + BOOST_REQUIRE_EQUAL(dirtyCount, (9 + 1 + 1) * 10 + 10); // 10 for s_tables, every table has + // 1 page(10 entry) and 1 meta + + // Low level can't touch high level's data + for (int i = 0; i < 10; ++i) + { + for (int j = 0; j < 10; ++j) + { + auto tableName = "table_" + boost::lexical_cast(i) + "_" + + boost::lexical_cast(j); + + auto table = storage->openTable(tableName); + if (i > index) + { + BOOST_REQUIRE(!table); + } + else + { + BOOST_REQUIRE(table); + + for (int k = 0; k < 10; ++k) + { + auto key = boost::lexical_cast(i) + + boost::lexical_cast(k); + BCOS_LOG(INFO) + << LOG_DESC("get") << LOG_KV("tableName", tableName) + << LOG_KV("key", key) << LOG_KV("index", index) << LOG_KV("i", i); + auto entry = table->getRow(key); + if (i > index) + { + BOOST_REQUIRE(!entry); + } + else + { + BOOST_REQUIRE(entry.has_value()); + + BOOST_REQUIRE_GT(entry->size(), 0); + if (i == index) + { + BOOST_REQUIRE_EQUAL(entry->dirty(), true); + } + else + { + BOOST_REQUIRE_EQUAL(entry->dirty(), false); + } + } + } + } + } + } + + + // After reading, current storage should include previous storage's data, previous data's + // dirty should be false + totalCount = 0; + tbb::concurrent_vector> checks; + storage->parallelTraverse(false, [&](auto&& table, auto&& key, auto&& entry) { + checks.push_back([table, key, entry] { + // BOOST_REQUIRE_NE(tableInfo, nullptr); + if (table != "s_tables" && !key.empty() && entry.status() != Entry::Status::DELETED) + { + auto l = entry.getField(0).size(); + BOOST_REQUIRE_GE(l, 10); + } + }); + + ++totalCount; + return true; + }); + + for (auto& it : checks) + { + it(); + } + + BOOST_REQUIRE_EQUAL(totalCount, 120 + 30 * index); + + checks.clear(); + dirtyCount = 0; + storage->parallelTraverse(true, [&](auto&& table, auto&& key, auto&& entry) { + checks.push_back([table, key, entry]() { + // BOOST_REQUIRE_NE(tableInfo, nullptr); + if (table != "s_tables" && !key.empty() && entry.status() != Entry::Status::DELETED) + { + auto l = entry.getField(0).size(); + BOOST_REQUIRE_GE(l, 10); + BOOST_REQUIRE_EQUAL(entry.dirty(), true); + } + }); + + ++dirtyCount; + return true; + }); + + for (auto& it : checks) + { + it(); + } + BOOST_REQUIRE_EQUAL(dirtyCount, 120); + storage->setReadOnly(true); + } +} + +BOOST_AUTO_TEST_CASE(getRows) +{ + std::vector storages; + auto valueFields = "value1,value2,value3"; + + auto stateStorage = make_shared(nullptr); + auto prev = std::make_shared(stateStorage); + auto tableStorage = std::make_shared(prev); + + BOOST_REQUIRE(prev->createTable("t_test", valueFields)); + + auto table = prev->openTable("t_test"); + BOOST_REQUIRE(table); + + for (size_t i = 0; i < 100; ++i) + { + auto entry = table->newEntry(); + entry.importFields({"data" + boost::lexical_cast(i)}); + table->setRow("key" + boost::lexical_cast(i), entry); + } + prev->setReadOnly(true); + + // query 50-150 + std::vector keys; + for (size_t i = 50; i < 150; ++i) + { + keys.push_back("key" + boost::lexical_cast(i)); + } + auto queryTable = tableStorage->openTable("t_test"); + BOOST_REQUIRE(queryTable); + + std::vector views; + for (auto& key : keys) + { + views.push_back(key); + } + auto values = queryTable->getRows(views); + + for (size_t i = 0; i < 100; ++i) + { + auto entry = values[i]; + if (i + 50 < 100) + { + BOOST_REQUIRE(entry.has_value()); + BOOST_REQUIRE_EQUAL(entry->dirty(), false); + BOOST_REQUIRE_GT(entry->size(), 0); + } + else + { + BOOST_REQUIRE(!entry); + } + } + + for (size_t i = 0; i < 10; ++i) + { + auto entry = queryTable->newEntry(); + entry.importFields({"data" + boost::lexical_cast(i)}); + queryTable->setRow("key" + boost::lexical_cast(i), entry); + } + + // Query 0-30 local(0-9) prev(10-29) + keys.clear(); + for (size_t i = 0; i < 30; ++i) + { + keys.push_back("key" + boost::lexical_cast(i)); + } + + views.clear(); + for (auto& key : keys) + { + views.push_back(key); + } + values = queryTable->getRows(views); + + for (size_t i = 0; i < 30; ++i) + { + auto entry = values[i]; + if (i < 10) + { + BOOST_REQUIRE(entry.has_value()); + BOOST_REQUIRE_EQUAL(entry->dirty(), true); + } + else + { + BOOST_REQUIRE(entry.has_value()); + BOOST_REQUIRE_EQUAL(entry->dirty(), false); + } + } + + // Test deleted entry + for (size_t i = 10; i < 20; ++i) + { + queryTable->setRow( + "key" + boost::lexical_cast(i), queryTable->newDeletedEntry()); + } + + auto values2 = queryTable->getRows(keys); + for (size_t i = 0; i < values2.size(); ++i) + { + if (i >= 10 && i < 20) + { + BOOST_REQUIRE(!values2[i]); + } + else + { + BOOST_REQUIRE(values2[i]); + } + } + + // Test rollback + auto recoder = std::make_shared(); + tableStorage->setRecoder(recoder); + for (size_t i = 70; i < 80; ++i) + { + Entry myEntry; + myEntry.importFields({"ddd1"}); + queryTable->setRow("key" + boost::lexical_cast(i), std::move(myEntry)); + } + + keys.clear(); + for (size_t i = 70; i < 80; ++i) + { + keys.push_back("key" + boost::lexical_cast(i)); + } + + auto values3 = queryTable->getRows(keys); + for (auto& it : values3) + { + BOOST_REQUIRE(it); + BOOST_REQUIRE_EQUAL(it->getField(0), "ddd1"); + BOOST_REQUIRE_EQUAL(it->dirty(), true); + } + + tableStorage->rollback(*recoder); + + auto values4 = queryTable->getRows(keys); + size_t count = 70; + for (auto& it : values4) + { + BOOST_REQUIRE(it); + BOOST_REQUIRE_EQUAL(it->getField(0), "data" + boost::lexical_cast(count)); + BOOST_REQUIRE_EQUAL(it->dirty(), false); + ++count; + } +} + +BOOST_AUTO_TEST_CASE(checkVersion) +{ + BOOST_REQUIRE_NO_THROW(tableFactory->createTable("testTable", "value1, value2, value3")); + auto table = tableFactory->openTable("testTable"); + + Entry value1; + value1.importFields({"v1"}); + table->setRow("abc", std::move(value1)); + + Entry value2; + value2.importFields({"v2"}); + BOOST_REQUIRE_NO_THROW(table->setRow("abc", std::move(value2))); + + Entry value3; + value3.importFields({"v3"}); + BOOST_REQUIRE_NO_THROW(table->setRow("abc", std::move(value3))); +} + +BOOST_AUTO_TEST_CASE(deleteAndGetRows) +{ + KeyPageStorage::Ptr storage1 = + std::make_shared(make_shared(nullptr)); + + storage1->asyncCreateTable( + "table", "value", [](Error::UniquePtr error, std::optional
table) { + BOOST_REQUIRE(!error); + BOOST_REQUIRE(table); + }); + + Entry entry1; + entry1.importFields({"value1"}); + storage1->asyncSetRow( + "table", "key1", std::move(entry1), [](Error::UniquePtr error) { BOOST_REQUIRE(!error); }); + + Entry entry2; + entry2.importFields({"value2"}); + storage1->asyncSetRow( + "table", "key2", std::move(entry2), [](Error::UniquePtr error) { BOOST_REQUIRE(!error); }); + + storage1->setReadOnly(true); + KeyPageStorage::Ptr storage2 = std::make_shared(storage1); + Entry deleteEntry; + deleteEntry.setStatus(Entry::DELETED); + storage2->asyncSetRow("table", "key2", std::move(deleteEntry), + [](Error::UniquePtr error) { BOOST_REQUIRE(!error); }); + + storage2->setReadOnly(true); + KeyPageStorage::Ptr storage3 = std::make_shared(storage2); + storage3->asyncGetPrimaryKeys( + "table", c, [](Error::UniquePtr error, std::vector keys) { + BOOST_REQUIRE(!error); + BOOST_REQUIRE_EQUAL(keys.size(), 1); + BOOST_REQUIRE_EQUAL(keys[0], "key1"); + }); +} + + +BOOST_AUTO_TEST_CASE(readPageWithInvalidKeyAndModifyNotChangePageKey) +{ + createDefaultTable(); + // write a page but the pageKey k0 is deleted, suppose the real pageKey is k1 + auto table = tableFactory->openTable(testTableName); + auto entry = std::make_optional(table->newEntry()); + entry->setField(0, "fruit999"); + BOOST_REQUIRE_NO_THROW(table->setRow("999", *entry)); + entry = table->getRow("999"); + BOOST_REQUIRE(entry.has_value()); + entry = std::make_optional(table->newEntry()); + entry->setField(0, "fruit99"); + BOOST_REQUIRE_NO_THROW(table->setRow("99", *entry)); + entry = table->getRow("99"); + BOOST_REQUIRE(entry.has_value()); + entry = std::make_optional(table->newDeletedEntry()); + BOOST_REQUIRE_NO_THROW(table->setRow("999", *entry)); + entry = table->getRow("999"); + BOOST_REQUIRE(!entry.has_value()); + tableFactory->setReadOnly(true); + + // read the page whose pageKey is invalid but not modify it + auto tableFactory0 = make_shared(tableFactory); + table = tableFactory0->openTable(testTableName); + entry = table->getRow("999"); + + // delete the the pageKey k1, insert entry less than k1 + entry = std::make_optional(table->newDeletedEntry()); + BOOST_REQUIRE_NO_THROW(table->setRow("99", *entry)); + entry = std::make_optional(table->newEntry()); + entry->setField(0, "fruit98"); + BOOST_REQUIRE_NO_THROW(table->setRow("98", *entry)); + entry = std::make_optional(table->newEntry()); + entry->setField(0, "fruit97"); + BOOST_REQUIRE_NO_THROW(table->setRow("97", *entry)); + + // commit the page, if has bug it will commit the page use invalid pageKey k0, otherwise it + // commit use k1 as the pageKey + tableFactory0->parallelTraverse(true, [&](auto& table, auto& key, auto& entry) { + if (entry.status() != Entry::DELETED) + { + BOOST_REQUIRE_NE(string(key), "999"); + } + if (table != "s_tables" && !key.empty() && entry.status() != Entry::Status::DELETED) + { + BOOST_REQUIRE_EQUAL(string(key), "99"); + } + return true; + }); +} + +BOOST_AUTO_TEST_CASE(readPageWithInvalidKeyAndDeleteNotChangePageKey) +{ + createDefaultTable(); + // write a page but the pageKey k0 is deleted, suppose the real pageKey is k1 + auto table = tableFactory->openTable(testTableName); + auto entry = std::make_optional(table->newEntry()); + entry->setField(0, "fruit999"); + BOOST_REQUIRE_NO_THROW(table->setRow("999", *entry)); + entry = table->getRow("999"); + BOOST_REQUIRE(entry.has_value()); + entry = std::make_optional(table->newEntry()); + entry->setField(0, "fruit99"); + BOOST_REQUIRE_NO_THROW(table->setRow("99", *entry)); + entry = table->getRow("99"); + BOOST_REQUIRE(entry.has_value()); + entry = std::make_optional(table->newDeletedEntry()); + BOOST_REQUIRE_NO_THROW(table->setRow("999", *entry)); + entry = table->getRow("999"); + BOOST_REQUIRE(!entry.has_value()); + tableFactory->setReadOnly(true); + + // read the page whose pageKey is invalid but not modify it + auto tableFactory0 = make_shared(tableFactory); + table = tableFactory0->openTable(testTableName); + entry = table->getRow("88"); + + // delete entry less than k1 and not change the pageKey, keep the pageKey is k1 + entry = std::make_optional(table->newDeletedEntry()); + BOOST_REQUIRE_NO_THROW(table->setRow("98", *entry)); + entry = std::make_optional(table->newDeletedEntry()); + BOOST_REQUIRE_NO_THROW(table->setRow("97", *entry)); + + // commit the page, if has bug it will commit the page use invalid pageKey k0, otherwise it + // commit use k1 as the pageKey + tableFactory0->parallelTraverse(true, [&](auto& table, auto& key, auto& entry) { + if (entry.status() != Entry::DELETED) + { + BOOST_REQUIRE_NE(string(key), "999"); + } + if (table != "s_tables" && !key.empty() && entry.status() != Entry::Status::DELETED) + { + BOOST_REQUIRE_EQUAL(string(key), "99"); + } + return true; + }); +} + +BOOST_AUTO_TEST_CASE(deletedAndGetRow) +{ + KeyPageStorage::Ptr storage1 = + std::make_shared(make_shared(nullptr)); + + storage1->asyncCreateTable( + "table", "value", [](Error::UniquePtr error, std::optional
table) { + BOOST_REQUIRE(!error); + BOOST_REQUIRE(table); + }); + + Entry entry1; + entry1.importFields({"value1"}); + storage1->asyncSetRow( + "table", "key1", std::move(entry1), [](Error::UniquePtr error) { BOOST_REQUIRE(!error); }); + + storage1->setReadOnly(true); + KeyPageStorage::Ptr storage2 = std::make_shared(storage1); + Entry deleteEntry; + deleteEntry.setStatus(Entry::DELETED); + storage2->asyncSetRow("table", "key1", std::move(deleteEntry), + [](Error::UniquePtr error) { BOOST_REQUIRE(!error); }); + + storage2->asyncGetRow("table", "key1", [](Error::UniquePtr error, std::optional entry) { + BOOST_REQUIRE(!error); + BOOST_REQUIRE(!entry); + }); + + storage2->asyncGetRow("table", "key1", [](Error::UniquePtr error, std::optional entry) { + BOOST_REQUIRE(!error); + BOOST_REQUIRE(!entry); + }); +} + +BOOST_AUTO_TEST_CASE(deletedAndGetRows) +{ + KeyPageStorage::Ptr storage1 = + std::make_shared(make_shared(nullptr)); + + storage1->asyncCreateTable( + "table", "value", [](Error::UniquePtr error, std::optional
table) { + BOOST_REQUIRE(!error); + BOOST_REQUIRE(table); + }); + + Entry entry1; + entry1.importFields({"value1"}); + storage1->asyncSetRow( + "table", "key1", std::move(entry1), [](Error::UniquePtr error) { BOOST_REQUIRE(!error); }); + + storage1->setReadOnly(true); + KeyPageStorage::Ptr storage2 = std::make_shared(storage1); + Entry deleteEntry; + deleteEntry.setStatus(Entry::DELETED); + storage2->asyncSetRow("table", "key1", std::move(deleteEntry), + [](Error::UniquePtr error) { BOOST_REQUIRE(!error); }); + + std::string_view keys[] = {"key1"}; + storage2->asyncGetRows( + "table", keys, [](Error::UniquePtr error, std::vector> entry) { + BOOST_REQUIRE(!error); + BOOST_REQUIRE_EQUAL(entry.size(), 1); + BOOST_REQUIRE(!entry[0]); + }); +} + +BOOST_AUTO_TEST_CASE(rollbackAndGetRow) +{ + KeyPageStorage::Ptr storage1 = + std::make_shared(make_shared(nullptr)); + + storage1->asyncCreateTable( + "table", "value", [](Error::UniquePtr error, std::optional
table) { + BOOST_REQUIRE(!error); + BOOST_REQUIRE(table); + }); + + Entry entry1; + entry1.importFields({"value1"}); + storage1->asyncSetRow( + "table", "key1", std::move(entry1), [](Error::UniquePtr error) { BOOST_REQUIRE(!error); }); + + storage1->setReadOnly(true); + KeyPageStorage::Ptr storage2 = std::make_shared(storage1); + auto recoder = std::make_shared(); + storage2->setRecoder(recoder); + + Entry entry2; + entry2.importFields({"value2"}); + storage2->asyncSetRow( + "table", "key1", std::move(entry2), [](Error::UniquePtr error) { BOOST_REQUIRE(!error); }); + + storage2->asyncGetRow("table", "key1", [](Error::UniquePtr error, std::optional entry) { + BOOST_REQUIRE(!error); + BOOST_REQUIRE(entry.has_value()); + BOOST_REQUIRE_EQUAL(entry->getField(0), "value2"); + }); + + storage2->rollback(*recoder); + + storage2->asyncGetRow("table", "key1", [](Error::UniquePtr error, std::optional entry) { + BOOST_REQUIRE(!error); + BOOST_REQUIRE(entry.has_value()); + BOOST_REQUIRE_EQUAL(entry->getField(0), "value1"); + }); +} + +BOOST_AUTO_TEST_CASE(rollbackAndGetRows) +{ + KeyPageStorage::Ptr storage1 = + std::make_shared(make_shared(nullptr)); + + storage1->asyncCreateTable( + "table", "value", [](Error::UniquePtr error, std::optional
table) { + BOOST_REQUIRE(!error); + BOOST_REQUIRE(table); + }); + + Entry entry1; + entry1.importFields({"value1"}); + storage1->asyncSetRow( + "table", "key1", std::move(entry1), [](Error::UniquePtr error) { BOOST_REQUIRE(!error); }); + + storage1->setReadOnly(true); + KeyPageStorage::Ptr storage2 = std::make_shared(storage1); + auto recoder = std::make_shared(); + storage2->setRecoder(recoder); + + Entry entry2; + entry2.importFields({"value2"}); + storage2->asyncSetRow( + "table", "key1", std::move(entry2), [](Error::UniquePtr error) { BOOST_REQUIRE(!error); }); + + std::string_view keys[] = {"key1"}; + storage2->asyncGetRows( + "table", keys, [](Error::UniquePtr error, std::vector> entry) { + BOOST_REQUIRE(!error); + BOOST_REQUIRE_EQUAL(entry.size(), 1); + BOOST_REQUIRE_EQUAL(entry[0].value().getField(0), "value2"); + }); + + storage2->rollback(*recoder); + + storage2->asyncGetRows( + "table", keys, [](Error::UniquePtr error, std::vector> entry) { + BOOST_REQUIRE(!error); + BOOST_REQUIRE_EQUAL(entry.size(), 1); + BOOST_REQUIRE_EQUAL(entry[0].value().getField(0), "value1"); + }); +} + +BOOST_AUTO_TEST_CASE(randomRWHash) +{ + std::vector> rwSet; + + std::random_device rd; + for (size_t i = 0; i < 3; ++i) + { + auto keyNum = rd(); + bool write = keyNum % 2; + + std::string table; + std::string key; + std::string value; + if (write || i == 0) + { + write = true; + table = boost::lexical_cast(keyNum % 10); + key = boost::lexical_cast(keyNum); + value = boost::lexical_cast(rd()); + } + else + { + auto index = keyNum % i; + table = std::get<1>(rwSet[index]); + key = std::get<2>(rwSet[index]); + } + + rwSet.emplace_back(std::make_tuple(write, table, key, value)); + } + + std::vector prevHashes; + for (size_t times = 0; times < 10; ++times) + { + std::vector hashes; + StateStorageInterface::Ptr prev = make_shared(nullptr); + for (size_t i = 0; i < 10; ++i) + { + KeyPageStorage::Ptr storage = std::make_shared(prev); + + for (auto& it : rwSet) + { + auto& [write, table, key, value] = it; + + storage->asyncOpenTable(table, [storage, tableName = table](Error::UniquePtr error, + std::optional
tableItem) { + BOOST_REQUIRE(!error); + if (!tableItem) + { + storage->asyncCreateTable(tableName, "value", + [](Error::UniquePtr error, std::optional
tableItem) { + BOOST_REQUIRE(!error); + BOOST_REQUIRE(tableItem); + }); + } + }); + + if (write) + { + Entry entry; + entry.importFields({value}); + storage->asyncSetRow(table, key, std::move(entry), + [](Error::UniquePtr error) { BOOST_REQUIRE(!error); }); + } + else + { + storage->asyncGetRow( + table, key, [](Error::UniquePtr error, std::optional) { + BOOST_REQUIRE(!error); + }); + } + } + + hashes.push_back(storage->hash(hashImpl)); + storage->setReadOnly(false); + storage->setReadOnly(true); + prev = storage; + } + + if (!prevHashes.empty()) + { + BOOST_REQUIRE_EQUAL_COLLECTIONS( + prevHashes.begin(), prevHashes.end(), hashes.begin(), hashes.end()); + } + prevHashes.swap(hashes); + hashes.clear(); + } +} + +BOOST_AUTO_TEST_CASE(hash_map) +{ + class EntryKey + { + public: + EntryKey() {} + EntryKey(std::string_view table, std::string_view key) : m_table(table), m_key(key) {} + EntryKey(std::string_view table, std::string key) : m_table(table), m_key(std::move(key)) {} + + EntryKey(const EntryKey&) = default; + EntryKey& operator=(const EntryKey&) = default; + EntryKey(EntryKey&&) noexcept = default; + EntryKey& operator=(EntryKey&&) noexcept = default; + + std::string_view table() const { return m_table; } + + std::string_view key() const + { + std::string_view view; + std::visit([&view](auto&& key) { view = key; }, m_key); + + return view; + } + + bool operator==(const EntryKey& rhs) const + { + return m_table == rhs.m_table && key() == rhs.key(); + } + + bool operator<(const EntryKey& rhs) const + { + if (m_table != rhs.m_table) + { + return m_table < rhs.m_table; + } + + return m_key < rhs.m_key; + } + + private: + std::string_view m_table; + std::variant m_key; + }; + + struct EntryKeyHasher + { + size_t hash(const EntryKey& dataKey) const + { + size_t seed = hashString(dataKey.table()); + boost::hash_combine(seed, hashString(dataKey.key())); + + return seed; + } + + bool equal(const EntryKey& lhs, const EntryKey& rhs) const + { + return lhs.table() == rhs.table() && lhs.key() == rhs.key(); + } + + std::hash hashString; + }; + + tbb::concurrent_hash_map data; + + std::string tableName = "table"; + std::string key = "key"; + + decltype(data)::const_accessor it; + BOOST_REQUIRE(data.emplace(it, EntryKey("table", std::string_view("key")), 100)); + + decltype(data)::const_accessor findIt; + BOOST_REQUIRE(data.find(findIt, EntryKey("table", std::string_view("key")))); +} + +BOOST_AUTO_TEST_CASE(pageMerge) +{ + auto valueFields = "value1"; + + auto stateStorage = make_shared(nullptr); + StateStorageInterface::Ptr prev = stateStorage; + + auto tableStorage = std::make_shared(prev, 1024); + for (int j = 0; j < 100; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + BOOST_REQUIRE(tableStorage->createTable(tableName, valueFields)); + + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); + + for (int k = 0; k < 100; ++k) + { + auto entry = std::make_optional(table->newEntry()); + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + entry->setField(0, boost::lexical_cast(k)); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + } + } + for (int j = 0; j < 100; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); + + for (int k = 0; k < 100; ++k) + { + if (k % 5 < 4) + { + auto entry = std::make_optional(table->newDeletedEntry()); + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + // BCOS_LOG(TRACE) << LOG_DESC("delete") << LOG_KV("tableName", tableName) + // << LOG_KV("key", key); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + } + } + } + for (int j = 0; j < 100; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); + + for (int k = 0; k < 100; ++k) + { + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + auto entry = table->getRow(key); + // BCOS_LOG(TRACE) << LOG_DESC("getRow") << LOG_KV("tableName", tableName) + // << LOG_KV("key", key); + if (k % 5 < 4) + { + BOOST_REQUIRE(!entry); + } + else + { + BOOST_REQUIRE(entry.has_value()); + BOOST_REQUIRE(entry->get() == boost::lexical_cast(k)); + } + } + } + std::atomic totalCount = 0; + tableStorage->parallelTraverse(false, [&](auto&&, auto&&, auto&&) { + ++totalCount; + return true; + }); + BOOST_REQUIRE_EQUAL(totalCount, 2200); // meta + 5page + s_table +} + +BOOST_AUTO_TEST_CASE(pageMergeRandom) +{ + auto valueFields = "value1"; + + auto stateStorage = make_shared(nullptr); + StateStorageInterface::Ptr prev = stateStorage; + + auto tableStorage = std::make_shared(prev, 1024); + auto entryCount = 100; + auto tableCount = 100; + +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int j = 0; j < tableCount; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + BOOST_REQUIRE(tableStorage->createTable(tableName, valueFields)); + + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); + + for (int k = 0; k < entryCount; ++k) + { + auto entry = std::make_optional(table->newEntry()); + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + + entry->setField(0, boost::lexical_cast(k)); + table->setRow(key, *entry); + // BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + // try + // { + // table->setRow(key, *entry); + // } + // catch (std::exception& e) + // { + // BCOS_LOG(TRACE) << LOG_DESC("set") << LOG_KV("tableName", tableName) + // << LOG_KV("key", key)<< LOG_KV("what", e.what()); + // } + } + } + auto hash = tableStorage->hash(hashImpl); + // BOOST_TEST( + // hash.hex() == + // crypto::HashType("4d4a5c95180905cb000000000000000000000000000000000000000000000000").hex()); + +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int j = 0; j < tableCount; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); + + for (int k = 0; k < entryCount; ++k) + { + if (k % 5 < 4) + { + auto entry = std::make_optional(table->newDeletedEntry()); + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + BCOS_LOG(TRACE) << LOG_DESC("delete") << LOG_KV("tableName", tableName) + << LOG_KV("key", key); + // BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + table->setRow(key, *entry); + } + } + } + + hash = tableStorage->hash(hashImpl); + // BOOST_TEST( + // hash.hex() == + // crypto::HashType("4d4a5c95180905cb000000000000000000000000000000000000000000000000").hex()); + +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int j = 0; j < tableCount; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); + + for (int k = 0; k < entryCount; ++k) + { + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + auto entry = table->getRow(key); + BCOS_LOG(TRACE) << LOG_DESC("getRow") << LOG_KV("tableName", tableName) + << LOG_KV("key", key); + if (k % 5 < 4) + { + BOOST_REQUIRE(!entry); + } + else + { + BOOST_REQUIRE(entry.has_value()); + BOOST_REQUIRE(entry->get() == boost::lexical_cast(k)); + } + } + } + + hash = tableStorage->hash(hashImpl); + // BOOST_TEST( + // hash.hex() == + // crypto::HashType("4d4a5c95180905cb000000000000000000000000000000000000000000000000").hex()); + + std::atomic totalCount = 0; + std::atomic deleted = 0; + tableStorage->parallelTraverse(false, [&](auto&&, auto&&, auto& e) { + ++totalCount; + if (e.status() == Entry::Status::DELETED) + { + ++deleted; + } + return true; + }); + BOOST_TEST(totalCount == 2200); // meta + 5page + s_table + BOOST_TEST(deleted == 1900); // meta + 5page + s_table +} + +BOOST_AUTO_TEST_CASE(pageMergeParallelRandom) +{ + auto valueFields = "value1"; + + auto stateStorage = make_shared(nullptr); + StateStorageInterface::Ptr prev = stateStorage; + + auto tableStorage = std::make_shared(prev, 1024); + for (int j = 0; j < 100; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + BOOST_REQUIRE(tableStorage->createTable(tableName, valueFields)); + + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); + +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int k = 0; k < 100; ++k) + { + auto entry = std::make_optional(table->newEntry()); + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + entry->setField(0, boost::lexical_cast(k)); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + } + } + for (int j = 0; j < 100; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); + +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int k = 0; k < 100; ++k) + { + if (k % 5 < 4) + { + auto entry = std::make_optional(table->newDeletedEntry()); + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + // BCOS_LOG(TRACE) << LOG_DESC("delete") << LOG_KV("tableName", tableName) + // << LOG_KV("key", key); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + } + } + } + + for (int j = 0; j < 100; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int k = 0; k < 100; ++k) + { + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + auto entry = table->getRow(key); + // BCOS_LOG(TRACE) << LOG_DESC("getRow") << LOG_KV("tableName", tableName) + // << LOG_KV("key", key); + if (k % 5 < 4) + { + BOOST_REQUIRE(!entry); + } + else + { + BOOST_REQUIRE(entry.has_value()); + BOOST_REQUIRE(entry->get() == boost::lexical_cast(k)); + } + } + } + // std::atomic totalCount = 0; + // tableStorage->parallelTraverse(false, [&](auto&&, auto&&, auto&&) { + // ++totalCount; + // return true; + // }); + // BOOST_REQUIRE_EQUAL(totalCount , 393); // meta + 5page + s_table +} + +BOOST_AUTO_TEST_CASE(parallelMix) +{ + auto valueFields = "value1"; + + auto stateStorage = make_shared(nullptr); + StateStorageInterface::Ptr prev = stateStorage; + + auto tableStorage = std::make_shared(prev, 1024); + // #pragma omp parallel for + for (int j = 0; j < 100; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + BOOST_REQUIRE(tableStorage->createTable(tableName, valueFields)); + + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int k = 0; k < 100; ++k) + { + auto entry = std::make_optional(table->newEntry()); + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + entry->setField(0, boost::lexical_cast(k)); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + } + } + // #pragma omp parallel for + for (int j = 0; j < 100; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int k = 0; k < 100; ++k) + { + if (k % 5 == 4) + { + auto entry = std::make_optional(table->newEntry()); + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + entry->setField(0, boost::lexical_cast(k + 1)); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + } + else + { + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + auto entry = table->getRow(key); + auto value = boost::lexical_cast(k); + BOOST_REQUIRE(entry.has_value()); + BOOST_REQUIRE(entry->get() == value); + auto deleteEntry = std::make_optional(table->newDeletedEntry()); + // BCOS_LOG(TRACE) << LOG_DESC("delete") << LOG_KV("tableName", tableName) + // << LOG_KV("key", key); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *deleteEntry)); + } + } + } + // #pragma omp parallel for + for (int j = 0; j < 100; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int k = 0; k < 100; ++k) + { + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + auto entry = table->getRow(key); + // BCOS_LOG(TRACE) << LOG_DESC("getRow") << LOG_KV("tableName", tableName) + // << LOG_KV("key", key); + if (k % 5 < 4) + { + BOOST_REQUIRE(!entry); + } + else + { + BOOST_REQUIRE(entry.has_value()); + BOOST_REQUIRE(entry->get() == boost::lexical_cast(k + 1)); + } + } + } + // std::atomic totalCount = 0; + // tableStorage->parallelTraverse(false, [&](auto&&, auto&&, auto&&) { + // ++totalCount; + // return true; + // }); + // BOOST_REQUIRE_EQUAL(totalCount , 392); // meta + 5page + s_table +} + +BOOST_AUTO_TEST_CASE(pageSplit) +{ + auto valueFields = "value1"; + + auto stateStorage = make_shared(nullptr); + StateStorageInterface::Ptr prev = stateStorage; + + auto tableStorage = std::make_shared(prev, 1024); + for (int j = 0; j < 100; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + BOOST_REQUIRE(tableStorage->createTable(tableName, valueFields)); + + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); + + for (int k = 0; k < 100; ++k) + { + auto entry = std::make_optional(table->newEntry()); + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + entry->setField(0, boost::lexical_cast(k)); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + } + } + for (int j = 0; j < 100; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); + + for (int k = 0; k < 100; ++k) + { + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + auto entry = table->getRow(key); + // BCOS_LOG(TRACE) << LOG_DESC("getRow") << LOG_KV("tableName", tableName) + // << LOG_KV("key", key); + BOOST_REQUIRE(entry.has_value()); + BOOST_REQUIRE(entry->get() == boost::lexical_cast(k)); + } + } + std::atomic totalCount = 0; + tableStorage->parallelTraverse(false, [&](auto&&, auto&&, auto&&) { + ++totalCount; + return true; + }); + BOOST_REQUIRE_EQUAL(totalCount, 2200); // meta + 5page + s_table +} + +BOOST_AUTO_TEST_CASE(pageSplitRandom) +{ + auto valueFields = "value1"; + + auto stateStorage = make_shared(nullptr); + StateStorageInterface::Ptr prev = stateStorage; + + auto tableStorage = std::make_shared(prev, 256); +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int j = 0; j < 100; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + BOOST_REQUIRE(tableStorage->createTable(tableName, valueFields)); + + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); + + for (int k = 0; k < 5000; ++k) + { + auto entry = std::make_optional(table->newEntry()); + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + entry->setField(0, boost::lexical_cast(k)); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + } + } +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int j = 0; j < 100; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); + + for (int k = 0; k < 500; ++k) + { + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + auto entry = table->getRow(key); + // BCOS_LOG(TRACE) << LOG_DESC("getRow") << LOG_KV("tableName", tableName) + // << LOG_KV("key", key); + BOOST_REQUIRE(entry.has_value()); + BOOST_REQUIRE(entry->get() == boost::lexical_cast(k)); + } + } + std::atomic totalCount = 0; + tableStorage->parallelTraverse(false, [&](auto&&, auto&&, auto&&) { + ++totalCount; + return true; + }); + BOOST_REQUIRE_EQUAL(totalCount, 9730); // meta + 5page + s_table +} + +BOOST_AUTO_TEST_CASE(pageSplitParallelRandom) +{ + auto valueFields = "value1"; + + auto stateStorage = make_shared(nullptr); + StateStorageInterface::Ptr prev = stateStorage; + + auto tableStorage = std::make_shared(prev, 256); + for (int j = 0; j < 100; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + BOOST_REQUIRE(tableStorage->createTable(tableName, valueFields)); + + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int k = 0; k < 500; ++k) + { + auto entry = std::make_optional(table->newEntry()); + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + entry->setField(0, boost::lexical_cast(k)); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + } + } + + for (int j = 0; j < 100; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int k = 0; k < 500; ++k) + { + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + auto entry = table->getRow(key); + // BCOS_LOG(TRACE) << LOG_DESC("getRow") << LOG_KV("tableName", tableName) + // << LOG_KV("key", key); + BOOST_REQUIRE(entry.has_value()); + BOOST_REQUIRE(entry->get() == boost::lexical_cast(k)); + } + } + // std::atomic totalCount = 0; + // tableStorage->parallelTraverse(false, [&](auto&&, auto&&, auto&&) { + // ++totalCount; + // return true; + // }); + // BOOST_REQUIRE_EQUAL(totalCount , 999); // meta + 5page + s_table +} + + +BOOST_AUTO_TEST_CASE(asyncGetPrimaryKeys) +{ // TODO: add ut for asyncGetPrimaryKeys and condition + auto valueFields = "value1"; + + auto stateStorage = make_shared(nullptr); + StateStorageInterface::Ptr prev = stateStorage; + + auto tableStorage = std::make_shared(prev, 256); + for (int j = 0; j < 100; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + BOOST_REQUIRE(tableStorage->createTable(tableName, valueFields)); + + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); +#pragma omp parallel for + for (int k = 0; k < 1000; ++k) + { + auto entry = std::make_optional(table->newEntry()); + auto key = boost::lexical_cast(k); + entry->setField(0, boost::lexical_cast(k)); +#pragma omp critical + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + } + } + + for (int j = 0; j < 100; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); +#pragma omp parallel for + for (int k = 0; k < 1000; ++k) + { + Condition c; + c.limit(0, 200); + auto keys = table->getPrimaryKeys(c); + +#pragma omp critical + BOOST_REQUIRE(keys.size() == 200); + c.limit(200, 300); + keys = table->getPrimaryKeys(c); +#pragma omp critical + BOOST_REQUIRE(keys.size() == 300); + c.limit(900, 200); + keys = table->getPrimaryKeys(c); +#pragma omp critical + BOOST_REQUIRE(keys.size() == 100); + c.GE("900"); +#pragma omp critical + BOOST_REQUIRE(keys.size() == 100); + } + } + auto tableName = "table_" + boost::lexical_cast(0); + auto table = tableStorage->openTable(tableName); + auto printVector = [](const std::vector& vec) -> string { + stringstream ss; + for (auto& s : vec) + { + ss << s << " "; + } + ss << endl; + return ss.str(); + }; + Condition c; + c.limit(0, 500); + c.LE("250"); + auto keys = table->getPrimaryKeys(c); + BCOS_LOG(TRACE) << "LE 250:" << printVector(keys); + BOOST_REQUIRE(keys.size() == 170); + Condition c2; + c2.limit(0, 500); + c2.LT("250"); + auto keys2 = table->getPrimaryKeys(c2); + BCOS_LOG(TRACE) << "LT 250:" << printVector(keys2); + BOOST_REQUIRE(keys2.size() == 169); + Condition c3; + c3.limit(750, 500); + c3.GT("250"); + auto keys3 = table->getPrimaryKeys(c3); + BCOS_LOG(TRACE) << "GT 250:" << printVector(keys3); + BOOST_REQUIRE(keys3.size() == 80); + Condition c4; + c4.limit(750, 500); + c4.GE("250"); + auto keys4 = table->getPrimaryKeys(c4); + BCOS_LOG(TRACE) << "GE 250:" << printVector(keys4); + BOOST_REQUIRE(keys4.size() == 81); + Condition c5; + c5.limit(500, 500); + c5.NE("250"); + auto keys5 = table->getPrimaryKeys(c5); + BCOS_LOG(TRACE) << "NE 250:" << printVector(keys5); + BOOST_REQUIRE(keys5.size() == 499); + + auto fruitTable = "table_fruit"; + BOOST_REQUIRE(tableStorage->createTable(fruitTable, valueFields)); + + table = tableStorage->openTable(fruitTable); + BOOST_REQUIRE(table); + auto entry = std::make_optional(table->newEntry()); + auto key = "fruit"; + entry->setField(0, "a"); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + Condition c6; + c6.limit(0, 0); + c6.GE("fruit"); + auto keys6 = table->getPrimaryKeys(c6); + // the default limit is 0 + BOOST_REQUIRE(keys6.size() == 0); +} + + +BOOST_AUTO_TEST_CASE(BigTableAdd) +{ + auto valueFields = "value1"; + auto cacheSize = 256 * 1024 * 1024; + auto pageSize = 512; + auto stateStorage0 = make_shared(nullptr); + stateStorage0->setMaxCapacity(cacheSize); + StateStorageInterface::Ptr prev0 = stateStorage0; + + auto tableName = "table_0"; + BOOST_REQUIRE(prev0->createTable(tableName, valueFields)); + + auto stateStorage1 = make_shared(nullptr); + stateStorage1->setMaxCapacity(cacheSize); + StateStorageInterface::Ptr prev1 = stateStorage1; + + BOOST_REQUIRE(prev1->createTable(tableName, valueFields)); + + size_t count = 100; + auto keyCount = 100; + auto index = 0; + srand(time(NULL)); + for (size_t i = 0; i < count; ++i) + { + auto tableStorage0 = std::make_shared(prev0, pageSize); + auto table0 = tableStorage0->openTable(tableName); + BOOST_REQUIRE(table0); + + auto tableStorage1 = std::make_shared(prev1, pageSize); + auto table1 = tableStorage1->openTable(tableName); + BOOST_REQUIRE(table1); + +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int k = 0; k < keyCount; ++k) + { + auto v = rand() + index + k; + auto key = boost::lexical_cast(v); + auto value = + boost::lexical_cast(i) + "_" + boost::lexical_cast(v); + auto entry0 = std::make_optional(table0->newEntry()); + entry0->setField(0, value); + BOOST_REQUIRE_NO_THROW(table0->setRow(key, *entry0)); + + auto entry1 = std::make_optional(table1->newEntry()); + entry1->setField(0, value); + BOOST_REQUIRE_NO_THROW(table1->setRow(key, *entry1)); + } + auto hash0 = tableStorage0->hash(hashImpl); + BCOS_LOG(DEBUG) << LOG_DESC(">>>>>>>>>>>>KeyPageStorage0") << LOG_KV("i", i) + << LOG_KV("hash", hash0.hex()); + BOOST_REQUIRE(hash0.hex() != crypto::HashType(0).hex()); + auto hash1 = tableStorage1->hash(hashImpl); + BCOS_LOG(DEBUG) << LOG_DESC(">>>>>>>>>>>>KeyPageStorage1") << LOG_KV("i", i) + << LOG_KV("hash", hash1.hex()); + BOOST_REQUIRE(hash1.hex() != crypto::HashType(0).hex()); + BOOST_TEST(hash0.hex() != crypto::HashType(0).hex()); + BOOST_TEST(hash0.hex() == hash1.hex()); + BOOST_REQUIRE(hash0.hex() == hash1.hex()); + tableStorage0->setReadOnly(true); + tableStorage1->setReadOnly(true); + stateStorage0->merge(true, *tableStorage0); + stateStorage1->merge(true, *tableStorage1); + hash0 = stateStorage0->hash(hashImpl); + BCOS_LOG(DEBUG) << LOG_DESC(">>>>>>>>>>>>stateStorage0") << LOG_KV("i", i) + << LOG_KV("hash", hash0.hex()); + hash1 = stateStorage1->hash(hashImpl); + BCOS_LOG(DEBUG) << LOG_DESC(">>>>>>>>>>>>stateStorage1") << LOG_KV("i", i) + << LOG_KV("hash", hash1.hex()); + // BOOST_TEST(hash0.hex() == hash1.hex()); + // BOOST_REQUIRE(hash0.hex() == hash1.hex()); + index += keyCount; + BCOS_LOG(DEBUG) << LOG_DESC("<<<<<<<<<<<<<<<<<<<<<<\n\n\n\n\n\n\n\n\n"); + } +} + +BOOST_AUTO_TEST_CASE(BigTableAddSerialize) +{ + auto valueFields = "value1"; + auto cacheSize = 256 * 1024 * 1024; + auto pageSize = 512; + auto stateStorage0 = make_shared(nullptr); + stateStorage0->setMaxCapacity(cacheSize); + StateStorageInterface::Ptr prev0 = stateStorage0; + + auto tableName = "table_0"; + BOOST_REQUIRE(prev0->createTable(tableName, valueFields)); + + auto stateStorage1 = make_shared(nullptr); + stateStorage1->setMaxCapacity(cacheSize); + StateStorageInterface::Ptr prev1 = stateStorage1; + + BOOST_REQUIRE(prev1->createTable(tableName, valueFields)); + + size_t count = 1; + auto keyCount = 100; + auto index = 0; + srand(time(NULL)); + for (size_t i = 0; i < count; ++i) + { + auto tableStorage0 = std::make_shared(prev0, pageSize); + auto table0 = tableStorage0->openTable(tableName); + BOOST_REQUIRE(table0); + + auto tableStorage1 = std::make_shared(prev1, pageSize); + auto table1 = tableStorage1->openTable(tableName); + BOOST_REQUIRE(table1); + + for (int k = 0; k < keyCount; ++k) + { + auto v = rand() + index + k; + auto key = boost::lexical_cast(v); + auto value = + boost::lexical_cast(i) + "_" + boost::lexical_cast(v); + auto entry0 = std::make_optional(table0->newEntry()); + entry0->setField(0, value); + BOOST_REQUIRE_NO_THROW(table0->setRow(key, *entry0)); + + auto entry1 = std::make_optional(table1->newEntry()); + entry1->setField(0, value); + BOOST_REQUIRE_NO_THROW(table1->setRow(key, *entry1)); + } + auto hash0 = tableStorage0->hash(hashImpl); + BCOS_LOG(DEBUG) << LOG_DESC(">>>>>>>>>>>>KeyPageStorage0") << LOG_KV("i", i) + << LOG_KV("hash", hash0.hex()); + BOOST_REQUIRE(hash0.hex() != crypto::HashType(0).hex()); + auto hash1 = tableStorage1->hash(hashImpl); + BCOS_LOG(DEBUG) << LOG_DESC(">>>>>>>>>>>>KeyPageStorage1") << LOG_KV("i", i) + << LOG_KV("hash", hash1.hex()); + BOOST_REQUIRE(hash0.hex() != crypto::HashType(0).hex()); + BOOST_TEST(hash0.hex() == hash1.hex()); + BOOST_REQUIRE(hash0.hex() == hash1.hex()); + tableStorage0->setReadOnly(true); + tableStorage1->setReadOnly(true); + stateStorage0->merge(true, *tableStorage0); + stateStorage1->merge(true, *tableStorage1); + hash0 = stateStorage0->hash(hashImpl); + BCOS_LOG(DEBUG) << LOG_DESC(">>>>>>>>>>>>stateStorage0") << LOG_KV("i", i) + << LOG_KV("hash", hash0.hex()); + hash1 = stateStorage1->hash(hashImpl); + BCOS_LOG(DEBUG) << LOG_DESC(">>>>>>>>>>>>stateStorage1") << LOG_KV("i", i) + << LOG_KV("hash", hash1.hex()); + BOOST_TEST(hash0.hex() == hash1.hex()); + BOOST_REQUIRE(hash0.hex() == hash1.hex()); + index += keyCount; + BCOS_LOG(DEBUG) << LOG_DESC("<<<<<<<<<<<<<<<<<<<<<<\n\n\n\n\n\n\n\n\n"); + } +} + +BOOST_AUTO_TEST_CASE(mockCommitProcess) +{ + auto valueFields = "value1"; + auto cacheSize = 256 * 1024 * 1024; + auto pageSize = 512; + auto stateStorage0 = make_shared(nullptr); + stateStorage0->setMaxCapacity(cacheSize); + StateStorageInterface::Ptr prev0 = stateStorage0; + + auto tableName = "table_0"; + BOOST_REQUIRE(prev0->createTable(tableName, valueFields)); + + auto stateStorage1 = make_shared(nullptr); + stateStorage1->setMaxCapacity(cacheSize); + StateStorageInterface::Ptr prev1 = stateStorage1; + BOOST_REQUIRE(prev1->createTable(tableName, valueFields)); + + auto stateStorage2 = make_shared(nullptr); + stateStorage2->setMaxCapacity(cacheSize); + StateStorageInterface::Ptr prev2 = stateStorage2; + BOOST_REQUIRE(prev2->createTable(tableName, valueFields)); + + + size_t count = 100; + auto keyCount = 1000; + auto index = 0; + srand(time(NULL)); + // std::list keypages0; + std::list keypages1; + std::list keypages2; + for (size_t i = 0; i < count; ++i) + { + auto tableStorage0 = std::make_shared(prev0, pageSize); + auto table0 = tableStorage0->openTable(tableName); + BOOST_REQUIRE(table0); + + auto tableStorage1 = std::make_shared(prev1, pageSize); + auto table1 = tableStorage1->openTable(tableName); + BOOST_REQUIRE(table1); + + auto tableStorage2 = std::make_shared(prev2, pageSize); + auto table2 = tableStorage2->openTable(tableName); + BOOST_REQUIRE(table2); + + for (int k = 0; k < keyCount; ++k) + { + auto v = rand() + index + k; + auto key = boost::lexical_cast(v); + auto value = + boost::lexical_cast(i) + "_" + boost::lexical_cast(v); + auto getKey = boost::lexical_cast(index + k); + auto entry0 = std::make_optional(table0->newEntry()); + entry0->setField(0, value); + BOOST_REQUIRE_NO_THROW(table0->setRow(key, *entry0)); + entry0 = table0->getRow(key); + BOOST_REQUIRE(entry0); + entry0 = table0->getRow(getKey); + + auto entry1 = std::make_optional(table1->newEntry()); + entry1->setField(0, value); + BOOST_REQUIRE_NO_THROW(table1->setRow(key, *entry1)); + entry1 = table1->getRow(key); + BOOST_REQUIRE(entry1); + entry1 = table1->getRow(getKey); + + auto entry2 = std::make_optional(table2->newEntry()); + entry2->setField(0, value); + BOOST_REQUIRE_NO_THROW(table2->setRow(key, *entry2)); + entry2 = table2->getRow(key); + BOOST_REQUIRE(entry2); + entry2 = table2->getRow(getKey); + } + auto hash0 = tableStorage0->hash(hashImpl); + BCOS_LOG(DEBUG) << LOG_DESC(">>>>>>>>>>>>KeyPageStorage0") << LOG_KV("i", i) + << LOG_KV("hash", hash0.hex()); + BOOST_REQUIRE(hash0.hex() != crypto::HashType(0).hex()); + auto hash1 = tableStorage1->hash(hashImpl); + BCOS_LOG(DEBUG) << LOG_DESC(">>>>>>>>>>>>KeyPageStorage1") << LOG_KV("i", i) + << LOG_KV("hash", hash1.hex()); + BOOST_REQUIRE(hash1.hex() != crypto::HashType(0).hex()); + auto hash2 = tableStorage2->hash(hashImpl); + BCOS_LOG(DEBUG) << LOG_DESC(">>>>>>>>>>>>KeyPageStorage2") << LOG_KV("i", i) + << LOG_KV("hash", hash2.hex()); + BOOST_TEST(hash0.hex() == hash1.hex()); + BOOST_TEST(hash2.hex() == hash1.hex()); + BOOST_REQUIRE(hash0.hex() == hash1.hex()); + BOOST_REQUIRE(hash2.hex() == hash1.hex()); + + tableStorage0->setReadOnly(true); + tableStorage1->setReadOnly(true); + tableStorage2->setReadOnly(true); + keypages1.push_back(tableStorage1); + keypages2.push_back(tableStorage2); + prev1 = tableStorage1; + prev2 = tableStorage2; + // 0 always commit + stateStorage0->merge(true, *tableStorage0); + if (i % 2 == 0) + { // 50% commit + auto s = keypages1.front(); + stateStorage1->merge(true, *s); + keypages1.pop_front(); + } + + if (rand() % 3 == 0) + { // random commit + auto s = keypages2.front(); + stateStorage2->merge(true, *s); + keypages2.pop_front(); + } + index += keyCount; + BCOS_LOG(DEBUG) << LOG_DESC("<<<<<<<<<<<<<<<<<<<<<<\n\n\n\n\n\n\n\n\n"); + } +} + +BOOST_AUTO_TEST_CASE(mockCommitProcessParallel) +{ + auto valueFields = "value1"; + auto cacheSize = 256 * 1024 * 1024; + auto pageSize = 512; + auto stateStorage0 = make_shared(nullptr); + stateStorage0->setMaxCapacity(cacheSize); + StateStorageInterface::Ptr prev0 = stateStorage0; + + auto tableName = "table_0"; + BOOST_REQUIRE(prev0->createTable(tableName, valueFields)); + + auto stateStorage1 = make_shared(nullptr); + stateStorage1->setMaxCapacity(cacheSize); + StateStorageInterface::Ptr prev1 = stateStorage1; + BOOST_REQUIRE(prev1->createTable(tableName, valueFields)); + + auto stateStorage2 = make_shared(nullptr); + stateStorage2->setMaxCapacity(cacheSize); + StateStorageInterface::Ptr prev2 = stateStorage2; + BOOST_REQUIRE(prev2->createTable(tableName, valueFields)); + + + size_t count = 10; + auto keyCount = 1000; + auto index = 0; + srand(time(NULL)); + // std::list keypages0; + std::list keypages1; + std::list keypages2; + for (size_t i = 0; i < count; ++i) + { + auto tableStorage0 = std::make_shared(prev0, pageSize); + auto table0 = tableStorage0->openTable(tableName); + BOOST_REQUIRE(table0); + + auto tableStorage1 = std::make_shared(prev1, pageSize); + auto table1 = tableStorage1->openTable(tableName); + BOOST_REQUIRE(table1); + + auto tableStorage2 = std::make_shared(prev2, pageSize); + auto table2 = tableStorage2->openTable(tableName); + BOOST_REQUIRE(table2); + +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int k = 0; k < keyCount; ++k) + { + auto v = rand() + index + k; + auto key = boost::lexical_cast(v); + auto value = + boost::lexical_cast(i) + "_" + boost::lexical_cast(v); + auto getKey = boost::lexical_cast(index + k); + auto entry0 = std::make_optional(table0->newEntry()); + entry0->setField(0, value); + BOOST_REQUIRE_NO_THROW(table0->setRow(key, *entry0)); + entry0 = table0->getRow(key); + BOOST_REQUIRE(entry0); + entry0 = table0->getRow(getKey); + + auto entry1 = std::make_optional(table1->newEntry()); + entry1->setField(0, value); + BOOST_REQUIRE_NO_THROW(table1->setRow(key, *entry1)); + entry1 = table1->getRow(key); + BOOST_REQUIRE(entry1); + entry1 = table1->getRow(getKey); + + auto entry2 = std::make_optional(table2->newEntry()); + entry2->setField(0, value); + BOOST_REQUIRE_NO_THROW(table2->setRow(key, *entry2)); + entry2 = table2->getRow(key); + BOOST_REQUIRE(entry2); + entry2 = table2->getRow(getKey); + } + auto hash0 = tableStorage0->hash(hashImpl); + BCOS_LOG(DEBUG) << LOG_DESC(">>>>>>>>>>>>KeyPageStorage0") << LOG_KV("i", i) + << LOG_KV("hash", hash0.hex()); + BOOST_REQUIRE(hash0.hex() != crypto::HashType(0).hex()); + auto hash1 = tableStorage1->hash(hashImpl); + BCOS_LOG(DEBUG) << LOG_DESC(">>>>>>>>>>>>KeyPageStorage1") << LOG_KV("i", i) + << LOG_KV("hash", hash1.hex()); + BOOST_REQUIRE(hash1.hex() != crypto::HashType(0).hex()); + auto hash2 = tableStorage2->hash(hashImpl); + BCOS_LOG(DEBUG) << LOG_DESC(">>>>>>>>>>>>KeyPageStorage2") << LOG_KV("i", i) + << LOG_KV("hash", hash2.hex()); + BOOST_TEST(hash0.hex() == hash1.hex()); + BOOST_TEST(hash2.hex() == hash1.hex()); + BOOST_REQUIRE(hash0.hex() == hash1.hex()); + BOOST_REQUIRE(hash2.hex() == hash1.hex()); + + tableStorage0->setReadOnly(true); + tableStorage1->setReadOnly(true); + tableStorage2->setReadOnly(true); + keypages1.push_back(tableStorage1); + keypages2.push_back(tableStorage2); + prev1 = tableStorage1; + prev2 = tableStorage2; + // 0 always commit + stateStorage0->merge(true, *tableStorage0); + if (i % 2 == 0) + { // 50% commit + auto s = keypages1.front(); + stateStorage1->merge(true, *s); + keypages1.pop_front(); + } + + if (rand() % 3 == 0) + { // random commit + auto s = keypages2.front(); + stateStorage2->merge(true, *s); + keypages2.pop_front(); + } + index += keyCount; + BCOS_LOG(DEBUG) << LOG_DESC("<<<<<<<<<<<<<<<<<<<<<<\n\n\n\n\n\n\n\n\n"); + } +} + +BOOST_AUTO_TEST_CASE(pageMergeBig) +{ + auto valueFields = "value1"; + + auto stateStorage = make_shared(nullptr); + StateStorageInterface::Ptr prev = stateStorage; + + auto tableStorage = std::make_shared(prev, 1024); + auto tableCount = 5; + auto rowCount = 20000; + srand(time(NULL)); + std::vector> keys; + keys.resize(tableCount); +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int j = 0; j < tableCount; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + BOOST_REQUIRE(tableStorage->createTable(tableName, valueFields)); + + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); + + for (int k = 0; k < rowCount; ++k) + { + keys[j][k] = true; + auto entry = std::make_optional(table->newEntry()); + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + entry->setField(0, boost::lexical_cast(k)); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + } + } + +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int j = 0; j < tableCount; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); + for (int k = rowCount - 1; k >= 0; --k) + { + if (k % 5 < 4) + { + keys[j][k] = false; + auto entry = std::make_optional(table->newDeletedEntry()); + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + // BCOS_LOG(TRACE) << LOG_DESC("delete") << LOG_KV("tableName", tableName) + // << LOG_KV("key", key); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + } + } + + for (int k = rowCount - 1; k >= 0; --k) + { + if (rand() % 2 == 0) + { + keys[j][k] = true; + auto entry = std::make_optional(table->newEntry()); + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + entry->setField(0, boost::lexical_cast(k)); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + } + } + + for (int k = 0; k < rowCount; ++k) + { + if (rand() % 3 == 0) + { + keys[j][k] = true; + auto entry = std::make_optional(table->newEntry()); + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + entry->setField(0, boost::lexical_cast(k)); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + } + } + + for (int k = rowCount - 1; k >= 0; --k) + { + if (k % 5 < 4) + { + keys[j][k] = false; + auto entry = std::make_optional(table->newDeletedEntry()); + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + // BCOS_LOG(TRACE) << LOG_DESC("delete") << LOG_KV("tableName", tableName) + // << LOG_KV("key", key); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + } + } + + for (int k = 0; k < rowCount; ++k) + { + if (rand() % 3 == 0) + { + keys[j][k] = true; + auto entry = std::make_optional(table->newEntry()); + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + entry->setField(0, boost::lexical_cast(k)); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + } + } + for (int k = 0; k < rowCount; ++k) + { + if (rand() % 3 == 0) + { + keys[j][k] = false; + auto entry = std::make_optional(table->newDeletedEntry()); + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + // BCOS_LOG(TRACE) << LOG_DESC("delete") << LOG_KV("tableName", tableName) + // << LOG_KV("key", key); + BOOST_REQUIRE_NO_THROW(table->setRow(key, *entry)); + } + } + } + + for (int j = 0; j < tableCount; ++j) + { + auto tableName = "table_" + boost::lexical_cast(j); + auto table = tableStorage->openTable(tableName); + BOOST_REQUIRE(table); + + for (int k = 0; k < rowCount; ++k) + { + auto key = + boost::lexical_cast(j) + "_" + boost::lexical_cast(k); + auto entry = table->getRow(key); + // BCOS_LOG(TRACE) << LOG_DESC("getRow") << LOG_KV("tableName", tableName) + // << LOG_KV("key", key); + if (keys[j][k]) + { + BOOST_REQUIRE(entry.has_value()); + BOOST_REQUIRE(entry->get() == boost::lexical_cast(k)); + } + } + } +} + +BOOST_AUTO_TEST_CASE(insertAndDelete) +{ + boost::log::core::get()->set_logging_enabled(true); + auto valueFields = "value1"; + auto cacheSize = 256 * 1024 * 1024; + auto pageSize = 512; + auto stateStorage0 = make_shared(nullptr); + stateStorage0->setMaxCapacity(cacheSize); + StateStorageInterface::Ptr prev0 = stateStorage0; + + auto tableName = "table_0"; + BOOST_REQUIRE(prev0->createTable(tableName, valueFields)); + + size_t count = 10; + auto keyCount = 100; + auto index = 0; + srand(time(NULL)); + for (size_t i = 0; i < count; ++i) + { + auto tableStorage0 = std::make_shared(prev0, pageSize); + auto table0 = tableStorage0->openTable(tableName); + BOOST_REQUIRE(table0); + + std::vector keys(keyCount, 0); +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int k = 0; k < keyCount; ++k) + { + auto v = rand() + index + k; + keys[k] = v; + auto key = boost::lexical_cast(v); + auto value = + boost::lexical_cast(i) + "_" + boost::lexical_cast(v); + auto entry0 = std::make_optional(table0->newEntry()); + entry0->setField(0, value); + BOOST_REQUIRE_NO_THROW(table0->setRow(key, *entry0)); + } + +#if defined(__APPLE__) +#pragma omp parallel for +#endif + for (int k = 0; k < keyCount; ++k) + { + auto v = rand() + index + k; + auto key = boost::lexical_cast(v); + auto entry0 = std::make_optional(table0->newDeletedEntry()); + BOOST_REQUIRE_NO_THROW(table0->setRow(key, *entry0)); + } + if (rand() % 2 == 0) + { + BCOS_LOG(DEBUG) << LOG_DESC("delete from head"); + for (int k = 0; k < keyCount; ++k) + { + auto key = boost::lexical_cast(keys[k]); + auto entry0 = std::make_optional(table0->newDeletedEntry()); + BOOST_REQUIRE_NO_THROW(table0->setRow(key, *entry0)); + } + } + else + { + BCOS_LOG(DEBUG) << LOG_DESC("delete from tail"); + for (int k = keyCount - 1; k >= 0; --k) + { + auto key = boost::lexical_cast(keys[k]); + auto entry0 = std::make_optional(table0->newDeletedEntry()); + BOOST_REQUIRE_NO_THROW(table0->setRow(key, *entry0)); + } + } + auto hash0 = tableStorage0->hash(hashImpl); + BCOS_LOG(DEBUG) << LOG_DESC(">>>>>>>>>>>>KeyPageStorage0") << LOG_KV("i", i) + << LOG_KV("hash", hash0.hex()); + BOOST_TEST(hash0.hex() != crypto::HashType(0).hex()); + tableStorage0->setReadOnly(true); + stateStorage0->merge(true, *tableStorage0); + hash0 = stateStorage0->hash(hashImpl); + BCOS_LOG(DEBUG) << LOG_DESC(">>>>>>>>>>>>stateStorage0") << LOG_KV("i", i) + << LOG_KV("hash", hash0.hex()); + index += keyCount; + } +} + + +BOOST_AUTO_TEST_SUITE_END() +} // namespace bcos::test diff --git "a/BFPL\345\243\271/bcos-table/test/unittests/libtable/TestStateStorage.cpp" "b/BFPL\345\243\271/bcos-table/test/unittests/libtable/TestStateStorage.cpp" new file mode 100644 index 00000000..91ace9e2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/test/unittests/libtable/TestStateStorage.cpp" @@ -0,0 +1,1230 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Unit tests for the Table + * @file Table.cpp + */ + +#include "Hash.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-table/src/StateStorage.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::storage; +using namespace bcos::crypto; + +#if defined(__APPLE__) +#undef __APPLE__ +#endif + +namespace std +{ +inline ostream& operator<<(ostream& os, const std::optional& entry) +{ + os << entry.has_value(); + return os; +} + +inline ostream& operator<<(ostream& os, const std::optional
& table) +{ + os << table.has_value(); + return os; +} + +inline ostream& operator<<(ostream& os, const std::unique_ptr& error) +{ + os << error->what(); + return os; +} + +inline ostream& operator<<(ostream& os, const std::tuple& pair) +{ + os << std::get<0>(pair) << " " << std::get<1>(pair).hex(); + return os; +} +} // namespace std + +namespace bcos +{ +namespace test +{ +struct TableFactoryFixture +{ + TableFactoryFixture() + { + hashImpl = make_shared(); + memoryStorage = make_shared(nullptr); + BOOST_TEST(memoryStorage != nullptr); + tableFactory = make_shared(memoryStorage); + BOOST_TEST(tableFactory != nullptr); + } + + ~TableFactoryFixture() {} + std::optional
createDefaultTable() + { + std::promise> createPromise; + tableFactory->asyncCreateTable( + testTableName, valueField, [&](auto&& error, std::optional
table) { + BOOST_CHECK(!error); + createPromise.set_value(table); + }); + return createPromise.get_future().get(); + } + + std::shared_ptr hashImpl = nullptr; + std::shared_ptr memoryStorage = nullptr; + protocol::BlockNumber m_blockNumber = 0; + std::shared_ptr tableFactory = nullptr; + std::string testTableName = "t_test"; + std::string keyField = "key"; + std::string valueField = "value"; +}; +BOOST_FIXTURE_TEST_SUITE(StateStorageTest, TableFactoryFixture) + +BOOST_AUTO_TEST_CASE(constructor) +{ + auto threadPool = ThreadPool("a", 1); + auto tf = std::make_shared(memoryStorage); +} + +BOOST_AUTO_TEST_CASE(create_Table) +{ + std::string tableName("t_test1"); + auto table = tableFactory->openTable(tableName); + + BOOST_TEST(!table); + auto ret = tableFactory->createTable(tableName, valueField); + BOOST_TEST(ret); + + table = tableFactory->openTable(tableName); + BOOST_TEST(table); + + BOOST_CHECK_THROW(tableFactory->createTable(tableName, valueField), bcos::Error); +} + +BOOST_AUTO_TEST_CASE(rollback) +{ + auto ret = createDefaultTable(); + BOOST_REQUIRE(ret); + auto table = tableFactory->openTable(testTableName); + + auto deleteEntry = table->newEntry(); + deleteEntry.setStatus(Entry::DELETED); + BOOST_REQUIRE_NO_THROW(table->setRow("name", deleteEntry)); + + auto hash = tableFactory->hash(hashImpl); + +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("ab98649ca506b076000000000000000000000000000000000000000000000001").hex()); +#endif + auto entry = std::make_optional(table->newEntry()); + BOOST_REQUIRE_NO_THROW(entry->setField(0, "Lili")); + BOOST_REQUIRE_NO_THROW(table->setRow("name", *entry)); + + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + BOOST_REQUIRE(entry->dirty() == true); + BOOST_REQUIRE(entry->getField(0) == "Lili"); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + auto savePoint = std::make_shared(); + tableFactory->setRecoder(savePoint); + + entry = table->newEntry(); + entry->setField(0, "12345"); + table->setRow("id", *entry); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("d26dbc9a92ed28b1000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("id"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("d26dbc9a92ed28b1000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("d26dbc9a92ed28b1000000000000000000000000000000000000000000000000").hex()); +#endif + auto savePoint1 = std::make_shared(); + tableFactory->setRecoder(savePoint1); + + entry = table->newEntry(); + entry->setField(0, "500"); + table->setRow("balance", *entry); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("2b7be3797d97dcf7000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("balance"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("2b7be3797d97dcf7000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("2b7be3797d97dcf7000000000000000000000000000000000000000000000000").hex()); +#endif + auto savePoint2 = std::make_shared(); + tableFactory->setRecoder(savePoint2); + + auto deleteEntry2 = std::make_optional(table->newDeletedEntry()); + table->setRow("name", *deleteEntry2); + hash = tableFactory->hash(hashImpl); + +// delete entry will cause hash mismatch +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("4160d337ddd671e0000000000000000000000000000000000000000000000001").hex()); +#endif + entry = table->getRow("name"); + BOOST_REQUIRE(!entry); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("4160d337ddd671e0000000000000000000000000000000000000000000000001").hex()); +#endif + std::cout << "Try remove balance" << std::endl; + tableFactory->rollback(*savePoint2); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("2b7be3797d97dcf7000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("name"); + BOOST_REQUIRE_NE(entry->status(), Entry::DELETED); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("2b7be3797d97dcf7000000000000000000000000000000000000000000000000").hex()); +#endif + tableFactory->rollback(*savePoint1); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("d26dbc9a92ed28b1000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("d26dbc9a92ed28b1000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("balance"); + BOOST_REQUIRE(!entry); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("d26dbc9a92ed28b1000000000000000000000000000000000000000000000000").hex()); +#endif + + tableFactory->rollback(*savePoint); + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("balance"); + BOOST_REQUIRE(!entry); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("id"); + BOOST_REQUIRE(!entry); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + + // insert without version + entry = table->newEntry(); + entry->setField(0, "new record"); + BOOST_REQUIRE_NO_THROW(table->setRow("id", *entry)); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("2c14904fc33bbbae000000000000000000000000000000000000000000000000").hex()); +#endif + + entry = table->newDeletedEntry(); + BOOST_REQUIRE_NO_THROW(table->setRow("id", *entry)); + hash = tableFactory->hash(hashImpl); + // delete entry will cause hash mismatch +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("159ca8eb7641c2c1000000000000000000000000000000000000000000000001").hex()); +#endif +} + +BOOST_AUTO_TEST_CASE(rollback2) +{ + auto hash0 = tableFactory->hash(hashImpl); + // auto savePoint0 = tableFactory->savepoint(); + auto savePoint0 = std::make_shared(); + tableFactory->setRecoder(savePoint0); + BOOST_REQUIRE(hash0 == crypto::HashType(0)); + auto ret = createDefaultTable(); + BOOST_REQUIRE(ret); + auto table = tableFactory->openTable(testTableName); + + auto deleteEntry = table->newDeletedEntry(); + table->setRow("name", deleteEntry); + auto hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("ab98649ca506b076000000000000000000000000000000000000000000000001").hex()); +#endif + auto entry = std::make_optional(table->newEntry()); + // entry->setField("key", "name"); + entry->setField(0, "Lili"); + table->setRow("name", *entry); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + // BOOST_REQUIRE(table->dirty() == true); + BOOST_REQUIRE(entry->dirty() == true); + BOOST_REQUIRE(entry->getField(0) == "Lili"); + // auto savePoint = tableFactory->savepoint(); + auto savePoint = std::make_shared(); + tableFactory->setRecoder(savePoint); + + entry = table->newEntry(); + // entry->setField("key", "id"); + entry->setField(0, "12345"); + table->setRow("id", *entry); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("d26dbc9a92ed28b1000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("id"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("d26dbc9a92ed28b1000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("d26dbc9a92ed28b1000000000000000000000000000000000000000000000000").hex()); +#endif + // BOOST_REQUIRE(table->dirty() == true); + + tableFactory->rollback(*savePoint); + + entry = table->getRow("name"); + BOOST_REQUIRE(entry.has_value()); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("balance"); + BOOST_REQUIRE(!entry); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + entry = table->getRow("id"); + BOOST_REQUIRE(!entry); + hash = tableFactory->hash(hashImpl); +#if defined(__APPLE__) + BOOST_CHECK_EQUAL(hash.hex(), + crypto::HashType("c18354d205471d61000000000000000000000000000000000000000000000000").hex()); +#endif + + // BOOST_REQUIRE(table->dirty() == true); + tableFactory->rollback(*savePoint0); + hash = tableFactory->hash(hashImpl); + BOOST_REQUIRE(hash.hex() == crypto::HashType("").hex()); + entry = table->getRow("name"); + BOOST_REQUIRE(!entry); + + auto hash00 = tableFactory->hash(hashImpl); + BOOST_REQUIRE(hash00 == crypto::HashType(0)); + + BOOST_REQUIRE_EQUAL_COLLECTIONS(hash0.begin(), hash0.end(), hash00.begin(), hash00.end()); + BOOST_REQUIRE(hash00 == hash0); + table = tableFactory->openTable(testTableName); + BOOST_REQUIRE(!table); +} + +BOOST_AUTO_TEST_CASE(rollback3) +{ + // Test rollback multi state storage +} + +BOOST_AUTO_TEST_CASE(hash) +{ + auto ret = createDefaultTable(); + BOOST_TEST(ret); + + tableFactory->setEnableTraverse(true); + + auto table = tableFactory->openTable(testTableName); + auto entry = std::make_optional(table->newEntry()); + // entry->setField("key", "name"); + entry->setField(0, "Lili"); + BOOST_CHECK_NO_THROW(table->setRow("name", *entry)); + entry = table->getRow("name"); + BOOST_TEST(entry); + + entry = std::make_optional(table->newEntry()); + // entry->setField("key", "id"); + entry->setField(0, "12345"); + BOOST_CHECK_NO_THROW(table->setRow("id", *entry)); + entry = table->getRow("id"); + BOOST_TEST(entry); + entry = table->getRow("name"); + BOOST_TEST(entry); + // BOOST_TEST(table->dirty() == true); + auto keys = table->getPrimaryKeys({}); + BOOST_TEST(keys.size() == 2); + + auto entries = table->getRows(keys); + BOOST_TEST(entries.size() == 2); + + auto dbHash1 = tableFactory->hash(hashImpl); + + auto savePoint = std::make_shared(); + tableFactory->setRecoder(savePoint); + auto idEntry = table->getRow("id"); + + auto deletedEntry = std::make_optional(table->newDeletedEntry()); + BOOST_CHECK_NO_THROW(table->setRow("id", *deletedEntry)); + entry = table->getRow("id"); + BOOST_CHECK(!entry); + + tableFactory->rollback(*savePoint); + entry = table->getRow("name"); + BOOST_TEST(entry); + entry = table->getRow("balance"); + BOOST_TEST(!entry); + // BOOST_TEST(table->dirty() == true); + + auto dbHash2 = tableFactory->hash(hashImpl); + BOOST_CHECK_EQUAL(dbHash1.hex(), dbHash2.hex()); + + // getPrimaryKeys and getRows + entry = table->newEntry(); + // entry->setField("key", "id"); + entry->setField(0, "12345"); + BOOST_CHECK_NO_THROW(table->setRow("id", *entry)); + entry = table->getRow("name"); + entry->setField(0, "Wang"); + BOOST_CHECK_NO_THROW(table->setRow("name", *entry)); + entry = table->newEntry(); + // entry->setField("key", "balance"); + entry->setField(0, "12345"); + BOOST_CHECK_NO_THROW(table->setRow("balance", *entry)); + BOOST_TEST(entry); + keys = table->getPrimaryKeys({}); + BOOST_TEST(keys.size() == 3); + + entries = table->getRows(keys); + BOOST_TEST(entries.size() == 3); + entry = table->getRow("name"); + BOOST_TEST(entry); + entry = table->getRow("balance"); + BOOST_TEST(entry); + entry = table->getRow("balance1"); + BOOST_TEST(!entry); + + auto nameEntry = table->getRow("name"); + auto deletedEntry2 = std::make_optional(table->newDeletedEntry()); + BOOST_CHECK_NO_THROW(table->setRow("name", *deletedEntry2)); + entry = table->getRow("name"); + BOOST_CHECK(!entry); + // BOOST_CHECK_EQUAL(entry->status(), Entry::DELETED); + keys = table->getPrimaryKeys({}); + BOOST_TEST(keys.size() == 2); + + entries = table->getRows(keys); + BOOST_TEST(entries.size() == 2); + + auto idEntry2 = table->getRow("id"); + auto deletedEntry3 = std::make_optional(table->newDeletedEntry()); + BOOST_CHECK_NO_THROW(table->setRow("id", *deletedEntry3)); + entry = table->getRow("id"); + BOOST_CHECK(!entry); + // BOOST_CHECK_EQUAL(entry->status(), Entry::DELETED); + keys = table->getPrimaryKeys({}); + BOOST_TEST(keys.size() == 1); + + entries = table->getRows(keys); + BOOST_TEST(entries.size() == 1); + // tableFactory->asyncCommit([](Error::Ptr, size_t) {}); +} + +BOOST_AUTO_TEST_CASE(open_sysTables) +{ + auto table = tableFactory->openTable(StorageInterface::SYS_TABLES); + BOOST_TEST(table); +} + +BOOST_AUTO_TEST_CASE(openAndCommit) +{ + auto hashImpl2 = make_shared(); + auto memoryStorage2 = make_shared(nullptr); + auto tableFactory2 = make_shared(memoryStorage2); + + for (int i = 10; i < 20; ++i) + { + BOOST_TEST(tableFactory2 != nullptr); + + std::string tableName = "testTable" + boost::lexical_cast(i); + auto key = "testKey" + boost::lexical_cast(i); + tableFactory2->createTable(tableName, "value"); + auto table = tableFactory2->openTable(tableName); + + auto entry = std::make_optional(table->newEntry()); + entry->setField(0, "hello world!"); + table->setRow(key, *entry); + + std::promise getRow; + table->asyncGetRow(key, [&](auto&& error, auto&& result) { + BOOST_CHECK(!error); + BOOST_CHECK_EQUAL(result->getField(0), "hello world!"); + + getRow.set_value(true); + }); + + getRow.get_future().get(); + } +} + +BOOST_AUTO_TEST_CASE(chainLink) +{ + std::vector storages; + auto valueFields = "value1"; + + StateStorage::Ptr prev = nullptr; + for (int i = 0; i < 10; ++i) + { + auto tableStorage = std::make_shared(prev); + for (int j = 0; j < 10; ++j) + { + auto tableName = "table_" + boost::lexical_cast(i) + "_" + + boost::lexical_cast(j); + BOOST_CHECK(tableStorage->createTable(tableName, valueFields)); + + auto table = tableStorage->openTable(tableName); + BOOST_TEST(table); + + for (int k = 0; k < 10; ++k) + { + auto entry = std::make_optional(table->newEntry()); + auto key = + boost::lexical_cast(i) + boost::lexical_cast(k); + entry->setField(0, boost::lexical_cast(i)); + BOOST_CHECK_NO_THROW(table->setRow(key, *entry)); + } + } + + prev = tableStorage; + storages.push_back(tableStorage); + } + + for (int index = 0; index < 10; ++index) + { + auto storage = storages[index]; + // Data count must be 10 * 10 + 10 + std::atomic totalCount = 0; + storage->parallelTraverse(false, [&](auto&&, auto&&, auto&&) { + ++totalCount; + return true; + }); + + BOOST_CHECK_EQUAL(totalCount, 10 * 10 + 10); // extra 100 for s_tables + + // Dirty data count must be 10 * 10 + 10 + std::atomic dirtyCount = 0; + storage->parallelTraverse(true, [&](auto&&, auto&&, auto&&) { + ++dirtyCount; + return true; + }); + + BOOST_CHECK_EQUAL(dirtyCount, 10 * 10 + 10); // extra 100 for s_tables + + // Low level can't touch high level's data + for (int i = 0; i < 10; ++i) + { + for (int j = 0; j < 10; ++j) + { + auto tableName = "table_" + boost::lexical_cast(i) + "_" + + boost::lexical_cast(j); + + auto table = storage->openTable(tableName); + if (i > index) + { + BOOST_TEST(!table); + } + else + { + BOOST_TEST(table); + + for (int k = 0; k < 10; ++k) + { + auto key = boost::lexical_cast(i) + + boost::lexical_cast(k); + + auto entry = table->getRow(key); + if (i > index) + { + BOOST_TEST(!entry); + } + else + { + BOOST_TEST(entry); + + BOOST_CHECK_GT(entry->size(), 0); + if (i == index) + { + BOOST_CHECK_EQUAL(entry->dirty(), true); + } + else + { + BOOST_CHECK_EQUAL(entry->dirty(), false); + } + BOOST_CHECK_EQUAL( + entry->getField(0), boost::lexical_cast(i)); + } + } + } + } + } + + // After reading, current storage should include previous storage's data, previous data's + // dirty should be false + totalCount = 0; + tbb::concurrent_vector> checks; + storage->parallelTraverse(false, [&](auto&& table, auto&&, auto&& entry) { + checks.push_back([index, table, entry] { + // BOOST_CHECK_NE(tableInfo, nullptr); + if (table != "s_tables") + { + auto i = boost::lexical_cast(entry.getField(0)); + + BOOST_CHECK_LE(i, index); + } + }); + + ++totalCount; + return true; + }); + + for (auto& it : checks) + { + it(); + } + + BOOST_CHECK_EQUAL(totalCount, (10 * 10 + 10) * (index + 1)); + + checks.clear(); + dirtyCount = 0; + storage->parallelTraverse(true, [&](auto&& table, auto&&, auto&& entry) { + checks.push_back([index, table, entry]() { + // BOOST_CHECK_NE(tableInfo, nullptr); + if (table != "s_tables") + { + auto i = boost::lexical_cast(entry.getField(0)); + + if (i == index) + { + BOOST_CHECK_EQUAL(entry.dirty(), true); + } + else + { + BOOST_CHECK_EQUAL(entry.dirty(), false); + } + } + }); + + ++dirtyCount; + return true; + }); + + for (auto& it : checks) + { + it(); + } + + BOOST_CHECK_EQUAL(dirtyCount, 10 * 10 + 10); + } +} + +BOOST_AUTO_TEST_CASE(getRows) +{ + std::vector storages; + auto valueFields = "value1,value2,value3"; + + StateStorage::Ptr prev = nullptr; + prev = std::make_shared(prev); + auto tableStorage = std::make_shared(prev); + + BOOST_CHECK(prev->createTable("t_test", valueFields)); + + auto table = prev->openTable("t_test"); + BOOST_TEST(table); + + for (size_t i = 0; i < 100; ++i) + { + auto entry = table->newEntry(); + entry.importFields({"data" + boost::lexical_cast(i)}); + table->setRow("key" + boost::lexical_cast(i), entry); + } + + // query 50-150 + std::vector keys; + for (size_t i = 50; i < 150; ++i) + { + keys.push_back("key" + boost::lexical_cast(i)); + } + + auto queryTable = tableStorage->openTable("t_test"); + BOOST_TEST(queryTable); + + std::vector views; + for (auto& key : keys) + { + views.push_back(key); + } + auto values = queryTable->getRows(views); + + for (size_t i = 0; i < 100; ++i) + { + auto entry = values[i]; + if (i + 50 < 100) + { + BOOST_TEST(entry); + BOOST_CHECK_EQUAL(entry->dirty(), false); + BOOST_CHECK_GT(entry->size(), 0); + } + else + { + BOOST_TEST(!entry); + } + } + + for (size_t i = 0; i < 10; ++i) + { + auto entry = queryTable->newEntry(); + entry.importFields({"data" + boost::lexical_cast(i)}); + queryTable->setRow("key" + boost::lexical_cast(i), entry); + } + + // Query 0-30 local(0-9) prev(10-29) + keys.clear(); + for (size_t i = 0; i < 30; ++i) + { + keys.push_back("key" + boost::lexical_cast(i)); + } + + views.clear(); + for (auto& key : keys) + { + views.push_back(key); + } + values = queryTable->getRows(views); + + for (size_t i = 0; i < 30; ++i) + { + auto entry = values[i]; + if (i < 10) + { + BOOST_TEST(entry); + BOOST_CHECK_EQUAL(entry->dirty(), true); + } + else + { + BOOST_TEST(entry); + BOOST_CHECK_EQUAL(entry->dirty(), false); + } + } + + // Test deleted entry + for (size_t i = 10; i < 20; ++i) + { + queryTable->setRow( + "key" + boost::lexical_cast(i), queryTable->newDeletedEntry()); + } + + auto values2 = queryTable->getRows(keys); + for (size_t i = 0; i < values2.size(); ++i) + { + if (i >= 10 && i < 20) + { + BOOST_CHECK(!values2[i]); + } + else + { + BOOST_CHECK(values2[i]); + } + } + + // Test rollback + auto recoder = std::make_shared(); + tableStorage->setRecoder(recoder); + for (size_t i = 70; i < 80; ++i) + { + Entry myEntry; + myEntry.importFields({"ddd1"}); + queryTable->setRow("key" + boost::lexical_cast(i), std::move(myEntry)); + } + + keys.clear(); + for (size_t i = 70; i < 80; ++i) + { + keys.push_back("key" + boost::lexical_cast(i)); + } + + auto values3 = queryTable->getRows(keys); + for (auto& it : values3) + { + BOOST_CHECK(it); + BOOST_CHECK_EQUAL(it->getField(0), "ddd1"); + BOOST_CHECK_EQUAL(it->dirty(), true); + } + + tableStorage->rollback(*recoder); + + auto values4 = queryTable->getRows(keys); + size_t count = 70; + for (auto& it : values4) + { + BOOST_CHECK(it); + BOOST_CHECK_EQUAL(it->getField(0), "data" + boost::lexical_cast(count)); + BOOST_CHECK_EQUAL(it->dirty(), false); + ++count; + } +} + +BOOST_AUTO_TEST_CASE(checkVersion) +{ + BOOST_CHECK_NO_THROW(tableFactory->createTable("testTable", "value1, value2, value3")); + auto table = tableFactory->openTable("testTable"); + + Entry value1; + value1.importFields({"v1"}); + table->setRow("abc", std::move(value1)); + + Entry value2; + value2.importFields({"v2"}); + BOOST_CHECK_NO_THROW(table->setRow("abc", std::move(value2))); + + Entry value3; + value3.importFields({"v3"}); + BOOST_CHECK_NO_THROW(table->setRow("abc", std::move(value3))); +} + +BOOST_AUTO_TEST_CASE(deleteAndGetRows) +{ + StateStorage::Ptr storage1 = std::make_shared(nullptr); + storage1->setEnableTraverse(true); + + storage1->asyncCreateTable( + "table", "value", [](Error::UniquePtr error, std::optional
table) { + BOOST_CHECK(!error); + BOOST_CHECK(table); + }); + + Entry entry1; + entry1.importFields({"value1"}); + storage1->asyncSetRow( + "table", "key1", std::move(entry1), [](Error::UniquePtr error) { BOOST_CHECK(!error); }); + + Entry entry2; + entry2.importFields({"value2"}); + storage1->asyncSetRow( + "table", "key2", std::move(entry2), [](Error::UniquePtr error) { BOOST_CHECK(!error); }); + + StateStorage::Ptr storage2 = std::make_shared(storage1); + 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); + storage3->asyncGetPrimaryKeys( + "table", std::nullopt, [](Error::UniquePtr error, std::vector keys) { + BOOST_CHECK(!error); + BOOST_CHECK_EQUAL(keys.size(), 1); + BOOST_CHECK_EQUAL(keys[0], "key1"); + }); +} + +BOOST_AUTO_TEST_CASE(deletedAndGetRow) +{ + StateStorage::Ptr storage1 = std::make_shared(nullptr); + + storage1->asyncCreateTable( + "table", "value", [](Error::UniquePtr error, std::optional
table) { + BOOST_CHECK(!error); + BOOST_CHECK(table); + }); + + Entry entry1; + entry1.importFields({"value1"}); + storage1->asyncSetRow( + "table", "key1", std::move(entry1), [](Error::UniquePtr error) { BOOST_CHECK(!error); }); + + StateStorage::Ptr storage2 = std::make_shared(storage1); + Entry deleteEntry; + deleteEntry.setStatus(Entry::DELETED); + storage2->asyncSetRow("table", "key1", std::move(deleteEntry), + [](Error::UniquePtr error) { BOOST_CHECK(!error); }); + + storage2->asyncGetRow("table", "key1", [](Error::UniquePtr error, std::optional entry) { + BOOST_CHECK(!error); + BOOST_CHECK(!entry); + }); + + storage2->asyncGetRow("table", "key1", [](Error::UniquePtr error, std::optional entry) { + BOOST_CHECK(!error); + BOOST_CHECK(!entry); + }); +} + +BOOST_AUTO_TEST_CASE(deletedAndGetRows) +{ + StateStorage::Ptr storage1 = std::make_shared(nullptr); + + storage1->asyncCreateTable( + "table", "value", [](Error::UniquePtr error, std::optional
table) { + BOOST_CHECK(!error); + BOOST_CHECK(table); + }); + + Entry entry1; + entry1.importFields({"value1"}); + storage1->asyncSetRow( + "table", "key1", std::move(entry1), [](Error::UniquePtr error) { BOOST_CHECK(!error); }); + + StateStorage::Ptr storage2 = std::make_shared(storage1); + Entry deleteEntry; + deleteEntry.setStatus(Entry::DELETED); + storage2->asyncSetRow("table", "key1", std::move(deleteEntry), + [](Error::UniquePtr error) { BOOST_CHECK(!error); }); + + std::string_view keys[] = {"key1"}; + storage2->asyncGetRows( + "table", keys, [](Error::UniquePtr error, std::vector> entry) { + BOOST_CHECK(!error); + BOOST_CHECK_EQUAL(entry.size(), 1); + BOOST_CHECK(!entry[0]); + }); +} + +BOOST_AUTO_TEST_CASE(rollbackAndGetRow) +{ + StateStorage::Ptr storage1 = std::make_shared(nullptr); + + storage1->asyncCreateTable( + "table", "value", [](Error::UniquePtr error, std::optional
table) { + BOOST_CHECK(!error); + BOOST_CHECK(table); + }); + + Entry entry1; + entry1.importFields({"value1"}); + storage1->asyncSetRow( + "table", "key1", std::move(entry1), [](Error::UniquePtr error) { BOOST_CHECK(!error); }); + + StateStorage::Ptr storage2 = std::make_shared(storage1); + auto recoder = std::make_shared(); + storage2->setRecoder(recoder); + + Entry entry2; + entry2.importFields({"value2"}); + storage2->asyncSetRow( + "table", "key1", std::move(entry2), [](Error::UniquePtr error) { BOOST_CHECK(!error); }); + + storage2->asyncGetRow("table", "key1", [](Error::UniquePtr error, std::optional entry) { + BOOST_CHECK(!error); + BOOST_CHECK(entry); + BOOST_CHECK_EQUAL(entry->getField(0), "value2"); + }); + + storage2->rollback(*recoder); + + storage2->asyncGetRow("table", "key1", [](Error::UniquePtr error, std::optional entry) { + BOOST_CHECK(!error); + BOOST_CHECK(entry); + BOOST_CHECK_EQUAL(entry->getField(0), "value1"); + }); +} + +BOOST_AUTO_TEST_CASE(rollbackAndGetRows) +{ + StateStorage::Ptr storage1 = std::make_shared(nullptr); + + storage1->asyncCreateTable( + "table", "value", [](Error::UniquePtr error, std::optional
table) { + BOOST_CHECK(!error); + BOOST_CHECK(table); + }); + + Entry entry1; + entry1.importFields({"value1"}); + storage1->asyncSetRow( + "table", "key1", std::move(entry1), [](Error::UniquePtr error) { BOOST_CHECK(!error); }); + + StateStorage::Ptr storage2 = std::make_shared(storage1); + auto recoder = std::make_shared(); + storage2->setRecoder(recoder); + + Entry entry2; + entry2.importFields({"value2"}); + storage2->asyncSetRow( + "table", "key1", std::move(entry2), [](Error::UniquePtr error) { BOOST_CHECK(!error); }); + + std::string_view keys[] = {"key1"}; + storage2->asyncGetRows( + "table", keys, [](Error::UniquePtr error, std::vector> entry) { + BOOST_CHECK(!error); + BOOST_CHECK_EQUAL(entry.size(), 1); + BOOST_CHECK_EQUAL(entry[0].value().getField(0), "value2"); + }); + + storage2->rollback(*recoder); + + storage2->asyncGetRows( + "table", keys, [](Error::UniquePtr error, std::vector> entry) { + BOOST_CHECK(!error); + BOOST_CHECK_EQUAL(entry.size(), 1); + BOOST_CHECK_EQUAL(entry[0].value().getField(0), "value1"); + }); +} + +BOOST_AUTO_TEST_CASE(randomRWHash) +{ + std::vector> rwSet; + + std::random_device rd; + for (size_t i = 0; i < 3; ++i) + { + auto keyNum = rd(); + bool write = keyNum % 2; + + std::string table; + std::string key; + std::string value; + if (write || i == 0) + { + write = true; + table = boost::lexical_cast(keyNum % 10); + key = boost::lexical_cast(keyNum); + value = boost::lexical_cast(rd()); + } + else + { + auto index = keyNum % i; + table = std::get<1>(rwSet[index]); + key = std::get<2>(rwSet[index]); + } + + rwSet.emplace_back(std::make_tuple(write, table, key, value)); + } + + std::vector prevHashes; + for (size_t times = 0; times < 10; ++times) + { + std::vector hashes; + StateStorage::Ptr prev; + for (size_t i = 0; i < 10; ++i) + { + StateStorage::Ptr storage = std::make_shared(prev); + + for (auto& it : rwSet) + { + auto& [write, table, key, value] = it; + + storage->asyncOpenTable(table, [storage, tableName = table](Error::UniquePtr error, + std::optional
tableItem) { + BOOST_CHECK(!error); + if (!tableItem) + { + storage->asyncCreateTable(tableName, "value", + [](Error::UniquePtr error, std::optional
tableItem) { + BOOST_CHECK(!error); + BOOST_CHECK(tableItem); + }); + } + }); + + if (write) + { + Entry entry; + entry.importFields({value}); + storage->asyncSetRow(table, key, std::move(entry), + [](Error::UniquePtr error) { BOOST_CHECK(!error); }); + } + else + { + storage->asyncGetRow(table, key, + [](Error::UniquePtr error, std::optional) { BOOST_CHECK(!error); }); + } + } + + hashes.push_back(storage->hash(hashImpl)); + storage->setReadOnly(false); + prev = storage; + } + + if (!prevHashes.empty()) + { + BOOST_CHECK_EQUAL_COLLECTIONS( + prevHashes.begin(), prevHashes.end(), hashes.begin(), hashes.end()); + } + prevHashes.swap(hashes); + hashes.clear(); + } +} + +BOOST_AUTO_TEST_CASE(hash_map) +{ + class EntryKey + { + public: + EntryKey() {} + EntryKey(std::string_view table, std::string_view key) : m_table(table), m_key(key) {} + EntryKey(std::string_view table, std::string key) : m_table(table), m_key(std::move(key)) {} + + EntryKey(const EntryKey&) = default; + EntryKey& operator=(const EntryKey&) = default; + EntryKey(EntryKey&&) noexcept = default; + EntryKey& operator=(EntryKey&&) noexcept = default; + + std::string_view table() const { return m_table; } + + std::string_view key() const + { + std::string_view view; + std::visit([&view](auto&& key) { view = key; }, m_key); + + return view; + } + + bool operator==(const EntryKey& rhs) const + { + return m_table == rhs.m_table && key() == rhs.key(); + } + + bool operator<(const EntryKey& rhs) const + { + if (m_table != rhs.m_table) + { + return m_table < rhs.m_table; + } + + return m_key < rhs.m_key; + } + + private: + std::string_view m_table; + std::variant m_key; + }; + + struct EntryKeyHasher + { + size_t hash(const EntryKey& dataKey) const + { + size_t seed = hashString(dataKey.table()); + boost::hash_combine(seed, hashString(dataKey.key())); + + return seed; + } + + bool equal(const EntryKey& lhs, const EntryKey& rhs) const + { + return lhs.table() == rhs.table() && lhs.key() == rhs.key(); + } + + std::hash hashString; + }; + + tbb::concurrent_hash_map data; + + std::string tableName = "table"; + std::string key = "key"; + + decltype(data)::const_accessor it; + BOOST_CHECK(data.emplace(it, EntryKey("table", std::string_view("key")), 100)); + + decltype(data)::const_accessor findIt; + BOOST_CHECK(data.find(findIt, EntryKey("table", std::string_view("key")))); +} + +BOOST_AUTO_TEST_CASE(importPrev) {} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-table/test/unittests/main/main.cpp" "b/BFPL\345\243\271/bcos-table/test/unittests/main/main.cpp" new file mode 100644 index 00000000..5029377e --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/test/unittests/main/main.cpp" @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 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 main.cpp + * @author: yujiechen, jimmyshi + * @date 2021-02-24 + */ +#define BOOST_TEST_MODULE FISCO_BCOS_Tests +#define BOOST_TEST_MAIN + +#include +#include diff --git "a/BFPL\345\243\271/bcos-table/test/unittests/mock/MockTransactionalStorage.h" "b/BFPL\345\243\271/bcos-table/test/unittests/mock/MockTransactionalStorage.h" new file mode 100644 index 00000000..d23ba306 --- /dev/null +++ "b/BFPL\345\243\271/bcos-table/test/unittests/mock/MockTransactionalStorage.h" @@ -0,0 +1,83 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +using namespace bcos::protocol; +namespace bcos::test +{ +class MockTransactionalStorage : public bcos::storage::TransactionalStorageInterface +{ +public: + MockTransactionalStorage(bcos::crypto::Hash::Ptr hashImpl) : m_hashImpl(std::move(hashImpl)) + { + m_inner = std::make_shared(nullptr); + m_inner->setEnableTraverse(true); + } + + void asyncGetPrimaryKeys(const std::string_view& table, + const std::optional& _condition, + std::function)> _callback) noexcept override + { + m_inner->asyncGetPrimaryKeys(table, _condition, std::move(_callback)); + } + + void asyncGetRow(const std::string_view& table, const std::string_view& _key, + std::function)> _callback) noexcept + override + { + m_inner->asyncGetRow(table, _key, std::move(_callback)); + } + + void asyncGetRows(const std::string_view& table, + const std::variant, + const gsl::span>& _keys, + std::function>)> + _callback) noexcept override + { + m_inner->asyncGetRows(table, _keys, std::move(_callback)); + } + + void asyncSetRow(const std::string_view& table, const std::string_view& key, + storage::Entry entry, std::function callback) noexcept override + { + m_inner->asyncSetRow(table, key, std::move(entry), std::move(callback)); + } + + void asyncOpenTable(std::string_view tableName, + std::function)> callback) noexcept + override + { + m_inner->asyncOpenTable(tableName, std::move(callback)); + } + + void asyncPrepare(const TwoPCParams& params, + const bcos::storage::TraverseStorageInterface::ConstPtr& storage, + std::function callback) noexcept override + { + boost::ignore_unused(params, storage); + callback(nullptr, 0); + } + + void asyncCommit(const TwoPCParams& params, + std::function callback) noexcept override + { + BOOST_CHECK_GT(params.number, 0); + callback(nullptr, 0); + } + + void asyncRollback( + const TwoPCParams& params, std::function callback) noexcept override + { + BOOST_CHECK_GT(params.number, 0); + callback(nullptr); + } + + bcos::storage::StateStorage::Ptr m_inner; + bcos::crypto::Hash::Ptr m_hashImpl; +}; +} // namespace bcos::test \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/CMakeLists.txt" "b/BFPL\345\243\271/bcos-tars-protocol/CMakeLists.txt" new file mode 100644 index 00000000..d2f82638 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/CMakeLists.txt" @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.10) +set(CMAKE_OSX_DEPLOYMENT_TARGET "11.3" CACHE STRING "Minimum OS X deployment version") + +include(Version) +project(bcos-tars-protocol VERSION ${VERSION}) + +# for tars generator +set(TARS_HEADER_DIR ${CMAKE_BINARY_DIR}/generated/bcos-tars-protocol/tars) +find_program(TARS_TARS2CPP tars2cpp REQUIRED) + +file(GLOB_RECURSE TARS_INPUT "*.tars") + +# generate tars +if(TARS_INPUT) + foreach(TARS_FILE ${TARS_INPUT}) + get_filename_component(TARS_NAME ${TARS_FILE} NAME_WE) + get_filename_component(TARS_PATH ${TARS_FILE} PATH) + add_custom_command( + OUTPUT ${TARS_HEADER_DIR}/${TARS_NAME}.h + WORKING_DIRECTORY ${TARS_PATH} + COMMAND ${TARS_TARS2CPP} ${TARS_FILE} --unjson --without-trace --dir=${TARS_HEADER_DIR} + COMMENT "generating ${TARS_FILE} to ${TARS_HEADER_DIR}" + VERBATIM + ) + + list(APPEND OUT_TARS_H_LIST ${TARS_HEADER_DIR}/${TARS_NAME}.h) + endforeach() +endif() + +set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${OUT_TARS_H_LIST}") + +file(GLOB_RECURSE SRC_LIST bcos-tars-protocol/*.cpp) + +find_package(tarscpp REQUIRED) +find_package(TBB REQUIRED) + +add_library(${TARS_PROTOCOL_TARGET} ${SRC_LIST} ${OUT_TARS_H_LIST}) +target_include_directories(${TARS_PROTOCOL_TARGET} PUBLIC + $ + $ + $ + $) +target_link_libraries(${TARS_PROTOCOL_TARGET} PUBLIC bcos-concepts bcos-crypto ${PROTOCOL_TARGET} ${TOOL_TARGET} tarscpp::tarsservant tarscpp::tarsutil TBB::tbb) + +if(TESTS) + enable_testing() + set(CTEST_OUTPUT_ON_FAILURE TRUE) + add_subdirectory(test) +endif() + +include(GNUInstallDirs) +install(TARGETS ${TARS_PROTOCOL_TARGET} EXPORT fiscobcosTargets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") +install(DIRECTORY "bcos-tars-protocol" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" FILES_MATCHING PATTERN "*.h") +install(DIRECTORY "${CMAKE_BINARY_DIR}/generated/bcos-tars-protocol" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" FILES_MATCHING PATTERN "*.h") \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/Common.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/Common.h" new file mode 100644 index 00000000..67f5910e --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/Common.h" @@ -0,0 +1,398 @@ +/** + * Copyright (C) 2021 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: ancelmo + * @date 2021-04-20 + */ + +#pragma once +#include "bcos-framework/executor/ParallelTransactionExecutorInterface.h" +#include "bcos-tars-protocol/tars/GatewayInfo.h" +#include "bcos-tars-protocol/tars/GroupInfo.h" +#include "bcos-tars-protocol/tars/LedgerConfig.h" +#include "bcos-tars-protocol/tars/TransactionReceipt.h" +#include "bcos-tars-protocol/tars/TwoPCParams.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcostars +{ +namespace protocol +{ +const static bcos::crypto::HashType emptyHash; + +template +class BufferWriter +{ +protected: + using ByteType = typename Container::value_type; + using SizeType = typename Container::size_type; + + mutable Container _buffer; + ByteType* _buf; + SizeType _len; + SizeType _buf_len; + std::function _reserve; + +private: + BufferWriter(const BufferWriter&); + BufferWriter& operator=(const BufferWriter& buf); + +public: + BufferWriter() : _buf(NULL), _len(0), _buf_len(0), _reserve({}) + { +#ifndef GEN_PYTHON_MASK + _reserve = [](BufferWriter& os, size_t len) { + os._buffer.resize(len); + return os._buffer.data(); + }; +#endif + } + + ~BufferWriter() {} + + void reset() { _len = 0; } + + void writeBuf(const ByteType* buf, size_t len) + { + TarsReserveBuf(*this, _len + len); + memcpy(_buf + _len, buf, len); + _len += len; + } + + const Container& getByteBuffer() const + { + _buffer.resize(_len); + return _buffer; + } + Container& getByteBuffer() + { + _buffer.resize(_len); + return _buffer; + } + const ByteType* getBuffer() const { return _buf; } + size_t getLength() const { return _len; } + void swap(std::vector& v) + { + _buffer.resize(_len); + v.swap(_buffer); + _buf = NULL; + _buf_len = 0; + _len = 0; + } + void swap(BufferWriter& buf) + { + buf._buffer.swap(_buffer); + std::swap(_buf, buf._buf); + std::swap(_buf_len, buf._buf_len); + std::swap(_len, buf._len); + } +}; + +using BufferWriterByteVector = BufferWriter>; +using BufferWriterStdByteVector = BufferWriter>; +using BufferWriterString = BufferWriter; +} // namespace protocol + +inline bcos::group::ChainNodeInfo::Ptr toBcosChainNodeInfo( + bcos::group::ChainNodeInfoFactory::Ptr _factory, bcostars::ChainNodeInfo const& _tarsNodeInfo) +{ + auto nodeInfo = _factory->createNodeInfo(); + nodeInfo->setNodeName(_tarsNodeInfo.nodeName); + nodeInfo->setNodeCryptoType((bcos::group::NodeCryptoType)_tarsNodeInfo.nodeCryptoType); + nodeInfo->setNodeID(_tarsNodeInfo.nodeID); + nodeInfo->setIniConfig(_tarsNodeInfo.iniConfig); + nodeInfo->setMicroService(_tarsNodeInfo.microService); + nodeInfo->setNodeType((bcos::protocol::NodeType)_tarsNodeInfo.nodeType); + for (auto const& it : _tarsNodeInfo.serviceInfo) + { + nodeInfo->appendServiceInfo((bcos::protocol::ServiceType)it.first, it.second); + } + // recover the nodeProtocolVersion + auto& protocolInfo = _tarsNodeInfo.protocolInfo; + auto bcosProtocolInfo = std::make_shared( + (bcos::protocol::ProtocolModuleID)protocolInfo.moduleID, + (bcos::protocol::ProtocolVersion)protocolInfo.minVersion, + (bcos::protocol::ProtocolVersion)protocolInfo.maxVersion); + nodeInfo->setNodeProtocol(std::move(*bcosProtocolInfo)); + // recover system version(data version) + nodeInfo->setCompatibilityVersion(_tarsNodeInfo.compatibilityVersion); + return nodeInfo; +} + +inline bcos::group::GroupInfo::Ptr toBcosGroupInfo( + bcos::group::ChainNodeInfoFactory::Ptr _nodeFactory, + bcos::group::GroupInfoFactory::Ptr _groupFactory, bcostars::GroupInfo const& _tarsGroupInfo) +{ + auto groupInfo = _groupFactory->createGroupInfo(); + groupInfo->setChainID(_tarsGroupInfo.chainID); + groupInfo->setGroupID(_tarsGroupInfo.groupID); + groupInfo->setGenesisConfig(_tarsGroupInfo.genesisConfig); + groupInfo->setIniConfig(_tarsGroupInfo.iniConfig); + groupInfo->setWasm(_tarsGroupInfo.isWasm); + for (auto const& tarsNodeInfo : _tarsGroupInfo.nodeList) + { + groupInfo->appendNodeInfo(toBcosChainNodeInfo(_nodeFactory, tarsNodeInfo)); + } + return groupInfo; +} + +inline bcostars::ChainNodeInfo toTarsChainNodeInfo(bcos::group::ChainNodeInfo::Ptr _nodeInfo) +{ + bcostars::ChainNodeInfo tarsNodeInfo; + if (!_nodeInfo) + { + return tarsNodeInfo; + } + tarsNodeInfo.nodeName = _nodeInfo->nodeName(); + tarsNodeInfo.nodeCryptoType = _nodeInfo->nodeCryptoType(); + tarsNodeInfo.nodeType = _nodeInfo->nodeType(); + auto const& info = _nodeInfo->serviceInfo(); + for (auto const& it : info) + { + tarsNodeInfo.serviceInfo[(int32_t)it.first] = it.second; + } + tarsNodeInfo.nodeID = _nodeInfo->nodeID(); + tarsNodeInfo.microService = _nodeInfo->microService(); + tarsNodeInfo.iniConfig = _nodeInfo->iniConfig(); + // set the nodeProtocolVersion + auto const& protocol = _nodeInfo->nodeProtocol(); + tarsNodeInfo.protocolInfo.moduleID = protocol->protocolModuleID(); + tarsNodeInfo.protocolInfo.minVersion = protocol->minVersion(); + tarsNodeInfo.protocolInfo.maxVersion = protocol->maxVersion(); + // write the compatibilityVersion + tarsNodeInfo.compatibilityVersion = _nodeInfo->compatibilityVersion(); + return tarsNodeInfo; +} + +inline bcostars::GroupInfo toTarsGroupInfo(bcos::group::GroupInfo::Ptr _groupInfo) +{ + bcostars::GroupInfo tarsGroupInfo; + if (!_groupInfo) + { + return tarsGroupInfo; + } + tarsGroupInfo.chainID = _groupInfo->chainID(); + tarsGroupInfo.groupID = _groupInfo->groupID(); + tarsGroupInfo.genesisConfig = _groupInfo->genesisConfig(); + tarsGroupInfo.iniConfig = _groupInfo->iniConfig(); + tarsGroupInfo.isWasm = _groupInfo->wasm() ? 1 : 0; + // set nodeList + std::vector tarsNodeList; + auto bcosNodeList = _groupInfo->nodeInfos(); + for (auto const& it : bcosNodeList) + { + auto const& nodeInfo = it.second; + tarsNodeList.emplace_back(toTarsChainNodeInfo(nodeInfo)); + } + tarsGroupInfo.nodeList = std::move(tarsNodeList); + return tarsGroupInfo; +} + +inline bcos::consensus::ConsensusNodeListPtr toConsensusNodeList( + bcos::crypto::KeyFactory::Ptr _keyFactory, + vector const& _tarsConsensusNodeList) +{ + auto consensusNodeList = std::make_shared(); + for (auto const& node : _tarsConsensusNodeList) + { + auto nodeID = _keyFactory->createKey( + bcos::bytesConstRef((bcos::byte*)node.nodeID.data(), node.nodeID.size())); + consensusNodeList->push_back( + std::make_shared(nodeID, node.weight)); + } + return consensusNodeList; +} + +inline bcos::ledger::LedgerConfig::Ptr toLedgerConfig( + bcostars::LedgerConfig const& _ledgerConfig, bcos::crypto::KeyFactory::Ptr _keyFactory) +{ + auto ledgerConfig = std::make_shared(); + auto consensusNodeList = toConsensusNodeList(_keyFactory, _ledgerConfig.consensusNodeList); + ledgerConfig->setConsensusNodeList(*consensusNodeList); + + auto observerNodeList = toConsensusNodeList(_keyFactory, _ledgerConfig.observerNodeList); + ledgerConfig->setObserverNodeList(*observerNodeList); + + auto hash = bcos::crypto::HashType(); + if (_ledgerConfig.hash.size() >= bcos::crypto::HashType::SIZE) + { + hash = bcos::crypto::HashType( + (const bcos::byte*)_ledgerConfig.hash.data(), bcos::crypto::HashType::SIZE); + } + ledgerConfig->setHash(hash); + ledgerConfig->setBlockNumber(_ledgerConfig.blockNumber); + ledgerConfig->setBlockTxCountLimit(_ledgerConfig.blockTxCountLimit); + ledgerConfig->setLeaderSwitchPeriod(_ledgerConfig.leaderSwitchPeriod); + ledgerConfig->setSealerId(_ledgerConfig.sealerId); + ledgerConfig->setGasLimit(std::make_tuple(_ledgerConfig.gasLimit, _ledgerConfig.blockNumber)); + ledgerConfig->setCompatibilityVersion(_ledgerConfig.compatibilityVersion); + return ledgerConfig; +} + +inline vector toTarsConsensusNodeList( + bcos::consensus::ConsensusNodeList const& _nodeList) +{ + // set consensusNodeList + vector tarsConsensusNodeList; + for (auto node : _nodeList) + { + bcostars::ConsensusNode consensusNode; + consensusNode.nodeID.assign(node->nodeID()->data().begin(), node->nodeID()->data().end()); + consensusNode.weight = node->weight(); + tarsConsensusNodeList.emplace_back(consensusNode); + } + return tarsConsensusNodeList; +} +inline bcostars::LedgerConfig toTarsLedgerConfig(bcos::ledger::LedgerConfig::Ptr _ledgerConfig) +{ + bcostars::LedgerConfig ledgerConfig; + if (!_ledgerConfig) + { + return ledgerConfig; + } + auto hash = _ledgerConfig->hash().asBytes(); + ledgerConfig.hash.assign(hash.begin(), hash.end()); + ledgerConfig.blockNumber = _ledgerConfig->blockNumber(); + ledgerConfig.blockTxCountLimit = _ledgerConfig->blockTxCountLimit(); + ledgerConfig.leaderSwitchPeriod = _ledgerConfig->leaderSwitchPeriod(); + ledgerConfig.sealerId = _ledgerConfig->sealerId(); + ledgerConfig.gasLimit = std::get<0>(_ledgerConfig->gasLimit()); + ledgerConfig.compatibilityVersion = _ledgerConfig->compatibilityVersion(); + + // set consensusNodeList + ledgerConfig.consensusNodeList = toTarsConsensusNodeList(_ledgerConfig->consensusNodeList()); + // set observerNodeList + ledgerConfig.observerNodeList = toTarsConsensusNodeList(_ledgerConfig->observerNodeList()); + return ledgerConfig; +} + +inline bcostars::P2PInfo toTarsP2PInfo(bcos::gateway::P2PInfo const& _p2pInfo) +{ + bcostars::P2PInfo tarsP2PInfo; + tarsP2PInfo.p2pID = _p2pInfo.p2pID; + tarsP2PInfo.agencyName = _p2pInfo.agencyName; + tarsP2PInfo.nodeName = _p2pInfo.nodeName; + tarsP2PInfo.host = _p2pInfo.nodeIPEndpoint.address(); + tarsP2PInfo.port = _p2pInfo.nodeIPEndpoint.port(); + return tarsP2PInfo; +} + +inline bcostars::GroupNodeInfo toTarsNodeIDInfo( + std::string const& _groupID, std::set const& _nodeIDList) +{ + GroupNodeInfo groupNodeIDInfo; + groupNodeIDInfo.groupID = _groupID; + groupNodeIDInfo.nodeIDList = std::vector(_nodeIDList.begin(), _nodeIDList.end()); + return groupNodeIDInfo; +} +inline bcostars::GatewayInfo toTarsGatewayInfo(bcos::gateway::GatewayInfo::Ptr _bcosGatewayInfo) +{ + bcostars::GatewayInfo tarsGatewayInfo; + if (!_bcosGatewayInfo) + { + return tarsGatewayInfo; + } + tarsGatewayInfo.p2pInfo = toTarsP2PInfo(_bcosGatewayInfo->p2pInfo()); + auto nodeIDList = _bcosGatewayInfo->nodeIDInfo(); + std::vector tarsNodeIDInfos; + for (auto const& it : nodeIDList) + { + tarsNodeIDInfos.emplace_back(toTarsNodeIDInfo(it.first, it.second)); + } + tarsGatewayInfo.nodeIDInfo = tarsNodeIDInfos; + return tarsGatewayInfo; +} + +// Note: use struct here maybe Inconvenient to override +inline bcos::gateway::P2PInfo toBcosP2PNodeInfo(bcostars::P2PInfo const& _tarsP2pInfo) +{ + bcos::gateway::P2PInfo p2pInfo; + p2pInfo.p2pID = _tarsP2pInfo.p2pID; + p2pInfo.agencyName = _tarsP2pInfo.agencyName; + p2pInfo.nodeName = _tarsP2pInfo.nodeName; + p2pInfo.nodeIPEndpoint = bcos::gateway::NodeIPEndpoint(_tarsP2pInfo.host, _tarsP2pInfo.port); + return p2pInfo; +} + +inline bcos::gateway::GatewayInfo::Ptr fromTarsGatewayInfo(bcostars::GatewayInfo _tarsGatewayInfo) +{ + auto bcosGatewayInfo = std::make_shared(); + auto p2pInfo = toBcosP2PNodeInfo(_tarsGatewayInfo.p2pInfo); + std::map, std::less<>> nodeIDInfos; + for (auto const& it : _tarsGatewayInfo.nodeIDInfo) + { + auto const& nodeIDListInfo = it.nodeIDList; + nodeIDInfos[it.groupID] = + std::set(nodeIDListInfo.begin(), nodeIDListInfo.end()); + } + bcosGatewayInfo->setP2PInfo(std::move(p2pInfo)); + bcosGatewayInfo->setNodeIDInfo(std::move(nodeIDInfos)); + return bcosGatewayInfo; +} + +inline bcostars::LogEntry toTarsLogEntry(bcos::protocol::LogEntry const& _logEntry) +{ + bcostars::LogEntry logEntry; + logEntry.address.assign(_logEntry.address().begin(), _logEntry.address().end()); + for (auto& topicIt : _logEntry.topics()) + { + logEntry.topic.push_back(std::vector(topicIt.begin(), topicIt.end())); + } + logEntry.data.assign(_logEntry.data().begin(), _logEntry.data().end()); + return logEntry; +} + +inline bcos::protocol::LogEntry toBcosLogEntry(bcostars::LogEntry const& _logEntry) +{ + std::vector topics; + for (auto& topicIt : _logEntry.topic) + { + topics.emplace_back((const bcos::byte*)topicIt.data(), topicIt.size()); + } + return bcos::protocol::LogEntry(bcos::bytes(_logEntry.address.begin(), _logEntry.address.end()), + topics, bcos::bytes(_logEntry.data.begin(), _logEntry.data.end())); +} + +inline bcos::protocol::TwoPCParams toBcosTwoPCParams(bcostars::TwoPCParams const& _param) +{ + bcos::protocol::TwoPCParams bcosTwoPCParams; + bcosTwoPCParams.number = _param.blockNumber; + bcosTwoPCParams.primaryKey = _param.primaryKey; + bcosTwoPCParams.timestamp = _param.timePoint; + return bcosTwoPCParams; +} + +inline bcostars::TwoPCParams toTarsTwoPCParams(bcos::protocol::TwoPCParams _param) +{ + bcostars::TwoPCParams tarsTwoPCParams; + tarsTwoPCParams.blockNumber = _param.number; + tarsTwoPCParams.primaryKey = _param.primaryKey; + tarsTwoPCParams.timePoint = _param.timestamp; + return tarsTwoPCParams; +} +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/ErrorConverter.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/ErrorConverter.h" new file mode 100644 index 00000000..3613c9d6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/ErrorConverter.h" @@ -0,0 +1,95 @@ +/** + * Copyright (C) 2021 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 ErrorConverter.h + * @author: ancelmo + * @date 2021-04-20 + */ + +#pragma once + +#include +#include + +namespace bcostars +{ +inline Error toTarsError(const bcos::Error& error) +{ + Error tarsError; + tarsError.errorCode = error.errorCode(); + tarsError.errorMessage = error.errorMessage(); + + return tarsError; +} + +template +inline Error toTarsError(const T& error) +{ + Error tarsError; + + if (error) + { + tarsError.errorCode = error->errorCode(); + tarsError.errorMessage = error->errorMessage(); + } + + return tarsError; +} + +inline bcos::Error::Ptr toBcosError(const bcostars::Error& error) +{ + if (error.errorCode == 0) + { + return nullptr; + } + + auto bcosError = std::make_shared(error.errorCode, error.errorMessage); + return bcosError; +} + +inline bcos::Error::Ptr toBcosError(tars::Int32 ret) +{ + if (ret == 0) + { + return nullptr; + } + + auto bcosError = std::make_shared(ret, "TARS error!"); + return bcosError; +} + +inline bcos::Error::UniquePtr toUniqueBcosError(const bcostars::Error& error) +{ + if (error.errorCode == 0) + { + return nullptr; + } + + auto bcosError = std::make_unique(error.errorCode, error.errorMessage); + return bcosError; +} + +inline bcos::Error::UniquePtr toUniqueBcosError(tars::Int32 ret) +{ + if (ret == 0) + { + return nullptr; + } + + auto bcosError = std::make_unique(ret, "TARS error!"); + return bcosError; +} + +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/ExecutorServiceClient.cpp" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/ExecutorServiceClient.cpp" new file mode 100644 index 00000000..235efd1a --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/ExecutorServiceClient.cpp" @@ -0,0 +1,588 @@ +/** + * 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 ExecutorServiceClient.cpp + * @author: yujiechen + * @date 2022-5-9 + */ +#include "ExecutorServiceClient.h" +#include "../Common.h" +#include "../ErrorConverter.h" +#include "../protocol/BlockHeaderImpl.h" +#include "../protocol/ExecutionMessageImpl.h" +#include + +using namespace bcostars; + +template +class AsyncCallback +{ +public: + AsyncCallback(std::weak_ptr threadPool, std::function callback) + : m_pool(threadPool), m_callback(std::move(callback)) + {} + + void operator()(Args&&... args) + { + auto pool = m_pool.lock(); + if (pool) + { + // m_callback(std::move(args)...); + pool->template enqueue( + [callback = std::move(m_callback), + args = std::make_shared>(std::make_tuple(std::forward( + args)...))]() mutable { std::apply(callback, std::move(*args)); }); + } + } + +private: + std::weak_ptr m_pool; + std::function m_callback; +}; + +void ExecutorServiceClient::status( + std::function callback) +{ + class Callback : public ExecutorServicePrxCallback + { + public: + Callback(std::weak_ptr threadPool, + std::function&& + _callback) + : m_callback(threadPool, std::move(_callback)) + {} + ~Callback() override {} + + void callback_status( + const bcostars::Error& ret, const bcostars::ExecutorStatus& _output) override + { + auto error = toUniqueBcosError(ret); + auto status = std::make_unique(); + if (!error) + { + status->setSeq(_output.seq); + } + m_callback(std::move(error), std::move(status)); + } + + void callback_status_exception(tars::Int32 ret) override + { + m_callback(toUniqueBcosError(ret), std::make_unique()); + } + + private: + AsyncCallback m_callback; + }; + // timeout is 30s + m_prx->tars_set_timeout(30000)->async_status(new Callback(m_callbackPool, std::move(callback))); +} + +void ExecutorServiceClient::nextBlockHeader(int64_t schedulerTermId, + const bcos::protocol::BlockHeader::ConstPtr& blockHeader, + std::function callback) +{ + class Callback : public ExecutorServicePrxCallback + { + public: + Callback(std::weak_ptr threadPool, + std::function&& _callback) + : m_callback(threadPool, std::move(_callback)) + {} + ~Callback() override {} + + void callback_nextBlockHeader(const bcostars::Error& ret) override + { + m_callback(toUniqueBcosError(ret)); + } + + void callback_nextBlockHeader_exception(tars::Int32 ret) override + { + m_callback(toUniqueBcosError(ret)); + } + + private: + AsyncCallback m_callback; + }; + auto blockHeaderImpl = + std::dynamic_pointer_cast(blockHeader); + // timeout is 30s + m_prx->tars_set_timeout(30000)->async_nextBlockHeader( + new Callback(m_callbackPool, std::move(callback)), schedulerTermId, + blockHeaderImpl->inner()); +} + +void ExecutorServiceClient::executeTransaction(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) +{ + class Callback : public ExecutorServicePrxCallback + { + public: + Callback(std::weak_ptr threadPool, + std::function&& _callback) + : m_callback(threadPool, std::move(_callback)) + {} + ~Callback() override {} + + void callback_executeTransaction( + const bcostars::Error& ret, bcostars::ExecutionMessage const& executionMessage) override + { + auto executionMsgImpl = std::make_unique( + [m_executionMessage = executionMessage]() mutable { return &m_executionMessage; }); + m_callback(toUniqueBcosError(ret), std::move(executionMsgImpl)); + } + + void callback_executeTransaction_exception(tars::Int32 ret) override + { + m_callback(toUniqueBcosError(ret), nullptr); + } + + private: + AsyncCallback + m_callback; + }; + auto& executionMsgImpl = dynamic_cast(*input); + + // timeout is 2 min + m_prx->tars_set_timeout(2 * 60 * 1000) + ->async_executeTransaction( + new Callback(m_callbackPool, std::move(callback)), executionMsgImpl.inner()); +} + +void ExecutorServiceClient ::call(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) +{ + class Callback : public ExecutorServicePrxCallback + { + public: + Callback(std::weak_ptr threadPool, + std::function&& _callback) + : m_callback(threadPool, std::move(_callback)) + {} + ~Callback() override {} + + void callback_call( + const bcostars::Error& ret, bcostars::ExecutionMessage const& executionMessage) override + { + auto bcosExecutionMessage = std::make_unique( + [m_executionMessage = executionMessage]() mutable { return &m_executionMessage; }); + m_callback(toUniqueBcosError(ret), std::move(bcosExecutionMessage)); + } + + void callback_call_exception(tars::Int32 ret) override + { + m_callback(toUniqueBcosError(ret), nullptr); + } + + private: + AsyncCallback + m_callback; + }; + auto& executionMsgImpl = dynamic_cast(*input); + // timeout is 2min + m_prx->tars_set_timeout(2 * 60 * 1000) + ->async_call(new Callback(m_callbackPool, std::move(callback)), executionMsgImpl.inner()); +} + +void ExecutorServiceClient::executeTransactions(std::string contractAddress, + gsl::span inputs, + std::function)> + callback) +{ + class Callback : public ExecutorServicePrxCallback + { + public: + Callback(std::weak_ptr threadPool, + std::function)>&& _callback) + : m_callback(threadPool, std::move(_callback)) + {} + ~Callback() override {} + + void callback_executeTransactions(const bcostars::Error& ret, + std::vector const& executionMessages) override + { + std::vector inputList; + for (auto const& it : executionMessages) + { + auto bcosExecutionMessage = + std::make_unique( + [m_executionMessage = it]() mutable { return &m_executionMessage; }); + inputList.emplace_back(std::move(bcosExecutionMessage)); + } + m_callback(toUniqueBcosError(ret), std::move(inputList)); + } + + void callback_executeTransactions_exception(tars::Int32 ret) override + { + m_callback( + toUniqueBcosError(ret), std::vector()); + } + + private: + AsyncCallback> + m_callback; + }; + std::vector tarsInputs; + for (auto const& it : inputs) + { + auto& executionMsgImpl = dynamic_cast(*it); + tarsInputs.emplace_back(executionMsgImpl.inner()); + } + // timeout is 2min + m_prx->tars_set_timeout(2 * 60 * 1000) + ->async_executeTransactions( + new Callback(m_callbackPool, std::move(callback)), contractAddress, tarsInputs); +} + +void ExecutorServiceClient::dmcExecuteTransactions(std::string contractAddress, + gsl::span inputs, + std::function)> + callback) +{ + class Callback : public ExecutorServicePrxCallback + { + public: + Callback(std::weak_ptr threadPool, + std::function)>&& _callback) + : m_callback(threadPool, std::move(_callback)) + {} + ~Callback() override {} + + void callback_dmcExecuteTransactions(const bcostars::Error& ret, + std::vector const& executionMessages) override + { + std::vector inputList; + for (auto const& it : executionMessages) + { + auto bcosExecutionMessage = + std::make_unique( + [m_executionMessage = it]() mutable { return &m_executionMessage; }); + inputList.emplace_back(std::move(bcosExecutionMessage)); + } + m_callback(toUniqueBcosError(ret), std::move(inputList)); + } + + void callback_dmcExecuteTransactions_exception(tars::Int32 ret) override + { + m_callback( + toUniqueBcosError(ret), std::vector()); + } + + private: + AsyncCallback> + m_callback; + }; + std::vector tarsInputs; + for (auto const& it : inputs) + { + auto& executionMsgImpl = dynamic_cast(*it); + tarsInputs.emplace_back(executionMsgImpl.inner()); + } + // timeout is 2min + m_prx->tars_set_timeout(2 * 60 * 1000) + ->async_dmcExecuteTransactions( + new Callback(m_callbackPool, std::move(callback)), contractAddress, tarsInputs); +} + +void ExecutorServiceClient::dagExecuteTransactions( + gsl::span inputs, + std::function)> + callback) +{ + class Callback : public ExecutorServicePrxCallback + { + public: + Callback(std::weak_ptr threadPool, + std::function)>&& _callback) + : m_callback(threadPool, std::move(_callback)) + {} + ~Callback() override {} + + void callback_dagExecuteTransactions(const bcostars::Error& ret, + std::vector const& executionMessages) override + { + std::vector inputList; + for (auto const& it : executionMessages) + { + auto bcosExecutionMessage = + std::make_unique( + [m_executionMessage = it]() mutable { return &m_executionMessage; }); + inputList.emplace_back(std::move(bcosExecutionMessage)); + } + m_callback(toUniqueBcosError(ret), std::move(inputList)); + } + + void callback_dagExecuteTransactions_exception(tars::Int32 ret) override + { + m_callback( + toUniqueBcosError(ret), std::vector()); + } + + private: + AsyncCallback> + m_callback; + }; + std::vector tarsInput; + for (auto const& it : inputs) + { + auto& executionMsgImpl = dynamic_cast(*it); + tarsInput.emplace_back(executionMsgImpl.inner()); + } + // timeout is 2min + m_prx->tars_set_timeout(2 * 60 * 1000) + ->async_dagExecuteTransactions( + new Callback(m_callbackPool, std::move(callback)), tarsInput); +} + +void ExecutorServiceClient::dmcCall(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) +{ + class Callback : public ExecutorServicePrxCallback + { + public: + Callback(std::weak_ptr threadPool, + std::function&& _callback) + : m_callback(threadPool, std::move(_callback)) + {} + ~Callback() override {} + + void callback_dmcCall( + const bcostars::Error& ret, bcostars::ExecutionMessage const& executionMessage) override + { + auto bcosExecutionMessage = std::make_unique( + [m_executionMessage = executionMessage]() mutable { return &m_executionMessage; }); + m_callback(toUniqueBcosError(ret), std::move(bcosExecutionMessage)); + } + + void callback_dmcCall_exception(tars::Int32 ret) override + { + m_callback(toUniqueBcosError(ret), nullptr); + } + + private: + AsyncCallback + m_callback; + }; + auto& executionMsgImpl = dynamic_cast(*input); + // timeout is 2min + m_prx->tars_set_timeout(2 * 60 * 1000) + ->async_dmcCall( + new Callback(m_callbackPool, std::move(callback)), executionMsgImpl.inner()); +} + +void ExecutorServiceClient::getHash(bcos::protocol::BlockNumber number, + std::function callback) +{ + class Callback : public ExecutorServicePrxCallback + { + public: + Callback(std::weak_ptr threadPool, + std::function&& _callback) + : m_callback(threadPool, std::move(_callback)) + {} + ~Callback() override {} + + void callback_getHash( + const bcostars::Error& ret, std::vector const& hashBytes) override + { + auto hash = bcos::crypto::HashType( + reinterpret_cast(hashBytes.data()), hashBytes.size()); + m_callback(toUniqueBcosError(ret), std::move(hash)); + } + + void callback_getHash_exception(tars::Int32 ret) override + { + m_callback(toUniqueBcosError(ret), bcos::crypto::HashType()); + } + + private: + AsyncCallback m_callback; + }; + // timeout is 30s + m_prx->tars_set_timeout(30000)->async_getHash( + new Callback(m_callbackPool, std::move(callback)), number); +} + +void ExecutorServiceClient::prepare( + const bcos::protocol::TwoPCParams& params, std::function callback) +{ + class Callback : public ExecutorServicePrxCallback + { + public: + Callback(std::weak_ptr threadPool, + std::function&& _callback) + : m_callback(threadPool, std::move(_callback)) + {} + ~Callback() override {} + + void callback_prepare(const bcostars::Error& ret) override { m_callback(toBcosError(ret)); } + + void callback_prepare_exception(tars::Int32 ret) override { m_callback(toBcosError(ret)); } + + private: + AsyncCallback m_callback; + }; + + // timeout is 30s + m_prx->tars_set_timeout(30000)->async_prepare( + new Callback(m_callbackPool, std::move(callback)), toTarsTwoPCParams(params)); +} + +void ExecutorServiceClient::commit( + const bcos::protocol::TwoPCParams& params, std::function callback) +{ + class Callback : public ExecutorServicePrxCallback + { + public: + Callback(std::weak_ptr threadPool, + std::function&& _callback) + : m_callback(threadPool, std::move(_callback)) + {} + ~Callback() override {} + + void callback_commit(const bcostars::Error& ret) override { m_callback(toBcosError(ret)); } + + void callback_commit_exception(tars::Int32 ret) override { m_callback(toBcosError(ret)); } + + private: + AsyncCallback m_callback; + }; + // timeout is 30s + m_prx->tars_set_timeout(30000)->async_commit( + new Callback(m_callbackPool, std::move(callback)), toTarsTwoPCParams(params)); +} + +void ExecutorServiceClient::rollback( + const bcos::protocol::TwoPCParams& params, std::function callback) +{ + class Callback : public ExecutorServicePrxCallback + { + public: + Callback(std::weak_ptr threadPool, + std::function&& _callback) + : m_callback(threadPool, std::move(_callback)) + {} + ~Callback() override {} + + void callback_rollback(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + + void callback_rollback_exception(tars::Int32 ret) override { m_callback(toBcosError(ret)); } + + private: + AsyncCallback m_callback; + }; + m_prx->tars_set_timeout(30000)->async_rollback( + new Callback(m_callbackPool, std::move(callback)), toTarsTwoPCParams(params)); +} + +void ExecutorServiceClient::reset(std::function callback) +{ + class Callback : public ExecutorServicePrxCallback + { + public: + Callback(std::weak_ptr threadPool, + std::function&& _callback) + : m_callback(threadPool, std::move(_callback)) + {} + ~Callback() override {} + + void callback_reset(const bcostars::Error& ret) override { m_callback(toBcosError(ret)); } + + void callback_reset_exception(tars::Int32 ret) override { m_callback(toBcosError(ret)); } + + private: + AsyncCallback m_callback; + }; + m_prx->tars_set_timeout(30000)->async_reset(new Callback(m_callbackPool, std::move(callback))); +} + +void ExecutorServiceClient::getCode( + std::string_view contract, std::function callback) +{ + class Callback : public ExecutorServicePrxCallback + { + public: + Callback(std::weak_ptr threadPool, + std::function&& _callback) + : m_callback(threadPool, std::move(_callback)) + {} + ~Callback() override {} + + void callback_getCode( + const bcostars::Error& ret, std::vector const& code) override + { + bcos::bytes codeBytes(code.begin(), code.end()); + m_callback(toBcosError(ret), std::move(codeBytes)); + } + + void callback_getCode_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), bcos::bytes()); + } + + private: + AsyncCallback m_callback; + }; + // timeout is 30s + m_prx->tars_set_timeout(30000)->async_getCode( + new Callback(m_callbackPool, std::move(callback)), std::string(contract)); +} + +void ExecutorServiceClient::getABI( + std::string_view contract, std::function callback) +{ + class Callback : public ExecutorServicePrxCallback + { + public: + Callback(std::weak_ptr threadPool, + std::function&& _callback) + : m_callback(threadPool, std::move(_callback)) + {} + ~Callback() override {} + + void callback_getABI(const bcostars::Error& ret, std::string const& abi) override + { + m_callback(toBcosError(ret), std::move(abi)); + } + + void callback_getABI_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), std::string()); + } + + private: + AsyncCallback m_callback; + }; + // timeout is 30s + m_prx->tars_set_timeout(30000)->async_getABI( + new Callback(m_callbackPool, std::move(callback)), std::string(contract)); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/ExecutorServiceClient.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/ExecutorServiceClient.h" new file mode 100644 index 00000000..42b17762 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/ExecutorServiceClient.h" @@ -0,0 +1,107 @@ +/** + * 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 ExecutorServiceClient.h + * @author: yujiechen + * @date 2022-5-9 + */ + +#pragma once +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include +#include +#include + +namespace bcostars +{ +class ExecutorServiceClient : public bcos::executor::ParallelTransactionExecutorInterface +{ +public: + using Ptr = std::shared_ptr; + ExecutorServiceClient(ExecutorServicePrx _prx) + : m_prx(_prx), + m_callbackPool(std::make_shared( + "executorCallback", std::thread::hardware_concurrency())) + {} + ~ExecutorServiceClient() override {} + + void status( + std::function + callback) override; + + void nextBlockHeader(int64_t schedulerTermId, + const bcos::protocol::BlockHeader::ConstPtr& blockHeader, + std::function callback) override; + + void executeTransaction(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override; + + void call(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override; + + void executeTransactions(std::string contractAddress, + gsl::span inputs, + std::function)> + callback) override; + + void dmcExecuteTransactions(std::string contractAddress, + gsl::span inputs, + std::function)> + callback) override; + + void dagExecuteTransactions(gsl::span inputs, + std::function)> + callback) override; + + void dmcCall(bcos::protocol::ExecutionMessage::UniquePtr input, + std::function + callback) override; + + void getHash(bcos::protocol::BlockNumber number, + std::function callback) override; + + // Write data to storage uncommitted + void prepare(const bcos::protocol::TwoPCParams& params, + std::function callback) override; + + // Commit uncommitted data + void commit(const bcos::protocol::TwoPCParams& params, + std::function callback) override; + + // Rollback the changes + void rollback(const bcos::protocol::TwoPCParams& params, + std::function callback) override; + + /* ----- XA Transaction interface End ----- */ + + // drop all status + void reset(std::function callback) override; + void getCode(std::string_view contract, + std::function callback) override; + void getABI(std::string_view contract, + std::function callback) override; + +private: + ExecutorServicePrx m_prx; + bcos::ThreadPool::Ptr m_callbackPool; +}; +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/FrontServiceClient.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/FrontServiceClient.h" new file mode 100644 index 00000000..b8b1c598 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/FrontServiceClient.h" @@ -0,0 +1,257 @@ +#pragma once + +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include "bcos-tars-protocol/Common.h" +#include "bcos-tars-protocol/ErrorConverter.h" +#include "bcos-tars-protocol/tars/FrontService.h" +#include +#include +#include +#include +#include + +namespace bcostars +{ +class FrontServiceClient : public bcos::front::FrontServiceInterface +{ +public: + void start() override {} + void stop() override {} + + FrontServiceClient(bcostars::FrontServicePrx proxy, bcos::crypto::KeyFactory::Ptr keyFactory) + : m_proxy(proxy), m_keyFactory(keyFactory) + {} + + void asyncGetGroupNodeInfo(bcos::front::GetGroupNodeInfoFunc _onGetGroupNodeInfo) override + { + class Callback : public FrontServicePrxCallback + { + public: + Callback(bcos::front::GetGroupNodeInfoFunc callback, FrontServiceClient* self) + : m_callback(callback) + {} + void callback_asyncGetGroupNodeInfo( + const bcostars::Error& ret, const GroupNodeInfo& groupNodeInfo) override + { + auto bcosGroupNodeInfo = std::make_shared( + [m_groupNodeInfo = groupNodeInfo]() mutable { return &m_groupNodeInfo; }); + m_callback(toBcosError(ret), bcosGroupNodeInfo); + } + void callback_asyncGetGroupNodeInfo_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), nullptr); + } + + private: + bcos::front::GetGroupNodeInfoFunc m_callback; + }; + + m_proxy->tars_set_timeout(c_frontServiceTimeout) + ->async_asyncGetGroupNodeInfo(new Callback(_onGetGroupNodeInfo, this)); + } + + void onReceiveGroupNodeInfo(const std::string& _groupID, + bcos::gateway::GroupNodeInfo::Ptr _groupNodeInfo, + bcos::front::ReceiveMsgFunc _receiveMsgCallback) override + { + class Callback : public FrontServicePrxCallback + { + public: + Callback(bcos::front::ReceiveMsgFunc callback) : m_callback(callback) {} + + void callback_onReceiveGroupNodeInfo(const bcostars::Error& ret) override + { + if (!m_callback) + { + return; + } + m_callback(toBcosError(ret)); + } + + void callback_onReceiveGroupNodeInfo_exception(tars::Int32 ret) override + { + if (!m_callback) + { + return; + } + m_callback(toBcosError(ret)); + } + + private: + bcos::front::ReceiveMsgFunc m_callback; + }; + auto groupNodeInfoImpl = + std::dynamic_pointer_cast(_groupNodeInfo); + auto tarsGroupNodeInfo = groupNodeInfoImpl->inner(); + m_proxy->tars_set_timeout(c_frontServiceTimeout) + ->async_onReceiveGroupNodeInfo( + new Callback(_receiveMsgCallback), _groupID, tarsGroupNodeInfo); + } + + void onReceiveMessage(const std::string& _groupID, bcos::crypto::NodeIDPtr _nodeID, + bcos::bytesConstRef _data, bcos::front::ReceiveMsgFunc _receiveMsgCallback) override + { + class Callback : public FrontServicePrxCallback + { + public: + Callback(bcos::front::ReceiveMsgFunc callback) : m_callback(callback) {} + + void callback_onReceiveMessage(const bcostars::Error& ret) override + { + if (!m_callback) + { + return; + } + m_callback(toBcosError(ret)); + } + + void callback_onReceiveMessage_exception(tars::Int32 ret) override + { + if (!m_callback) + { + return; + } + m_callback(toBcosError(ret)); + } + + private: + bcos::front::ReceiveMsgFunc m_callback; + }; + auto nodeIDData = _nodeID->data(); + m_proxy->tars_set_timeout(c_frontServiceTimeout) + ->async_onReceiveMessage(new Callback(_receiveMsgCallback), _groupID, + std::vector(nodeIDData.begin(), nodeIDData.end()), + std::vector(_data.begin(), _data.end())); + } + + // Note: the _receiveMsgCallback maybe null in some cases + void onReceiveBroadcastMessage(const std::string& _groupID, bcos::crypto::NodeIDPtr _nodeID, + bcos::bytesConstRef _data, bcos::front::ReceiveMsgFunc _receiveMsgCallback) override + { + class Callback : public FrontServicePrxCallback + { + public: + Callback(bcos::front::ReceiveMsgFunc callback) : m_callback(callback) {} + + void callback_onReceiveBroadcastMessage(const bcostars::Error& ret) override + { + if (!m_callback) + { + return; + } + m_callback(toBcosError(ret)); + } + + void callback_onReceiveBroadcastMessage_exception(tars::Int32 ret) override + { + if (!m_callback) + { + return; + } + m_callback(toBcosError(ret)); + } + + private: + bcos::front::ReceiveMsgFunc m_callback; + }; + auto nodeIDData = _nodeID->data(); + m_proxy->tars_set_timeout(c_frontServiceTimeout) + ->async_onReceiveBroadcastMessage(new Callback(_receiveMsgCallback), _groupID, + std::vector(nodeIDData.begin(), nodeIDData.end()), + std::vector(_data.begin(), _data.end())); + } + + // Note: the _callback maybe null in some cases + void asyncSendMessageByNodeID(int _moduleID, bcos::crypto::NodeIDPtr _nodeID, + bcos::bytesConstRef _data, uint32_t _timeout, bcos::front::CallbackFunc _callback) override + { + class Callback : public FrontServicePrxCallback + { + public: + Callback(bcos::front::CallbackFunc callback, FrontServiceClient* self) + : m_callback(callback), m_self(self) + {} + + void callback_asyncSendMessageByNodeID(const bcostars::Error& ret, + const vector& responseNodeID, const vector& responseData, + const std::string& seq) override + { + if (!m_callback) + { + return; + } + auto bcosNodeID = m_self->m_keyFactory->createKey( + bcos::bytesConstRef((bcos::byte*)responseNodeID.data(), responseNodeID.size())); + m_callback(toBcosError(ret), bcosNodeID, + bcos::bytesConstRef((bcos::byte*)responseData.data(), responseData.size()), seq, + bcos::front::ResponseFunc()); + } + + void callback_asyncSendMessageByNodeID_exception(tars::Int32 ret) override + { + if (!m_callback) + { + return; + } + m_callback(toBcosError(ret), nullptr, bcos::bytesConstRef(), "", + bcos::front::ResponseFunc()); + } + + private: + bcos::front::CallbackFunc m_callback; + FrontServiceClient* m_self; + }; + + auto nodeIDData = _nodeID->data(); + m_proxy->tars_set_timeout(c_frontServiceTimeout) + ->async_asyncSendMessageByNodeID(new Callback(_callback, this), _moduleID, + std::vector(nodeIDData.begin(), nodeIDData.end()), + std::vector(_data.begin(), _data.end()), _timeout, + (_callback ? true : false)); + } + + void asyncSendResponse(const std::string& _id, int _moduleID, bcos::crypto::NodeIDPtr _nodeID, + bcos::bytesConstRef _data, bcos::front::ReceiveMsgFunc _receiveMsgCallback) override + { + auto nodeIDData = _nodeID->data(); + m_proxy->tars_set_timeout(c_frontServiceTimeout) + ->asyncSendResponse(_id, _moduleID, + std::vector(nodeIDData.begin(), nodeIDData.end()), + std::vector(_data.begin(), _data.end())); + } + + void asyncSendMessageByNodeIDs(int _moduleID, + const std::vector& _nodeIDs, bcos::bytesConstRef _data) override + { + std::vector> tarsNodeIDs; + tarsNodeIDs.reserve(_nodeIDs.size()); + for (auto const& it : _nodeIDs) + { + auto nodeIDData = it->data(); + tarsNodeIDs.emplace_back(nodeIDData.begin(), nodeIDData.end()); + } + m_proxy->tars_set_timeout(c_frontServiceTimeout) + ->async_asyncSendMessageByNodeIDs( + nullptr, _moduleID, tarsNodeIDs, std::vector(_data.begin(), _data.end())); + } + + void asyncSendBroadcastMessage( + uint16_t _type, int _moduleID, bcos::bytesConstRef _data) override + { + auto data = _data.toBytes(); + m_proxy->tars_set_timeout(c_frontServiceTimeout) + ->async_asyncSendBroadcastMessage( + nullptr, _type, _moduleID, std::vector(data.begin(), data.end())); + } + +private: + // 30s + const int c_frontServiceTimeout = 30000; + + bcostars::FrontServicePrx m_proxy; + bcos::crypto::KeyFactory::Ptr m_keyFactory; + std::string const c_moduleName = "FrontServiceClient"; +}; +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/GatewayServiceClient.cpp" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/GatewayServiceClient.cpp" new file mode 100644 index 00000000..93e5b09b --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/GatewayServiceClient.cpp" @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2021 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 GatewayServiceClient.cpp + * @author: ancelmo + * @date 2021-04-20 + */ +#include "GatewayServiceClient.h" + +std::atomic bcostars::GatewayServiceClient::s_tarsTimeoutCount = {0}; +const int64_t bcostars::GatewayServiceClient::c_maxTarsTimeoutCount = 500; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/GatewayServiceClient.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/GatewayServiceClient.h" new file mode 100644 index 00000000..f7c7f4c0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/GatewayServiceClient.h" @@ -0,0 +1,443 @@ +/** + * Copyright (C) 2021 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 GatewayServiceClient.h + * @author: ancelmo + * @date 2021-04-20 + */ + +#pragma once + +#include "fisco-bcos-tars-service/Common/TarsUtils.h" +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include "../Common.h" +#include "../ErrorConverter.h" +#include "../protocol/GroupNodeInfoImpl.h" +#include "bcos-tars-protocol/tars/GatewayService.h" +#include +#include +#include + +#define GATEWAYCLIENT_LOG(LEVEL) BCOS_LOG(LEVEL) << "[GATEWAYCLIENT][INITIALIZER]" +#define GATEWAYCLIENT_BADGE "[GATEWAYCLIENT]" +namespace bcostars +{ +class GatewayServiceClient : public bcos::gateway::GatewayInterface +{ +public: + GatewayServiceClient(bcostars::GatewayServicePrx _prx, std::string const& _serviceName, + bcos::crypto::KeyFactory::Ptr _keyFactory) + : GatewayServiceClient(_prx, _serviceName) + { + m_keyFactory = _keyFactory; + } + GatewayServiceClient(bcostars::GatewayServicePrx _prx, std::string const& _serviceName) + : m_prx(_prx), m_gatewayServiceName(_serviceName) + {} + virtual ~GatewayServiceClient() {} + + void setKeyFactory(bcos::crypto::KeyFactory::Ptr keyFactory) { m_keyFactory = keyFactory; } + + void asyncSendMessageByNodeID(const std::string& _groupID, int _moduleID, + bcos::crypto::NodeIDPtr _srcNodeID, bcos::crypto::NodeIDPtr _dstNodeID, + bcos::bytesConstRef _payload, bcos::gateway::ErrorRespFunc _errorRespFunc) override + { + class Callback : public bcostars::GatewayServicePrxCallback + { + public: + Callback(bcos::gateway::ErrorRespFunc callback) : m_callback(callback) {} + + void callback_asyncSendMessageByNodeID(const bcostars::Error& ret) override + { + s_tarsTimeoutCount.store(0); + m_callback(toBcosError(ret)); + } + void callback_asyncSendMessageByNodeID_exception(tars::Int32 ret) override + { + s_tarsTimeoutCount++; + m_callback(toBcosError(ret)); + } + + private: + bcos::gateway::ErrorRespFunc m_callback; + }; + auto shouldBlockCall = shouldStopCall(); + auto ret = checkConnection( + c_moduleName, "asyncSendMessageByNodeID", m_prx, + [_errorRespFunc](bcos::Error::Ptr _error) { + if (_errorRespFunc) + { + _errorRespFunc(_error); + } + }, + shouldBlockCall); + if (!ret && shouldBlockCall) + { + return; + } + auto srcNodeID = _srcNodeID->data(); + auto destNodeID = _dstNodeID->data(); + m_prx->tars_set_timeout(c_networkTimeout) + ->async_asyncSendMessageByNodeID(new Callback(_errorRespFunc), _groupID, _moduleID, + std::vector(srcNodeID.begin(), srcNodeID.end()), + std::vector(destNodeID.begin(), destNodeID.end()), + std::vector(_payload.begin(), _payload.end())); + } + + void asyncGetPeers(std::function + _callback) override + { + class Callback : public bcostars::GatewayServicePrxCallback + { + public: + Callback(std::function + callback) + : m_callback(callback) + {} + + void callback_asyncGetPeers(const bcostars::Error& ret, + const bcostars::GatewayInfo& _localInfo, + const vector& _peers) override + { + s_tarsTimeoutCount.store(0); + auto localGatewayInfo = fromTarsGatewayInfo(_localInfo); + auto peersGatewayInfos = std::make_shared(); + for (auto const& peerNodeInfo : _peers) + { + peersGatewayInfos->emplace_back(fromTarsGatewayInfo(peerNodeInfo)); + } + m_callback(toBcosError(ret), localGatewayInfo, peersGatewayInfos); + } + void callback_asyncGetPeers_exception(tars::Int32 ret) override + { + s_tarsTimeoutCount++; + m_callback(toBcosError(ret), nullptr, nullptr); + } + + private: + std::function + m_callback; + }; + auto shouldBlockCall = shouldStopCall(); + auto ret = checkConnection( + c_moduleName, "asyncGetPeers", m_prx, + [_callback](bcos::Error::Ptr _error) { + if (_callback) + { + _callback(_error, nullptr, nullptr); + } + }, + shouldBlockCall); + if (!ret && shouldBlockCall) + { + return; + } + m_prx->async_asyncGetPeers(new Callback(_callback)); + } + + void asyncSendMessageByNodeIDs(const std::string& _groupID, int _moduleID, + bcos::crypto::NodeIDPtr _srcNodeID, const bcos::crypto::NodeIDs& _dstNodeIDs, + bcos::bytesConstRef _payload) override + { + std::vector> tarsNodeIDs; + for (auto const& it : _dstNodeIDs) + { + auto nodeID = it->data(); + tarsNodeIDs.emplace_back(nodeID.begin(), nodeID.end()); + } + auto shouldBlockCall = shouldStopCall(); + auto ret = checkConnection( + c_moduleName, "asyncSendMessageByNodeIDs", m_prx, nullptr, shouldBlockCall); + if (!ret && shouldBlockCall) + { + return; + } + auto srcNodeID = _srcNodeID->data(); + m_prx->async_asyncSendMessageByNodeIDs(nullptr, _groupID, _moduleID, + std::vector(srcNodeID.begin(), srcNodeID.end()), tarsNodeIDs, + std::vector(_payload.begin(), _payload.end())); + } + + void asyncSendBroadcastMessage(uint16_t _type, const std::string& _groupID, int _moduleID, + bcos::crypto::NodeIDPtr _srcNodeID, bcos::bytesConstRef _payload) override + { + auto shouldBlockCall = shouldStopCall(); + auto ret = checkConnection( + c_moduleName, "asyncSendBroadcastMessage", m_prx, nullptr, shouldBlockCall); + if (!ret && shouldBlockCall) + { + return; + } + auto srcNodeID = _srcNodeID->data(); + m_prx->async_asyncSendBroadcastMessage(nullptr, _type, _groupID, _moduleID, + std::vector(srcNodeID.begin(), srcNodeID.end()), + std::vector(_payload.begin(), _payload.end())); + } + + void asyncGetGroupNodeInfo(const std::string& _groupID, + bcos::gateway::GetGroupNodeInfoFunc _onGetGroupNodeInfo) override + { + class Callback : public GatewayServicePrxCallback + { + public: + Callback(bcos::gateway::GetGroupNodeInfoFunc callback, + bcos::crypto::KeyFactory::Ptr keyFactory) + : m_callback(callback), m_keyFactory(keyFactory) + {} + void callback_asyncGetGroupNodeInfo( + const bcostars::Error& ret, const GroupNodeInfo& groupNodeInfo) override + { + s_tarsTimeoutCount.store(0); + auto bcosGroupNodeInfo = std::make_shared( + [m_groupNodeInfo = groupNodeInfo]() mutable { return &m_groupNodeInfo; }); + m_callback(toBcosError(ret), bcosGroupNodeInfo); + } + void callback_asyncGetGroupNodeInfo_exception(tars::Int32 ret) override + { + s_tarsTimeoutCount++; + m_callback(toBcosError(ret), nullptr); + } + + private: + bcos::gateway::GetGroupNodeInfoFunc m_callback; + bcos::crypto::KeyFactory::Ptr m_keyFactory; + }; + auto shouldBlockCall = shouldStopCall(); + auto ret = checkConnection( + c_moduleName, "asyncGetGroupNodeInfo", m_prx, + [_onGetGroupNodeInfo](bcos::Error::Ptr _error) { + if (_onGetGroupNodeInfo) + { + _onGetGroupNodeInfo(_error, nullptr); + } + }, + shouldBlockCall); + if (!ret && shouldBlockCall) + { + return; + } + m_prx->async_asyncGetGroupNodeInfo( + new Callback(_onGetGroupNodeInfo, m_keyFactory), _groupID); + } + + void asyncNotifyGroupInfo(bcos::group::GroupInfo::Ptr _groupInfo, + std::function _callback) override + { + class Callback : public bcostars::GatewayServicePrxCallback + { + public: + Callback(std::function callback) : m_callback(callback) {} + + void callback_asyncNotifyGroupInfo(const bcostars::Error& ret) override + { + s_tarsTimeoutCount.store(0); + m_callback(toBcosError(ret)); + } + void callback_asyncNotifyGroupInfo_exception(tars::Int32 ret) override + { + s_tarsTimeoutCount++; + m_callback(toBcosError(ret)); + } + + private: + std::function m_callback; + }; + auto shouldBlockCall = shouldStopCall(); + auto ret = checkConnection( + c_moduleName, "asyncNotifyGroupInfo", m_prx, + [_callback](bcos::Error::Ptr _error) { + if (_callback) + { + _callback(std::move(_error)); + } + }, + shouldBlockCall); + if (!ret && shouldBlockCall) + { + return; + } + + auto activeEndPoints = tarsProxyAvailableEndPoints(m_prx); + auto tarsGroupInfo = toTarsGroupInfo(_groupInfo); + + // notify groupInfo to all gateway nodes + for (auto const& endPoint : activeEndPoints) + { + auto prx = + bcostars::createServantProxy(m_gatewayServiceName, endPoint); + prx->async_asyncNotifyGroupInfo(new Callback(_callback), tarsGroupInfo); + } + } + + void asyncSendMessageByTopic(const std::string& _topic, bcos::bytesConstRef _data, + std::function _respFunc) override + { + class Callback : public bcostars::GatewayServicePrxCallback + { + public: + Callback(std::function callback) + : m_callback(callback) + {} + void callback_asyncSendMessageByTopic(const bcostars::Error& ret, tars::Int32 _type, + const vector& _responseData) override + { + s_tarsTimeoutCount.store(0); + auto data = + std::make_shared(_responseData.begin(), _responseData.end()); + m_callback(toBcosError(ret), _type, data); + } + void callback_asyncSendMessageByTopic_exception(tars::Int32 ret) override + { + s_tarsTimeoutCount++; + return m_callback(toBcosError(ret), 0, nullptr); + } + + private: + std::function m_callback; + }; + auto shouldBlockCall = shouldStopCall(); + auto ret = checkConnection( + c_moduleName, "asyncSendMessageByTopic", m_prx, + [_respFunc](bcos::Error::Ptr _error) { + if (_respFunc) + { + _respFunc(std::move(_error), 0, nullptr); + } + }, + shouldBlockCall); + if (!ret && shouldBlockCall) + { + return; + } + vector tarsRequestData(_data.begin(), _data.end()); + m_prx->tars_set_timeout(c_amopTimeout) + ->async_asyncSendMessageByTopic(new Callback(_respFunc), _topic, tarsRequestData); + } + + void asyncSendBroadcastMessageByTopic( + const std::string& _topic, bcos::bytesConstRef _data) override + { + auto shouldBlockCall = shouldStopCall(); + auto ret = checkConnection( + c_moduleName, "asyncSendBroadcastMessageByTopic", m_prx, nullptr, shouldBlockCall); + if (!ret && shouldBlockCall) + { + return; + } + vector tarsRequestData(_data.begin(), _data.end()); + m_prx->async_asyncSendBroadcastMessageByTopic(nullptr, _topic, tarsRequestData); + } + + void asyncSubscribeTopic(std::string const& _clientID, std::string const& _topicInfo, + std::function _callback) override + { + class Callback : public bcostars::GatewayServicePrxCallback + { + public: + Callback(std::function callback) : m_callback(callback) {} + void callback_asyncSubscribeTopic(const bcostars::Error& ret) override + { + s_tarsTimeoutCount.store(0); + m_callback(toBcosError(ret)); + } + void callback_asyncSubscribeTopic_exception(tars::Int32 ret) override + { + s_tarsTimeoutCount++; + return m_callback(toBcosError(ret)); + } + + private: + std::function m_callback; + }; + auto shouldBlockCall = shouldStopCall(); + auto ret = checkConnection( + c_moduleName, "asyncSubscribeTopic", m_prx, + [_callback](bcos::Error::Ptr _error) { + if (_callback) + { + _callback(std::move(_error)); + } + }, + shouldBlockCall); + if (!ret && shouldBlockCall) + { + return; + } + m_prx->async_asyncSubscribeTopic(new Callback(_callback), _clientID, _topicInfo); + } + + void asyncRemoveTopic(std::string const& _clientID, std::vector const& _topicList, + std::function _callback) override + { + class Callback : public bcostars::GatewayServicePrxCallback + { + public: + Callback(std::function callback) : m_callback(callback) {} + void callback_asyncRemoveTopic(const bcostars::Error& ret) override + { + s_tarsTimeoutCount.store(0); + m_callback(toBcosError(ret)); + } + void callback_asyncRemoveTopic_exception(tars::Int32 ret) override + { + s_tarsTimeoutCount++; + return m_callback(toBcosError(ret)); + } + + private: + std::function m_callback; + }; + auto shouldBlockCall = shouldStopCall(); + auto ret = checkConnection( + c_moduleName, "asyncRemoveTopic", m_prx, + [_callback](bcos::Error::Ptr _error) { + if (_callback) + { + _callback(std::move(_error)); + } + }, + shouldBlockCall); + if (!ret && shouldBlockCall) + { + return; + } + m_prx->async_asyncRemoveTopic(new Callback(_callback), _clientID, _topicList); + } + + bcostars::GatewayServicePrx prx() { return m_prx; } + +protected: + void start() override {} + void stop() override {} + static bool shouldStopCall() { return (s_tarsTimeoutCount >= c_maxTarsTimeoutCount); } + +private: + bcostars::GatewayServicePrx m_prx; + std::string m_gatewayServiceName; + // Note: only useful for asyncGetGroupNodeInfo + bcos::crypto::KeyFactory::Ptr m_keyFactory; + std::string const c_moduleName = "GatewayServiceClient"; + // AMOP timeout 40s + const int c_amopTimeout = 40000; + const int c_networkTimeout = 40000; + static std::atomic s_tarsTimeoutCount; + static const int64_t c_maxTarsTimeoutCount; +}; +} // namespace bcostars diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/LedgerServiceClient.cpp" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/LedgerServiceClient.cpp" new file mode 100644 index 00000000..6bb0ca85 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/LedgerServiceClient.cpp" @@ -0,0 +1,337 @@ +/** + * Copyright (C) 2021 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 LedgerServiceClient.cpp + * @author: yujiechen + * @date 2021-10-17 + */ +#include "LedgerServiceClient.h" +#include "../Common.h" +#include "../ErrorConverter.h" +#include "../protocol/BlockImpl.h" +#include "../protocol/TransactionImpl.h" +#include "../protocol/TransactionReceiptImpl.h" + +using namespace bcostars; + +void LedgerServiceClient::asyncGetBlockDataByNumber(bcos::protocol::BlockNumber _blockNumber, + int32_t _blockFlag, + std::function _onGetBlock) +{ + class Callback : public LedgerServicePrxCallback + { + public: + Callback(std::function _callback, + bcos::protocol::BlockFactory::Ptr _blockFactory) + : m_callback(_callback), m_blockFactory(_blockFactory) + {} + void callback_asyncGetBlockDataByNumber( + const bcostars::Error& ret, const bcostars::Block& _block) override + { + auto bcosBlock = m_blockFactory->createBlock(); + auto tarsBlock = std::dynamic_pointer_cast(bcosBlock); + tarsBlock->setInner(std::move(*const_cast(&_block))); + m_callback(toBcosError(ret), tarsBlock); + } + void callback_asyncGetBlockDataByNumber_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), nullptr); + } + + private: + std::function m_callback; + bcos::protocol::BlockFactory::Ptr m_blockFactory; + }; + m_prx->async_asyncGetBlockDataByNumber( + new Callback(_onGetBlock, m_blockFactory), _blockNumber, _blockFlag); +} + +void LedgerServiceClient::asyncGetBlockNumber( + std::function _onGetBlock) +{ + class Callback : public LedgerServicePrxCallback + { + public: + Callback(std::function _callback) + : m_callback(_callback) + {} + void callback_asyncGetBlockNumber( + const bcostars::Error& ret, tars::Int64 _blockNumber) override + { + m_callback(toBcosError(ret), _blockNumber); + } + void callback_asyncGetBlockNumber_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), -1); + } + + private: + std::function m_callback; + }; + m_prx->async_asyncGetBlockNumber(new Callback(_onGetBlock)); +} + +void LedgerServiceClient::asyncGetBlockHashByNumber(bcos::protocol::BlockNumber _blockNumber, + std::function _onGetBlock) +{ + class Callback : public LedgerServicePrxCallback + { + public: + Callback(std::function _callback) + : m_callback(_callback) + {} + void callback_asyncGetBlockHashByNumber( + const bcostars::Error& ret, const vector& _blockHash) override + { + if (_blockHash.size() >= bcos::crypto::HashType::SIZE) + { + auto hashData = + bcos::crypto::HashType(reinterpret_cast(_blockHash.data()), + bcos::crypto::HashType::SIZE); + m_callback(toBcosError(ret), hashData); + return; + } + m_callback(toBcosError(ret), bcos::crypto::HashType()); + } + void callback_asyncGetBlockHashByNumber_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), bcos::crypto::HashType()); + } + + private: + std::function m_callback; + }; + m_prx->async_asyncGetBlockHashByNumber(new Callback(_onGetBlock), _blockNumber); +} + +void LedgerServiceClient::asyncGetBlockNumberByHash(bcos::crypto::HashType const& _blockHash, + std::function _onGetBlock) +{ + class Callback : public LedgerServicePrxCallback + { + public: + Callback(std::function _callback) + : m_callback(_callback) + {} + void callback_asyncGetBlockNumberByHash( + const bcostars::Error& ret, tars::Int64 _blockNumber) override + { + m_callback(toBcosError(ret), _blockNumber); + } + void callback_asyncGetBlockNumberByHash_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), -1); + } + + private: + std::function m_callback; + }; + std::vector blockHash(_blockHash.begin(), _blockHash.end()); + m_prx->async_asyncGetBlockNumberByHash(new Callback(_onGetBlock), blockHash); +} + +void LedgerServiceClient::asyncGetBatchTxsByHashList(bcos::crypto::HashListPtr _txHashList, + bool _withProof, + std::function>)> + _onGetTx) +{ + class Callback : public LedgerServicePrxCallback + { + public: + Callback(std::function>)> + _callback, + bcos::crypto::CryptoSuite::Ptr _cryptoSuite) + : m_callback(_callback), m_cryptoSuite(_cryptoSuite) + {} + + void callback_asyncGetBatchTxsByHashList(const bcostars::Error& ret, + const vector& _txs, + const map>& _merkleProofList) override + { + // decode the txsList + auto bcosTxsList = std::make_shared(); + for (auto const& tx : _txs) + { + auto bcosTx = std::make_shared( + m_cryptoSuite, [m_tx = std::move(tx)]() mutable { return &m_tx; }); + bcosTxsList->emplace_back(bcosTx); + } + // decode the proof list + auto proofList = + std::make_shared>(); + for (auto const& it : _merkleProofList) + { + auto const& proof = it.second; + auto bcosProof = std::make_shared(); + for (auto const& item : proof) + { + bcosProof->emplace_back(std::pair(item.left, item.right)); + } + (*proofList)[it.first] = bcosProof; + } + m_callback(toBcosError(ret), bcosTxsList, proofList); + } + void callback_asyncGetBatchTxsByHashList_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), nullptr, nullptr); + } + + private: + std::function>)> + m_callback; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + }; + std::vector> tarsTxsHashList; + for (auto const& txHash : *_txHashList) + { + tarsTxsHashList.emplace_back(vector(txHash.begin(), txHash.end())); + } + m_prx->async_asyncGetBatchTxsByHashList( + new Callback(_onGetTx, m_cryptoSuite), tarsTxsHashList, _withProof); +} + +void LedgerServiceClient::asyncGetTransactionReceiptByHash(bcos::crypto::HashType const& _txHash, + bool _withProof, + std::function + _onGetTx) +{ + class Callback : public LedgerServicePrxCallback + { + public: + Callback(std::function + _callback, + bcos::crypto::CryptoSuite::Ptr _cryptoSuite) + : m_callback(_callback), m_cryptoSuite(_cryptoSuite) + {} + void callback_asyncGetTransactionReceiptByHash(const bcostars::Error& ret, + const bcostars::TransactionReceipt& _receipt, + const vector& _proof) override + { + auto bcosReceipt = std::make_shared( + m_cryptoSuite, [m_receipt = std::move(_receipt)]() mutable { return &m_receipt; }); + auto bcosProof = std::make_shared(); + for (auto const& item : _proof) + { + bcosProof->emplace_back(std::pair(item.left, item.right)); + } + m_callback(toBcosError(ret), bcosReceipt, bcosProof); + } + void callback_asyncGetTransactionReceiptByHash_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), nullptr, nullptr); + } + + private: + std::function + m_callback; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + }; + std::vector txHash(_txHash.begin(), _txHash.end()); + m_prx->async_asyncGetTransactionReceiptByHash( + new Callback(_onGetTx, m_cryptoSuite), txHash, _withProof); +} + +void LedgerServiceClient::asyncGetTotalTransactionCount( + std::function + _callback) +{ + class Callback : public LedgerServicePrxCallback + { + public: + Callback(std::function + _callback) + : m_callback(_callback) + {} + void callback_asyncGetTotalTransactionCount(const bcostars::Error& ret, + tars::Int64 _totalTxCount, tars::Int64 _failedTxCount, + tars::Int64 _latestBlockNumber) override + { + m_callback(toBcosError(ret), _totalTxCount, _failedTxCount, _latestBlockNumber); + } + void callback_asyncGetTotalTransactionCount_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), -1, -1, -1); + } + + private: + std::function + m_callback; + }; + m_prx->async_asyncGetTotalTransactionCount(new Callback(_callback)); +} + +void LedgerServiceClient::asyncGetSystemConfigByKey(std::string_view const& _key, + std::function _onGetConfig) +{ + class Callback : public LedgerServicePrxCallback + { + public: + Callback(std::function + _callback) + : m_callback(_callback) + {} + void callback_asyncGetSystemConfigByKey(const bcostars::Error& ret, + const std::string& _value, tars::Int64 _blockNumber) override + { + m_callback(toBcosError(ret), _value, _blockNumber); + } + void callback_asyncGetSystemConfigByKey_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), "", -1); + } + + private: + std::function m_callback; + }; + m_prx->async_asyncGetSystemConfigByKey(new Callback(_onGetConfig), std::string{_key}); +} + +void LedgerServiceClient::asyncGetNodeListByType(std::string_view const& _type, + std::function _onGetConfig) +{ + class Callback : public LedgerServicePrxCallback + { + public: + Callback( + std::function _callback, + bcos::crypto::KeyFactory::Ptr _keyFactory) + : m_callback(_callback), m_keyFactory(_keyFactory) + {} + virtual void callback_asyncGetNodeListByType( + const bcostars::Error& ret, const vector& _nodeList) + { + m_callback(toBcosError(ret), toConsensusNodeList(m_keyFactory, _nodeList)); + } + virtual void callback_asyncGetNodeListByType_exception(tars::Int32 ret) + { + m_callback(toBcosError(ret), nullptr); + } + + private: + std::function m_callback; + bcos::crypto::KeyFactory::Ptr m_keyFactory; + }; + m_prx->async_asyncGetNodeListByType( + new Callback(_onGetConfig, m_keyFactory), std::string{_type}); +} diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/LedgerServiceClient.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/LedgerServiceClient.h" new file mode 100644 index 00000000..bbfccdc3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/LedgerServiceClient.h" @@ -0,0 +1,115 @@ +/** + * Copyright (C) 2021 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 LedgerServiceClient.h + * @author: yujiechen + * @date 2021-10-17 + */ +#pragma once + +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include +#include +#include +#include +namespace bcostars +{ +class LedgerServiceClient : public bcos::ledger::LedgerInterface +{ +public: + LedgerServiceClient( + bcostars::LedgerServicePrx _prx, bcos::protocol::BlockFactory::Ptr _blockFactory) + : m_prx(_prx), m_blockFactory(_blockFactory) + { + if (m_blockFactory) + { + m_cryptoSuite = m_blockFactory->cryptoSuite(); + m_keyFactory = m_cryptoSuite->keyFactory(); + } + } + ~LedgerServiceClient() override {} + + // TODO: implement this + void asyncPrewriteBlock(bcos::storage::StorageInterface::Ptr, bcos::protocol::TransactionsPtr, + bcos::protocol::Block::ConstPtr, std::function) override + { + BCOS_LOG(ERROR) << LOG_DESC("unimplement method asyncPrewriteBlock"); + } + + void asyncPreStoreBlockTxs(bcos::protocol::TransactionsPtr, bcos::protocol::Block::ConstPtr, + std::function) override + { + BCOS_LOG(ERROR) << LOG_DESC("unimplement method asyncPreStoreBlockTxs"); + } + + // TODO: implement this + void asyncStoreTransactions(std::shared_ptr>, + bcos::crypto::HashListPtr, std::function) override + { + BCOS_LOG(ERROR) << LOG_DESC("unimplement method asyncStoreTransactions"); + } + + void asyncGetBlockDataByNumber(bcos::protocol::BlockNumber _blockNumber, int32_t _blockFlag, + std::function _onGetBlock) override; + + void asyncGetBlockNumber( + std::function _onGetBlock) override; + + void asyncGetBlockHashByNumber(bcos::protocol::BlockNumber _blockNumber, + std::function _onGetBlock) override; + + void asyncGetBlockNumberByHash(bcos::crypto::HashType const& _blockHash, + std::function _onGetBlock) override; + + void asyncGetBatchTxsByHashList(bcos::crypto::HashListPtr _txHashList, bool _withProof, + std::function>)> + _onGetTx) override; + + void asyncGetTransactionReceiptByHash(bcos::crypto::HashType const& _txHash, bool _withProof, + std::function + _onGetTx) override; + + void asyncGetTotalTransactionCount(std::function + _callback) override; + + void asyncGetSystemConfigByKey(std::string_view const& _key, + std::function + _onGetConfig) override; + + void asyncGetNodeListByType(std::string_view const& _type, + std::function _onGetConfig) + override; + + // TODO: implement this + void asyncGetNonceList(bcos::protocol::BlockNumber, int64_t, + std::function>)>) + override + { + BCOS_LOG(ERROR) << LOG_DESC("unimplement method asyncGetNonceList"); + } + +private: + bcostars::LedgerServicePrx m_prx; + bcos::protocol::BlockFactory::Ptr m_blockFactory; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + bcos::crypto::KeyFactory::Ptr m_keyFactory; +}; +} // namespace bcostars diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/PBFTServiceClient.cpp" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/PBFTServiceClient.cpp" new file mode 100644 index 00000000..246a1a53 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/PBFTServiceClient.cpp" @@ -0,0 +1,207 @@ +/** + * Copyright (C) 2021 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. + * + * @brief client for the PBFTService + * @file PBFTServiceClient.cpp + * @author: yujiechen + * @date 2021-06-29 + */ + +#include "PBFTServiceClient.h" +#include "../protocol/BlockFactoryImpl.h" +using namespace bcostars; + +void PBFTServiceClient::asyncSubmitProposal(bool _containSysTxs, bcos::bytesConstRef _proposalData, + bcos::protocol::BlockNumber _proposalIndex, bcos::crypto::HashType const& _proposalHash, + std::function _onProposalSubmitted) +{ + m_proxy->async_asyncSubmitProposal(new PBFTServiceCommonCallback(_onProposalSubmitted), + _containSysTxs, std::vector(_proposalData.begin(), _proposalData.end()), + _proposalIndex, std::vector(_proposalHash.begin(), _proposalHash.end())); +} + +void PBFTServiceClient::asyncGetPBFTView( + std::function _onGetView) +{ + class Callback : public PBFTServicePrxCallback + { + public: + explicit Callback( + std::function _callback) + : PBFTServicePrxCallback(), m_callback(_callback) + {} + ~Callback() override {} + + void callback_asyncGetPBFTView(const bcostars::Error& ret, tars::Int64 _view) override + { + m_callback(toBcosError(ret), _view); + } + void callback_asyncGetPBFTView_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), 0); + } + + private: + std::function m_callback; + }; + m_proxy->async_asyncGetPBFTView(new Callback(_onGetView)); +} + +// the sync module calls this interface to check block +void PBFTServiceClient::asyncCheckBlock( + bcos::protocol::Block::Ptr _block, std::function _onVerifyFinish) +{ + class Callback : public PBFTServicePrxCallback + { + public: + explicit Callback(std::function _callback) + : PBFTServicePrxCallback(), m_callback(_callback) + {} + ~Callback() override {} + + void callback_asyncCheckBlock(const bcostars::Error& ret, tars::Bool _verifyResult) override + { + m_callback(toBcosError(ret), _verifyResult); + } + void callback_asyncCheckBlock_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), false); + } + + private: + std::function m_callback; + }; + + auto blockImpl = std::dynamic_pointer_cast(_block); + m_proxy->async_asyncCheckBlock(new Callback(_onVerifyFinish), blockImpl->inner()); +} + +// the sync module calls this interface to notify new block +void PBFTServiceClient::asyncNotifyNewBlock( + bcos::ledger::LedgerConfig::Ptr _ledgerConfig, std::function _onRecv) +{ + m_proxy->async_asyncNotifyNewBlock( + new PBFTServiceCommonCallback(_onRecv), toTarsLedgerConfig(_ledgerConfig)); +} + +// called by frontService to dispatch message +void PBFTServiceClient::asyncNotifyConsensusMessage(bcos::Error::Ptr, std::string const& _uuid, + bcos::crypto::NodeIDPtr _nodeID, bcos::bytesConstRef _data, + std::function _onRecv) +{ + auto nodeIDData = _nodeID->data(); + m_proxy->async_asyncNotifyConsensusMessage(new PBFTServiceCommonCallback(_onRecv), _uuid, + std::vector(nodeIDData.begin(), nodeIDData.end()), + std::vector(_data.begin(), _data.end())); +} + +// Note: used for the txpool notify the unsealed txsSize +void PBFTServiceClient::asyncNoteUnSealedTxsSize( + uint64_t _unsealedTxsSize, std::function _onRecv) +{ + m_proxy->async_asyncNoteUnSealedTxsSize( + new PBFTServiceCommonCallback(_onRecv), _unsealedTxsSize); +} + +void BlockSyncServiceClient::asyncGetSyncInfo( + std::function _onGetSyncInfo) +{ + class Callback : public PBFTServicePrxCallback + { + public: + explicit Callback(std::function _callback) + : PBFTServicePrxCallback(), m_callback(_callback) + {} + ~Callback() override {} + + void callback_asyncGetSyncInfo( + const bcostars::Error& ret, const std::string& _syncInfo) override + { + m_callback(toBcosError(ret), _syncInfo); + } + void callback_asyncGetSyncInfo_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), "callback_asyncGetSyncInfo_exception"); + } + + private: + std::function m_callback; + }; + m_proxy->async_asyncGetSyncInfo(new Callback(_onGetSyncInfo)); +} + +void BlockSyncServiceClient::notifyConnectedNodes(bcos::crypto::NodeIDSet const& _connectedNodes, + std::function _onRecvResponse) +{ + PBFTServiceClient::notifyConnectedNodes(_connectedNodes, _onRecvResponse); +} + +void PBFTServiceClient::notifyConnectedNodes(bcos::crypto::NodeIDSet const& _connectedNodes, + std::function _onResponse) +{ + class Callback : public bcostars::PBFTServicePrxCallback + { + public: + Callback(std::function callback) : m_callback(callback) {} + + void callback_asyncNotifyConnectedNodes(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + + void callback_asyncNotifyConnectedNodes_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + + private: + std::function m_callback; + }; + + std::vector> tarsConnectedNodes; + for (auto const& it : _connectedNodes) + { + auto nodeID = it->data(); + tarsConnectedNodes.emplace_back(nodeID.begin(), nodeID.end()); + } + m_proxy->async_asyncNotifyConnectedNodes(new Callback(_onResponse), tarsConnectedNodes); +} + +void PBFTServiceClient::asyncGetConsensusStatus( + std::function _onGetConsensusStatus) +{ + class Callback : public PBFTServicePrxCallback + { + public: + explicit Callback(std::function _callback) + : PBFTServicePrxCallback(), m_callback(_callback) + {} + ~Callback() override {} + + void callback_asyncGetConsensusStatus( + const bcostars::Error& ret, const std::string& _consensusStatus) override + { + m_callback(toBcosError(ret), _consensusStatus); + } + void callback_asyncGetConsensusStatus_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), "callback_asyncGetConsensusStatus_exception"); + } + + private: + std::function m_callback; + }; + m_proxy->async_asyncGetConsensusStatus(new Callback(_onGetConsensusStatus)); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/PBFTServiceClient.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/PBFTServiceClient.h" new file mode 100644 index 00000000..22175726 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/PBFTServiceClient.h" @@ -0,0 +1,219 @@ +/** + * Copyright (C) 2021 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. + * + * @brief client for the PBFTService + * @file PBFTServiceClient.h + * @author: yujiechen + * @date 2021-06-29 + */ + +#pragma once +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#include "../ErrorConverter.h" +#include +#include +#include +#include + +namespace bcostars +{ +class PBFTServiceCommonCallback : public bcostars::PBFTServicePrxCallback +{ +public: + PBFTServiceCommonCallback(std::function _callback) + : PBFTServicePrxCallback(), m_callback(_callback) + {} + ~PBFTServiceCommonCallback() override {} + + void callback_asyncNoteUnSealedTxsSize(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + void callback_asyncNoteUnSealedTxsSize_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + void callback_asyncNotifyConsensusMessage(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + void callback_asyncNotifyConsensusMessage_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + void callback_asyncNotifyNewBlock(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + + void callback_asyncNotifyNewBlock_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + void callback_asyncSubmitProposal(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + void callback_asyncSubmitProposal_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + + void callback_asyncNotifyBlockSyncMessage(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + + void callback_asyncNotifyBlockSyncMessage_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + +private: + std::function m_callback; +}; + +class PBFTServiceClient : virtual public bcos::consensus::ConsensusInterface, + virtual public bcos::sealer::SealerInterface +{ +public: + using Ptr = std::shared_ptr; + PBFTServiceClient(bcostars::PBFTServicePrx _proxy) : m_proxy(_proxy) {} + ~PBFTServiceClient() override {} + + void start() override {} + void stop() override {} + // called by frontService to dispatch message + void asyncNotifyConsensusMessage(bcos::Error::Ptr _error, std::string const& _uuid, + bcos::crypto::NodeIDPtr _nodeID, bcos::bytesConstRef _data, + std::function _onRecv) override; + void asyncGetPBFTView( + std::function _onGetView) override; + + // the txpool notify the unsealed txsSize to the sealer module + void asyncNoteUnSealedTxsSize( + uint64_t _unsealedTxsSize, std::function _onRecvResponse) override; + + // the sealer submit proposal to the consensus module + // Note: if the sealer module integrates with the PBFT module, no need to implement this + // interface + void asyncSubmitProposal(bool _containSysTxs, bcos::bytesConstRef _proposalData, + bcos::protocol::BlockNumber _proposalIndex, bcos::crypto::HashType const& _proposalHash, + std::function _onProposalSubmitted) override; + + // the sync module calls this interface to check block + // Note: if the sync module integrates with the PBFT module, no need to implement this + // interface + void asyncCheckBlock(bcos::protocol::Block::Ptr _block, + std::function _onVerifyFinish) override; + + // the sync module calls this interface to notify new block + // Note: if the sync module integrates with the PBFT module, no need to implement this + // interface + void asyncNotifyNewBlock(bcos::ledger::LedgerConfig::Ptr _ledgerConfig, + std::function _onRecv) override; + + // for the sync module to notify the syncing number + // Note: since the sync module is integrated with the PBFT module, no need to implement the + // client interface + void notifyHighestSyncingNumber(bcos::protocol::BlockNumber _number) override + { + throw std::runtime_error( + "PBFTServiceClient: notifyHighestSyncingNumber: unimplemented interface!"); + } + void asyncNotifySealProposal( + uint64_t, uint64_t, uint64_t, std::function) override + { + throw std::runtime_error( + "PBFTServiceClient: asyncNotifySealProposal: unimplemented interface!"); + } + // for the consensus module to notify the latest blockNumber to the sealer module + // Note: since the sealer module is integrated with the PBFT module, no need to implement + // the client interface + void asyncNoteLatestBlockNumber(int64_t) override + { + throw std::runtime_error( + "PBFTServiceClient: asyncNoteLatestBlockNumber: unimplemented interface!"); + } + + // the consensus module notify the sealer to reset sealing when viewchange + void asyncResetSealing(std::function _onRecvResponse) override + { + throw std::runtime_error("PBFTServiceClient: asyncResetSealing: unimplemented interface!"); + } + + void asyncGetConsensusStatus( + std::function _onGetConsensusStatus) override; + + void notifyConnectedNodes(bcos::crypto::NodeIDSet const& _connectedNodes, + std::function _onResponse) override; + +protected: + bcostars::PBFTServicePrx m_proxy; +}; + +class BlockSyncServiceClient : virtual public bcos::sync::BlockSyncInterface, + public PBFTServiceClient +{ +public: + using Ptr = std::shared_ptr; + BlockSyncServiceClient(bcostars::PBFTServicePrx _proxy) : PBFTServiceClient(_proxy) {} + ~BlockSyncServiceClient() override {} + + // called by the consensus module when commit a new block + void asyncNotifyNewBlock( + bcos::ledger::LedgerConfig::Ptr, std::function) override + { + throw std::runtime_error( + "BlockSyncServiceClient: asyncNotifyNewBlock: unimplemented interface!"); + } + + // called by the consensus module to notify the consensusing block number + void asyncNotifyCommittedIndex( + bcos::protocol::BlockNumber, std::function) override + { + throw std::runtime_error( + "BlockSyncServiceClient: asyncNotifyCommittedIndex: unimplemented interface!"); + } + + bool faultyNode(bcos::crypto::NodeIDPtr _nodeID) override + { + throw std::runtime_error("BlockSyncServiceClient: faultyNode: unimplemented interface!"); + } + + // called by the RPC to get the sync status + void asyncGetSyncInfo( + std::function _onGetSyncInfo) 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, + std::function _onRecv) override + { + auto nodeIDData = _nodeID->data(); + m_proxy->async_asyncNotifyBlockSyncMessage(new PBFTServiceCommonCallback(_onRecv), _uuid, + std::vector(nodeIDData.begin(), nodeIDData.end()), + std::vector(_data.begin(), _data.end())); + } + + void notifyConnectedNodes(bcos::crypto::NodeIDSet const& _connectedNodes, + std::function _onRecvResponse) override; + +protected: + void start() override {} + void stop() override {} +}; +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/RpcServiceClient.cpp" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/RpcServiceClient.cpp" new file mode 100644 index 00000000..40fce5ea --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/RpcServiceClient.cpp" @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2021 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 RPCServiceClient.cpp + * @author: ancelmo + * @date 2021-04-20 + */ +#include "RpcServiceClient.h" + +std::atomic bcostars::RpcServiceClient::s_tarsTimeoutCount = {0}; +const int64_t bcostars::RpcServiceClient::c_maxTarsTimeoutCount = 500; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/RpcServiceClient.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/RpcServiceClient.h" new file mode 100644 index 00000000..7e645802 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/RpcServiceClient.h" @@ -0,0 +1,251 @@ +/** + * Copyright (C) 2021 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 RPCServiceClient.h + * @author: ancelmo + * @date 2021-04-20 + */ + +#pragma once + +#include "fisco-bcos-tars-service/Common/TarsUtils.h" +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include "../Common.h" +#include "../ErrorConverter.h" +#include "../protocol/TransactionSubmitResultImpl.h" +#include +#include +#include +#include +#include + +namespace bcostars +{ +class RpcServiceClient : public bcos::rpc::RPCInterface +{ +public: + RpcServiceClient(bcostars::RpcServicePrx _prx, std::string const& _rpcServiceName) + : m_prx(_prx), m_rpcServiceName(_rpcServiceName) + {} + ~RpcServiceClient() override {} + + class Callback : public RpcServicePrxCallback + { + public: + explicit Callback(std::function _callback) + : RpcServicePrxCallback(), m_callback(_callback) + {} + ~Callback() override {} + + void callback_asyncNotifyBlockNumber(const bcostars::Error& ret) override + { + s_tarsTimeoutCount.store(0); + m_callback(toBcosError(ret)); + } + void callback_asyncNotifyBlockNumber_exception(tars::Int32 ret) override + { + s_tarsTimeoutCount++; + m_callback(toBcosError(ret)); + } + + private: + std::function m_callback; + }; + + void asyncNotifyBlockNumber(std::string const& _groupID, std::string const& _nodeName, + bcos::protocol::BlockNumber _blockNumber, + std::function _callback) override + { + auto shouldBlockCall = shouldStopCall(); + auto ret = checkConnection( + c_moduleName, "asyncNotifyBlockNumber", m_prx, [_callback](bcos::Error::Ptr _error) { + if (_callback) + { + _callback(_error); + } + }); + if (!ret && shouldBlockCall) + { + return; + } + + auto activeEndPoints = tarsProxyAvailableEndPoints(m_prx); + // notify block number to all rpc nodes + for (auto const& endPoint : activeEndPoints) + { + auto prx = bcostars::createServantProxy(m_rpcServiceName, endPoint); + prx->async_asyncNotifyBlockNumber( + new Callback(_callback), _groupID, _nodeName, _blockNumber); + } + } + + void asyncNotifyGroupInfo(bcos::group::GroupInfo::Ptr _groupInfo, + std::function _callback) override + { + class Callback : public bcostars::RpcServicePrxCallback + { + public: + Callback(std::function callback) : m_callback(callback) {} + + void callback_asyncNotifyGroupInfo(const bcostars::Error& ret) override + { + s_tarsTimeoutCount.store(0); + m_callback(toBcosError(ret)); + } + void callback_asyncNotifyGroupInfo_exception(tars::Int32 ret) override + { + s_tarsTimeoutCount++; + m_callback(toBcosError(ret)); + } + + private: + std::function m_callback; + }; + auto shouldBlockCall = shouldStopCall(); + auto ret = checkConnection( + c_moduleName, "asyncNotifyGroupInfo", m_prx, + [_callback](bcos::Error::Ptr _error) { + if (_callback) + { + _callback(std::move(_error)); + } + }, + shouldBlockCall); + if (!ret && shouldBlockCall) + { + return; + } + + auto activeEndPoints = tarsProxyAvailableEndPoints(m_prx); + auto tarsGroupInfo = toTarsGroupInfo(_groupInfo); + + for (auto const& endPoint : activeEndPoints) + { + auto prx = bcostars::createServantProxy(m_rpcServiceName, endPoint); + prx->async_asyncNotifyGroupInfo(new Callback(_callback), tarsGroupInfo); + } + } + + void asyncNotifyAMOPMessage(int16_t _type, std::string const& _topic, bcos::bytesConstRef _data, + std::function _callback) + override + { + class Callback : public bcostars::RpcServicePrxCallback + { + public: + Callback(std::function callback) + : m_callback(callback) + {} + + void callback_asyncNotifyAMOPMessage( + const bcostars::Error& ret, const vector& _responseData) override + { + s_tarsTimeoutCount.store(0); + auto responseData = + std::make_shared(_responseData.begin(), _responseData.end()); + m_callback(toBcosError(ret), responseData); + } + void callback_asyncNotifyAMOPMessage_exception(tars::Int32 ret) override + { + s_tarsTimeoutCount++; + m_callback(toBcosError(ret), nullptr); + } + + private: + std::function m_callback; + }; + auto shouldBlockCall = shouldStopCall(); + auto ret = checkConnection( + c_moduleName, "asyncNotifyAMOPMessage", m_prx, + [_callback](bcos::Error::Ptr _error) { + if (_callback) + { + _callback(std::move(_error), nullptr); + } + }, + shouldBlockCall); + if (!ret && shouldBlockCall) + { + return; + } + vector request(_data.begin(), _data.end()); + m_prx->tars_set_timeout(c_amopTimeout) + ->async_asyncNotifyAMOPMessage(new Callback(_callback), _type, _topic, request); + } + + void asyncNotifySubscribeTopic( + std::function _callback) override + { + class Callback : public bcostars::RpcServicePrxCallback + { + public: + Callback(std::function callback) + : m_callback(callback) + {} + + void callback_asyncNotifySubscribeTopic( + const bcostars::Error& ret, std::string const& _topicInfo) override + { + s_tarsTimeoutCount.store(0); + m_callback(toBcosError(ret), _topicInfo); + } + void callback_asyncNotifySubscribeTopic_exception(tars::Int32 ret) override + { + s_tarsTimeoutCount++; + m_callback(toBcosError(ret), ""); + } + + private: + std::function m_callback; + }; + auto shouldBlockCall = shouldStopCall(); + auto ret = checkConnection( + c_moduleName, "asyncNotifySubscribeTopic", m_prx, + [_callback](bcos::Error::Ptr _error) { + if (_callback) + { + _callback(std::move(_error), ""); + } + }, + shouldBlockCall); + if (!ret && shouldBlockCall) + { + return; + } + m_prx->async_asyncNotifySubscribeTopic(new Callback(_callback)); + } + + bcostars::RpcServicePrx prx() { return m_prx; } + +protected: + void start() override {} + void stop() override {} + + static bool shouldStopCall() { return (s_tarsTimeoutCount >= c_maxTarsTimeoutCount); } + +private: + bcostars::RpcServicePrx m_prx; + std::string m_rpcServiceName; + std::string const c_moduleName = "RpcServiceClient"; + // AMOP timeout 40s + const int c_amopTimeout = 40000; + + static std::atomic s_tarsTimeoutCount; + static const int64_t c_maxTarsTimeoutCount; +}; + +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/SchedulerServiceClient.cpp" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/SchedulerServiceClient.cpp" new file mode 100644 index 00000000..a9951f09 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/SchedulerServiceClient.cpp" @@ -0,0 +1,246 @@ +/** + * Copyright (C) 2021 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 SchedulerServiceClient.cpp + * @author: yujiechen + * @date 2021-10-17 + */ +#include "SchedulerServiceClient.h" +#include "../Common.h" +#include "../ErrorConverter.h" +#include "../protocol/BlockImpl.h" +#include "../protocol/TransactionImpl.h" +#include "../protocol/TransactionReceiptImpl.h" + +using namespace bcostars; + +void SchedulerServiceClient::call(bcos::protocol::Transaction::Ptr _tx, + std::function _callback) +{ + class Callback : public SchedulerServicePrxCallback + { + public: + Callback(std::function + _callback, + bcos::crypto::CryptoSuite::Ptr _cryptoSuite) + : m_callback(_callback), m_cryptoSuite(_cryptoSuite) + {} + ~Callback() override {} + + void callback_call( + const bcostars::Error& ret, const bcostars::TransactionReceipt& _receipt) override + { + auto bcosReceipt = std::make_shared( + m_cryptoSuite, [m_receipt = std::move(_receipt)]() mutable { return &m_receipt; }); + m_callback(toBcosError(ret), bcosReceipt); + } + + void callback_call_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), nullptr); + } + + private: + std::function + m_callback; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + }; + auto tarsTx = std::dynamic_pointer_cast(_tx); + m_prx->async_call(new Callback(_callback, m_cryptoSuite), tarsTx->inner()); +} + +void SchedulerServiceClient::getCode( + std::string_view contract, std::function callback) +{ + class Callback : public SchedulerServicePrxCallback + { + public: + Callback(std::function callback) + : m_callback(std::move(callback)) + {} + ~Callback() override {} + + void callback_getCode(const bcostars::Error& ret, const vector& code) override + { + bcos::bytes outCode(code.begin(), code.end()); + + m_callback(toBcosError(ret), std::move(outCode)); + } + + void callback_getCode_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), {}); + } + + private: + std::function m_callback; + }; + + m_prx->async_getCode(new Callback(std::move(callback)), std::string(contract)); +} + +void SchedulerServiceClient::getABI( + std::string_view contract, std::function callback) +{ + class Callback : public SchedulerServicePrxCallback + { + public: + Callback(std::function callback) + : m_callback(std::move(callback)) + {} + ~Callback() override {} + + void callback_getABI(const bcostars::Error& ret, const std::string& abi) override + { + std::string outCode(abi.begin(), abi.end()); + + m_callback(toBcosError(ret), std::move(outCode)); + } + + void callback_getABI_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), {}); + } + + private: + std::function m_callback; + }; + + m_prx->async_getABI(new Callback(std::move(callback)), std::string(contract)); +} + + +void SchedulerServiceClient::executeBlock(bcos::protocol::Block::Ptr _block, bool _verify, + std::function _callback) +{ + class Callback : public SchedulerServicePrxCallback + { + public: + Callback(std::function + _callback, + bcos::crypto::CryptoSuite::Ptr _cryptoSuite) + : m_callback(std::move(_callback)), m_cryptoSuite(_cryptoSuite) + {} + ~Callback() override {} + + void callback_executeBlock(const bcostars::Error& ret, + const bcostars::BlockHeader& _executedHeader, tars::Bool _sysBlock) override + { + auto bcosBlockHeader = std::make_shared( + m_cryptoSuite, [m_header = _executedHeader]() mutable { return &m_header; }); + m_callback(toBcosError(ret), std::move(bcosBlockHeader), _sysBlock); + } + + void callback_executeBlock_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), nullptr, false); + } + + private: + std::function + m_callback; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + }; + auto tarsBlock = std::dynamic_pointer_cast(_block); + m_prx->async_executeBlock( + new Callback(std::move(_callback), m_cryptoSuite), tarsBlock->inner(), _verify); +} + +void SchedulerServiceClient::commitBlock(bcos::protocol::BlockHeader::Ptr _blockHeader, + std::function _callback) +{ + class Callback : public SchedulerServicePrxCallback + { + public: + Callback( + std::function&& _callback, + bcos::crypto::CryptoSuite::Ptr _cryptoSuite) + : m_callback(std::move(_callback)), m_cryptoSuite(_cryptoSuite) + {} + ~Callback() override {} + + void callback_commitBlock( + const bcostars::Error& ret, const bcostars::LedgerConfig& _ledgerConfig) override + { + m_callback( + toBcosError(ret), toLedgerConfig(_ledgerConfig, m_cryptoSuite->keyFactory())); + } + + void callback_commitBlock_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), nullptr); + } + + private: + std::function m_callback; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + }; + auto tarsBlockHeader = + std::dynamic_pointer_cast(_blockHeader); + m_prx->async_commitBlock( + new Callback(std::move(_callback), m_cryptoSuite), tarsBlockHeader->inner()); +} + +void SchedulerServiceClient::registerExecutor(std::string _name, + bcos::executor::ParallelTransactionExecutorInterface::Ptr, + std::function _callback) +{ + class Callback : public SchedulerServicePrxCallback + { + public: + Callback(std::function _callback) : m_callback(_callback) {} + ~Callback() override {} + + void callback_registerExecutor(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + + void callback_registerExecutor_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + + private: + std::function m_callback; + }; + m_prx->async_registerExecutor(new Callback(_callback), _name); +} + +void SchedulerServiceClient::preExecuteBlock( + bcos::protocol::Block::Ptr block, bool verify, std::function callback) +{ + class Callback : public SchedulerServicePrxCallback + { + public: + Callback(std::function _callback) : m_callback(_callback) {} + ~Callback() override {} + + void callback_registerExecutor(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + + void callback_registerExecutor_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + + private: + std::function m_callback; + }; + auto tarsBlock = std::dynamic_pointer_cast(block); + m_prx->async_preExecuteBlock(new Callback(callback), tarsBlock->inner(), verify); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/SchedulerServiceClient.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/SchedulerServiceClient.h" new file mode 100644 index 00000000..c997a066 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/SchedulerServiceClient.h" @@ -0,0 +1,84 @@ +/** + * Copyright (C) 2021 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 SchedulerServiceClient.h + * @author: yujiechen + * @date 2021-10-17 + */ +#pragma once +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include +#include +#include +#include + +namespace bcostars +{ +class SchedulerServiceClient : public bcos::scheduler::SchedulerInterface +{ +public: + SchedulerServiceClient(SchedulerServicePrx _prx, bcos::crypto::CryptoSuite::Ptr _cryptoSuite) + : m_prx(_prx), m_cryptoSuite(_cryptoSuite) + {} + ~SchedulerServiceClient() override {} + + void call(bcos::protocol::Transaction::Ptr tx, + std::function) + override; + + void executeBlock(bcos::protocol::Block::Ptr _block, bool _verify, + std::function _callback) + override; + + void commitBlock(bcos::protocol::BlockHeader::Ptr _blockHeader, + std::function _callback) + override; + + void getCode(std::string_view contract, + std::function callback) override; + + void getABI(std::string_view contract, + std::function callback) override; + + void preExecuteBlock(bcos::protocol::Block::Ptr block, bool verify, + std::function callback) override; + + void status( + std::function) override + { + BCOS_LOG(ERROR) << LOG_DESC("unimplemented method status"); + } + + + void registerExecutor(std::string, bcos::executor::ParallelTransactionExecutorInterface::Ptr, + std::function) override; + + void unregisterExecutor(const std::string&, std::function) override + { + BCOS_LOG(ERROR) << LOG_DESC("unimplemented method unregisterExecutor"); + } + + void reset(std::function) override + { + BCOS_LOG(ERROR) << LOG_DESC("unimplemented method reset"); + } + +private: + SchedulerServicePrx m_prx; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; +}; +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/TxPoolServiceClient.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/TxPoolServiceClient.h" new file mode 100644 index 00000000..2da85286 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/client/TxPoolServiceClient.h" @@ -0,0 +1,500 @@ +#pragma once + +#include +#include +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include "bcos-tars-protocol/ErrorConverter.h" +#include "bcos-tars-protocol/protocol/BlockImpl.h" +#include "bcos-tars-protocol/protocol/TransactionImpl.h" +#include "bcos-tars-protocol/protocol/TransactionSubmitResultImpl.h" +#include "bcos-tars-protocol/tars/Transaction.h" +#include "bcos-tars-protocol/tars/TransactionSubmitResult.h" +#include "bcos-tars-protocol/tars/TxPoolService.h" +#include +#include +#include +#include +#include + +namespace bcostars +{ +class TxPoolServiceClient : public bcos::txpool::TxPoolInterface +{ +public: + TxPoolServiceClient(bcostars::TxPoolServicePrx _proxy, + bcos::crypto::CryptoSuite::Ptr _cryptoSuite, + bcos::protocol::BlockFactory::Ptr _blockFactory) + : m_proxy(_proxy), m_cryptoSuite(_cryptoSuite), m_blockFactory(_blockFactory) + {} + ~TxPoolServiceClient() override {} + + bcos::task::Task submitTransaction( + bcos::protocol::Transaction::Ptr transaction) override + { + struct TarsCallback : public bcostars::TxPoolServicePrxCallback + { + void callback_submit(const bcostars::Error& ret, + const bcostars::TransactionSubmitResult& result) override + { + auto bcosResult = std::make_shared( + std::move(m_cryptoSuite), + [inner = std::move(const_cast( + result))]() mutable { return &inner; }); + m_submitResult = std::move(bcosResult); + m_handle.resume(); + } + void callback_submit_exception(tars::Int32 ret) override + { + m_submitResult = toBcosError(ret); + m_handle.resume(); + } + + CO_STD::coroutine_handle<> m_handle; + std::variant + m_submitResult; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + }; + + struct Awaitable + { + constexpr bool await_ready() { return false; } + void await_suspend(CO_STD::coroutine_handle<> handle) + { + m_callback->m_handle = handle; + m_proxy->tars_set_timeout(600000)->async_submit(m_callback, + dynamic_cast(*m_transaction) + .inner()); // tars take the m_callback ownership + } + bcostars::protocol::TransactionSubmitResultImpl::Ptr await_resume() + { + if (std::holds_alternative(m_callback->m_submitResult)) + { + BOOST_THROW_EXCEPTION(*std::get(m_callback->m_submitResult)); + } + + if (!std::holds_alternative( + m_callback->m_submitResult)) + { + BOOST_THROW_EXCEPTION(BCOS_ERROR(-1, "No value!")); + } + + return std::move(std::get( + m_callback->m_submitResult)); + } + + TarsCallback* m_callback = nullptr; + bcos::protocol::Transaction::Ptr m_transaction; + bcostars::TxPoolServicePrx m_proxy; + }; + + auto tarsCallback = std::make_unique(); + tarsCallback->m_cryptoSuite = m_cryptoSuite; + + auto awaitable = Awaitable{.m_callback = tarsCallback.release(), + .m_transaction = std::move(transaction), + .m_proxy = m_proxy}; + + co_return co_await awaitable; + } + + void asyncSealTxs(uint64_t _txsLimit, bcos::txpool::TxsHashSetPtr _avoidTxs, + std::function + _sealCallback) override + { + class Callback : public bcostars::TxPoolServicePrxCallback + { + public: + Callback(bcos::protocol::BlockFactory::Ptr _blockFactory, + std::function + callback) + : m_blockFactory(_blockFactory), m_callback(callback) + {} + + void callback_asyncSealTxs(const bcostars::Error& ret, const bcostars::Block& _txsList, + const bcostars::Block& _sysTxList) override + { + auto txsList = m_blockFactory->createBlock(); + std::dynamic_pointer_cast(txsList)->setInner( + std::move(*const_cast(&_txsList))); + + auto sysTxList = m_blockFactory->createBlock(); + std::dynamic_pointer_cast(sysTxList)->setInner( + std::move(*const_cast(&_sysTxList))); + + m_callback(toBcosError(ret), txsList, sysTxList); + } + + void callback_asyncSealTxs_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), nullptr, nullptr); + } + + private: + bcos::protocol::BlockFactory::Ptr m_blockFactory; + std::function + m_callback; + }; + + vector> tarsAvoidTxs; + if (_avoidTxs && _avoidTxs->size() > 0) + { + for (auto& it : *_avoidTxs) + { + tarsAvoidTxs.push_back(std::vector(it.begin(), it.end())); + } + } + + m_proxy->async_asyncSealTxs( + new Callback(m_blockFactory, _sealCallback), _txsLimit, tarsAvoidTxs); + } + + void asyncMarkTxs(bcos::crypto::HashListPtr _txsHash, bool _sealedFlag, + bcos::protocol::BlockNumber _batchId, bcos::crypto::HashType const& _batchHash, + std::function _onRecvResponse) override + { + class Callback : public bcostars::TxPoolServicePrxCallback + { + public: + Callback(std::function callback) : m_callback(callback) {} + + void callback_asyncMarkTxs(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + + void callback_asyncMarkTxs_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + + private: + std::function m_callback; + }; + + vector> txHashList; + for (auto& it : *_txsHash) + { + txHashList.push_back(std::vector(it.begin(), it.end())); + } + + m_proxy->async_asyncMarkTxs(new Callback(_onRecvResponse), txHashList, _sealedFlag, + _batchId, std::vector(_batchHash.begin(), _batchHash.end())); + } + + void asyncVerifyBlock(bcos::crypto::PublicPtr _generatedNodeID, + bcos::bytesConstRef const& _block, + std::function _onVerifyFinished) override + { + class Callback : public bcostars::TxPoolServicePrxCallback + { + public: + Callback(std::function callback) : m_callback(callback) {} + + void callback_asyncVerifyBlock(const bcostars::Error& ret, tars::Bool result) override + { + m_callback(toBcosError(ret), result); + } + + void callback_asyncVerifyBlock_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), false); + } + + private: + std::function m_callback; + }; + + auto nodeID = _generatedNodeID->data(); + m_proxy->async_asyncVerifyBlock(new Callback(_onVerifyFinished), + std::vector(nodeID.begin(), nodeID.end()), + std::vector(_block.begin(), _block.end())); + } + + void asyncFillBlock(bcos::crypto::HashListPtr _txsHash, + std::function _onBlockFilled) + override + { + class Callback : public bcostars::TxPoolServicePrxCallback + { + public: + Callback( + std::function callback, + bcos::crypto::CryptoSuite::Ptr cryptoSuite) + : m_callback(callback), m_cryptoSuite(cryptoSuite) + {} + + void callback_asyncFillBlock( + const bcostars::Error& ret, const vector& filled) override + { + auto mutableFilled = const_cast*>(&filled); + auto txs = std::make_shared(); + for (auto&& it : *mutableFilled) + { + auto tx = std::make_shared( + m_cryptoSuite, [m_tx = std::move(it)]() mutable { return &m_tx; }); + txs->push_back(tx); + } + m_callback(toBcosError(ret), txs); + } + + void callback_asyncFillBlock_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), nullptr); + } + + private: + std::function m_callback; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + }; + + vector> hashList; + for (auto hashData : *_txsHash) + { + hashList.emplace_back(hashData.begin(), hashData.end()); + } + + m_proxy->async_asyncFillBlock(new Callback(_onBlockFilled, m_cryptoSuite), hashList); + } + + void asyncNotifyBlockResult(bcos::protocol::BlockNumber _blockNumber, + bcos::protocol::TransactionSubmitResultsPtr _txsResult, + std::function _onNotifyFinished) override + { + class Callback : public bcostars::TxPoolServicePrxCallback + { + public: + Callback(std::function callback) : m_callback(callback) {} + + void callback_asyncNotifyBlockResult(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + + void callback_asyncNotifyBlockResult_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + + private: + std::function m_callback; + }; + + vector resultList; + for (auto& it : *_txsResult) + { + resultList.emplace_back( + std::dynamic_pointer_cast(it) + ->inner()); + } + + m_proxy->async_asyncNotifyBlockResult( + new Callback(_onNotifyFinished), _blockNumber, resultList); + } + + void asyncNotifyTxsSyncMessage(bcos::Error::Ptr _error, std::string const& _id, + bcos::crypto::NodeIDPtr _nodeID, bcos::bytesConstRef _data, + std::function _onRecv) override + { + class Callback : public bcostars::TxPoolServicePrxCallback + { + public: + Callback(std::function callback) : m_callback(callback) + {} + + void callback_asyncNotifyTxsSyncMessage(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + + void callback_asyncNotifyTxsSyncMessage_exception(tars::Int32 ret) override + { + bcos::bytes empty; + m_callback(toBcosError(ret)); + } + + private: + std::function m_callback; + }; + + auto nodeID = _nodeID->data(); + m_proxy->async_asyncNotifyTxsSyncMessage(new Callback(_onRecv), toTarsError(_error), _id, + std::vector(nodeID.begin(), nodeID.end()), + std::vector(_data.begin(), _data.end())); + } + + void notifyConnectedNodes(bcos::crypto::NodeIDSet const& _connectedNodes, + std::function _onRecvResponse) override + { + class Callback : public bcostars::TxPoolServicePrxCallback + { + public: + Callback(std::function callback) : m_callback(callback) + {} + + void callback_notifyConnectedNodes(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + + void callback_notifyConnectedNodes_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + + private: + std::function m_callback; + }; + + std::vector> tarsConnectedNodes; + for (auto const& it : _connectedNodes) + { + auto nodeID = it->data(); + tarsConnectedNodes.emplace_back(nodeID.begin(), nodeID.end()); + } + m_proxy->async_notifyConnectedNodes(new Callback(_onRecvResponse), tarsConnectedNodes); + } + + void notifyConsensusNodeList(bcos::consensus::ConsensusNodeList const& _consensusNodeList, + std::function _onRecvResponse) override + { + class Callback : public bcostars::TxPoolServicePrxCallback + { + public: + Callback(std::function callback) : m_callback(callback) + {} + + void callback_notifyConsensusNodeList(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + + void callback_notifyConsensusNodeList_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + + private: + std::function m_callback; + }; + + std::vector tarsConsensusNodeList; + for (auto const& it : _consensusNodeList) + { + bcostars::ConsensusNode node; + + auto nodeID = it->nodeID()->data(); + node.nodeID.assign(nodeID.begin(), nodeID.end()); + node.weight = it->weight(); + tarsConsensusNodeList.emplace_back(node); + } + + m_proxy->async_notifyConsensusNodeList( + new Callback(_onRecvResponse), tarsConsensusNodeList); + } + + void notifyObserverNodeList(bcos::consensus::ConsensusNodeList const& _observerNodeList, + std::function _onRecvResponse) override + { + class Callback : public bcostars::TxPoolServicePrxCallback + { + public: + Callback(std::function callback) : m_callback(callback) + {} + + void callback_notifyObserverNodeList(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + + void callback_notifyObserverNodeList_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + + private: + std::function m_callback; + }; + + std::vector tarsConsensusNodeList; + for (auto const& it : _observerNodeList) + { + bcostars::ConsensusNode node; + auto nodeID = it->nodeID()->data(); + node.nodeID.assign(nodeID.begin(), nodeID.end()); + node.weight = it->weight(); + tarsConsensusNodeList.emplace_back(node); + } + + m_proxy->async_notifyObserverNodeList(new Callback(_onRecvResponse), tarsConsensusNodeList); + } + + // for RPC to get pending transactions + void asyncGetPendingTransactionSize( + std::function _onGetTxsSize) override + { + class Callback : public TxPoolServicePrxCallback + { + public: + explicit Callback(std::function _callback) + : TxPoolServicePrxCallback(), m_callback(_callback) + {} + ~Callback() override {} + + void callback_asyncGetPendingTransactionSize( + const bcostars::Error& ret, tars::Int64 _txsSize) override + { + m_callback(toBcosError(ret), _txsSize); + } + void callback_asyncGetPendingTransactionSize_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret), 0); + } + + private: + std::function m_callback; + }; + m_proxy->async_asyncGetPendingTransactionSize(new Callback(_onGetTxsSize)); + } + + void asyncResetTxPool(std::function _onRecv) override + { + class Callback : public TxPoolServicePrxCallback + { + public: + explicit Callback(std::function _callback) + : TxPoolServicePrxCallback(), m_callback(_callback) + {} + ~Callback() override {} + + void callback_asyncResetTxPool(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + void callback_asyncResetTxPool_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + + private: + std::function m_callback; + }; + m_proxy->async_asyncResetTxPool(new Callback(_onRecv)); + } + +protected: + void start() override {} + void stop() override {} + +private: + bcostars::TxPoolServicePrx m_proxy; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + bcos::protocol::BlockFactory::Ptr m_blockFactory; +}; + +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/doc/README_CN.md" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/doc/README_CN.md" new file mode 100644 index 00000000..f9eadc78 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/doc/README_CN.md" @@ -0,0 +1,29 @@ +![](https://github.com/FISCO-BCOS/FISCO-BCOS/raw/master/docs/images/FISCO_BCOS_Logo.svg?sanitize=true) + +[English](../README.md) / 中文 + +# 基于Tars的数据编码协议 + +[![codecov](https://codecov.io/gh/FISCO-BCOS/bcos-tars-protocol/branch/master/graph/badge.svg)](https://codecov.io/gh/FISCO-BCOS/bcos-tars-protocol) +[![GitHub All Releases](https://img.shields.io/github/downloads/FISCO-BCOS/bcos-tars-protocol/total.svg)](https://github.com/FISCO-BCOS/bcos-tars-protocol) +[![Code Lines](https://tokei.rs/b1/github/FISCO-BCOS/bcos-tars-protocol?category=code)](https://github.com/FISCO-BCOS/bcos-tars-protocol) +[![version](https://img.shields.io/github/tag/FISCO-BCOS/bcos-tars-protocol.svg)](https://github.com/FISCO-BCOS/bcos-tars-protocol/releases/latest) + +bcos-tars-protocol基于[Tars](https://newdoc.tarsyun.com/#/markdown/TarsCloud/TarsDocs/README.md)编码协议实现了[FISCO BCOS 3.x](https://github.com/FISCO-BCOS/FISCO-BCOS)的基础数据协议,包括交易、交易回执、区块、区块头、区块执行结果等。 + +## 文档 + +- [FISCO BCOS 3.x文档](https://fisco-bcos-doc.readthedocs.io/) + +## 加入社区 + +FISCO BCOS开源社区是国内活跃的开源社区,社区长期为机构和个人开发者提供各类支持与帮助。已有来自各行业的数千名技术爱好者在研究和使用FISCO BCOS。如您对FISCO BCOS开源技术及应用感兴趣,欢迎加入社区获得更多支持与帮助。 + +![](https://raw.githubusercontent.com/FISCO-BCOS/LargeFiles/master/images/QR_image.png) + + +## License + +[![](https://img.shields.io/github/license/FISCO-BCOS/bcos-tars-protocol.svg)](../LICENSE) + +bcos-tars-protocol的开源协议为Apache License, 详情参考[LICENSE](../LICENSE). \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsHashable.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsHashable.h" new file mode 100644 index 00000000..8944482a --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsHashable.h" @@ -0,0 +1,140 @@ +#pragma once +#include "../Common.h" +#include "bcos-tars-protocol/tars/TransactionReceipt.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::concepts::hash +{ + +template +void impl_calculate( + bcostars::Transaction const& transaction, bcos::concepts::bytebuffer::ByteBuffer auto& out) +{ + if (!transaction.dataHash.empty()) + { + bcos::concepts::bytebuffer::assignTo(transaction.dataHash, out); + return; + } + + Hasher hasher; + auto const& hashFields = transaction.data; + + int32_t version = boost::endian::native_to_big((int32_t)hashFields.version); + hasher.update(version); + hasher.update(hashFields.chainID); + hasher.update(hashFields.groupID); + int64_t blockLimit = boost::endian::native_to_big((int64_t)hashFields.blockLimit); + hasher.update(blockLimit); + hasher.update(hashFields.nonce); + hasher.update(hashFields.to); + hasher.update(hashFields.input); + hasher.update(hashFields.abi); + + hasher.final(out); +} + +template +void impl_calculate( + bcostars::TransactionReceipt const& receipt, bcos::concepts::bytebuffer::ByteBuffer auto& out) +{ + if (!receipt.dataHash.empty()) + { + bcos::concepts::bytebuffer::assignTo(receipt.dataHash, out); + return; + } + + Hasher hasher; + auto const& hashFields = receipt.data; + int32_t version = boost::endian::native_to_big((int32_t)hashFields.version); + hasher.update(version); + hasher.update(hashFields.gasUsed); + hasher.update(hashFields.contractAddress); + int32_t status = boost::endian::native_to_big((int32_t)hashFields.status); + hasher.update(status); + hasher.update(hashFields.output); + // vector logEntries: 6 + for (auto const& log : hashFields.logEntries) + { + hasher.update(log.address); + for (auto const& topicItem : log.topic) + { + hasher.update(topicItem); + } + hasher.update(log.data); + } + int64_t blockNumber = boost::endian::native_to_big((int64_t)hashFields.blockNumber); + hasher.update(blockNumber); + hasher.final(out); +} + +template +auto impl_calculate( + bcostars::BlockHeader const& blockHeader, bcos::concepts::bytebuffer::ByteBuffer auto& out) +{ + if (!blockHeader.dataHash.empty()) + { + bcos::concepts::bytebuffer::assignTo(blockHeader.dataHash, out); + return; + } + + Hasher hasher; + auto const& hashFields = blockHeader.data; + + int32_t version = boost::endian::native_to_big((int32_t)hashFields.version); + hasher.update(version); + for (auto const& parent : hashFields.parentInfo) + { + int64_t blockNumber = boost::endian::native_to_big((int64_t)parent.blockNumber); + hasher.update(blockNumber); + hasher.update(parent.blockHash); + } + hasher.update(hashFields.txsRoot); + hasher.update(hashFields.receiptRoot); + hasher.update(hashFields.stateRoot); + + int64_t number = boost::endian::native_to_big((int64_t)hashFields.blockNumber); + hasher.update(number); + hasher.update(hashFields.gasUsed); + + int64_t timestamp = boost::endian::native_to_big((int64_t)hashFields.timestamp); + hasher.update(timestamp); + + int64_t sealer = boost::endian::native_to_big((int64_t)hashFields.sealer); + hasher.update(sealer); + + for (auto const& nodeID : hashFields.sealerList) + { + hasher.update(nodeID); + } + // update extraData to hashBuffer: 12 + hasher.update(hashFields.extraData); + // update consensusWeights to hashBuffer: 13 + for (auto weight : hashFields.consensusWeights) + { + int64_t networkWeight = boost::endian::native_to_big((int64_t)weight); + hasher.update(networkWeight); + } + + hasher.final(out); +} + +template +auto impl_calculate(bcostars::Block const& block, bcos::concepts::bytebuffer::ByteBuffer auto& out) +{ + if (!block.blockHeader.dataHash.empty()) + { + bcos::concepts::bytebuffer::assignTo(block.blockHeader.dataHash, out); + return; + } + + impl_calculate(block.blockHeader, out); +} + +} // namespace bcos::concepts::hash diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsOutput.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsOutput.h" new file mode 100644 index 00000000..19e3c8ab --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsOutput.h" @@ -0,0 +1,13 @@ +#pragma once + +#include "TarsStruct.h" +#include + +namespace std +{ +ostream& operator<<(ostream& os, bcostars::protocol::impl::TarsStruct auto const& st) +{ + st.displaySimple(os); + return os; +} +} // namespace std \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsSerializable.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsSerializable.h" new file mode 100644 index 00000000..6893445b --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsSerializable.h" @@ -0,0 +1,37 @@ +#pragma once +#include "../Common.h" +#include "TarsStruct.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::concepts::serialize +{ + +void impl_encode(bcostars::protocol::impl::TarsStruct auto const& object, + bcos::concepts::bytebuffer::ByteBuffer auto& out) +{ + using ContainerType = typename std::remove_cvref_t; + using WriterType = bcostars::protocol::BufferWriter; + + tars::TarsOutputStream output; + object.writeTo(output); + + output.getByteBuffer().swap(out); +} + +void impl_decode(bcos::concepts::bytebuffer::ByteBuffer auto const& in, + bcostars::protocol::impl::TarsStruct auto& out) +{ + tars::TarsInputStream input; + input.setBuffer((const char*)RANGES::data(in), RANGES::size(in)); + + out.readFrom(input); +} + +} // namespace bcos::concepts::serialize \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsServantProxyCallback.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsServantProxyCallback.h" new file mode 100644 index 00000000..0c137ff2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsServantProxyCallback.h" @@ -0,0 +1,216 @@ +/** + * Copyright (C) 2021 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. + * + * @brief tars implementation for tars ServantProxyCallback + * @file TarsServantProxyCallback.h + * @author: octopuswang + * @date 2022-07-24 + */ +#pragma once + +#include "bcos-utilities/BoostLog.h" +#include "bcos-utilities/Error.h" +#include "bcos-utilities/Timer.h" +#include "servant/ServantProxy.h" +#include +#include +#include +#include +#include +#include +#include + +#define TARS_PING_PERIOD (3000) + +namespace bcostars +{ +struct TC_EndpointCompare +{ + bool operator()(const tars::TC_Endpoint& lhs, const tars::TC_Endpoint& rhs) const + { + return lhs.toString() < rhs.toString(); + } +}; + +using EndpointSet = std::set; + +using TarsServantProxyOnConnectHandler = std::function; +using TarsServantProxyOnCloseHandler = std::function; + +class TarsServantProxyCallback : public tars::ServantProxyCallback + +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + +public: + TarsServantProxyCallback(const std::string& _serviceName) : m_serviceName(_serviceName) + { + BCOS_LOG(INFO) << LOG_BADGE("[NEWOBJ][TarsServantProxyCallback]") + << LOG_KV("_serviceName", _serviceName) << LOG_KV("this", this); + } + + TarsServantProxyCallback(TarsServantProxyCallback&&) = delete; + TarsServantProxyCallback(const TarsServantProxyCallback&) = delete; + const TarsServantProxyCallback& operator=(const TarsServantProxyCallback&) = delete; + TarsServantProxyCallback& operator=(TarsServantProxyCallback&&) = delete; + + ~TarsServantProxyCallback() override + { + // BCOS_LOG(INFO) << LOG_BADGE("[DELOBJ][TarsServantProxyCallback]") << LOG_KV("this", + // this); + } + +public: + int onDispatch(tars::ReqMessagePtr) override { return 0; } + + void onClose(const tars::TC_Endpoint& ep) override + { + try + { + auto p = addInactiveEndpoint(ep); + BCOS_LOG(INFO) << LOG_DESC("onClose:") << m_serviceName + << LOG_KV("endpoint", ep.toString()) + << LOG_KV("inActiveEndPointSize", p.second); + if (p.first && m_onCloseHandler) + { + m_onCloseHandler(ep); + } + } + catch (const std::exception& e) + { + std::cout << "[TarsServantProxyCallback::onClose] " + << "exception: " << boost::diagnostic_information(e) << std::endl; + } + } + + void onConnect(const tars::TC_Endpoint& ep) override + { + auto p = addActiveEndpoint(ep); + BCOS_LOG(INFO) << LOG_BADGE("ServantProxyCallback::onConnect") << LOG_KV("this", this) + << LOG_KV("serviceName", m_serviceName) << LOG_KV("endpoint", ep.toString()) + << LOG_KV("result", p.first) << LOG_KV("activeEndpoints size", p.second); + + if (p.first && m_onConnectHandler) + { + m_onConnectHandler(ep); + } + } + +public: + const std::string& serviceName() const { return m_serviceName; } + + auto activeEndpoints() + { + std::shared_lock l(x_endpoints); + return m_activeEndpoints; + } + + EndpointSet inactiveEndpoints() + { + std::shared_lock l(x_endpoints); + return m_inactiveEndpoints; + } + + std::pair addActiveEndpoint(const tars::TC_Endpoint& ep) + { + std::unique_lock l(x_endpoints); + auto result = m_activeEndpoints.insert(ep); + m_inactiveEndpoints.erase(ep); + return std::make_pair(result.second, m_activeEndpoints.size()); + } + + std::pair addInactiveEndpoint(const tars::TC_Endpoint& ep) + { + { + std::shared_lock l(x_endpoints); + if (m_inactiveEndpoints.find(ep) != m_inactiveEndpoints.end()) + { + return std::make_pair(false, m_inactiveEndpoints.size()); + } + } + + { + std::unique_lock l(x_endpoints); + auto result = m_inactiveEndpoints.insert(ep); + m_activeEndpoints.erase(ep); + + BCOS_LOG(INFO) << LOG_BADGE("ServantProxyCallback::addInactiveEndpoint") + << LOG_KV("this", this) << LOG_KV("result", result.second) + << LOG_KV("endpoint", ep.toString()); + + return std::make_pair(result.second, m_inactiveEndpoints.size()); + } + } + + TarsServantProxyOnConnectHandler onConnectHandler() const { return m_onConnectHandler; } + TarsServantProxyOnCloseHandler onCloseHandler() const { return m_onCloseHandler; } + + void setOnConnectHandler(TarsServantProxyOnConnectHandler _handler) + { + m_onConnectHandler = _handler; + } + + void setOnCloseHandler(TarsServantProxyOnCloseHandler _handler) { m_onCloseHandler = _handler; } + + bool available() { return !activeEndpoints().empty(); } + +private: + std::string m_serviceName; + + // lock for m_activeEndpoints and m_inactiveEndpoints + mutable std::shared_mutex x_endpoints; + // all active tars endpoints + EndpointSet m_activeEndpoints; + // all inactive tars endpoints + EndpointSet m_inactiveEndpoints; + + std::function m_onConnectHandler; + std::function m_onCloseHandler; +}; + +template +bool checkConnection(std::string const& _module, std::string const& _func, const T& prx, + std::function _errorCallback, bool _callsErrorCallback = true) +{ + auto cb = prx->tars_get_push_callback(); + assert(cb); + auto* tarsServantProxyCallback = (TarsServantProxyCallback*)cb.get(); + + if (tarsServantProxyCallback->available()) + { + return true; + } + + if (_errorCallback && _callsErrorCallback) + { + std::string errorMessage = + _module + " calls interface " + _func + " failed for empty connection"; + _errorCallback(std::make_shared(-1, errorMessage)); + } + return false; +} + +template +auto tarsProxyAvailableEndPoints(const T& prx) +{ + auto cb = prx->tars_get_push_callback(); + assert(cb); + return ((TarsServantProxyCallback*)cb.get())->activeEndpoints(); +} + +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsStruct.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsStruct.h" new file mode 100644 index 00000000..25e1dd6d --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/impl/TarsStruct.h" @@ -0,0 +1,19 @@ +#pragma once +#include +#include + +namespace bcostars::protocol::impl +{ + +template +concept TarsStruct = requires(TarsStructType tarsStruct) +{ + { + tarsStruct.className() + } -> std::same_as; + { + tarsStruct.MD5() + } -> std::same_as; + tarsStruct.resetDefautlt(); +}; +} // namespace bcostars::protocol::impl \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockFactoryImpl.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockFactoryImpl.h" new file mode 100644 index 00000000..95689676 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockFactoryImpl.h" @@ -0,0 +1,105 @@ +/** + * Copyright (C) 2021 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. + * + * @brief tars implementation for BlockFactory + * @file BlockFactoryImpl.h + * @author: ancelmo + * @date 2021-04-20 + */ +#pragma once + +#include "../Common.h" +#include "BlockImpl.h" +#include "bcos-tars-protocol/tars/Block.h" +#include +#include +#include +#include + +namespace bcostars +{ +namespace protocol +{ +class BlockFactoryImpl : public bcos::protocol::BlockFactory +{ +public: + BlockFactoryImpl(bcos::crypto::CryptoSuite::Ptr cryptoSuite, + bcos::protocol::BlockHeaderFactory::Ptr blockHeaderFactory, + bcos::protocol::TransactionFactory::Ptr transactionFactory, + bcos::protocol::TransactionReceiptFactory::Ptr receiptFactory) + : m_cryptoSuite(cryptoSuite), + m_blockHeaderFactory(blockHeaderFactory), + m_transactionFactory(transactionFactory), + m_receiptFactory(receiptFactory){}; + + ~BlockFactoryImpl() override {} + bcos::protocol::Block::Ptr createBlock() override + { + return std::make_shared(m_transactionFactory, m_receiptFactory); + } + + bcos::protocol::Block::Ptr createBlock( + bcos::bytes const& _data, bool _calculateHash = true, bool _checkSig = true) override + { + return createBlock(bcos::ref(_data), _calculateHash, _checkSig); + } + + bcos::protocol::Block::Ptr createBlock( + bcos::bytesConstRef _data, bool _calculateHash = true, bool _checkSig = true) override + { + auto block = std::make_shared(m_transactionFactory, m_receiptFactory); + block->decode(_data, _calculateHash, _checkSig); + + return block; + } + + bcos::crypto::CryptoSuite::Ptr cryptoSuite() override { return m_cryptoSuite; } + bcos::protocol::BlockHeaderFactory::Ptr blockHeaderFactory() override + { + return m_blockHeaderFactory; + } + bcos::protocol::TransactionFactory::Ptr transactionFactory() override + { + return m_transactionFactory; + } + bcos::protocol::TransactionReceiptFactory::Ptr receiptFactory() override + { + return m_receiptFactory; + } + + bcos::protocol::TransactionMetaData::Ptr createTransactionMetaData() override + { + return std::make_shared( + [inner = bcostars::TransactionMetaData()]() mutable { return &inner; }); + } + + bcos::protocol::TransactionMetaData::Ptr createTransactionMetaData( + bcos::crypto::HashType const _hash, std::string const& _to) override + { + auto txMetaData = std::make_shared(); + txMetaData->setHash(_hash); + txMetaData->setTo(_to); + + return txMetaData; + } + +private: + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + bcos::protocol::BlockHeaderFactory::Ptr m_blockHeaderFactory; + bcos::protocol::TransactionFactory::Ptr m_transactionFactory; + bcos::protocol::TransactionReceiptFactory::Ptr m_receiptFactory; +}; +} // namespace protocol +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockHeaderFactoryImpl.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockHeaderFactoryImpl.h" new file mode 100644 index 00000000..9b20a218 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockHeaderFactoryImpl.h" @@ -0,0 +1,64 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for BlockHeaderFactory + * @file BlockHeaderFactoryImpl.h + * @author: ancelmo + * @date 2021-04-20 + */ +#pragma once +#include "BlockHeaderImpl.h" +#include + + +namespace bcostars +{ +namespace protocol +{ +class BlockHeaderFactoryImpl : public bcos::protocol::BlockHeaderFactory +{ +public: + BlockHeaderFactoryImpl(bcos::crypto::CryptoSuite::Ptr cryptoSuite) : m_cryptoSuite(cryptoSuite) + {} + ~BlockHeaderFactoryImpl() override {} + bcos::protocol::BlockHeader::Ptr createBlockHeader() override + { + return std::make_shared( + m_cryptoSuite, [m_header = bcostars::BlockHeader()]() mutable { return &m_header; }); + } + bcos::protocol::BlockHeader::Ptr createBlockHeader(bcos::bytes const& _data) override + { + return createBlockHeader(bcos::ref(_data)); + } + bcos::protocol::BlockHeader::Ptr createBlockHeader(bcos::bytesConstRef _data) override + { + auto blockHeader = createBlockHeader(); + blockHeader->decode(_data); + + return blockHeader; + } + bcos::protocol::BlockHeader::Ptr createBlockHeader(bcos::protocol::BlockNumber _number) override + { + auto blockHeader = createBlockHeader(); + blockHeader->setNumber(_number); + + return blockHeader; + } + +private: + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; +}; +} // namespace protocol +} // namespace bcostars diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockHeaderImpl.cpp" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockHeaderImpl.cpp" new file mode 100644 index 00000000..9dea1579 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockHeaderImpl.cpp" @@ -0,0 +1,172 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for BlockHeader + * @file BlockHeaderImpl.cpp + * @author: ancelmo + * @date 2021-04-20 + */ + +#include "../impl/TarsHashable.h" + +#include "BlockHeaderImpl.h" +#include +#include +#include + +using namespace bcostars; +using namespace bcostars::protocol; +void BlockHeaderImpl::decode(bcos::bytesConstRef _data) +{ + tars::TarsInputStream input; + input.setBuffer((const char*)_data.data(), _data.size()); + + m_inner()->readFrom(input); +} + +void BlockHeaderImpl::encode(bcos::bytes& _encodeData) const +{ + tars::TarsOutputStream output; + + m_inner()->writeTo(output); + output.getByteBuffer().swap(_encodeData); +} + + +bcos::crypto::HashType BlockHeaderImpl::hash() const +{ + bcos::UpgradableGuard l(x_inner); + if (!m_inner()->dataHash.empty()) + { + return *(reinterpret_cast(m_inner()->dataHash.data())); + } + auto hashImpl = m_cryptoSuite->hashImpl(); + auto anyHasher = hashImpl->hasher(); + + bcos::crypto::HashType hashResult; + std::visit( + [this, &hashResult, &l](auto& hasher) { + using Hasher = std::remove_cvref_t; + bcos::concepts::hash::calculate(*m_inner(), hashResult); + + bcos::UpgradeGuard ul(l); + m_inner()->dataHash.assign(hashResult.begin(), hashResult.end()); + }, + anyHasher); + return hashResult; +} + +void BlockHeaderImpl::clear() +{ + m_inner()->resetDefautlt(); + m_parentInfo.clear(); +} + +gsl::span BlockHeaderImpl::parentInfo() const +{ + bcos::ReadGuard l(x_inner); + if (m_parentInfo.empty()) + { + for (auto const& it : m_inner()->data.parentInfo) + { + bcos::protocol::ParentInfo parentInfo; + parentInfo.blockNumber = it.blockNumber; + parentInfo.blockHash = + *(reinterpret_cast(it.blockHash.data())); + m_parentInfo.emplace_back(parentInfo); + } + } + + return gsl::span(m_parentInfo.data(), m_parentInfo.size()); +} + +bcos::crypto::HashType BlockHeaderImpl::txsRoot() const +{ + if (!m_inner()->data.txsRoot.empty()) + { + return *(reinterpret_cast(m_inner()->data.txsRoot.data())); + } + return {}; +} + +bcos::crypto::HashType BlockHeaderImpl::stateRoot() const +{ + if (!m_inner()->data.stateRoot.empty()) + { + return *(reinterpret_cast(m_inner()->data.stateRoot.data())); + } + return {}; +} + +bcos::crypto::HashType BlockHeaderImpl::receiptsRoot() const +{ + if (!m_inner()->data.receiptRoot.empty()) + { + return *( + reinterpret_cast(m_inner()->data.receiptRoot.data())); + } + return {}; +} + +bcos::u256 BlockHeaderImpl::gasUsed() const +{ + if (!m_inner()->data.gasUsed.empty()) + { + return boost::lexical_cast(m_inner()->data.gasUsed); + } + return {}; +} + +void BlockHeaderImpl::setParentInfo(gsl::span const& _parentInfo) +{ + bcos::WriteGuard l(x_inner); + m_parentInfo.clear(); + m_inner()->data.parentInfo.clear(); + for (auto& it : _parentInfo) + { + ParentInfo parentInfo; + parentInfo.blockNumber = it.blockNumber; + parentInfo.blockHash.assign(it.blockHash.begin(), it.blockHash.end()); + m_inner()->data.parentInfo.emplace_back(parentInfo); + } + clearDataHash(); +} + +void BlockHeaderImpl::setSealerList(gsl::span const& _sealerList) +{ + m_inner()->data.sealerList.clear(); + for (auto const& it : _sealerList) + { + m_inner()->data.sealerList.push_back(std::vector(it.begin(), it.end())); + } + clearDataHash(); +} + +void BlockHeaderImpl::setSignatureList( + gsl::span const& _signatureList) +{ + bcos::WriteGuard l(x_inner); + // Note: must clear the old signatureList when set the new signatureList + // in case of the consensus module get the cached-sync-blockHeader with signatureList which will + // cause redundant signature lists to be stored + m_inner()->signatureList.clear(); + for (auto& it : _signatureList) + { + Signature signature; + signature.sealerIndex = it.index; + signature.signature.assign(it.signature.begin(), it.signature.end()); + m_inner()->signatureList.emplace_back(signature); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockHeaderImpl.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockHeaderImpl.h" new file mode 100644 index 00000000..dc01ab8f --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockHeaderImpl.h" @@ -0,0 +1,204 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for BlockHeader + * @file BlockHeaderImpl.h + * @author: ancelmo + * @date 2021-04-20 + */ + +#pragma once + +#include "bcos-tars-protocol/tars/Block.h" + +#include +#include +#include +#include +#include + +namespace bcostars +{ +namespace protocol +{ +class BlockHeaderImpl : public bcos::protocol::BlockHeader +{ +public: + virtual ~BlockHeaderImpl() {} + + BlockHeaderImpl() = delete; + + BlockHeaderImpl( + bcos::crypto::CryptoSuite::Ptr cryptoSuite, std::function inner) + : bcos::protocol::BlockHeader(cryptoSuite), m_inner(inner) + {} + + void decode(bcos::bytesConstRef _data) override; + void encode(bcos::bytes& _encodeData) const override; + bcos::crypto::HashType hash() const override; + + void clear() override; + + uint32_t version() const override { return m_inner()->data.version; } + gsl::span parentInfo() const override; + + bcos::crypto::HashType txsRoot() const override; + bcos::crypto::HashType stateRoot() const override; + bcos::crypto::HashType receiptsRoot() const override; + bcos::protocol::BlockNumber number() const override { return m_inner()->data.blockNumber; } + bcos::u256 gasUsed() const override; + int64_t timestamp() const override { return m_inner()->data.timestamp; } + int64_t sealer() const override { return m_inner()->data.sealer; } + + gsl::span sealerList() const override + { + return gsl::span(reinterpret_cast(m_inner()->data.sealerList.data()), + m_inner()->data.sealerList.size()); + } + bcos::bytesConstRef extraData() const override + { + return bcos::bytesConstRef( + reinterpret_cast(m_inner()->data.extraData.data()), + m_inner()->data.extraData.size()); + } + gsl::span signatureList() const override + { + bcos::ReadGuard l(x_inner); + return gsl::span( + reinterpret_cast(m_inner()->signatureList.data()), + m_inner()->signatureList.size()); + } + + gsl::span consensusWeights() const override + { + return gsl::span(reinterpret_cast(m_inner()->data.consensusWeights.data()), + m_inner()->data.consensusWeights.size()); + } + + void setVersion(uint32_t _version) override + { + m_inner()->data.version = _version; + clearDataHash(); + } + + void setParentInfo(gsl::span const& _parentInfo) override; + + void setParentInfo(bcos::protocol::ParentInfoList&& _parentInfo) override + { + setParentInfo(gsl::span(_parentInfo.data(), _parentInfo.size())); + clearDataHash(); + } + + void setTxsRoot(bcos::crypto::HashType _txsRoot) override + { + m_inner()->data.txsRoot.assign(_txsRoot.begin(), _txsRoot.end()); + clearDataHash(); + } + void setReceiptsRoot(bcos::crypto::HashType _receiptsRoot) override + { + m_inner()->data.receiptRoot.assign(_receiptsRoot.begin(), _receiptsRoot.end()); + clearDataHash(); + } + void setStateRoot(bcos::crypto::HashType _stateRoot) override + { + m_inner()->data.stateRoot.assign(_stateRoot.begin(), _stateRoot.end()); + clearDataHash(); + } + void setNumber(bcos::protocol::BlockNumber _blockNumber) override + { + m_inner()->data.blockNumber = _blockNumber; + clearDataHash(); + } + void setGasUsed(bcos::u256 _gasUsed) override + { + m_inner()->data.gasUsed = boost::lexical_cast(_gasUsed); + clearDataHash(); + } + void setTimestamp(int64_t _timestamp) override + { + m_inner()->data.timestamp = _timestamp; + clearDataHash(); + } + void setSealer(int64_t _sealerId) override + { + m_inner()->data.sealer = _sealerId; + clearDataHash(); + } + void setSealerList(gsl::span const& _sealerList) override; + void setSealerList(std::vector&& _sealerList) override + { + setSealerList(gsl::span(_sealerList.data(), _sealerList.size())); + clearDataHash(); + } + + void setConsensusWeights(gsl::span const& _weightList) override + { + m_inner()->data.consensusWeights.assign(_weightList.begin(), _weightList.end()); + clearDataHash(); + } + + void setConsensusWeights(std::vector&& _weightList) override + { + setConsensusWeights(gsl::span(_weightList.data(), _weightList.size())); + clearDataHash(); + } + + void setExtraData(bcos::bytes const& _extraData) override + { + m_inner()->data.extraData.assign(_extraData.begin(), _extraData.end()); + clearDataHash(); + } + void setExtraData(bcos::bytes&& _extraData) override + { + m_inner()->data.extraData.assign(_extraData.begin(), _extraData.end()); + clearDataHash(); + } + void setSignatureList( + gsl::span const& _signatureList) override; + + void setSignatureList(bcos::protocol::SignatureList&& _signatureList) override + { + setSignatureList(gsl::span(_signatureList.data(), _signatureList.size())); + } + + const bcostars::BlockHeader& inner() const + { + bcos::ReadGuard l(x_inner); + return *m_inner(); + } + + void setInner(const bcostars::BlockHeader& blockHeader) + { + bcos::WriteGuard l(x_inner); + *m_inner() = blockHeader; + } + void setInner(bcostars::BlockHeader&& blockHeader) + { + bcos::WriteGuard l(x_inner); + *m_inner() = std::move(blockHeader); + } + +protected: + // Note: When the field in the header used to calculate the hash changes, the dataHash needs to + // be cleaned up + virtual void clearDataHash() { m_inner()->dataHash = std::vector(); } + +private: + std::function m_inner; + mutable std::vector m_parentInfo; + mutable bcos::SharedMutex x_inner; +}; +} // namespace protocol +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockImpl.cpp" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockImpl.cpp" new file mode 100644 index 00000000..c370aa14 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockImpl.cpp" @@ -0,0 +1,165 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for Block + * @file BlockImpl.cpp + * @author: ancelmo + * @date 2021-04-20 + */ + +#include "../impl/TarsSerializable.h" + +#include "BlockImpl.h" +#include + +using namespace bcostars; +using namespace bcostars::protocol; + +void BlockImpl::decode(bcos::bytesConstRef _data, bool, bool) +{ + bcos::concepts::serialize::decode(_data, *m_inner); +} + +void BlockImpl::encode(bcos::bytes& _encodeData) const +{ + bcos::concepts::serialize::encode(*m_inner, _encodeData); +} + +bcos::protocol::BlockHeader::Ptr BlockImpl::blockHeader() +{ + bcos::ReadGuard l(x_blockHeader); + return std::make_shared( + m_transactionFactory->cryptoSuite(), + [inner = this->m_inner]() mutable { return &inner->blockHeader; }); +} + +bcos::protocol::BlockHeader::ConstPtr BlockImpl::blockHeaderConst() const +{ + bcos::ReadGuard l(x_blockHeader); + return std::make_shared( + m_transactionFactory->cryptoSuite(), + [inner = this->m_inner]() { return &inner->blockHeader; }); +} + +bcos::protocol::Transaction::ConstPtr BlockImpl::transaction(uint64_t _index) const +{ + return std::make_shared( + m_transactionFactory->cryptoSuite(), + [inner = m_inner, _index]() { return &(inner->transactions[_index]); }); +} + +bcos::protocol::TransactionReceipt::ConstPtr BlockImpl::receipt(uint64_t _index) const +{ + return std::make_shared( + m_transactionFactory->cryptoSuite(), + [inner = m_inner, _index]() { return &(inner->receipts[_index]); }); +} + +void BlockImpl::setBlockHeader(bcos::protocol::BlockHeader::Ptr _blockHeader) +{ + if (_blockHeader) + { + bcos::WriteGuard l(x_blockHeader); + m_inner->blockHeader = + std::dynamic_pointer_cast(_blockHeader)->inner(); + } +} + +void BlockImpl::setReceipt(uint64_t _index, bcos::protocol::TransactionReceipt::Ptr _receipt) +{ + if (_index >= m_inner->receipts.size()) + { + m_inner->receipts.resize(m_inner->transactions.size()); + } + auto innerReceipt = + std::dynamic_pointer_cast(_receipt)->inner(); + m_inner->receipts[_index] = innerReceipt; +} + +void BlockImpl::appendReceipt(bcos::protocol::TransactionReceipt::Ptr _receipt) +{ + m_inner->receipts.emplace_back( + std::dynamic_pointer_cast(_receipt)->inner()); +} + +void BlockImpl::setNonceList(bcos::protocol::NonceList const& _nonceList) +{ + m_inner->nonceList.clear(); + m_inner->nonceList.reserve(_nonceList.size()); + for (auto const& it : _nonceList) + { + m_inner->nonceList.emplace_back(boost::lexical_cast(it)); + } + + m_nonceList.clear(); +} + +void BlockImpl::setNonceList(bcos::protocol::NonceList&& _nonceList) +{ + m_inner->nonceList.clear(); + m_inner->nonceList.reserve(_nonceList.size()); + for (auto const& it : _nonceList) + { + m_inner->nonceList.emplace_back(boost::lexical_cast(it)); + } + + m_nonceList.clear(); +} +bcos::protocol::NonceList const& BlockImpl::nonceList() const +{ + if (m_nonceList.empty()) + { + m_nonceList.reserve(m_inner->nonceList.size()); + + for (auto const& it : m_inner->nonceList) + { + if (it.empty()) + { + m_nonceList.push_back(bcos::protocol::NonceType(0)); + } + else + { + m_nonceList.push_back(boost::lexical_cast(it)); + } + } + } + + return m_nonceList; +} + +bcos::protocol::TransactionMetaData::ConstPtr BlockImpl::transactionMetaData(uint64_t _index) const +{ + if (_index >= transactionsMetaDataSize()) + { + return nullptr; + } + + auto txMetaData = std::make_shared( + [inner = m_inner, _index]() { return &inner->transactionsMetaData[_index]; }); + + return txMetaData; +} + +void BlockImpl::appendTransactionMetaData(bcos::protocol::TransactionMetaData::Ptr _txMetaData) +{ + auto txMetaDataImpl = + std::dynamic_pointer_cast(_txMetaData); + m_inner->transactionsMetaData.emplace_back(txMetaDataImpl->inner()); +} + +uint64_t BlockImpl::transactionsMetaDataSize() const +{ + return m_inner->transactionsMetaData.size(); +} diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockImpl.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockImpl.h" new file mode 100644 index 00000000..29519c7b --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/BlockImpl.h" @@ -0,0 +1,205 @@ +/** + * Copyright (C) 2021 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. + * + * @brief tars implementation for Block + * @file BlockImpl.h + * @author: ancelmo + * @date 2021-04-20 + */ +#pragma once + +#include "../impl/TarsHashable.h" + +#include "../Common.h" +#include "BlockHeaderImpl.h" +#include "TransactionImpl.h" +#include "TransactionMetaDataImpl.h" +#include "TransactionReceiptImpl.h" +#include "bcos-concepts/Hash.h" +#include "bcos-framework/protocol/Transaction.h" +#include "bcos-tars-protocol/tars/Block.h" +#include "bcos-tars-protocol/tars/Transaction.h" +#include "bcos-tars-protocol/tars/TransactionMetaData.h" +#include "bcos-tars-protocol/tars/TransactionReceipt.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcostars +{ +namespace protocol +{ +class BlockImpl : public bcos::protocol::Block, public std::enable_shared_from_this +{ +public: + BlockImpl(bcos::protocol::TransactionFactory::Ptr _transactionFactory, + bcos::protocol::TransactionReceiptFactory::Ptr _receiptFactory) + : bcos::protocol::Block(_transactionFactory, _receiptFactory), + m_inner(std::make_shared()), + x_mutex(std::make_shared()) + {} + BlockImpl(bcos::protocol::TransactionFactory::Ptr _transactionFactory, + bcos::protocol::TransactionReceiptFactory::Ptr _receiptFactory, bcostars::Block _block) + : BlockImpl(_transactionFactory, _receiptFactory) + { + *m_inner = std::move(_block); + } + ~BlockImpl() override{}; + + void decode(bcos::bytesConstRef _data, bool _calculateHash, bool _checkSig) override; + void encode(bcos::bytes& _encodeData) const override; + + int32_t version() const override { return m_inner->blockHeader.data.version; } + void setVersion(int32_t _version) override { m_inner->blockHeader.data.version = _version; } + + bcos::protocol::BlockType blockType() const override + { + return (bcos::protocol::BlockType)m_inner->type; + } + // FIXME: this will cause the same blockHeader calculate hash multiple times + bcos::protocol::BlockHeader::Ptr blockHeader() override; + bcos::protocol::BlockHeader::ConstPtr blockHeaderConst() const override; + + bcos::protocol::Transaction::ConstPtr transaction(uint64_t _index) const override; + bcos::protocol::TransactionReceipt::ConstPtr receipt(uint64_t _index) const override; + + // get transaction metaData + bcos::protocol::TransactionMetaData::ConstPtr transactionMetaData( + uint64_t _index) const override; + void setBlockType(bcos::protocol::BlockType _blockType) override + { + m_inner->type = (int32_t)_blockType; + } + + // set blockHeader + void setBlockHeader(bcos::protocol::BlockHeader::Ptr _blockHeader) override; + + void setTransaction(uint64_t _index, bcos::protocol::Transaction::Ptr _transaction) override + { + m_inner->transactions[_index] = + std::dynamic_pointer_cast(_transaction)->inner(); + } + void appendTransaction(bcos::protocol::Transaction::Ptr _transaction) override + { + m_inner->transactions.emplace_back( + std::dynamic_pointer_cast(_transaction)->inner()); + } + + void setReceipt(uint64_t _index, bcos::protocol::TransactionReceipt::Ptr _receipt) override; + void appendReceipt(bcos::protocol::TransactionReceipt::Ptr _receipt) override; + + void appendTransactionMetaData(bcos::protocol::TransactionMetaData::Ptr _txMetaData) override; + + // get transactions size + uint64_t transactionsSize() const override { return m_inner->transactions.size(); } + uint64_t transactionsMetaDataSize() const override; + // get receipts size + uint64_t receiptsSize() const override { return m_inner->receipts.size(); } + + void setNonceList(bcos::protocol::NonceList const& _nonceList) override; + void setNonceList(bcos::protocol::NonceList&& _nonceList) override; + bcos::protocol::NonceList const& nonceList() const override; + + const bcostars::Block& inner() const { return *m_inner; } + void setInner(const bcostars::Block& inner) { *m_inner = inner; } + void setInner(bcostars::Block&& inner) { *m_inner = std::move(inner); } + + bcos::crypto::HashType calculateTransactionRoot() const override + { + auto txsRoot = bcos::crypto::HashType(); + // with no transactions + if (transactionsSize() == 0 && transactionsMetaDataSize() == 0) + { + return txsRoot; + } + + auto anyHasher = m_transactionFactory->cryptoSuite()->hashImpl()->hasher(); + std::visit( + [this, &txsRoot](auto& hasher) { + using Hasher = std::remove_reference_t; + bcos::crypto::merkle::Merkle merkle; + + if (transactionsSize() > 0) + { + auto hashesRange = + m_inner->transactions | + RANGES::views::transform([](const bcostars::Transaction& transaction) { + std::array hash; + bcos::concepts::hash::calculate(transaction, hash); + return hash; + }); + merkle.generateMerkle(hashesRange, m_inner->transactionsMerkle); + } + else if (transactionsMetaDataSize() > 0) + { + auto hashesRange = + m_inner->transactionsMetaData | + RANGES::views::transform( + [](const bcostars::TransactionMetaData& transactionMetaData) { + return transactionMetaData.hash; + }); + merkle.generateMerkle(hashesRange, m_inner->transactionsMerkle); + } + bcos::concepts::bytebuffer::assignTo( + *RANGES::rbegin(m_inner->transactionsMerkle), txsRoot); + }, + anyHasher); + + return txsRoot; + } + + bcos::crypto::HashType calculateReceiptRoot() const override + { + auto receiptsRoot = bcos::crypto::HashType(); + // with no receipts + if (receiptsSize() == 0) + { + return receiptsRoot; + } + auto anyHasher = m_transactionFactory->cryptoSuite()->hashImpl()->hasher(); + std::visit( + [this, &receiptsRoot](auto& hasher) { + using Hasher = std::remove_reference_t; + auto hashesRange = + m_inner->receipts | + RANGES::views::transform([](const bcostars::TransactionReceipt& receipt) { + std::array hash; + bcos::concepts::hash::calculate(receipt, hash); + return hash; + }); + bcos::crypto::merkle::Merkle merkle; + merkle.generateMerkle(hashesRange, m_inner->receiptsMerkle); + bcos::concepts::bytebuffer::assignTo( + *RANGES::rbegin(m_inner->receiptsMerkle), receiptsRoot); + }, + anyHasher); + + return receiptsRoot; + } + +private: + std::shared_ptr m_inner; + mutable bcos::protocol::NonceList m_nonceList; + std::shared_ptr x_mutex; + mutable bcos::SharedMutex x_blockHeader; +}; +} // namespace protocol +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ExecutionMessageImpl.cpp" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ExecutionMessageImpl.cpp" new file mode 100644 index 00000000..66d4b39d --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ExecutionMessageImpl.cpp" @@ -0,0 +1,86 @@ +/** + * 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. + * + * @brief tars implementation for ExecutionMessage + * @file ExecutionMessageImpl.cpp + * @author: yujiechen + * @date 2022-05-09 + */ +#include "ExecutionMessageImpl.h" +#include "../Common.h" +using namespace bcostars; +using namespace bcostars::protocol; + +void ExecutionMessageImpl::decodeLogEntries() +{ + m_logEntries.reserve(m_inner()->logEntries.size()); + for (auto& it : m_inner()->logEntries) + { + auto bcosLogEntry = toBcosLogEntry(it); + m_logEntries.emplace_back(std::move(bcosLogEntry)); + } +} + +void ExecutionMessageImpl::decodeKeyLocks() +{ + for (auto const& _keyLock : m_inner()->keyLocks) + { + m_keyLocks.emplace_back(_keyLock); + } +} + +gsl::span const ExecutionMessageImpl::logEntries() const +{ + return m_logEntries; +} +std::vector ExecutionMessageImpl::takeLogEntries() +{ + return std::move(m_logEntries); +} + +void ExecutionMessageImpl::setLogEntries(std::vector _logEntries) +{ + m_logEntries = _logEntries; + m_inner()->logEntries.clear(); + m_inner()->logEntries.reserve(_logEntries.size()); + + for (auto& it : _logEntries) + { + auto tarsLogEntry = toTarsLogEntry(it); + m_inner()->logEntries.emplace_back(std::move(tarsLogEntry)); + } +} + +gsl::span ExecutionMessageImpl::keyLocks() const +{ + return m_keyLocks; +} +std::vector ExecutionMessageImpl::takeKeyLocks() +{ + // Note: must clear the tars-container here when takeKeyLocks + m_inner()->keyLocks.clear(); + return std::move(m_keyLocks); +} + +void ExecutionMessageImpl::setKeyLocks(std::vector keyLocks) +{ + m_keyLocks = std::move(keyLocks); + // Note: must clear the tars-container here before set new keyLocks + m_inner()->keyLocks.clear(); + for (auto const& keyLock : m_keyLocks) + { + m_inner()->keyLocks.emplace_back(keyLock); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ExecutionMessageImpl.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ExecutionMessageImpl.h" new file mode 100644 index 00000000..0bf3d312 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ExecutionMessageImpl.h" @@ -0,0 +1,236 @@ +/** + * 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. + * + * @brief tars implementation for ExecutionMessage + * @file ExecutionMessageImpl.h + * @author: yujiechen + * @date 2022-05-09 + */ + +#pragma once + +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include +#include +#include +#include +namespace bcostars +{ +namespace protocol +{ +class ExecutionMessageImpl : public bcos::protocol::ExecutionMessage +{ +public: + using Ptr = std::shared_ptr; + using UniquePtr = std::unique_ptr; + using UniqueConstPtr = std::unique_ptr; + ExecutionMessageImpl() + : m_inner([m_executionMessage = bcostars::ExecutionMessage()]() mutable { + return &m_executionMessage; + }) + { + decodeLogEntries(); + decodeKeyLocks(); + } + ExecutionMessageImpl(std::function _inner) : m_inner(_inner) + { + decodeLogEntries(); + decodeKeyLocks(); + } + + ~ExecutionMessageImpl() override {} + + Type type() const override { return (Type)m_inner()->type; } + void setType(Type _type) override { m_inner()->type = _type; } + + bcos::crypto::HashType transactionHash() const override + { + if (m_inner()->transactionHash.size() < bcos::crypto::HashType::SIZE) + { + return bcos::crypto::HashType(); + } + return *(reinterpret_cast(m_inner()->transactionHash.data())); + } + void setTransactionHash(bcos::crypto::HashType hash) override + { + m_inner()->transactionHash.assign(hash.begin(), hash.end()); + } + + int64_t contextID() const override { return m_inner()->contextID; } + void setContextID(int64_t contextID) override { m_inner()->contextID = contextID; } + + int64_t seq() const override { return m_inner()->seq; } + void setSeq(int64_t seq) override { m_inner()->seq = seq; } + + std::string_view origin() const override { return m_inner()->origin; } + void setOrigin(std::string origin) override { m_inner()->origin = origin; } + + std::string_view from() const override { return m_inner()->from; } + void setFrom(std::string from) override { m_inner()->from = from; } + + std::string_view to() const override { return m_inner()->to; } + void setTo(std::string to) override { m_inner()->to = to; } + + std::string_view abi() const override { return m_inner()->abi; } + void setABI(std::string abi) override { m_inner()->abi = abi; } + + int32_t depth() const override { return m_inner()->depth; } + void setDepth(int32_t depth) override { m_inner()->depth = depth; } + + bool create() const override { return m_inner()->create; } + void setCreate(bool create) override { m_inner()->create = create; } + + bool internalCreate() const override { return m_inner()->internalCreate; } + void setInternalCreate(bool internalCreate) override + { + m_inner()->internalCreate = internalCreate; + } + + bool internalCall() const override { return m_inner()->internalCall; } + void setInternalCall(bool internalCall) override { m_inner()->internalCall = internalCall; } + + + int64_t gasAvailable() const override { return m_inner()->gasAvailable; } + void setGasAvailable(int64_t gasAvailable) override { m_inner()->gasAvailable = gasAvailable; } + + bcos::bytesConstRef data() const override + { + return bcos::bytesConstRef( + reinterpret_cast(m_inner()->data.data()), m_inner()->data.size()); + } + + bcos::bytes takeData() override + { + return bcos::bytes(m_inner()->data.begin(), m_inner()->data.end()); + } + void setData(bcos::bytes data) override { m_inner()->data.assign(data.begin(), data.end()); } + + bool staticCall() const override { return m_inner()->staticCall; } + void setStaticCall(bool staticCall) override { m_inner()->staticCall = staticCall; } + + // for evm + std::optional createSalt() const override + { + std::optional emptySalt; + if (m_inner()->salt.size() == 0) + { + return emptySalt; + } + try + { + return std::optional(boost::lexical_cast(m_inner()->salt)); + } + catch (std::exception const& e) + { + return emptySalt; + } + } + + void setCreateSalt(bcos::u256 createSalt) override + { + auto salt = boost::lexical_cast(createSalt); + m_inner()->salt = salt; + } + + int32_t status() const override { return m_inner()->status; } + void setStatus(int32_t status) override { m_inner()->status = status; } + + int32_t evmStatus() const override { return m_inner()->evmStatus; } + void setEvmStatus(int32_t evmStatus) override { m_inner()->evmStatus = evmStatus; } + + std::string_view message() const override { return m_inner()->message; } + void setMessage(std::string message) override { m_inner()->message = message; } + + // for evm + std::string_view newEVMContractAddress() const override + { + return m_inner()->newEVMContractAddress; + } + void setNewEVMContractAddress(std::string newEVMContractAddress) override + { + m_inner()->newEVMContractAddress = newEVMContractAddress; + } + std::string_view keyLockAcquired() const override { return m_inner()->keyLockAcquired; } + void setKeyLockAcquired(std::string keyLock) override { m_inner()->keyLockAcquired = keyLock; } + + gsl::span keyLocks() const override; + std::vector takeKeyLocks() override; + void setKeyLocks(std::vector keyLocks) override; + gsl::span const logEntries() const override; + std::vector takeLogEntries() override; + void setLogEntries(std::vector logEntries) override; + + bool delegateCall() const override { return m_inner()->delegateCall; } + void setDelegateCall(bool delegateCall) override { m_inner()->delegateCall = delegateCall; } + + std::string_view delegateCallAddress() const override { return m_inner()->delegateCallAddress; } + void setDelegateCallAddress(std::string delegateCallAddress) override + { + m_inner()->delegateCallAddress = delegateCallAddress; + } + + + bcos::bytesConstRef delegateCallCode() const override + { + return bcos::bytesConstRef( + reinterpret_cast(m_inner()->delegateCallCode.data()), + m_inner()->delegateCallCode.size()); + } + + bcos::bytes takeDelegateCallCode() override + { + return bcos::bytes(m_inner()->delegateCallCode.begin(), m_inner()->delegateCallCode.end()); + } + void setDelegateCallCode(bcos::bytes delegateCallCode) override + { + m_inner()->delegateCallCode.assign(delegateCallCode.begin(), delegateCallCode.end()); + } + + + std::string_view delegateCallSender() const override { return m_inner()->delegateCallSender; } + void setDelegateCallSender(std::string delegateCallSender) override + { + m_inner()->delegateCallSender = delegateCallSender; + } + + bcostars::ExecutionMessage inner() const { return *(m_inner()); } + +protected: + virtual void decodeLogEntries(); + virtual void decodeKeyLocks(); + +private: + std::function m_inner; + mutable std::vector m_logEntries; + std::vector m_keyLocks; +}; + +class ExecutionMessageFactoryImpl : public bcos::protocol::ExecutionMessageFactory +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + ExecutionMessageFactoryImpl() = default; + ~ExecutionMessageFactoryImpl() override {} + + bcos::protocol::ExecutionMessage::UniquePtr createExecutionMessage() override + { + return std::make_unique(); + } +}; +} // namespace protocol +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ExecutionResultImpl.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ExecutionResultImpl.h" new file mode 100644 index 00000000..747e0291 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ExecutionResultImpl.h" @@ -0,0 +1,62 @@ +/** + * Copyright (C) 2021 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. + * + * @brief tars implementation for ExecutionResult + * @file ExecutionResultImpl.h + * @author: ancelmo + * @date 2021-04-20 + */ + +#pragma once + +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include "TransactionReceiptImpl.h" +#include "bcos-tars-protocol/tars/ExecutionResult.h" +#include +#include + +namespace bcostars +{ +namespace protocol +{ +class ExecutionResultImpl : public bcos::protocol::ExecutionResult +{ +public: + ~ExecutionResultImpl() override {} + + Status status() const noexcept override { return (Status)m_inner->status; } + + bcos::protocol::TransactionReceipt::ConstPtr receipt() const noexcept override + { + std::shared_ptr receipt = + std::make_shared( + m_cryptoSuite, [m_inner = this->m_inner]() { return &m_inner->receipt; }); + + return receipt; + } + + bcos::bytesConstRef to() const noexcept override + { + return bcos::bytesConstRef((bcos::byte*)m_inner->to.data(), m_inner->to.size()); + } + +private: + std::shared_ptr m_inner; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; +}; +} // namespace protocol +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/GroupInfoCodecImpl.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/GroupInfoCodecImpl.h" new file mode 100644 index 00000000..01253590 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/GroupInfoCodecImpl.h" @@ -0,0 +1,64 @@ +/** + * Copyright (C) 2021 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. + * + * @brief the information used to deploy new node + * @file GroupInfoCodecImpl.h + * @author: yujiechen + * @date 2022-03-29 + */ +#pragma once +#include "bcos-framework/multigroup/GroupInfoCodec.h" +#include "bcos-tars-protocol/Common.h" + +namespace bcostars +{ +namespace protocol +{ +class GroupInfoCodecImpl : public bcos::group::GroupInfoCodec +{ +public: + GroupInfoCodecImpl() + : m_nodeFactory(std::make_shared()), + m_groupFactory(std::make_shared()) + {} + + ~GroupInfoCodecImpl() override {} + + bcos::group::GroupInfo::Ptr deserialize(const std::string& _encodedData) override + { + tars::TarsInputStream input; + input.setBuffer((const char*)_encodedData.data(), _encodedData.size()); + + bcostars::GroupInfo tarsGroupInfo; + tarsGroupInfo.readFrom(input); + return toBcosGroupInfo(m_nodeFactory, m_groupFactory, tarsGroupInfo); + } + + Json::Value serialize(bcos::group::GroupInfo::Ptr) override { return {}; } + + void serialize(std::string& _encodedData, bcos::group::GroupInfo::Ptr _groupInfo) override + { + auto tarsGroupInfo = toTarsGroupInfo(_groupInfo); + tars::TarsOutputStream output; + tarsGroupInfo.writeTo(output); + output.swap(_encodedData); + } + +private: + bcos::group::ChainNodeInfoFactory::Ptr m_nodeFactory; + bcos::group::GroupInfoFactory::Ptr m_groupFactory; +}; +} // namespace protocol +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/GroupNodeInfoImpl.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/GroupNodeInfoImpl.h" new file mode 100644 index 00000000..75a95708 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/GroupNodeInfoImpl.h" @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2021 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 GroupNodeInfoImpl.h + * @author: yujiechen + * @date 2022-3-8 + */ +#pragma once + +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include +#include +#include +#include + +namespace bcostars +{ +namespace protocol +{ +class GroupNodeInfoImpl : public bcos::gateway::GroupNodeInfo +{ +public: + GroupNodeInfoImpl() + : m_inner( + [m_groupNodeInfo = bcostars::GroupNodeInfo()]() mutable { return &m_groupNodeInfo; }) + { + decodeInner(); + } + explicit GroupNodeInfoImpl(std::function inner) : m_inner(inner) + { + decodeInner(); + } + ~GroupNodeInfoImpl() override {} + + // the groupID + void setGroupID(std::string const& _groupID) override { m_inner()->groupID = _groupID; } + // the nodeIDList + void setNodeIDList(std::vector&& _nodeIDList) override + { + m_inner()->nodeIDList = std::move(_nodeIDList); + } + // the groupType + void setType(uint16_t _type) override { m_inner()->type = _type; } + + std::string const& groupID() const override { return m_inner()->groupID; } + // Note: externally ensure thread safety + std::vector const& nodeIDList() const override { return m_inner()->nodeIDList; } + int type() const override { return m_inner()->type; } + void setNodeProtocolList( + std::vector&& _protocolList) override + { + // encode protocolList into m_inner() + for (auto const& protocol : _protocolList) + { + appendInnerProtocol(protocol); + } + m_protocolList = std::move(_protocolList); + } + + std::vector const& nodeProtocolList() const override + { + return m_protocolList; + } + void appendNodeID(std::string const& _nodeID) override + { + m_inner()->nodeIDList.emplace_back(_nodeID); + } + + void appendProtocol(bcos::protocol::ProtocolInfo::ConstPtr _protocol) override + { + m_protocolList.emplace_back(_protocol); + appendInnerProtocol(_protocol); + } + bcos::protocol::ProtocolInfo::ConstPtr protocol(uint64_t _index) const override + { + if (m_protocolList.size() <= _index) + { + return nullptr; + } + return m_protocolList.at(_index); + } + const bcostars::GroupNodeInfo& inner() const { return *m_inner(); } + +protected: + virtual void appendInnerProtocol(bcos::protocol::ProtocolInfo::ConstPtr _protocol) + { + bcostars::ProtocolInfo tarsProtocol; + tarsProtocol.moduleID = _protocol->protocolModuleID(); + tarsProtocol.minVersion = _protocol->minVersion(); + tarsProtocol.maxVersion = _protocol->maxVersion(); + m_inner()->protocolList.emplace_back(std::move(tarsProtocol)); + } + + virtual void decodeInner() + { + // recover m_protocolList + auto const& tarsProtocols = m_inner()->protocolList; + for (auto const& protocol : tarsProtocols) + { + auto protocolInfo = std::make_shared( + (bcos::protocol::ProtocolModuleID)protocol.moduleID, + (bcos::protocol::ProtocolVersion)protocol.minVersion, + (bcos::protocol::ProtocolVersion)protocol.maxVersion); + m_protocolList.emplace_back(protocolInfo); + } + } + +private: + std::function m_inner; + std::vector m_protocolList; +}; +} // namespace protocol +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/MemberImpl.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/MemberImpl.h" new file mode 100644 index 00000000..5880f691 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/MemberImpl.h" @@ -0,0 +1,86 @@ +/** + * 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. + * + * @brief implementation for member + * @file MemberImpl.h + * @author: yujiechen + * @date 2022-04-26 + */ +#pragma once +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include "bcos-tars-protocol/tars/Member.h" +#include +namespace bcostars +{ +namespace protocol +{ +class MemberImpl : public bcos::protocol::MemberInterface +{ +public: + using Ptr = std::shared_ptr; + MemberImpl(std::function _inner) : m_inner(_inner) {} + MemberImpl() : m_inner([m_rawMember = bcostars::Member()]() mutable { return &m_rawMember; }) {} + MemberImpl(std::string const& _memberData) : MemberImpl() { decode(_memberData); } + + ~MemberImpl() override {} + + // the memberID of different service, should be unique + void setMemberID(std::string const& _memberID) override { m_inner()->memberID = _memberID; } + // the memberConfig of different service + void setMemberConfig(std::string const& _config) override { m_inner()->memberConfig = _config; } + + std::string const& memberID() const override { return m_inner()->memberID; } + virtual std::string const& memberConfig() const override { return m_inner()->memberConfig; } + + // encode the member into string + virtual void encode(std::string& _encodedData) override + { + tars::TarsOutputStream output; + m_inner()->writeTo(output); + output.swap(_encodedData); + } + + // decode the member info + virtual void decode(std::string const& _memberData) override + { + tars::TarsInputStream input; + input.setBuffer(_memberData.data(), _memberData.length()); + m_inner()->readFrom(input); + } + +private: + std::function m_inner; +}; + +class MemberFactoryImpl : public bcos::protocol::MemberFactoryInterface +{ +public: + using Ptr = std::shared_ptr; + MemberFactoryImpl() = default; + ~MemberFactoryImpl() override {} + + bcos::protocol::MemberInterface::Ptr createMember() override + { + return std::make_shared(); + } + bcos::protocol::MemberInterface::Ptr createMember(std::string const& _memberData) override + { + return std::make_shared(_memberData); + } +}; +} // namespace protocol +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ProtocolInfoCodecImpl.cpp" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ProtocolInfoCodecImpl.cpp" new file mode 100644 index 00000000..1d286c18 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ProtocolInfoCodecImpl.cpp" @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implement for ProtocolInfoCodec + * @file ProtocolInfoCodecImpl.h + * @author: yujiechen + * @date 2022-03-22 + */ +#include "ProtocolInfoCodecImpl.h" +#include "../Common.h" + +using namespace bcostars; +using namespace bcostars::protocol; + +void ProtocolInfoCodecImpl::encode( + bcos::protocol::ProtocolInfo::ConstPtr _protocol, bcos::bytes& _encodeData) const +{ + bcostars::ProtocolInfo tarsProtcolInfo; + tarsProtcolInfo.moduleID = _protocol->protocolModuleID(); + tarsProtcolInfo.minVersion = _protocol->minVersion(); + tarsProtcolInfo.maxVersion = _protocol->maxVersion(); + tars::TarsOutputStream output; + tarsProtcolInfo.writeTo(output); + output.getByteBuffer().swap(_encodeData); +} + +bcos::protocol::ProtocolInfo::Ptr ProtocolInfoCodecImpl::decode(bcos::bytesConstRef _data) const +{ + tars::TarsInputStream input; + input.setBuffer((const char*)_data.data(), _data.size()); + bcostars::ProtocolInfo tarsProtcolInfo; + tarsProtcolInfo.readFrom(input); + + auto protocolInfo = std::make_shared(); + protocolInfo->setProtocolModuleID((bcos::protocol::ProtocolModuleID)tarsProtcolInfo.moduleID); + protocolInfo->setMinVersion(tarsProtcolInfo.minVersion); + protocolInfo->setMaxVersion(tarsProtcolInfo.maxVersion); + return protocolInfo; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ProtocolInfoCodecImpl.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ProtocolInfoCodecImpl.h" new file mode 100644 index 00000000..78dd507a --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/ProtocolInfoCodecImpl.h" @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implement for ProtocolInfoCodec + * @file ProtocolInfoCodecImpl.h + * @author: yujiechen + * @date 2022-03-22 + */ +#pragma once +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include +#include + +namespace bcostars +{ +namespace protocol +{ +class ProtocolInfoCodecImpl : public bcos::protocol::ProtocolInfoCodec +{ +public: + ProtocolInfoCodecImpl() = default; + ~ProtocolInfoCodecImpl() override {} + + void encode( + bcos::protocol::ProtocolInfo::ConstPtr _protocol, bcos::bytes& _encodeData) const override; + bcos::protocol::ProtocolInfo::Ptr decode(bcos::bytesConstRef _data) const override; +}; +} // namespace protocol +} // namespace bcostars diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/RouterTable.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/RouterTable.tars" new file mode 100644 index 00000000..ad67ce89 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/RouterTable.tars" @@ -0,0 +1,13 @@ +module bcostars +{ +struct RouterTableEntry +{ + 1 require string dstNode; + 2 optional string nextHop; + 3 require int distance; +}; +struct RouterTable +{ + 1 optional vector routerEntries; +}; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionFactoryImpl.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionFactoryImpl.h" new file mode 100644 index 00000000..7c03c34c --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionFactoryImpl.h" @@ -0,0 +1,100 @@ +/** + * Copyright (C) 2021 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. + * + * @brief tars implementation for TransactionFactory + * @file TransactionFactoryImpl.h + * @author: ancelmo + * @date 2021-04-20 + */ +#pragma once +#include "TransactionImpl.h" +#include + +namespace bcostars +{ +namespace protocol +{ +class TransactionFactoryImpl : public bcos::protocol::TransactionFactory +{ +public: + TransactionFactoryImpl(bcos::crypto::CryptoSuite::Ptr cryptoSuite) : m_cryptoSuite(cryptoSuite) + {} + ~TransactionFactoryImpl() override {} + + bcos::protocol::Transaction::Ptr createTransaction( + bcos::bytesConstRef _txData, bool _checkSig = true) override + { + auto transaction = std::make_shared(m_cryptoSuite, + [m_transaction = bcostars::Transaction()]() mutable { return &m_transaction; }); + + transaction->decode(_txData); + if (_checkSig) + { + transaction->verify(); + } + return transaction; + } + + bcos::protocol::Transaction::Ptr createTransaction( + bcos::bytes const& _txData, bool _checkSig = true) override + { + return createTransaction(bcos::ref(_txData), _checkSig); + } + + bcos::protocol::Transaction::Ptr createTransaction(int32_t _version, + const std::string_view& _to, bcos::bytes const& _input, bcos::u256 const& _nonce, + int64_t _blockLimit, std::string const& _chainId, std::string const& _groupId, + int64_t _importTime) override + { + auto transaction = std::make_shared(m_cryptoSuite, + [m_transaction = bcostars::Transaction()]() mutable { return &m_transaction; }); + auto const& inner = transaction->innerGetter(); + inner()->data.version = _version; + inner()->data.to.assign(_to.begin(), _to.end()); + inner()->data.input.assign(_input.begin(), _input.end()); + inner()->data.blockLimit = _blockLimit; + inner()->data.chainID = _chainId; + inner()->data.groupID = _groupId; + inner()->data.nonce = boost::lexical_cast(_nonce); + inner()->importTime = _importTime; + + return transaction; + } + + bcos::protocol::Transaction::Ptr createTransaction(int32_t _version, + const std::string_view& _to, bcos::bytes const& _input, bcos::u256 const& _nonce, + int64_t _blockLimit, std::string const& _chainId, std::string const& _groupId, + int64_t _importTime, bcos::crypto::KeyPairInterface::Ptr keyPair) override + { + auto tx = createTransaction( + _version, _to, _input, _nonce, _blockLimit, _chainId, _groupId, _importTime); + auto sign = m_cryptoSuite->signatureImpl()->sign(*keyPair, tx->hash(), true); + + auto tarsTx = std::dynamic_pointer_cast(tx); + auto const& inner = tarsTx->innerGetter(); + inner()->signature.assign(sign->begin(), sign->end()); + + return tx; + } + + void setCryptoSuite(bcos::crypto::CryptoSuite::Ptr cryptoSuite) { m_cryptoSuite = cryptoSuite; } + bcos::crypto::CryptoSuite::Ptr cryptoSuite() override { return m_cryptoSuite; } + +private: + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; +}; + +} // namespace protocol +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.cpp" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.cpp" new file mode 100644 index 00000000..d4a05d75 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.cpp" @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2021 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. + * + * @brief tars implementation for Transaction + * @file TransactionImpl.cpp + * @author: ancelmo + * @date 2021-04-20 + */ + +#include "../impl/TarsHashable.h" +#include "../impl/TarsSerializable.h" + +#include "TransactionImpl.h" +#include +#include +#include + +using namespace bcostars; +using namespace bcostars::protocol; + +void TransactionImpl::decode(bcos::bytesConstRef _txData) +{ + bcos::concepts::serialize::decode(_txData, *m_inner()); +} + +void TransactionImpl::encode(bcos::bytes& txData) const +{ + bcos::concepts::serialize::encode(*m_inner(), txData); +} + +bcos::crypto::HashType TransactionImpl::hash(bool _useCache) const +{ + bcos::UpgradableGuard l(x_hash); + if (!m_inner()->dataHash.empty() && _useCache) + { + return *(reinterpret_cast(m_inner()->dataHash.data())); + } + auto hashImpl = m_cryptoSuite->hashImpl(); + auto anyHasher = hashImpl->hasher(); + + bcos::crypto::HashType hashResult; + std::visit( + [this, &hashResult, &l](auto& hasher) { + using Hasher = std::remove_cvref_t; + bcos::concepts::hash::calculate(*m_inner(), hashResult); + + bcos::UpgradeGuard ul(l); + m_inner()->dataHash.assign(hashResult.begin(), hashResult.end()); + }, + anyHasher); + return hashResult; +} + +bcos::u256 TransactionImpl::nonce() const +{ + if (!m_inner()->data.nonce.empty()) + { + m_nonce = boost::lexical_cast(m_inner()->data.nonce); + } + return m_nonce; +} + +bcos::bytesConstRef TransactionImpl::input() const +{ + return bcos::bytesConstRef(reinterpret_cast(m_inner()->data.input.data()), + m_inner()->data.input.size()); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.h" new file mode 100644 index 00000000..e99d799b --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionImpl.h" @@ -0,0 +1,100 @@ +/** + * Copyright (C) 2021 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. + * + * @brief tars implementation for Transaction + * @file TransactionImpl.h + * @author: ancelmo + * @date 2021-04-20 + */ + +#pragma once + +#include "bcos-tars-protocol/tars/Transaction.h" +#include +#include +#include +#include +#include + +namespace bcostars +{ +namespace protocol +{ +class TransactionImpl : public bcos::protocol::Transaction +{ +public: + explicit TransactionImpl( + bcos::crypto::CryptoSuite::Ptr _cryptoSuite, std::function inner) + : bcos::protocol::Transaction(_cryptoSuite), m_inner(inner) + {} + + ~TransactionImpl() {} + + friend class TransactionFactoryImpl; + + bool operator==(const Transaction& rhs) const { return this->hash() == rhs.hash(); } + + void decode(bcos::bytesConstRef _txData) override; + void encode(bcos::bytes& txData) const override; + + bcos::crypto::HashType hash(bool _useCache = true) const override; + int32_t version() const override { return m_inner()->data.version; } + std::string_view chainId() const override { return m_inner()->data.chainID; } + std::string_view groupId() const override { return m_inner()->data.groupID; } + int64_t blockLimit() const override { return m_inner()->data.blockLimit; } + bcos::u256 nonce() const override; + std::string_view to() const override { return m_inner()->data.to; } + std::string_view abi() const override { return m_inner()->data.abi; } + bcos::bytesConstRef input() const override; + int64_t importTime() const override { return m_inner()->importTime; } + void setImportTime(int64_t _importTime) override { m_inner()->importTime = _importTime; } + bcos::bytesConstRef signatureData() const override + { + return bcos::bytesConstRef(reinterpret_cast(m_inner()->signature.data()), + m_inner()->signature.size()); + } + std::string_view sender() const override + { + return std::string_view(m_inner()->sender.data(), m_inner()->sender.size()); + } + void forceSender(bcos::bytes _sender) const override + { + m_inner()->sender.assign(_sender.begin(), _sender.end()); + } + + void setSignatureData(bcos::bytes& signature) + { + m_inner()->signature.assign(signature.begin(), signature.end()); + } + + uint32_t attribute() const override { return m_inner()->attribute; } + void setAttribute(uint32_t attribute) override { m_inner()->attribute = attribute; } + + std::string_view source() const override { return m_inner()->source; } + void setSource(std::string const& source) override { m_inner()->source = source; } + + const bcostars::Transaction& inner() const { return *m_inner(); } + void setInner(bcostars::Transaction inner) { *m_inner() = std::move(inner); } + + std::function const& innerGetter() { return m_inner; } + + +private: + std::function m_inner; + mutable bcos::SharedMutex x_hash; + mutable bcos::u256 m_nonce; +}; +} // namespace protocol +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionMetaDataImpl.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionMetaDataImpl.h" new file mode 100644 index 00000000..d2df7655 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionMetaDataImpl.h" @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2021 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. + * + * @brief implement for TransactionMetaData + * @file TransactionMetaDataImpl.h + * @author: yujiechen + * @date: 2021-09-07 + */ +#pragma once + +#include "bcos-tars-protocol/tars/TransactionMetaData.h" +#include + +namespace bcostars +{ +namespace protocol +{ +class TransactionMetaDataImpl : public bcos::protocol::TransactionMetaData +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + TransactionMetaDataImpl() + : m_inner([inner = bcostars::TransactionMetaData()]() mutable { return &inner; }) + {} + + TransactionMetaDataImpl(bcos::crypto::HashType hash, std::string to) : TransactionMetaDataImpl() + { + setHash(std::move(hash)); + setTo(std::move(to)); + } + + explicit TransactionMetaDataImpl(std::function inner) + : m_inner(std::move(inner)) + {} + + ~TransactionMetaDataImpl() override {} + + bcos::crypto::HashType hash() const override + { + auto const& hashBytes = m_inner()->hash; + if (hashBytes.size() == bcos::crypto::HashType::SIZE) + { + bcos::crypto::HashType hash(reinterpret_cast(hashBytes.data()), + bcos::crypto::HashType::SIZE); + return hash; + } + return bcos::crypto::HashType(); + } + void setHash(bcos::crypto::HashType _hash) override + { + m_inner()->hash.assign(_hash.begin(), _hash.end()); + } + + std::string_view to() const override { return m_inner()->to; } + void setTo(std::string _to) override { m_inner()->to = std::move(_to); } + + uint32_t attribute() const override { return m_inner()->attribute; } + void setAttribute(uint32_t attribute) override { m_inner()->attribute = attribute; } + + std::string_view source() const override { return m_inner()->source; } + void setSource(std::string source) override { m_inner()->source = std::move(source); } + + const bcostars::TransactionMetaData& inner() const { return *m_inner(); } + bcostars::TransactionMetaData& mutableInner() { return *m_inner(); } + bcostars::TransactionMetaData takeInner() { return std::move(*m_inner()); } + void setInner(bcostars::TransactionMetaData inner) { *m_inner() = std::move(inner); } + +private: + std::function m_inner; +}; +} // namespace protocol +} // namespace bcostars diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptFactoryImpl.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptFactoryImpl.h" new file mode 100644 index 00000000..d358e4d3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptFactoryImpl.h" @@ -0,0 +1,96 @@ +/** + * Copyright (C) 2021 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. + * + * @brief tars implementation for TransactionReceiptFactory + * @file TransactionReceiptFactoryImpl.h + * @author: ancelmo + * @date 2021-04-20 + */ +#pragma once + +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include "TransactionReceiptImpl.h" +#include + + +namespace bcostars +{ +namespace protocol +{ +class TransactionReceiptFactoryImpl : public bcos::protocol::TransactionReceiptFactory +{ +public: + TransactionReceiptFactoryImpl(bcos::crypto::CryptoSuite::Ptr cryptoSuite) + : m_cryptoSuite(cryptoSuite) + {} + ~TransactionReceiptFactoryImpl() override {} + + TransactionReceiptImpl::Ptr createReceipt(bcos::bytesConstRef _receiptData) override + { + auto transactionReceipt = std::make_shared(m_cryptoSuite, + [m_receipt = bcostars::TransactionReceipt()]() mutable { return &m_receipt; }); + + transactionReceipt->decode(_receiptData); + + return transactionReceipt; + } + + TransactionReceiptImpl::Ptr createReceipt(bcos::bytes const& _receiptData) override + { + return createReceipt(bcos::ref(_receiptData)); + } + + TransactionReceiptImpl::Ptr createReceipt(bcos::u256 const& _gasUsed, + const std::string_view& _contractAddress, + std::shared_ptr> _logEntries, int32_t _status, + bcos::bytes const& _output, bcos::protocol::BlockNumber _blockNumber) override + { + auto transactionReceipt = std::make_shared(m_cryptoSuite, + [m_receipt = bcostars::TransactionReceipt()]() mutable { return &m_receipt; }); + auto const& inner = transactionReceipt->innerGetter(); + // required: version + inner()->data.version = 0; + // required: gasUsed + inner()->data.gasUsed = boost::lexical_cast(_gasUsed); + // optional: contractAddress + inner()->data.contractAddress.assign(_contractAddress.begin(), _contractAddress.end()); + // required: status + inner()->data.status = _status; + // optional: output + inner()->data.output.assign(_output.begin(), _output.end()); + transactionReceipt->setLogEntries(*_logEntries); + + inner()->data.blockNumber = _blockNumber; + return transactionReceipt; + } + + TransactionReceiptImpl::Ptr createReceipt(bcos::u256 const& _gasUsed, + const std::string_view& _contractAddress, + std::shared_ptr> _logEntries, int32_t _status, + bcos::bytes&& _output, bcos::protocol::BlockNumber _blockNumber) override + { + return createReceipt( + _gasUsed, _contractAddress, _logEntries, _status, _output, _blockNumber); + } + + void setCryptoSuite(bcos::crypto::CryptoSuite::Ptr cryptoSuite) { m_cryptoSuite = cryptoSuite; } + bcos::crypto::CryptoSuite::Ptr cryptoSuite() override { return m_cryptoSuite; } + + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; +}; +} // namespace protocol +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.cpp" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.cpp" new file mode 100644 index 00000000..65d1aa3d --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.cpp" @@ -0,0 +1,70 @@ +/** + * Copyright (C) 2021 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. + * + * @brief tars implementation for TransactionReceipt + * @file TransactionReceiptImpl.cpp + * @author: ancelmo + * @date 2021-04-20 + */ +#include "../impl/TarsHashable.h" +#include "../impl/TarsSerializable.h" + +#include "TransactionReceiptImpl.h" +#include +#include + +using namespace bcostars; +using namespace bcostars::protocol; + +void TransactionReceiptImpl::decode(bcos::bytesConstRef _receiptData) +{ + bcos::concepts::serialize::decode(_receiptData, *m_inner()); +} + +void TransactionReceiptImpl::encode(bcos::bytes& _encodedData) const +{ + bcos::concepts::serialize::encode(*m_inner(), _encodedData); +} + +// Note: not thread-safe +bcos::crypto::HashType TransactionReceiptImpl::hash() const +{ + if (!m_inner()->dataHash.empty()) + { + return *(reinterpret_cast(m_inner()->dataHash.data())); + } + auto hashImpl = m_cryptoSuite->hashImpl(); + auto anyHasher = hashImpl->hasher(); + + bcos::crypto::HashType hashResult; + std::visit( + [this, &hashResult](auto& hasher) { + using Hasher = std::remove_cvref_t; + bcos::concepts::hash::calculate(*m_inner(), hashResult); + + m_inner()->dataHash.assign(hashResult.begin(), hashResult.end()); + }, + anyHasher); + return hashResult; +} + +bcos::u256 TransactionReceiptImpl::gasUsed() const +{ + if (!m_inner()->data.gasUsed.empty()) + { + return boost::lexical_cast(m_inner()->data.gasUsed); + } + return {}; +} diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.h" new file mode 100644 index 00000000..fd82f75f --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionReceiptImpl.h" @@ -0,0 +1,112 @@ +/** + * Copyright (C) 2021 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. + * + * @brief tars implementation for TransactionReceipt + * @file TransactionReceiptImpl.h + * @author: ancelmo + * @date 2021-04-20 + */ + +#pragma once + +#include "../Common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcostars +{ +namespace protocol +{ +class TransactionReceiptImpl : public bcos::protocol::TransactionReceipt +{ +public: + TransactionReceiptImpl() = delete; + + explicit TransactionReceiptImpl(bcos::crypto::CryptoSuite::Ptr _cryptoSuite, + std::function inner) + : bcos::protocol::TransactionReceipt(_cryptoSuite), m_inner(inner) + {} + + ~TransactionReceiptImpl() override {} + void decode(bcos::bytesConstRef _receiptData) override; + void encode(bcos::bytes& _encodedData) const override; + bcos::crypto::HashType hash() const override; + + int32_t version() const override { return m_inner()->data.version; } + bcos::u256 gasUsed() const override; + + std::string_view contractAddress() const override { return m_inner()->data.contractAddress; } + int32_t status() const override { return m_inner()->data.status; } + bcos::bytesConstRef output() const override + { + return bcos::bytesConstRef( + (const unsigned char*)m_inner()->data.output.data(), m_inner()->data.output.size()); + } + gsl::span logEntries() const override + { + 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 gsl::span(m_logEntries.data(), m_logEntries.size()); + } + bcos::protocol::BlockNumber blockNumber() const override { return m_inner()->data.blockNumber; } + + const bcostars::TransactionReceipt& inner() const { return *m_inner(); } + + void setInner(const bcostars::TransactionReceipt& inner) { *m_inner() = inner; } + void setInner(bcostars::TransactionReceipt&& inner) { *m_inner() = std::move(inner); } + + std::function const& innerGetter() { return m_inner; } + + void setLogEntries(std::vector const& _logEntries) + { + m_logEntries.clear(); + m_inner()->data.logEntries.clear(); + m_inner()->data.logEntries.reserve(_logEntries.size()); + + for (auto& it : _logEntries) + { + auto tarsLogEntry = toTarsLogEntry(it); + m_inner()->data.logEntries.emplace_back(std::move(tarsLogEntry)); + } + } + + std::string const& message() const override { return m_inner()->message; } + + void setMessage(std::string const& _message) override { m_inner()->message = _message; } + + void setMessage(std::string&& _message) override { m_inner()->message = std::move(_message); } + +private: + std::function m_inner; + mutable std::vector m_logEntries; +}; +} // namespace protocol +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionSubmitResultFactoryImpl.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionSubmitResultFactoryImpl.h" new file mode 100644 index 00000000..85feac78 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionSubmitResultFactoryImpl.h" @@ -0,0 +1,50 @@ +/** + * Copyright (C) 2021 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. + * + * @brief tars implementation for TransactionSubmitResultFactory + * @file TransactionSubmitResultFactoryImpl.h + * @author: ancelmo + * @date 2021-04-20 + */ + +#pragma once +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include "TransactionSubmitResultImpl.h" +#include +#include +namespace bcostars +{ +namespace protocol +{ +class TransactionSubmitResultFactoryImpl : public bcos::protocol::TransactionSubmitResultFactory +{ +public: + bcos::protocol::TransactionSubmitResult::Ptr createTxSubmitResult() override + { + return std::make_shared(m_cryptoSuite); + } + + void setCryptoSuite(bcos::crypto::CryptoSuite::Ptr _cryptoSuite) + { + m_cryptoSuite = _cryptoSuite; + } + +private: + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; +}; +} // namespace protocol +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionSubmitResultImpl.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionSubmitResultImpl.h" new file mode 100644 index 00000000..47304488 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/protocol/TransactionSubmitResultImpl.h" @@ -0,0 +1,117 @@ +/** + * Copyright (C) 2021 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. + * + * @brief tars implementation for TransactionSubmitResult + * @file TransactionSubmitResultImpl.h + * @author: ancelmo + * @date 2021-04-20 + */ + +#pragma once +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include "../Common.h" +#include "TransactionReceiptImpl.h" +#include "bcos-tars-protocol/tars/TransactionReceipt.h" +#include "bcos-tars-protocol/tars/TransactionSubmitResult.h" +#include +#include +#include +#include +#include +#include + +namespace bcostars +{ +namespace protocol +{ +// Note: this will create a default transactionReceipt +class TransactionSubmitResultImpl : public bcos::protocol::TransactionSubmitResult +{ +public: + TransactionSubmitResultImpl(bcos::crypto::CryptoSuite::Ptr _cryptoSuite) + : m_cryptoSuite(_cryptoSuite), + m_inner([inner = bcostars::TransactionSubmitResult()]() mutable { return &inner; }) + {} + + TransactionSubmitResultImpl(bcos::crypto::CryptoSuite::Ptr _cryptoSuite, + std::function inner) + : m_cryptoSuite(_cryptoSuite), m_inner(std::move(inner)) + {} + uint32_t status() const override { return m_inner()->status; } + void setStatus(uint32_t status) override { m_inner()->status = status; } + + bcos::crypto::HashType txHash() const override + { + if (m_inner()->txHash.size() == bcos::crypto::HashType::SIZE) + { + return *(reinterpret_cast(m_inner()->txHash.data())); + } + return bcos::crypto::HashType(); + } + void setTxHash(bcos::crypto::HashType txHash) override + { + m_inner()->txHash.assign(txHash.begin(), txHash.end()); + } + + bcos::crypto::HashType blockHash() const override + { + if (m_inner()->blockHash.size() == bcos::crypto::HashType::SIZE) + { + return *(reinterpret_cast(m_inner()->blockHash.data())); + } + return bcos::crypto::HashType(); + } + void setBlockHash(bcos::crypto::HashType blockHash) override + { + m_inner()->blockHash.assign(blockHash.begin(), blockHash.end()); + } + + int64_t transactionIndex() const override { return m_inner()->transactionIndex; } + void setTransactionIndex(int64_t index) override { m_inner()->transactionIndex = index; } + + bcos::protocol::NonceType nonce() const override + { + return bcos::protocol::NonceType(m_inner()->nonce); + } + void setNonce(bcos::protocol::NonceType nonce) override { m_inner()->nonce = nonce.str(); } + + bcos::protocol::TransactionReceipt::Ptr transactionReceipt() const override + { + return std::make_shared( + m_cryptoSuite, [innerPtr = &m_inner()->transactionReceipt]() { return innerPtr; }); + } + void setTransactionReceipt(bcos::protocol::TransactionReceipt::Ptr transactionReceipt) override + { + auto transactionReceiptImpl = + std::dynamic_pointer_cast(transactionReceipt); + m_inner()->transactionReceipt = transactionReceiptImpl->inner(); // FIXME: copy here! + } + + bcostars::TransactionSubmitResult const& inner() { return *m_inner(); } + + std::string const& sender() const override { return m_inner()->sender; } + void setSender(std::string const& _sender) override { m_inner()->sender = _sender; } + + std::string const& to() const override { return m_inner()->to; } + void setTo(std::string const& _to) override { m_inner()->to = _to; } + +private: + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + std::function m_inner; +}; +} // namespace protocol +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/Block.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/Block.tars" new file mode 100644 index 00000000..4f879144 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/Block.tars" @@ -0,0 +1,50 @@ +#include "Transaction.tars" +#include "TransactionReceipt.tars" +#include "TransactionMetaData.tars" + +module bcostars { + struct ParentInfo { + 1 optional long blockNumber; + 2 optional vector blockHash; + }; + + struct Signature { + 1 optional long sealerIndex; + 2 optional vector signature; + }; + + struct BlockHeaderData { + 2 optional int version; + 3 optional vector parentInfo; + 4 optional vector txsRoot; + 5 optional vector receiptRoot; + 6 optional vector stateRoot; + 7 optional long blockNumber; + 8 optional string gasUsed; + 9 optional long timestamp; + 10 optional long sealer; + 11 optional vector> sealerList; + 12 optional vector extraData; + 13 optional vector consensusWeights; + }; + + struct BlockHeader { + 1 optional BlockHeaderData data; + 2 optional vector dataHash; + 3 optional vector signatureList; + }; + + struct Block { + 1 optional int version; + 2 optional int type; + 3 optional BlockHeader blockHeader; + 4 optional vector transactions; + 5 optional vector receipts; + 6 optional vector transactionsMetaData; + 7 optional vector> receiptsHash; + 8 optional vector nonceList; + + 9 optional vector> transactionsMerkle; + 10 optional vector> receiptsMerkle; + }; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/CommonProtocol.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/CommonProtocol.tars" new file mode 100644 index 00000000..cb2a60c3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/CommonProtocol.tars" @@ -0,0 +1,6 @@ +module bcostars { + struct Error { + 1 optional int errorCode; + 2 optional string errorMessage; + }; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ExecutionMessage.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ExecutionMessage.tars" new file mode 100644 index 00000000..2d38da01 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ExecutionMessage.tars" @@ -0,0 +1,33 @@ +#include "TransactionReceipt.tars" +module bcostars { +struct ExecutionMessage +{ + 1 require int type; + 2 optional vector transactionHash; + 3 require long contextID; + 4 require long seq; + 5 optional string origin; + 6 optional string from; + 7 optional string to; + 8 optional string abi; + 9 optional int depth; + 10 require bool create = false; + 11 optional bool internalCreate = false; + 12 optional bool internalCall = false; + 13 optional long gasAvailable; + 14 optional vector data; + 15 require bool staticCall = false; + 16 optional string salt; + 17 optional int status; + 18 optional string message; + 19 optional vector logEntries; + 20 optional string newEVMContractAddress; + 21 optional vector keyLocks; + 22 optional string keyLockAcquired; + 23 require bool delegateCall = false; + 24 optional string delegateCallAddress; + 25 optional vector delegateCallCode; + 26 optional string delegateCallSender; + 27 optional int evmStatus; +}; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ExecutionResult.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ExecutionResult.tars" new file mode 100644 index 00000000..33ec3caf --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ExecutionResult.tars" @@ -0,0 +1,9 @@ +#include "TransactionReceipt.tars" + +module bcostars { + struct ExecutionResult { + 1 optional int status; + 2 optional TransactionReceipt receipt; + 3 optional vector to; + }; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ExecutorService.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ExecutorService.tars" new file mode 100644 index 00000000..369bb4a1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ExecutorService.tars" @@ -0,0 +1,32 @@ +#include "Block.tars" +#include "CommonProtocol.tars" +#include "TwoPCParams.tars" +#include "ExecutionMessage.tars" +#include "ExecutorStatus.tars" +module bcostars { + interface ExecutorService + { + Error status(out ExecutorStatus _output); + + Error nextBlockHeader(long schedulerTermID, BlockHeader _blockHeader); + Error executeTransaction(ExecutionMessage _input, out ExecutionMessage _output); + Error call(ExecutionMessage _input, out ExecutionMessage _output); + + Error executeTransactions(string _contractAddress, vector _inputs, out vector _outputs); + //Error call(ExecutionMessage _input, out ExecutionMessage _output); + + Error dmcExecuteTransactions(string _contractAddress, vector _inputs, out vector _outputs); + Error dagExecuteTransactions(vector _inputs, out vector _outputs); + + Error dmcCall(ExecutionMessage _input, out ExecutionMessage _output); + Error getHash(long _blockNumber, out vector _hash); + + Error prepare(TwoPCParams _params); + Error commit(TwoPCParams _params); + Error rollback(TwoPCParams _params); + + Error getCode(string _contractAddress, out vector _code); + Error getABI(string _contractAddress, out string _abi); + Error reset(); + }; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ExecutorStatus.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ExecutorStatus.tars" new file mode 100644 index 00000000..e2764856 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ExecutorStatus.tars" @@ -0,0 +1,7 @@ + +module bcostars { +struct ExecutorStatus +{ + 1 require long seq; +}; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/FrontService.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/FrontService.tars" new file mode 100644 index 00000000..9a1bdab8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/FrontService.tars" @@ -0,0 +1,22 @@ +#include "CommonProtocol.tars" +#include "GatewayInfo.tars" +module bcostars { + interface FrontService { + Error asyncGetGroupNodeInfo(out GroupNodeInfo groupNodeInfo); + + Error onReceiveGroupNodeInfo(string groupID, GroupNodeInfo groupNodeInfo); + + Error onReceiveMessage(string groupID, vector nodeID, vector data); + + Error onReceiveBroadcastMessage(string groupID, vector nodeID, vector data); + + Error asyncSendMessageByNodeID(int moduleID, vector nodeID, vector data, unsigned int timeout, bool requireRespCallback, + out vector responseNodeID, out vector responseData, out string seq); + + Error asyncSendResponse(string id, int moduleID, vector nodeID, vector data); + + void asyncSendMessageByNodeIDs(int moduleID, vector> nodeIDs, vector data); + + void asyncSendBroadcastMessage(int _nodeType, int moduleID, vector data); + }; +}; diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/GatewayInfo.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/GatewayInfo.tars" new file mode 100644 index 00000000..b1f72d7b --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/GatewayInfo.tars" @@ -0,0 +1,32 @@ +#include "ProtocolInfo.tars" +module bcostars +{ +struct P2PInfo +{ + 1 require string p2pID; + 2 optional string agencyName; + 3 optional string nodeName; + 4 optional string host; + 5 optional int port; +}; + +struct GroupNodeInfo +{ + 1 optional string groupID; + 2 optional vector nodeIDList; + 3 optional int type; + 4 optional vector protocolList; +}; +struct GatewayInfo +{ + 1 require P2PInfo p2pInfo; + 2 require vector nodeIDInfo; +}; + +struct GatewayNodeStatus +{ + 1 require string uuid; + 2 require int seq; + 3 optional vector nodeList; +}; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/GatewayService.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/GatewayService.tars" new file mode 100644 index 00000000..b5340772 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/GatewayService.tars" @@ -0,0 +1,32 @@ +#include "CommonProtocol.tars" +#include "GroupInfo.tars" +#include "GatewayInfo.tars" +module bcostars +{ + interface GatewayService + { + Error asyncSendMessageByNodeID( + string groupID, int moduleID, vector srcNodeID, vector dstNodeID, vector payload); + + Error asyncSendMessageByNodeIDs(string groupID, int moduleID, vector srcNodeID, + vector> dstNodeID, vector payload); + + Error asyncSendBroadcastMessage( + int _nodeType, string groupID, int moduleID, vector srcNodeID, vector payload); + + Error asyncGetPeers(out GatewayInfo localInfo, out vector peers); + + Error asyncGetGroupNodeInfo(string groupID, out GroupNodeInfo groupNodeInfo); + + Error asyncNotifyGroupInfo(GroupInfo groupInfo); + + Error asyncSendMessageByTopic( + string _topic, vector _data, out int _type, out vector _responseData); + + Error asyncSendBroadcastMessageByTopic(string _topic, vector _data); + + Error asyncSubscribeTopic(string _clientID, string _topicInfo); + + Error asyncRemoveTopic(string _clientID, vector _topicList); + }; +}; diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/GroupInfo.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/GroupInfo.tars" new file mode 100644 index 00000000..0fc14dd5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/GroupInfo.tars" @@ -0,0 +1,25 @@ +#include "ProtocolInfo.tars" +module bcostars +{ +struct ChainNodeInfo +{ + 1 require string nodeName; + 2 require int nodeCryptoType; + 3 optional string iniConfig; + 4 optional map serviceInfo; + 5 optional string nodeID; + 6 require bool microService = false; + 7 optional int nodeType; + 8 optional ProtocolInfo protocolInfo; + 9 optional int compatibilityVersion; +}; +struct GroupInfo +{ + 1 require string chainID; + 2 require string groupID; + 3 require vector nodeList; + 4 optional string genesisConfig; + 5 optional string iniConfig; + 6 optional int isWasm; +}; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/LedgerConfig.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/LedgerConfig.tars" new file mode 100644 index 00000000..d384eb63 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/LedgerConfig.tars" @@ -0,0 +1,19 @@ +module bcostars +{ + struct ConsensusNode { + 1 optional vector nodeID; + 2 optional long weight; + }; + struct LedgerConfig + { + 1 optional vector consensusNodeList; + 2 optional vector observerNodeList; + 3 optional vector hash; + 4 optional long blockNumber; + 5 optional long blockTxCountLimit; + 6 optional long leaderSwitchPeriod; + 7 optional long sealerId; + 8 optional long gasLimit; + 9 optional int compatibilityVersion; + }; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/LedgerService.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/LedgerService.tars" new file mode 100644 index 00000000..1f79d513 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/LedgerService.tars" @@ -0,0 +1,23 @@ +#include "Block.tars" +#include "TransactionReceipt.tars" +#include "LedgerConfig.tars" +#include "CommonProtocol.tars" + +module bcostars { + struct MerkleProofItem + { + 1 require vector left; + 2 require vector right; + }; + interface LedgerService{ + Error asyncGetBlockDataByNumber(long _blockNumber, long _blockFlag, out Block _block); + Error asyncGetBlockNumber(out long _blockNumber); + Error asyncGetBlockHashByNumber(long _blockNumber, out vector _blockHash); + Error asyncGetBlockNumberByHash(vector _blockHash, out long _blockNumber); + Error asyncGetBatchTxsByHashList(vector> _txsHashList, bool _withProof, out vector _transactions, out map> _merkleProofList); + Error asyncGetTransactionReceiptByHash(vector _txHash, bool _withProof, out TransactionReceipt _receipt, out vector _proof); + Error asyncGetTotalTransactionCount(out long _totalTxCount, out long _failedTxCount, out long _latestBlockNumber); + Error asyncGetSystemConfigByKey(string _key, out string _value, out long _blockNumber); + Error asyncGetNodeListByType(string _type, out vector _nodeList); + }; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/LightNode.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/LightNode.tars" new file mode 100644 index 00000000..679553e4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/LightNode.tars" @@ -0,0 +1,68 @@ +#include "Block.tars" +#include "Transaction.tars" +#include "TransactionReceipt.tars" +#include "CommonProtocol.tars" + +module bcostars { + +struct RequestBlock +{ + 1 optional unsigned int blockNumber; + 2 optional bool onlyHeader; +}; + +struct ResponseBlock +{ + 1 optional Error error; + 2 optional Block block; +}; + +struct RequestTransactions +{ + 1 optional vector> hashes; + 2 optional bool withProof; +}; + +struct ResponseTransactions +{ + 1 optional Error error; + 2 optional vector transactions; +}; + +struct RequestReceipts +{ + 1 optional vector> hashes; + 2 optional bool withProof; +}; + +struct ResponseReceipts +{ + 1 optional Error error; + 2 optional vector receipts; +}; + +struct RequestGetStatus +{ + 0 optional int nothing; +}; + +struct ResponseGetStatus +{ + 1 optional Error error; + 2 optional unsigned int total; + 3 optional unsigned int failed; + 4 optional unsigned int blockNumber; +}; + +struct RequestSendTransaction +{ + 1 optional Transaction transaction; +}; + +struct ResponseSendTransaction +{ + 1 optional Error error; + 2 optional TransactionReceipt receipt; +}; + +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/Member.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/Member.tars" new file mode 100644 index 00000000..2cb37724 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/Member.tars" @@ -0,0 +1,6 @@ +module bcostars { + struct Member { + 1 require string memberID; + 2 optional string memberConfig; + }; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/PBFTService.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/PBFTService.tars" new file mode 100644 index 00000000..f422be99 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/PBFTService.tars" @@ -0,0 +1,19 @@ +#include "CommonProtocol.tars" +#include "Block.tars" +#include "LedgerConfig.tars" + +module bcostars { + interface PBFTService { + Error asyncNotifyConsensusMessage(string _uuid, vector _nodeId, vector _data); + Error asyncSubmitProposal(bool _containSysTxs, vector _proposalData, long _proposalIndex, vector _proposalHash); + Error asyncGetPBFTView(out long _view); + Error asyncCheckBlock(Block _block, out bool _verifyResult); + Error asyncNotifyNewBlock(LedgerConfig _ledgerConfig); + Error asyncNotifyBlockSyncMessage(string _uuid, vector _nodeId, vector _data); + Error asyncNoteUnSealedTxsSize(long _unsealedTxsSize); + + Error asyncGetSyncInfo(out string _syncInfo); + Error asyncGetConsensusStatus(out string _consensusStatus); + Error asyncNotifyConnectedNodes(vector> connectedNodes); + }; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ProtocolInfo.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ProtocolInfo.tars" new file mode 100644 index 00000000..2807fe48 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/ProtocolInfo.tars" @@ -0,0 +1,9 @@ +module bcostars +{ +struct ProtocolInfo +{ + 1 require int moduleID; + 2 require int minVersion; + 3 require int maxVersion; +}; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/RpcService.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/RpcService.tars" new file mode 100644 index 00000000..c51673d1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/RpcService.tars" @@ -0,0 +1,12 @@ +#include "CommonProtocol.tars" +#include "GroupInfo.tars" +#include "TransactionSubmitResult.tars" + +module bcostars { + interface RpcService { + Error asyncNotifyBlockNumber(string groupID, string nodeName, long blockNumber); + Error asyncNotifyGroupInfo(GroupInfo groupInfo); + Error asyncNotifyAMOPMessage(int _type, string _topic, vector _requestData, out vector _responseData); + Error asyncNotifySubscribeTopic(out string _topicInfo); + }; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/SchedulerService.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/SchedulerService.tars" new file mode 100644 index 00000000..5ebe647d --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/SchedulerService.tars" @@ -0,0 +1,21 @@ + +#include "Transaction.tars" +#include "Block.tars" +#include "TransactionReceipt.tars" +#include "CommonProtocol.tars" +#include "LedgerConfig.tars" + +module bcostars { + interface SchedulerService + { + Error preExecuteBlock(Block _block, bool _verify); + Error executeBlock(Block _block, bool _verify, out BlockHeader _executedHeader, out bool _sysBlock); + Error commitBlock(BlockHeader _header, out LedgerConfig _ledgerConfig); + + Error call(Transaction _tx, out TransactionReceipt _receipt); + Error getCode(string _contract, out vector _codeData); + Error getABI(string _contract, out string _abi); + + Error registerExecutor(string _name); + }; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/StorageService.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/StorageService.tars" new file mode 100644 index 00000000..b28f0004 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/StorageService.tars" @@ -0,0 +1,52 @@ +#include "CommonProtocol.tars" + +module bcostars +{ + +struct TableInfo { + 1 optional string name; + 2 optional string _key; + 3 optional vector fields; + 4 optional vector indices; + 5 optional bool enableConsensus; + 6 optional bool enableCache; + 7 optional bool newTable; +}; + +struct Condition { + 1 optional vector conditions; + 2 optional long offset; + 3 optional long size; +}; + +struct Entry { + 1 optional long num; + 2 optional int status; + 3 optional map fields; + 4 optional bool isNull; +}; + +struct TableFactory { + 1 optional long num; + 2 optional vector tableInfos; + 3 optional vector> datas; +}; + +interface StorageService +{ + Error getPrimaryKeys(TableInfo tableInfo, Condition condition, out vector keys); + Error getRow(TableInfo tableInfo, string keys, out Entry row); + Error getRows(TableInfo tableInfo, vector keys, out map rows); + Error commitBlock(long blockNumber, vector infos, vector> datas, out long count); + + Error addStateCache(long blockNumber, TableFactory tableFactory); + Error dropStateCache(long blockNumber); + Error getStateCache(long blockNumber, out TableFactory tableFactory); + + Error put(string columnFamily, string _key, string value); + Error get(string columnFamily, string _key, out string value); + Error remove(string columnFamily, string _key); + Error getBatch(string columnFamily, vector keys, out vector values); +}; + +}; diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/Transaction.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/Transaction.tars" new file mode 100644 index 00000000..9ca649ee --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/Transaction.tars" @@ -0,0 +1,22 @@ +module bcostars { + struct TransactionData { + 1 optional int version; + 2 optional string chainID; + 3 optional string groupID; + 4 optional long blockLimit; + 5 optional string nonce; + 6 optional string to; + 7 optional vector input; + 8 optional string abi; + }; + + struct Transaction { + 1 optional TransactionData data; + 2 optional vector dataHash; + 3 optional vector signature; + 7 optional vector sender; + 4 optional long importTime; + 5 optional int attribute; + 6 optional string source; + }; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TransactionMetaData.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TransactionMetaData.tars" new file mode 100644 index 00000000..2b4b6710 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TransactionMetaData.tars" @@ -0,0 +1,9 @@ +module bcostars { + struct TransactionMetaData + { + 1 optional vector hash; + 2 optional string to; + 3 optional string source; + 4 optional unsigned int attribute; + }; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TransactionReceipt.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TransactionReceipt.tars" new file mode 100644 index 00000000..a4613faa --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TransactionReceipt.tars" @@ -0,0 +1,23 @@ +module bcostars { + struct LogEntry { + 1 optional string address; + 2 optional vector> topic; + 3 optional vector data; + }; + + struct TransactionReceiptData { + 1 require int version; + 2 optional string gasUsed; + 3 optional string contractAddress; + 4 optional int status; + 5 optional vector output; + 6 optional vector logEntries; + 7 optional long blockNumber; + }; + + struct TransactionReceipt { + 1 optional TransactionReceiptData data; + 2 optional vector dataHash; + 3 optional string message; + }; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TransactionSubmitResult.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TransactionSubmitResult.tars" new file mode 100644 index 00000000..1ce39730 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TransactionSubmitResult.tars" @@ -0,0 +1,15 @@ +#include "CommonProtocol.tars" +#include "TransactionReceipt.tars" + +module bcostars { + struct TransactionSubmitResult { + 1 optional int status; + 2 optional vector txHash; + 3 optional vector blockHash; + 4 optional long transactionIndex; + 5 optional string nonce = "-1"; + 6 optional TransactionReceipt transactionReceipt; + 7 optional string sender; + 8 optional string to; + }; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TwoPCParams.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TwoPCParams.tars" new file mode 100644 index 00000000..bb747139 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TwoPCParams.tars" @@ -0,0 +1,8 @@ +module bcostars { +struct TwoPCParams +{ + 1 require long blockNumber; + 2 require long timePoint; + 3 optional string primaryKey; +}; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TxPoolService.tars" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TxPoolService.tars" new file mode 100644 index 00000000..ff25a5a1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/tars/TxPoolService.tars" @@ -0,0 +1,25 @@ +#include "CommonProtocol.tars" +#include "Transaction.tars" +#include "Block.tars" +#include "TransactionSubmitResult.tars" +#include "LedgerConfig.tars" + +module bcostars { + interface TxPoolService { + Error submit(Transaction tx, out TransactionSubmitResult result); + + Error asyncSealTxs(long txsLimit, vector> avoidTxs, out Block txsList, out Block sysTxsList); + Error asyncMarkTxs(vector> txHashs, bool sealedFlag, long batchId, vector batchHash); + Error asyncVerifyBlock(vector generatedNodeID, vector block, out bool result); + Error asyncFillBlock(vector> txHashs, out vector filled); + Error asyncNotifyBlockResult(long blockNumber, vector result); + Error asyncNotifyTxsSyncMessage(Error error, string id, vector nodeID, vector data); + Error notifyConnectedNodes(vector> connectedNodes); + Error notifyConsensusNodeList(vector consensusNodeList); + Error notifyObserverNodeList(vector observerNodeList); + + Error asyncResetTxPool(); + + Error asyncGetPendingTransactionSize(out long _txsSize); + }; +}; \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/testutil/FakeBlock.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/testutil/FakeBlock.h" new file mode 100644 index 00000000..2054c1f6 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/testutil/FakeBlock.h" @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2021 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. + * + * @brief test for PBBlock + * @file PBBlockTest.cpp + * @author: yujiechen + * @date: 2021-03-23 + */ +#pragma once +#include "FakeBlockHeader.h" +#include "FakeTransaction.h" +#include "FakeTransactionReceipt.h" +#include "bcos-tars-protocol/protocol/BlockFactoryImpl.h" +#include "bcos-tars-protocol/protocol/BlockHeaderFactoryImpl.h" +#include "bcos-tars-protocol/protocol/BlockHeaderImpl.h" +#include "bcos-tars-protocol/protocol/TransactionFactoryImpl.h" +#include "bcos-tars-protocol/protocol/TransactionMetaDataImpl.h" +#include "bcos-tars-protocol/protocol/TransactionReceiptImpl.h" +#include +#include +#include +#include +#include +#include +#include +using namespace bcos; +using namespace bcos::protocol; +using namespace bcos::crypto; + +namespace bcos +{ +namespace test +{ +inline CryptoSuite::Ptr createNormalCryptoSuite() +{ + auto hashImpl = std::make_shared(); + auto signImpl = std::make_shared(); + return std::make_shared(hashImpl, signImpl, nullptr); +} + +inline CryptoSuite::Ptr createSMCryptoSuite() +{ + auto hashImpl = std::make_shared(); + auto signImpl = std::make_shared(); + return std::make_shared(hashImpl, signImpl, nullptr); +} + +inline BlockFactory::Ptr createBlockFactory(CryptoSuite::Ptr _cryptoSuite) +{ + auto blockHeaderFactory = + std::make_shared(_cryptoSuite); + auto transactionFactory = + std::make_shared(_cryptoSuite); + auto receiptFactory = + std::make_shared(_cryptoSuite); + return std::make_shared( + _cryptoSuite, blockHeaderFactory, transactionFactory, receiptFactory); +} + +inline void checkBlock(CryptoSuite::Ptr _cryptoSuite, Block::Ptr block, Block::Ptr decodedBlock) +{ + BOOST_CHECK(block->blockType() == decodedBlock->blockType()); + // check BlockHeader + if (block->blockHeader()) + { + checkBlockHeader(block->blockHeader(), decodedBlock->blockHeader()); + } + // check transactions + BOOST_CHECK_EQUAL(decodedBlock->transactionsSize(), block->transactionsSize()); + for (size_t i = 0; i < block->transactionsSize(); ++i) + { + checkTransaction(block->transaction(i), decodedBlock->transaction(i)); + } + /* + for (auto transaction : *(block->transactions())) + { + checkTransaction(transaction, (*(decodedBlock->transactions()))[index++]); + } + */ + // check receipts + BOOST_CHECK_EQUAL(decodedBlock->receiptsSize(), block->receiptsSize()); + for (size_t i = 0; i < block->receiptsSize(); ++i) + { + checkReceipts(_cryptoSuite->hashImpl(), block->receipt(i), decodedBlock->receipt(i)); + } + /* + for (auto receipt : *(block->receipts())) + { + checkReceipts(_cryptoSuite->hashImpl(), receipt, (*(decodedBlock->receipts()))[index++]); + } + */ + for (size_t i = 0; i < decodedBlock->transactionsHashSize(); i++) + { + BOOST_CHECK(decodedBlock->transactionHash(i) == block->transactionHash(i)); + BOOST_CHECK( + decodedBlock->transactionMetaData(i)->hash() == block->transactionMetaData(i)->hash()); + BOOST_CHECK( + decodedBlock->transactionMetaData(i)->to() == block->transactionMetaData(i)->to()); + } + // check receiptsRoot + h256 originHash = h256(); + if (block->blockHeader()) + { + originHash = block->blockHeader()->hash(); + } + BOOST_CHECK(block->calculateReceiptRoot() == decodedBlock->calculateReceiptRoot()); + + if (block->blockHeader()) + { + BOOST_CHECK_EQUAL(block->blockHeader()->receiptsRoot(), block->calculateReceiptRoot()); + BOOST_CHECK_EQUAL( + decodedBlock->blockHeader()->receiptsRoot(), decodedBlock->calculateReceiptRoot()); + BOOST_CHECK_EQUAL(decodedBlock->blockHeader()->hash(), originHash); + originHash = block->blockHeader()->hash(); + } + // check transactionsRoot + BOOST_CHECK(block->calculateTransactionRoot() == decodedBlock->calculateTransactionRoot()); + if (block->blockHeader()) + { + BOOST_CHECK_EQUAL(block->blockHeader()->txsRoot(), block->calculateTransactionRoot()); + BOOST_CHECK_EQUAL( + decodedBlock->blockHeader()->txsRoot(), block->calculateTransactionRoot()); + BOOST_CHECK_EQUAL(decodedBlock->blockHeader()->hash(), originHash); + originHash = decodedBlock->blockHeader()->hash(); + } + // Check idempotence + auto txsRoot = block->calculateTransactionRoot(); + auto receiptsRoot = block->calculateReceiptRoot(); + BOOST_CHECK(txsRoot == block->calculateTransactionRoot()); + BOOST_CHECK(receiptsRoot == block->calculateReceiptRoot()); + if (decodedBlock->blockHeader()) + { + BOOST_CHECK(decodedBlock->blockHeader()->hash() == originHash); + } +} + +inline Block::Ptr fakeAndCheckBlock(CryptoSuite::Ptr _cryptoSuite, BlockFactory::Ptr _blockFactory, + bool _withHeader, size_t _txsNum, size_t _txsHashNum, bool _check = true) +{ + auto block = _blockFactory->createBlock(); + if (_withHeader) + { + auto blockHeader = testPBBlockHeader(_cryptoSuite); + block->setBlockHeader(blockHeader); + } + + block->setBlockType(CompleteBlock); + // fake transactions + for (size_t i = 0; i < _txsNum; i++) + { + auto tx = fakeTransaction(_cryptoSuite, utcTime() + i); + block->appendTransaction(tx); + + auto metaData = std::make_shared(); + metaData->setHash(tx->hash()); + metaData->setTo(std::string(tx->to())); + + block->appendTransactionMetaData(metaData); + } + + _txsHashNum = _txsNum; + // fake receipts + for (size_t i = 0; i < _txsNum; i++) + { + auto receipt = testPBTransactionReceipt(_cryptoSuite, _check); + block->setReceipt(i, receipt); + } + // fake txsHash + // for (size_t i = 0; i < _txsHashNum; i++) + // { + // auto content = "transaction: " + std::to_string(i); + // auto hash = _cryptoSuite->hashImpl()->hash(content); + // boost::ignore_unused(hash); + + // auto metaData = std::make_shared(); + // metaData->setHash(hash); + // metaData->setTo(*toHexString(hash)); + // // auto txMetaData = _blockFactory->createTransactionMetaData(hash, *toHexString(hash)); + // block->appendTransactionMetaData(metaData); + // } + + if (block->blockHeaderConst()) + { + block->blockHeader()->setReceiptsRoot(block->calculateReceiptRoot()); + block->blockHeader()->setTxsRoot(block->calculateTransactionRoot()); + } + if (!_check) + { + return block; + } + + // encode block + auto encodedData = std::make_shared(); + block->encode(*encodedData); + // decode block + auto decodedBlock = _blockFactory->createBlock(*encodedData, true, true); + checkBlock(_cryptoSuite, block, decodedBlock); + // check txsHash + BOOST_CHECK(decodedBlock->transactionsHashSize() == _txsHashNum); + BOOST_CHECK(decodedBlock->transactionsMetaDataSize() == _txsHashNum); + for (size_t i = 0; i < _txsHashNum; i++) + { + auto hash = block->transaction(i)->hash(); + BOOST_CHECK(decodedBlock->transactionHash(i) == hash); + BOOST_CHECK(decodedBlock->transactionMetaData(i)->hash() == hash); + } + // exception test + /*(*encodedData)[0] += 1; + BOOST_CHECK_THROW( + _blockFactory->createBlock(*encodedData, true, true), PBObjectDecodeException);*/ + return block; +} + + +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/testutil/FakeBlockHeader.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/testutil/FakeBlockHeader.h" new file mode 100644 index 00000000..fd7dd3ad --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/testutil/FakeBlockHeader.h" @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2021 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 FakeBlockHeader.h + * @author: yujiechen + * @date: 2021-03-16 + */ +#pragma once +#include "bcos-protocol/Common.h" +#include "bcos-tars-protocol/protocol/BlockHeaderFactoryImpl.h" +#include +#include +#include +#include +using namespace bcos; +using namespace bcos::protocol; +using namespace bcos::crypto; +namespace bcos +{ +namespace test +{ +inline void checkBlockHeader(BlockHeader::Ptr blockHeader, BlockHeader::Ptr decodedBlockHeader) +{ + BOOST_CHECK(decodedBlockHeader->version() == blockHeader->version()); + BOOST_CHECK(decodedBlockHeader->parentInfo() == blockHeader->parentInfo()); + BOOST_CHECK(decodedBlockHeader->txsRoot() == blockHeader->txsRoot()); + BOOST_CHECK(decodedBlockHeader->receiptsRoot() == blockHeader->receiptsRoot()); + BOOST_CHECK(decodedBlockHeader->stateRoot() == blockHeader->stateRoot()); + BOOST_CHECK(decodedBlockHeader->number() == blockHeader->number()); + BOOST_CHECK(decodedBlockHeader->gasUsed() == blockHeader->gasUsed()); + BOOST_CHECK(decodedBlockHeader->timestamp() == blockHeader->timestamp()); + BOOST_CHECK(decodedBlockHeader->sealer() == blockHeader->sealer()); + + auto decodedSealerList = decodedBlockHeader->sealerList(); + BOOST_CHECK(decodedSealerList.size() == blockHeader->sealerList().size()); + for (decltype(decodedSealerList)::size_type i = 0; i < decodedSealerList.size(); i++) + { + BOOST_CHECK(decodedSealerList[i] == blockHeader->sealerList()[i]); + } + BOOST_CHECK(decodedBlockHeader->extraData().toBytes() == blockHeader->extraData().toBytes()); + BOOST_CHECK((decodedBlockHeader->consensusWeights()) == (blockHeader->consensusWeights())); + // check signature + BOOST_CHECK(decodedBlockHeader->signatureList().size() == blockHeader->signatureList().size()); + size_t index = 0; + for (auto signature : decodedBlockHeader->signatureList()) + { + BOOST_CHECK(signature.index == blockHeader->signatureList()[index].index); + BOOST_CHECK(signature.signature == blockHeader->signatureList()[index].signature); + index++; + } +#if 0 + std::cout << "### PBBlockHeaderTest: version:" << decodedBlockHeader->version() << std::endl; + std::cout << "### PBBlockHeaderTest: txsRoot:" << decodedBlockHeader->txsRoot().hex() + << std::endl; + std::cout << "### PBBlockHeaderTest: receiptsRoot:" << decodedBlockHeader->receiptsRoot().hex() + << std::endl; + std::cout << "### PBBlockHeaderTest: stateRoot:" << decodedBlockHeader->stateRoot().hex() + << std::endl; + std::cout << "### PBBlockHeaderTest: number:" << decodedBlockHeader->number() << std::endl; + std::cout << "### PBBlockHeaderTest: gasUsed:" << decodedBlockHeader->gasUsed() << std::endl; + std::cout << "### PBBlockHeaderTest: timestamp:" << decodedBlockHeader->timestamp() + << std::endl; + std::cout << "### PBBlockHeaderTest: sealer:" << decodedBlockHeader->sealer() << std::endl; + std::cout << "### PBBlockHeaderTest: sealer:" << *toHexString(decodedBlockHeader->extraData()) + << std::endl; + std::cout << "#### hash:" << decodedBlockHeader->hash().hex() << std::endl; + if (blockHeader->parentInfo().size() >= 1) + { + std::cout << "### parentHash:" << blockHeader->parentInfo()[0].blockHash.hex() << std::endl; + } +#endif +} + +inline BlockHeader::Ptr fakeAndTestBlockHeader(CryptoSuite::Ptr _cryptoSuite, int32_t _version, + const ParentInfoList& _parentInfo, h256 const& _txsRoot, h256 const& _receiptsRoot, + h256 const& _stateRoot, int64_t _number, u256 const& _gasUsed, int64_t _timestamp, + int64_t _sealer, const std::vector& _sealerList, bytes const& _extraData, + SignatureList _signatureList, bool _check = true) +{ + BlockHeaderFactory::Ptr blockHeaderFactory = + std::make_shared(_cryptoSuite); + BlockHeader::Ptr blockHeader = blockHeaderFactory->createBlockHeader(); + blockHeader->setVersion(_version); + blockHeader->setParentInfo(_parentInfo); + blockHeader->setTxsRoot(_txsRoot); + blockHeader->setReceiptsRoot(_receiptsRoot); + blockHeader->setStateRoot(_stateRoot); + blockHeader->setNumber(_number); + blockHeader->setGasUsed(_gasUsed); + blockHeader->setTimestamp(_timestamp); + blockHeader->setSealer(_sealer); + blockHeader->setSealerList(gsl::span(_sealerList)); + blockHeader->setExtraData(_extraData); + blockHeader->setSignatureList(_signatureList); + WeightList weights; + weights.push_back(0); + blockHeader->setConsensusWeights(weights); + if (_check) + { + tbb::parallel_invoke([blockHeader]() { blockHeader->hash(); }, + [blockHeader]() { blockHeader->hash(); }, [blockHeader]() { blockHeader->hash(); }, + [blockHeader]() { blockHeader->hash(); }); + // encode + std::shared_ptr encodedData = std::make_shared(); + blockHeader->encode(*encodedData); + + bcos::bytes buffer; + blockHeader->encode(buffer); + BOOST_CHECK(*encodedData == buffer); + + // decode + auto decodedBlockHeader = blockHeaderFactory->createBlockHeader(*encodedData); +#if 0 + std::cout << "### PBBlockHeaderTest: encodedData:" << *toHexString(*encodedData) << std::endl; +#endif + // Same tested in tars + // check the data of decodedBlockHeader + // checkBlockHeader(blockHeader, decodedBlockHeader); + // test encode exception + // (*encodedData)[0] += 1; + // bcostars::protocol::BlockHeaderImpl decodedBlock(_cryptoSuite); + // BOOST_CHECK_THROW( + // std::make_shared(_cryptoSuite, *encodedData), + // PBObjectDecodeException); + + // update the hash data field + blockHeader->setNumber(_number + 1); + BOOST_CHECK(blockHeader->hash() != decodedBlockHeader->hash()); + BOOST_CHECK(blockHeader->number() == decodedBlockHeader->number() + 1); + // recover the hash field + blockHeader->setNumber(_number); + BOOST_CHECK(blockHeader->hash() == decodedBlockHeader->hash()); + BOOST_CHECK(decodedBlockHeader->consensusWeights().size() == 1); + BOOST_CHECK(decodedBlockHeader->consensusWeights()[0] == 0); + } + return blockHeader; +} + +inline ParentInfoList fakeParentInfo(Hash::Ptr _hashImpl, size_t _size) +{ + ParentInfoList parentInfos; + for (size_t i = 0; i < _size; i++) + { + ParentInfo parentInfo; + parentInfo.blockNumber = i; + parentInfo.blockHash = _hashImpl->hash(std::to_string(i)); + parentInfos.emplace_back(parentInfo); + } + return parentInfos; +} + +inline std::vector fakeSealerList( + std::vector& _keyPairVec, SignatureCrypto::Ptr _signImpl, size_t size) +{ + std::vector sealerList; + for (size_t i = 0; i < size; i++) + { + KeyPairInterface::Ptr keyPair = _signImpl->generateKeyPair(); + _keyPairVec.emplace_back(keyPair); + sealerList.emplace_back(*(keyPair->publicKey()->encode())); + } + return sealerList; +} + +inline SignatureList fakeSignatureList(SignatureCrypto::Ptr _signImpl, + std::vector& _keyPairVec, h256 const& _hash) +{ + auto sealerIndex = 0; + SignatureList signatureList; + for (auto keyPair : _keyPairVec) + { + auto signature = _signImpl->sign(*keyPair, _hash); + Signature sig; + sig.index = sealerIndex++; + sig.signature = *signature; + signatureList.push_back(sig); + } + return signatureList; +} + +inline BlockHeader::Ptr testPBBlockHeader(CryptoSuite::Ptr _cryptoSuite) +{ + auto hashImpl = _cryptoSuite->hashImpl(); + auto signImpl = _cryptoSuite->signatureImpl(); + auto cryptoSuite = std::make_shared(hashImpl, signImpl, nullptr); + int version = 10; + auto parentInfo = fakeParentInfo(hashImpl, 3); + auto txsRoot = hashImpl->hash((std::string) "txsRoot"); + auto receiptsRoot = hashImpl->hash((std::string) "receiptsRoot"); + auto stateRoot = hashImpl->hash((std::string) "stateRoot"); + int64_t number = 12312312412; + u256 gasUsed = 3453456346534; + int64_t timestamp = 9234234234; + int64_t sealer = 100; + std::vector keyPairVec; + auto sealerList = fakeSealerList(keyPairVec, signImpl, 4); + bytes extraData = stateRoot.asBytes(); + auto signatureList = fakeSignatureList(signImpl, keyPairVec, receiptsRoot); + + auto blockHeader = + fakeAndTestBlockHeader(cryptoSuite, version, parentInfo, txsRoot, receiptsRoot, stateRoot, + number, gasUsed, timestamp, sealer, sealerList, extraData, signatureList); + + // test verifySignatureList + signatureList = fakeSignatureList(signImpl, keyPairVec, blockHeader->hash()); + blockHeader->setSignatureList(signatureList); + blockHeader->verifySignatureList(); + + auto invalidSignatureList = fakeSignatureList(signImpl, keyPairVec, receiptsRoot); + blockHeader->setSignatureList(invalidSignatureList); + BOOST_CHECK_THROW(blockHeader->verifySignatureList(), InvalidSignatureList); + + blockHeader->setSignatureList(signatureList); + blockHeader->verifySignatureList(); + return blockHeader; +} +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/testutil/FakeTransaction.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/testutil/FakeTransaction.h" new file mode 100644 index 00000000..717b0cec --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/testutil/FakeTransaction.h" @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2021 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 FakeTransaction.h + * @author: yujiechen + * @date: 2021-03-16 + */ +#pragma once +#include "../protocol/TransactionImpl.h" +#include "bcos-tars-protocol/protocol/TransactionFactoryImpl.h" +#include +#include +#include + +using namespace bcos; +using namespace bcos::crypto; +using namespace bcos::protocol; + +namespace bcos +{ +namespace test +{ +inline auto fakeTransaction(CryptoSuite::Ptr _cryptoSuite, KeyPairInterface::Ptr _keyPair, + const std::string_view& _to, bytes const& _input, u256 const& _nonce, int64_t _blockLimit, + std::string const& _chainId, std::string const& _groupId, std::string const& _abi = "") +{ + bcostars::Transaction transaction; + transaction.data.to = _to; + transaction.data.input.assign(_input.begin(), _input.end()); + transaction.data.nonce = boost::lexical_cast(_nonce); + transaction.data.blockLimit = _blockLimit; + transaction.data.chainID = _chainId; + transaction.data.groupID = _groupId; + transaction.data.abi = _abi; + auto pbTransaction = std::make_shared(_cryptoSuite, + [m_transaction = std::move(transaction)]() mutable { return &m_transaction; }); + auto signData = _cryptoSuite->signatureImpl()->sign(*_keyPair, pbTransaction->hash(), true); + pbTransaction->setSignatureData(*signData); + pbTransaction->forceSender(_keyPair->address(_cryptoSuite->hashImpl()).asBytes()); + return pbTransaction; +} + +inline void checkTransaction( + Transaction::ConstPtr pbTransaction, Transaction::ConstPtr decodedTransaction) +{ + // check the fields + BOOST_CHECK(decodedTransaction->hash() == pbTransaction->hash()); + BOOST_CHECK(decodedTransaction->sender() == pbTransaction->sender()); + BOOST_CHECK(decodedTransaction->type() == pbTransaction->type()); + BOOST_CHECK(decodedTransaction->to() == pbTransaction->to()); + // check the transaction hash fields + BOOST_CHECK(decodedTransaction->input().toBytes() == pbTransaction->input().toBytes()); + BOOST_CHECK(decodedTransaction->nonce() == pbTransaction->nonce()); + BOOST_CHECK(decodedTransaction->blockLimit() == pbTransaction->blockLimit()); + BOOST_CHECK(decodedTransaction->chainId() == pbTransaction->chainId()); + BOOST_CHECK(decodedTransaction->groupId() == pbTransaction->groupId()); +} + +inline Transaction::Ptr testTransaction(CryptoSuite::Ptr _cryptoSuite, + KeyPairInterface::Ptr _keyPair, const std::string_view& _to, bytes const& _input, + u256 const& _nonce, int64_t _blockLimit, std::string const& _chainId, + std::string const& _groupId) +{ + auto factory = std::make_shared(_cryptoSuite); + auto pbTransaction = fakeTransaction( + _cryptoSuite, _keyPair, _to, _input, _nonce, _blockLimit, _chainId, _groupId); + if (_to.empty()) + { + BOOST_CHECK(pbTransaction->type() == TransactionType::ContractCreation); + } + else + { + BOOST_CHECK(pbTransaction->type() == TransactionType::MessageCall); + } + auto addr = _keyPair->address(_cryptoSuite->hashImpl()); + BOOST_CHECK(pbTransaction->sender() == std::string_view((char*)addr.data(), 20)); + bcos::bytes encodedData; + pbTransaction->encode(encodedData); + // auto encodedDataCache = pbTransaction->encode(); + // BOOST_CHECK(encodedData.toBytes() == encodedDataCache.toBytes()); +#if 0 + std::cout << "#### encodedData is:" << *toHexString(encodedData) << std::endl; + std::cout << "### hash:" << pbTransaction->hash().hex() << std::endl; + std::cout << "### sender:" << *toHexString(pbTransaction->sender()) << std::endl; + std::cout << "### type:" << pbTransaction->type() << std::endl; + std::cout << "### to:" << *toHexString(pbTransaction->to()) << std::endl; +#endif + // decode + auto decodedTransaction = factory->createTransaction(encodedData, true); + checkTransaction(pbTransaction, decodedTransaction); + return decodedTransaction; +} + +inline Transaction::Ptr fakeTransaction(CryptoSuite::Ptr _cryptoSuite, u256 nonce = 120012323, + int64_t blockLimit = 1000023, std::string chainId = "chainId", std::string groupId = "groupId", + bytes _to = bytes()) +{ + KeyPairInterface::Ptr keyPair = _cryptoSuite->signatureImpl()->generateKeyPair(); + auto to = keyPair->address(_cryptoSuite->hashImpl()).asBytes(); + if (_to != bytes()) + { + to = _to; + } + std::string inputStr = "testTransaction"; + bytes input = asBytes(inputStr); + return fakeTransaction(_cryptoSuite, keyPair, std::string_view((char*)_to.data(), _to.size()), + input, nonce, blockLimit, chainId, groupId); +} +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/testutil/FakeTransactionReceipt.h" "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/testutil/FakeTransactionReceipt.h" new file mode 100644 index 00000000..52438a34 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/bcos-tars-protocol/testutil/FakeTransactionReceipt.h" @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2021 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 FakeTransactionReceipt.h + * @author: yujiechen + * @date: 2021-03-16 + */ +#pragma once +#include "../protocol/TransactionReceiptFactoryImpl.h" +#include "bcos-protocol/TransactionStatus.h" +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::crypto; +using namespace bcos::protocol; + +namespace bcos +{ +namespace test +{ +inline protocol::LogEntriesPtr fakeLogEntries(Hash::Ptr _hashImpl, size_t _size) +{ + auto logEntries = std::make_shared(); + for (size_t i = 0; i < _size; i++) + { + auto topic = _hashImpl->hash(std::to_string(i)); + h256s topics; + topics.push_back(topic); + auto address = right160(topic).asBytes(); + bytes output = topic.asBytes(); + protocol::LogEntry logEntry(address, topics, output); + logEntries->push_back(logEntry); + } + return logEntries; +} + +inline void checkReceipts(Hash::Ptr hashImpl, bcos::protocol::TransactionReceipt::ConstPtr receipt, + TransactionReceipt::ConstPtr decodedReceipt) +{ + // check the decodedReceipt + BOOST_CHECK(decodedReceipt->version() == receipt->version()); + BOOST_CHECK(decodedReceipt->gasUsed() == receipt->gasUsed()); + BOOST_CHECK(decodedReceipt->contractAddress() == receipt->contractAddress()); + BOOST_CHECK(decodedReceipt->status() == receipt->status()); + BOOST_CHECK(decodedReceipt->output().toBytes() == receipt->output().toBytes()); + // BOOST_CHECK(decodedReceipt->hash() == receipt->hash()); + // check LogEntries + BOOST_CHECK(decodedReceipt->logEntries().size() == 2); + BOOST_CHECK(decodedReceipt->logEntries().size() == receipt->logEntries().size()); + auto& logEntry = (decodedReceipt->logEntries())[1]; + auto expectedTopic = hashImpl->hash(std::to_string(1)); + BOOST_CHECK(logEntry.topics()[0] == expectedTopic); + + // BOOST_CHECK(std::string(logEntry.address()) == right160(expectedTopic)); + // BOOST_CHECK(logEntry.data().toBytes() == expectedTopic.asBytes()); +} + +inline TransactionReceipt::Ptr testPBTransactionReceipt( + CryptoSuite::Ptr _cryptoSuite, bool _check = true) +{ + auto hashImpl = _cryptoSuite->hashImpl(); + u256 gasUsed = 12343242342; + auto contractAddress = std::string("5fe3c4c3e2079879a0dba1937aca95ac16e68f0f"); + auto logEntries = fakeLogEntries(hashImpl, 2); + TransactionStatus status = TransactionStatus::BadJumpDestination; + bytes output = toAddress(contractAddress).asBytes(); + for (int i = 0; i < 10; i++) + { + output += toAddress(contractAddress).asBytes(); + } + auto factory = + std::make_shared(_cryptoSuite); + auto receipt = + factory->createReceipt(gasUsed, std::string_view((char*)contractAddress.data(), 20), + logEntries, (int32_t)status, output, 0); + if (!_check) + { + return receipt; + } + // encode + std::shared_ptr encodedData = std::make_shared(); + for (size_t i = 0; i < 2; i++) + { + receipt->encode(*encodedData); + } +#if 0 + std::cout << "##### testPBTransactionReceipt:" + << "encodedData:" << *toHexString(*encodedData) << std::endl; + std::cout << "receipt->output():" << *toHexString(receipt->output()) << std::endl; + std::cout << "receipt->contractAddress():" << *toHexString(receipt->contractAddress()) + << std::endl; + std::cout << "receipt->hash().hex(): " << receipt->hash().hex() << std::endl; + auto& logEntry = (receipt->logEntries())[1]; + std::cout << "(logEntry.topics()[0]).hex():" << (logEntry.topics()[0]).hex() << std::endl; + std::cout << "*toHexString(logEntry.address()):" << *toHexString(logEntry.address()) + << std::endl; + std::cout << "*toHexString(logEntry.data()):" << *toHexString(logEntry.data()) << std::endl; + + std::cout << "##### ScaleReceipt encodeT: " << (utcTime() - start) + << ", encodedData size:" << encodedData->size() << std::endl; +#endif + + auto encodedDataCache = std::make_shared(); + receipt->encode(*encodedDataCache); + BOOST_CHECK(*encodedData == *encodedDataCache); + + // decode + std::shared_ptr decodedReceipt; + for (size_t i = 0; i < 2; i++) + { + decodedReceipt = factory->createReceipt(*encodedData); + } +#if 0 + std::cout << "##### ScaleReceipt decodeT: " << (utcTime() - start) << std::endl; +#endif + checkReceipts(hashImpl, receipt, decodedReceipt); + return decodedReceipt; +} +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-tars-protocol/test/CMakeLists.txt" new file mode 100644 index 00000000..89f098c5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/test/CMakeLists.txt" @@ -0,0 +1,31 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of bcos-tars-protocol +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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(GLOB_RECURSE SOURCES "*.cpp") + +# cmake settings +set(TEST_BINARY_NAME test-bcos-tars-protocol) + +add_executable(${TEST_BINARY_NAME} ${SOURCES}) +target_include_directories(${TEST_BINARY_NAME} PRIVATE . ${CMAKE_SOURCE_DIR}) + +find_package(Boost REQUIRED unit_test_framework) +find_package(jsoncpp REQUIRED) + +target_compile_options(${TEST_BINARY_NAME} PRIVATE -Wno-unused-variable) +target_link_libraries(${TEST_BINARY_NAME} PRIVATE ${TARS_PROTOCOL_TARGET} ${CRYPTO_TARGET} Boost::unit_test_framework jsoncpp_static) +add_test(NAME test-tars-protocol WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/test/ProtocolTest.cpp" "b/BFPL\345\243\271/bcos-tars-protocol/test/ProtocolTest.cpp" new file mode 100644 index 00000000..149cb017 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/test/ProtocolTest.cpp" @@ -0,0 +1,695 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcostars +{ +namespace test +{ +struct Fixture +{ + Fixture() + { + cryptoSuite = + std::make_shared(std::make_shared(), + std::make_shared(), nullptr); + + blockHeaderFactory = + std::make_shared(cryptoSuite); + transactionFactory = + std::make_shared(cryptoSuite); + transactionReceiptFactory = + std::make_shared(cryptoSuite); + blockFactory = std::make_shared( + cryptoSuite, blockHeaderFactory, transactionFactory, transactionReceiptFactory); + } + + bcos::crypto::CryptoSuite::Ptr cryptoSuite; + std::shared_ptr blockHeaderFactory; + std::shared_ptr transactionFactory; + std::shared_ptr transactionReceiptFactory; + std::shared_ptr blockFactory; +}; + +BOOST_FIXTURE_TEST_SUITE(TestProtocol, Fixture) + +inline std::vector fakeSealerList( + std::vector& _keyPairVec, + bcos::crypto::SignatureCrypto::Ptr _signImpl, size_t size) +{ + std::vector sealerList; + for (size_t i = 0; i < size; i++) + { + bcos::crypto::KeyPairInterface::Ptr keyPair = _signImpl->generateKeyPair(); + _keyPairVec.emplace_back(keyPair); + sealerList.emplace_back(*(keyPair->publicKey()->encode())); + } + return sealerList; +} + +BOOST_AUTO_TEST_CASE(transaction) +{ + std::string to("Target"); + bcos::bytes input(bcos::asBytes("Arguments")); + bcos::u256 nonce(800); + + bcostars::protocol::TransactionFactoryImpl factory(cryptoSuite); + auto tx = factory.createTransaction(0, to, input, nonce, 100, "testChain", "testGroup", 1000, + cryptoSuite->signatureImpl()->generateKeyPair()); + + tx->verify(); + BOOST_CHECK(!tx->sender().empty()); + bcos::bytes buffer; + tx->encode(buffer); + + auto decodedTx = factory.createTransaction(buffer, true); + + BOOST_CHECK_EQUAL(tx->hash(), decodedTx->hash()); + BOOST_CHECK_EQUAL(tx->version(), 0); + BOOST_CHECK_EQUAL(tx->to(), to); + BOOST_CHECK_EQUAL(bcos::asString(tx->input()), bcos::asString(input)); + + BOOST_CHECK_EQUAL(tx->nonce(), nonce); + BOOST_CHECK_EQUAL(tx->blockLimit(), 100); + BOOST_CHECK_EQUAL(tx->chainId(), "testChain"); + BOOST_CHECK_EQUAL(tx->groupId(), "testGroup"); + BOOST_CHECK_EQUAL(tx->importTime(), 1000); + BOOST_CHECK_EQUAL(decodedTx->sender(), tx->sender()); + + auto block = blockFactory->createBlock(); + block->appendTransaction(std::move(decodedTx)); + + auto blockTx = block->transaction(0); + BOOST_CHECK_EQUAL(blockTx->sender(), tx->sender()); +} + +BOOST_AUTO_TEST_CASE(transactionMetaData) +{ + bcos::h256 hash("5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9"); + + bcostars::protocol::TransactionMetaDataImpl metaData( + [inner = bcostars::TransactionMetaData()]() mutable { return &inner; }); + metaData.setTo(hash.hex()); + metaData.setTo("Hello world!"); + + tars::TarsOutputStream output; + metaData.inner().writeTo(output); + bcos::bytes buffer; + output.swap(buffer); + + bcostars::protocol::TransactionMetaDataImpl metaData2( + [inner = bcostars::TransactionMetaData()]() mutable { return &inner; }); + tars::TarsInputStream input; + input.setBuffer((const char*)buffer.data(), buffer.size()); + metaData2.mutableInner().readFrom(input); + + BOOST_CHECK_EQUAL(metaData2.hash().hex(), metaData.hash().hex()); + + bcostars::protocol::TransactionMetaDataImpl metaData3(hash, "Hello world!"); + BOOST_CHECK_EQUAL(metaData3.hash().hex(), hash.hex()); + BOOST_CHECK_EQUAL(metaData3.to(), "Hello world!"); +} + +BOOST_AUTO_TEST_CASE(transactionReceipt) +{ + bcos::crypto::HashType stateRoot(bcos::asBytes("root1")); + bcos::u256 gasUsed(8858); + std::string contractAddress("contract Address!"); + + auto logEntries = std::make_shared>(); + for (auto i : {1, 2, 3}) + { + bcos::h256s topics; + for (auto j : {100, 200, 300}) + { + topics.push_back( + bcos::h256(bcos::asBytes("topic: " + boost::lexical_cast(j)))); + } + bcos::protocol::LogEntry entry( + bcos::asBytes("Address: " + boost::lexical_cast(i)), topics, + bcos::asBytes("Data: " + boost::lexical_cast(i))); + logEntries->emplace_back(entry); + } + bcos::bytes output(bcos::asBytes("Output!")); + + bcostars::protocol::TransactionReceiptFactoryImpl factory(cryptoSuite); + auto receipt = factory.createReceipt(gasUsed, contractAddress, + std::make_shared>(*logEntries), 50, output, 888); + + bcos::bytes buffer; + receipt->encode(buffer); + + auto decodedReceipt = factory.createReceipt(buffer); + + BOOST_CHECK_EQUAL(receipt->hash().hex(), decodedReceipt->hash().hex()); + BOOST_CHECK_EQUAL(receipt->version(), 0); + BOOST_CHECK_EQUAL(receipt->gasUsed(), gasUsed); + BOOST_CHECK_EQUAL(receipt->contractAddress(), contractAddress); + BOOST_CHECK_EQUAL(receipt->logEntries().size(), logEntries->size()); + for (auto i = 0u; i < receipt->logEntries().size(); ++i) + { + BOOST_CHECK_EQUAL(receipt->logEntries()[i].address(), (*logEntries)[i].address()); + BOOST_CHECK_EQUAL( + receipt->logEntries()[i].topics().size(), (*logEntries)[i].topics().size()); + for (auto j = 0u; j < receipt->logEntries()[i].topics().size(); ++j) + { + BOOST_CHECK_EQUAL( + receipt->logEntries()[i].topics()[j].hex(), (*logEntries)[i].topics()[j].hex()); + } + BOOST_CHECK_EQUAL( + receipt->logEntries()[i].data().toString(), (*logEntries)[i].data().toString()); + } + + BOOST_CHECK_EQUAL(receipt->status(), 50); + BOOST_CHECK_EQUAL(bcos::asString(receipt->output()), bcos::asString(output)); + BOOST_CHECK_EQUAL(receipt->blockNumber(), 888); +} + +BOOST_AUTO_TEST_CASE(block) +{ + auto block = blockFactory->createBlock(); + block->setVersion(883); + block->setBlockType(bcos::protocol::WithTransactionsHash); + + std::string to("Target"); + bcos::bytes input(bcos::asBytes("Arguments")); + bcos::u256 nonce(100); + + bcos::crypto::HashType stateRoot(bcos::asBytes("root1")); + std::string contractAddress("contract Address!"); + + // set the blockHeader + std::vector keyPairVec; + auto sealerList = fakeSealerList(keyPairVec, cryptoSuite->signatureImpl(), 4); + + auto header = block->blockHeader(); + header->setNumber(100); + header->setGasUsed(1000); + header->setStateRoot(bcos::crypto::HashType("62384386743874")); + header->setTimestamp(500); + + header->setSealerList(gsl::span(sealerList)); + BOOST_CHECK(header->sealerList().size() == 4); + + auto signatureList = std::make_shared>(); + for (int64_t i = 0; i < 2; i++) + { + bcos::protocol::Signature signature; + signature.index = i; + std::string signatureStr = "signature"; + signature.signature = bcos::bytes(signatureStr.begin(), signatureStr.end()); + signatureList->push_back(signature); + } + header->setSignatureList(*signatureList); + BOOST_CHECK(header->signatureList().size() == 2); + header->hash(); + BOOST_CHECK(header->signatureList().size() == 2); + + for (size_t i = 0; i < 100; ++i) + { + auto constHeader = block->blockHeaderConst(); + BOOST_CHECK(constHeader->signatureList().size() == 2); + std::cout << "### getHash:" << constHeader->hash().abridged() << std::endl; + + auto header2 = block->blockHeader(); + BOOST_CHECK(header2->signatureList().size() == 2); + } + + auto logEntries = std::make_shared>(); + for (auto i : {1, 2, 3}) + { + bcos::h256s topics; + for (auto j : {100, 200, 300}) + { + topics.push_back( + bcos::h256(bcos::asBytes("topic: " + boost::lexical_cast(j)))); + } + bcos::protocol::LogEntry entry( + bcos::asBytes("Address: " + boost::lexical_cast(i)), topics, + bcos::asBytes("Data: " + boost::lexical_cast(i))); + logEntries->emplace_back(entry); + } + bcos::bytes output(bcos::asBytes("Output!")); + + for (size_t i = 0; i < 1000; ++i) + { + auto transaction = transactionFactory->createTransaction( + 117, to, input, nonce, i, "testChain", "testGroup", 1000); + block->appendTransaction(transaction); + auto txMetaData = blockFactory->createTransactionMetaData( + transaction->hash(), transaction->hash().abridged()); + block->appendTransactionMetaData(txMetaData); + + auto receipt = transactionReceiptFactory->createReceipt(1000, contractAddress, + std::make_shared>(*logEntries), 50, output, i); + block->appendReceipt(receipt); + } + + bcos::bytes buffer; + BOOST_CHECK_NO_THROW(block->encode(buffer)); + + auto decodedBlock = blockFactory->createBlock(buffer); + + BOOST_CHECK(decodedBlock->blockHeader()->sealerList().size() == header->sealerList().size()); + // ensure the sealerlist lifetime + auto decodedSealerList = decodedBlock->blockHeader()->sealerList(); + for (auto i = 0u; i < decodedSealerList.size(); i++) + { + BOOST_CHECK(decodedSealerList[i] == sealerList[i]); + } + auto decodedBlockHeader = decodedBlock->blockHeader(); + BOOST_CHECK(decodedBlockHeader->signatureList().size() == 2); + + // ensure the blockheader lifetime + for (auto i = 0u; i < decodedBlock->blockHeader()->sealerList().size(); i++) + { + BOOST_CHECK(decodedBlock->blockHeader()->sealerList()[i] == sealerList[i]); + std::cout << "##### decodedSealerList size:" + << decodedBlock->blockHeader()->sealerList()[i].size() << std::endl; + } + BOOST_CHECK_EQUAL(block->blockHeader()->number(), decodedBlock->blockHeader()->number()); + BOOST_CHECK_EQUAL(block->blockHeader()->gasUsed(), decodedBlock->blockHeader()->gasUsed()); + BOOST_CHECK_EQUAL(block->blockHeader()->stateRoot(), decodedBlock->blockHeader()->stateRoot()); + BOOST_CHECK_EQUAL(block->blockHeader()->timestamp(), block->blockHeader()->timestamp()); + + BOOST_CHECK_EQUAL(block->version(), decodedBlock->version()); + BOOST_CHECK_EQUAL(block->blockType(), decodedBlock->blockType()); + + BOOST_CHECK_EQUAL(block->transactionsSize(), decodedBlock->transactionsSize()); + BOOST_CHECK_EQUAL(block->transactionsHashSize(), decodedBlock->transactionsHashSize()); + BOOST_CHECK_EQUAL(block->transactionsMetaDataSize(), decodedBlock->transactionsMetaDataSize()); + std::cout << "transactionsMetaDataSize:" << block->transactionsMetaDataSize() << std::endl; + for (size_t i = 0; i < block->transactionsSize(); ++i) + { + { + auto lhs = block->transaction(i); + auto rhs = decodedBlock->transaction(i); + + // check if transaction hash re-encode + bcos::bytes reencodeBuffer; + rhs->encode(reencodeBuffer); + auto redecodeBlock = transactionFactory->createTransaction(reencodeBuffer, false); + BOOST_CHECK_EQUAL(redecodeBlock->hash().hex(), lhs->hash().hex()); + + BOOST_CHECK_EQUAL(lhs->hash().hex(), rhs->hash().hex()); + BOOST_CHECK_EQUAL(lhs->version(), rhs->version()); + BOOST_CHECK_EQUAL(lhs->to(), rhs->to()); + BOOST_CHECK_EQUAL(bcos::asString(lhs->input()), bcos::asString(rhs->input())); + + BOOST_CHECK_EQUAL(lhs->nonce(), rhs->nonce()); + BOOST_CHECK_EQUAL(lhs->blockLimit(), rhs->blockLimit()); + BOOST_CHECK_EQUAL(lhs->chainId(), rhs->chainId()); + BOOST_CHECK_EQUAL(lhs->groupId(), rhs->groupId()); + BOOST_CHECK_EQUAL(lhs->importTime(), rhs->importTime()); + + // check the txMetaData + BOOST_CHECK_EQUAL(block->transactionMetaData(i)->hash(), + decodedBlock->transactionMetaData(i)->hash()); + BOOST_CHECK_EQUAL( + block->transactionMetaData(i)->to(), decodedBlock->transactionMetaData(i)->to()); + BOOST_CHECK_EQUAL(block->transactionHash(i), block->transactionHash(i)); + } + + { + // ensure the transaction's lifetime + BOOST_CHECK_EQUAL( + block->transaction(i)->hash().hex(), decodedBlock->transaction(i)->hash().hex()); + BOOST_CHECK_EQUAL( + block->transaction(i)->version(), decodedBlock->transaction(i)->version()); + BOOST_CHECK_EQUAL(block->transaction(i)->to(), decodedBlock->transaction(i)->to()); + BOOST_CHECK_EQUAL(bcos::asString(block->transaction(i)->input()), + bcos::asString(decodedBlock->transaction(i)->input())); + + BOOST_CHECK_EQUAL( + block->transaction(i)->nonce(), decodedBlock->transaction(i)->nonce()); + BOOST_CHECK_EQUAL( + block->transaction(i)->blockLimit(), decodedBlock->transaction(i)->blockLimit()); + BOOST_CHECK_EQUAL( + block->transaction(i)->chainId(), decodedBlock->transaction(i)->chainId()); + BOOST_CHECK_EQUAL( + block->transaction(i)->groupId(), decodedBlock->transaction(i)->groupId()); + BOOST_CHECK_EQUAL( + block->transaction(i)->importTime(), decodedBlock->transaction(i)->importTime()); + } + } + + BOOST_CHECK_EQUAL(block->receiptsSize(), decodedBlock->receiptsSize()); + for (size_t i = 0; i < block->receiptsSize(); ++i) + { + { + auto lhs = block->receipt(i); + auto rhs = decodedBlock->receipt(i); + + BOOST_CHECK_EQUAL(lhs->hash().hex(), rhs->hash().hex()); + BOOST_CHECK_EQUAL(lhs->version(), rhs->version()); + BOOST_CHECK_EQUAL(lhs->gasUsed(), rhs->gasUsed()); + BOOST_CHECK_EQUAL(lhs->contractAddress(), rhs->contractAddress()); + BOOST_CHECK_EQUAL(lhs->logEntries().size(), rhs->logEntries().size()); + for (auto i = 0u; i < lhs->logEntries().size(); ++i) + { + BOOST_CHECK_EQUAL(lhs->logEntries()[i].address(), rhs->logEntries()[i].address()); + BOOST_CHECK_EQUAL( + lhs->logEntries()[i].topics().size(), rhs->logEntries()[i].topics().size()); + for (auto j = 0u; j < lhs->logEntries()[i].topics().size(); ++j) + { + BOOST_CHECK_EQUAL(lhs->logEntries()[i].topics()[j].hex(), + rhs->logEntries()[i].topics()[j].hex()); + } + BOOST_CHECK_EQUAL( + lhs->logEntries()[i].data().toString(), rhs->logEntries()[i].data().toString()); + } + + BOOST_CHECK_EQUAL(lhs->status(), rhs->status()); + BOOST_CHECK_EQUAL(bcos::asString(lhs->output()), bcos::asString(rhs->output())); + BOOST_CHECK_EQUAL(lhs->blockNumber(), rhs->blockNumber()); + } + // ensure the receipt's lifetime + { + BOOST_CHECK_EQUAL( + block->receipt(i)->hash().hex(), decodedBlock->receipt(i)->hash().hex()); + BOOST_CHECK_EQUAL(block->receipt(i)->version(), decodedBlock->receipt(i)->version()); + BOOST_CHECK_EQUAL(block->receipt(i)->gasUsed(), decodedBlock->receipt(i)->gasUsed()); + BOOST_CHECK_EQUAL( + block->receipt(i)->contractAddress(), decodedBlock->receipt(i)->contractAddress()); + BOOST_CHECK_EQUAL(block->receipt(i)->logEntries().size(), + decodedBlock->receipt(i)->logEntries().size()); + for (auto i = 0u; i < block->receipt(i)->logEntries().size(); ++i) + { + BOOST_CHECK_EQUAL(block->receipt(i)->logEntries()[i].address(), + decodedBlock->receipt(i)->logEntries()[i].address()); + BOOST_CHECK_EQUAL(block->receipt(i)->logEntries()[i].topics().size(), + decodedBlock->receipt(i)->logEntries()[i].topics().size()); + for (auto j = 0u; j < block->receipt(i)->logEntries()[i].topics().size(); ++j) + { + BOOST_CHECK_EQUAL(block->receipt(i)->logEntries()[i].topics()[j].hex(), + decodedBlock->receipt(i)->logEntries()[i].topics()[j].hex()); + } + BOOST_CHECK_EQUAL(block->receipt(i)->logEntries()[i].data().toString(), + decodedBlock->receipt(i)->logEntries()[i].data().toString()); + } + + BOOST_CHECK_EQUAL(block->receipt(i)->status(), decodedBlock->receipt(i)->status()); + BOOST_CHECK_EQUAL(bcos::asString(block->receipt(i)->output()), + bcos::asString(decodedBlock->receipt(i)->output())); + BOOST_CHECK_EQUAL( + block->receipt(i)->blockNumber(), decodedBlock->receipt(i)->blockNumber()); + } + } +} + +BOOST_AUTO_TEST_CASE(blockHeader) +{ + auto header = blockHeaderFactory->createBlockHeader(); + + BOOST_CHECK_EQUAL(header->gasUsed(), bcos::u256(0)); + + header->setNumber(100); + header->setTimestamp(200); + + bcos::u256 gasUsed(1000); + header->setGasUsed(gasUsed); + + bcos::protocol::ParentInfo parentInfo; + parentInfo.blockHash = bcos::crypto::HashType(10000); + parentInfo.blockNumber = 2000; + + std::vector parentInfoList; + parentInfoList.emplace_back(parentInfo); + + header->setParentInfo(std::move(parentInfoList)); + + bcos::bytes buffer; + header->encode(buffer); + + auto decodedHeader = blockHeaderFactory->createBlockHeader(buffer); + + BOOST_CHECK_EQUAL(header->number(), decodedHeader->number()); + BOOST_CHECK_EQUAL(header->timestamp(), decodedHeader->timestamp()); + BOOST_CHECK_EQUAL(header->gasUsed(), decodedHeader->gasUsed()); + BOOST_CHECK_EQUAL(header->parentInfo().size(), decodedHeader->parentInfo().size()); + for (auto i = 0u; i < decodedHeader->parentInfo().size(); ++i) + { + BOOST_CHECK_EQUAL(bcos::toString(header->parentInfo()[i].blockHash), + bcos::toString(decodedHeader->parentInfo()[i].blockHash)); + BOOST_CHECK_EQUAL( + header->parentInfo()[i].blockNumber, decodedHeader->parentInfo()[i].blockNumber); + } + + BOOST_CHECK_NO_THROW(header->setExtraData(header->extraData().toBytes())); +} + +BOOST_AUTO_TEST_CASE(emptyBlockHeader) +{ + auto blockHeaderFactory = + std::make_shared(cryptoSuite); + auto transactionFactory = + std::make_shared(cryptoSuite); + auto transactionReceiptFactory = + std::make_shared(cryptoSuite); + bcostars::protocol::BlockFactoryImpl blockFactory( + cryptoSuite, blockHeaderFactory, transactionFactory, transactionReceiptFactory); + + auto block = blockFactory.createBlock(); + + BOOST_CHECK_NO_THROW(block->setBlockHeader(nullptr)); +} + +BOOST_AUTO_TEST_CASE(submitResult) +{ + protocol::TransactionSubmitResultImpl submitResult(nullptr); + submitResult.setNonce(bcos::protocol::NonceType("1234567")); + + BOOST_CHECK_EQUAL(submitResult.nonce().str(), "1234567"); +} + +BOOST_AUTO_TEST_CASE(tarsMovable) +{ + bcostars::Transaction tx1; + tx1.data.chainID = "chainID"; + std::string input("input data for test"); + tx1.data.input.assign(input.begin(), input.end()); + + auto addressTx1 = tx1.data.input.data(); + + bcostars::Transaction tx2 = std::move(tx1); + + BOOST_CHECK_EQUAL((intptr_t)addressTx1, (intptr_t)tx2.data.input.data()); + + BOOST_CHECK_EQUAL((intptr_t)tx1.data.input.data(), (intptr_t) nullptr); +} + +BOOST_AUTO_TEST_CASE(testMemberImpl) +{ + auto memberFactory = std::make_shared(); + auto member = memberFactory->createMember(); + std::string memberID = "testID"; + std::string memberConfig = "testConfig"; + member->setMemberID(memberID); + member->setMemberConfig(memberConfig); + BOOST_CHECK(member->memberID() == memberID); + BOOST_CHECK(member->memberConfig() == memberConfig); + + std::string encodedData; + member->encode(encodedData); + + auto member2 = memberFactory->createMember(encodedData); + BOOST_CHECK(member2->memberID() == memberID); + BOOST_CHECK(member2->memberConfig() == memberConfig); + + // test groupInfoCodec + auto groupInfoCodec = std::make_shared(); + std::string chainID = "test_chain"; + std::string groupID = "groupID"; + std::string genesisConfig = "genesis;"; + std::string iniConfig = "ini"; + + // the nodeInfo + std::string nodeName = "node_test"; + std::string nodeID = "node_tid"; + bcos::group::NodeCryptoType nodeCryptoType = bcos::group::NodeCryptoType::SM_NODE; + bcos::protocol::ProtocolInfo protocolInfo; + protocolInfo.setProtocolModuleID(bcos::protocol::ProtocolModuleID::GatewayService); + protocolInfo.setMaxVersion(10); + protocolInfo.setMinVersion(1); + auto groupInfo = std::make_shared(chainID, groupID); + for (int i = 0; i < 3; i++) + { + auto chainNode = std::make_shared(); + chainNode->setNodeName(nodeName + std::to_string(i)); + chainNode->setNodeCryptoType(nodeCryptoType); + chainNode->appendServiceInfo(bcos::protocol::ServiceType::SCHEDULER, "SCHEDULER"); + chainNode->setIniConfig(iniConfig); + chainNode->setNodeID(nodeID); + chainNode->setCompatibilityVersion(10); + chainNode->setNodeType(bcos::protocol::NodeType::CONSENSUS_NODE); + chainNode->setMicroService(true); + chainNode->setNodeProtocol(protocolInfo); + groupInfo->appendNodeInfo(chainNode); + } + groupInfo->setGenesisConfig(genesisConfig); + groupInfo->setIniConfig(iniConfig); + std::string encodedData2; + groupInfoCodec->serialize(encodedData2, groupInfo); + + auto decodedGroupInfo = groupInfoCodec->deserialize(encodedData2); + BOOST_CHECK(decodedGroupInfo->groupID() == groupID); + BOOST_CHECK(decodedGroupInfo->chainID() == chainID); + BOOST_CHECK(decodedGroupInfo->iniConfig() == iniConfig); + BOOST_CHECK(decodedGroupInfo->genesisConfig() == genesisConfig); + BOOST_CHECK(decodedGroupInfo->nodesNum() == 3); + + auto nodesInfo = decodedGroupInfo->nodeInfos(); + auto firstNodeInfo = nodesInfo.at(nodeName + std::to_string(0)); + BOOST_CHECK(firstNodeInfo->nodeCryptoType() == nodeCryptoType); + BOOST_CHECK(firstNodeInfo->iniConfig() == iniConfig); + BOOST_CHECK(firstNodeInfo->nodeID() == nodeID); + BOOST_CHECK(firstNodeInfo->microService() == true); + BOOST_CHECK(firstNodeInfo->compatibilityVersion() == 10); + BOOST_CHECK(firstNodeInfo->nodeType() == bcos::protocol::NodeType::CONSENSUS_NODE); + BOOST_CHECK(firstNodeInfo->serviceName(bcos::protocol::ServiceType::SCHEDULER) == "SCHEDULER"); + + auto decodedProtocolInfo = firstNodeInfo->nodeProtocol(); + BOOST_CHECK(decodedProtocolInfo->protocolModuleID() == protocolInfo.protocolModuleID()); + BOOST_CHECK(decodedProtocolInfo->maxVersion() == protocolInfo.maxVersion()); + BOOST_CHECK(decodedProtocolInfo->minVersion() == protocolInfo.minVersion()); +} + +void checkExecutionMessage(bcostars::protocol::ExecutionMessageImpl::Ptr executionMsg, + bcostars::protocol::ExecutionMessageImpl::Ptr anotherExecutionMsg) +{ + BOOST_CHECK_EQUAL((int8_t)anotherExecutionMsg->type(), executionMsg->type()); + BOOST_CHECK_EQUAL( + anotherExecutionMsg->transactionHash().hex(), executionMsg->transactionHash().hex()); + BOOST_CHECK_EQUAL(anotherExecutionMsg->contextID(), executionMsg->contextID()); + BOOST_CHECK_EQUAL(anotherExecutionMsg->seq(), executionMsg->seq()); + BOOST_CHECK_EQUAL(anotherExecutionMsg->origin(), executionMsg->origin()); + BOOST_CHECK_EQUAL(anotherExecutionMsg->from(), executionMsg->from()); + BOOST_CHECK_EQUAL(anotherExecutionMsg->to(), executionMsg->to()); + BOOST_CHECK_EQUAL(anotherExecutionMsg->abi(), executionMsg->abi()); + BOOST_CHECK_EQUAL(anotherExecutionMsg->depth(), executionMsg->depth()); + BOOST_CHECK_EQUAL(anotherExecutionMsg->create(), executionMsg->create()); + BOOST_CHECK_EQUAL(anotherExecutionMsg->internalCreate(), executionMsg->internalCreate()); + BOOST_CHECK_EQUAL(anotherExecutionMsg->internalCall(), executionMsg->internalCall()); + BOOST_CHECK_EQUAL(anotherExecutionMsg->gasAvailable(), executionMsg->gasAvailable()); + BOOST_CHECK_EQUAL(*(bcos::toHexString(anotherExecutionMsg->data().toBytes())), + *(bcos::toHexString(executionMsg->data().toBytes()))); + BOOST_CHECK_EQUAL(anotherExecutionMsg->staticCall(), executionMsg->staticCall()); + BOOST_CHECK_EQUAL( + anotherExecutionMsg->createSalt().value(), executionMsg->createSalt().value()); + BOOST_CHECK_EQUAL(anotherExecutionMsg->status(), executionMsg->status()); + BOOST_CHECK_EQUAL(anotherExecutionMsg->message(), executionMsg->message()); + + BOOST_CHECK_EQUAL(anotherExecutionMsg->keyLocks().size(), executionMsg->keyLocks().size()); + auto keyLocks = executionMsg->keyLocks(); + auto anotherKeyLocks = anotherExecutionMsg->keyLocks(); + for (int i = 0; i < 10; i++) + { + BOOST_CHECK_EQUAL(keyLocks[i], anotherKeyLocks[i]); + } +} + +BOOST_AUTO_TEST_CASE(testExecutionMessage) +{ + auto executionMsg = std::make_shared(); + int8_t type = 2; + executionMsg->setType((bcos::protocol::ExecutionMessage::Type)type); + executionMsg->transactionHash(); + + auto txsHash = cryptoSuite->hash("###abc"); + executionMsg->setTransactionHash(txsHash); + int64_t contextID = 10000; + executionMsg->setContextID(contextID); + int64_t seq = 123432; + executionMsg->setSeq(seq); + std::string origin = "abcde"; + executionMsg->setOrigin(origin); + std::string from = "##sdksdf"; + executionMsg->setFrom(from); + std::string to = "### to"; + executionMsg->setTo(to); + std::string abi = "abixx"; + executionMsg->setABI(abi); + int32_t depth = 23; + executionMsg->setDepth(depth); + bool create = false; + executionMsg->setCreate(create); + bool internalCreate = true; + executionMsg->setInternalCreate(internalCreate); + bool internalCall = false; + executionMsg->setInternalCall(internalCall); + int64_t gasAvailable = 23423423; + executionMsg->setGasAvailable(gasAvailable); + std::string dataStr = "abdcsd"; + bcos::bytes data(dataStr.begin(), dataStr.end()); + executionMsg->setData(data); + bool staticCall = false; + executionMsg->setStaticCall(staticCall); + bcos::u256 salt(787667543453); + executionMsg->setCreateSalt(salt); + int status = -1000001; + executionMsg->setStatus(status); + std::string message = "abctest"; + executionMsg->setMessage(message); + + // check + BOOST_CHECK_EQUAL((int8_t)executionMsg->type(), type); + BOOST_CHECK_EQUAL(executionMsg->transactionHash().hex(), txsHash.hex()); + BOOST_CHECK_EQUAL(executionMsg->contextID(), contextID); + BOOST_CHECK_EQUAL(executionMsg->seq(), seq); + BOOST_CHECK_EQUAL(executionMsg->origin(), origin); + BOOST_CHECK_EQUAL(executionMsg->from(), from); + BOOST_CHECK_EQUAL(executionMsg->to(), to); + BOOST_CHECK_EQUAL(executionMsg->abi(), abi); + BOOST_CHECK_EQUAL(executionMsg->depth(), depth); + BOOST_CHECK_EQUAL(executionMsg->create(), create); + BOOST_CHECK_EQUAL(executionMsg->internalCreate(), internalCreate); + BOOST_CHECK_EQUAL(executionMsg->internalCall(), internalCall); + BOOST_CHECK_EQUAL(executionMsg->gasAvailable(), gasAvailable); + BOOST_CHECK_EQUAL( + *(bcos::toHexString(executionMsg->data().toBytes())), *(bcos::toHexString(data))); + BOOST_CHECK_EQUAL(executionMsg->staticCall(), staticCall); + BOOST_CHECK_EQUAL(executionMsg->createSalt().value(), salt); + BOOST_CHECK_EQUAL(executionMsg->status(), status); + BOOST_CHECK_EQUAL(executionMsg->message(), message); + + std::vector keyLocks; + for (int i = 0; i < 10; i++) + { + keyLocks.emplace_back("keyLock" + std::to_string(i)); + } + executionMsg->setKeyLocks(keyLocks); + BOOST_CHECK_EQUAL(executionMsg->keyLocks().size(), 10); + auto keyLocksData = executionMsg->keyLocks(); + for (int i = 0; i < 10; i++) + { + BOOST_CHECK_EQUAL(keyLocksData[i], "keyLock" + std::to_string(i)); + } + auto anotherExecutionMsg = std::make_shared( + [m_inner = executionMsg->inner()]() mutable { return &m_inner; }); + checkExecutionMessage(anotherExecutionMsg, executionMsg); +} +BOOST_AUTO_TEST_SUITE_END() + +} // namespace test +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/test/ProxyCallbackTest.cpp" "b/BFPL\345\243\271/bcos-tars-protocol/test/ProxyCallbackTest.cpp" new file mode 100644 index 00000000..cb2830f7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/test/ProxyCallbackTest.cpp" @@ -0,0 +1,149 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for TarsServantProxyCallback + * @author: octopus + * @date 2022-07-24 + */ +#include "bcos-tars-protocol/client/GatewayServiceClient.h" +#include "bcos-tars-protocol/client/LedgerServiceClient.h" +#include "bcos-tars-protocol/client/PBFTServiceClient.h" +#include "bcos-tars-protocol/client/RpcServiceClient.h" +#include "bcos-tars-protocol/client/SchedulerServiceClient.h" +#include "bcos-tars-protocol/client/TxPoolServiceClient.h" +#include "bcos-tars-protocol/tars/GatewayService.h" +#include "bcos-utilities/Exceptions.h" +#include "fisco-bcos-tars-service/Common/TarsUtils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::test; +namespace bcostars +{ +namespace test +{ + +BOOST_FIXTURE_TEST_SUITE(TarsServantProxyCallbackTest, TestPromptFixture) +BOOST_AUTO_TEST_CASE(testTarsServantProxyCallbackTest) +{ + tars::TC_Endpoint ep("127.0.0.1", 1111, 0); + tars::TC_Endpoint ep0("127.0.0.2", 1112, 0); + tars::TC_Endpoint ep1("127.0.0.3", 1113, 0); + + std::string serviceName = "HelloApp.HelloServer.HelloObj"; + + TarsServantProxyCallback cb(serviceName); + + int conCount = 0; + cb.setOnConnectHandler([&conCount](const tars::TC_Endpoint& _ep) { conCount++; }); + + int closeCount = 0; + cb.setOnCloseHandler([&closeCount](const tars::TC_Endpoint& _ep) { closeCount++; }); + + std::this_thread::sleep_for(std::chrono::seconds(3)); + + auto activeEndpoints = cb.activeEndpoints(); + auto inactiveEndpoints = cb.inactiveEndpoints(); + + BOOST_CHECK_EQUAL(cb.serviceName(), serviceName); + + BOOST_CHECK_EQUAL(activeEndpoints.size(), 0); + BOOST_CHECK_EQUAL(inactiveEndpoints.size(), 0); + BOOST_CHECK(!cb.available()); + + auto p = cb.addActiveEndpoint(ep); + BOOST_CHECK(p.first); + BOOST_CHECK(p.second == 1); + BOOST_CHECK_EQUAL(cb.activeEndpoints().size(), 1); + + p = cb.addActiveEndpoint(ep); + BOOST_CHECK(!p.first); + BOOST_CHECK(p.second == 1); + BOOST_CHECK_EQUAL(cb.activeEndpoints().size(), 1); + + p = cb.addActiveEndpoint(ep); + BOOST_CHECK(!p.first); + BOOST_CHECK(p.second == 1); + BOOST_CHECK_EQUAL(cb.activeEndpoints().size(), 1); + + p = cb.addInactiveEndpoint(ep); + BOOST_CHECK(p.first); + BOOST_CHECK_EQUAL(p.second, 1); + BOOST_CHECK_EQUAL(cb.activeEndpoints().size(), 0); + BOOST_CHECK_EQUAL(cb.inactiveEndpoints().size(), 1); + + p = cb.addInactiveEndpoint(ep); + BOOST_CHECK(!p.first); + BOOST_CHECK(p.second == 1); + BOOST_CHECK_EQUAL(cb.activeEndpoints().size(), 0); + BOOST_CHECK_EQUAL(cb.inactiveEndpoints().size(), 1); + + p = cb.addInactiveEndpoint(ep); + BOOST_CHECK(!p.first); + BOOST_CHECK(p.second == 1); + BOOST_CHECK_EQUAL(cb.activeEndpoints().size(), 0); + BOOST_CHECK_EQUAL(cb.inactiveEndpoints().size(), 1); + + BOOST_CHECK(!cb.available()); + + cb.onConnect(ep); + BOOST_CHECK(cb.available()); + BOOST_CHECK_EQUAL(conCount, 1); + + cb.onConnect(ep0); + BOOST_CHECK(cb.available()); + BOOST_CHECK_EQUAL(conCount, 2); + + cb.onConnect(ep1); + BOOST_CHECK(cb.available()); + BOOST_CHECK_EQUAL(conCount, 3); + + activeEndpoints = cb.activeEndpoints(); + inactiveEndpoints = cb.inactiveEndpoints(); + + BOOST_CHECK_EQUAL(activeEndpoints.size(), 3); + BOOST_CHECK_EQUAL(inactiveEndpoints.size(), 0); + + cb.onClose(ep); + BOOST_CHECK(cb.available()); + BOOST_CHECK_EQUAL(closeCount, 1); + + cb.onClose(ep0); + BOOST_CHECK(cb.available()); + BOOST_CHECK_EQUAL(closeCount, 2); + + cb.onClose(ep1); + BOOST_CHECK(!cb.available()); + BOOST_CHECK_EQUAL(closeCount, 3); + + activeEndpoints = cb.activeEndpoints(); + inactiveEndpoints = cb.inactiveEndpoints(); + + BOOST_CHECK_EQUAL(activeEndpoints.size(), 0); + BOOST_CHECK_EQUAL(inactiveEndpoints.size(), 3); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace test +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/test/ServiceClientTest.cpp" "b/BFPL\345\243\271/bcos-tars-protocol/test/ServiceClientTest.cpp" new file mode 100644 index 00000000..5654690c --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/test/ServiceClientTest.cpp" @@ -0,0 +1,137 @@ +/** + * Copyright (C) 2021 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. + * + * @brief test for ServiceClient + * @file ServiceClientTest.h + * @author: yujiechen + * @date 2021-10-13 + */ +#include "bcos-tars-protocol/client/GatewayServiceClient.h" +#include "bcos-tars-protocol/client/LedgerServiceClient.h" +#include "bcos-tars-protocol/client/PBFTServiceClient.h" +#include "bcos-tars-protocol/client/RpcServiceClient.h" +#include "bcos-tars-protocol/client/SchedulerServiceClient.h" +#include "bcos-tars-protocol/client/TxPoolServiceClient.h" +#include "bcos-utilities/Exceptions.h" +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::test; +namespace bcostars +{ +namespace test +{ + +#if 0 + +BOOST_FIXTURE_TEST_SUITE(testServiceClient, TestPromptFixture) +BOOST_AUTO_TEST_CASE(testGatewayService) +{ + bcostars::GatewayServicePrx proxy; + std::make_shared(proxy, "", nullptr); +} + +BOOST_AUTO_TEST_CASE(testPBFTService) +{ + bcostars::PBFTServicePrx proxy; + std::make_shared(proxy); +} + +BOOST_AUTO_TEST_CASE(testRpcService) +{ + bcostars::RpcServicePrx proxy; + std::make_shared(proxy, ""); +} + +BOOST_AUTO_TEST_CASE(testTxPoolService) +{ + bcostars::TxPoolServicePrx proxy; + std::make_shared(proxy, nullptr, nullptr); +} +BOOST_AUTO_TEST_CASE(testLedgerService) +{ + bcostars::LedgerServicePrx prx; + std::make_shared(prx, nullptr); +} +BOOST_AUTO_TEST_CASE(testSchedulerService) +{ + bcostars::SchedulerServicePrx prx; + std::make_shared(prx, nullptr); +} + +BOOST_AUTO_TEST_CASE(testEndPointToString) +{ + { + std::string serviceName = "HelloApp.HelloServant.HelloObj"; + std::string host = "127.0.0.1"; + uint16_t port = 1111; + + auto r = endPointToString(serviceName, host, port); + BOOST_CHECK_EQUAL(r, "HelloApp.HelloServant.HelloObj@tcp -h 127.0.0.1 -p 1111"); + } + + { + std::string serviceName = "HelloApp.HelloServant.HelloObj"; + std::string host = "127.0.0.1"; + uint16_t port = 12345; + + tars::TC_Endpoint endPoint(host, port, 0); + + auto r = endPointToString(serviceName, host, port); + BOOST_CHECK_EQUAL(r, "HelloApp.HelloServant.HelloObj@tcp -h 127.0.0.1 -p 12345"); + } + + { + std::string serviceName = "HelloApp.HelloServant.HelloObj"; + std::vector endPoints; + BOOST_CHECK_THROW(endPointToString(serviceName, endPoints), bcos::InvalidParameter); + } + + { + std::string serviceName = "HelloApp.HelloServant.HelloObj"; + + std::string host0 = "127.0.0.1"; + uint16_t port0 = 11111; + tars::TC_Endpoint endPoint0(host0, port0, 0); + + std::string host1 = "127.0.0.2"; + uint16_t port1 = 22222; + tars::TC_Endpoint endPoint1(host1, port1, 0); + + std::string host2 = "127.0.0.3"; + uint16_t port2 = 33333; + tars::TC_Endpoint endPoint2(host2, port2, 0); + + std::vector endPoints; + endPoints.push_back(endPoint0); + endPoints.push_back(endPoint1); + endPoints.push_back(endPoint2); + + auto r = endPointToString(serviceName, endPoints); + BOOST_CHECK_EQUAL( + "HelloApp.HelloServant.HelloObj@tcp -h 127.0.0.1 -p 11111:tcp -h 127.0.0.2 -p " + "22222:tcp -h 127.0.0.3 -p 33333", + r); + } +} + +BOOST_AUTO_TEST_SUITE_END() + +#endif +} // namespace test +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tars-protocol/test/main.cpp" "b/BFPL\345\243\271/bcos-tars-protocol/test/main.cpp" new file mode 100644 index 00000000..0e27edc0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tars-protocol/test/main.cpp" @@ -0,0 +1,3 @@ +#define BOOST_TEST_MAIN + +#include \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tool/CMakeLists.txt" "b/BFPL\345\243\271/bcos-tool/CMakeLists.txt" new file mode 100644 index 00000000..9c8329c4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tool/CMakeLists.txt" @@ -0,0 +1,50 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for bcos-tool +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 bcos-tool +# 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. +#------------------------------------------------------------------------------ + +cmake_minimum_required(VERSION 3.10) +set(CMAKE_OSX_DEPLOYMENT_TARGET "11.3" CACHE STRING "Minimum OS X deployment version") + +include(Version) +project(bcos-tool VERSION ${VERSION}) + +find_package(Boost REQUIRED COMPONENTS serialization) +find_package(jsoncpp CONFIG REQUIRED) +find_package(tarscpp REQUIRED) +find_package(TBB REQUIRED) + +file(GLOB SRCS bcos-tool/*.cpp) +add_library(${TOOL_TARGET} ${SRCS}) +target_include_directories(${TOOL_TARGET} PUBLIC + $ +) +target_link_libraries(${TOOL_TARGET} PUBLIC ${TABLE_TARGET} ${UTILITIES_TARGET} bcos-framework jsoncpp_static tarscpp::tarsservant tarscpp::tarsutil TBB::tbb) + +if(TESTS) + enable_testing() + set(ENV{CTEST_OUTPUT_ON_FAILURE} True) + add_subdirectory(test) +endif() + +# for code coverage +if (COVERAGE) + include(Coverage) + config_coverage("tool-cov" "'/usr*' '${CMAKE_CURRENT_SOURCE_DIR}/bcos-cmake-scripts*' '${CMAKE_CURRENT_SOURCE_DIR}/test/bcos-test*'") +endif () + +include(GNUInstallDirs) +install(TARGETS ${TOOL_TARGET} EXPORT fiscobcosTargets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tool/bcos-tool/BfsFileFactory.cpp" "b/BFPL\345\243\271/bcos-tool/bcos-tool/BfsFileFactory.cpp" new file mode 100644 index 00000000..f5ea2836 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tool/bcos-tool/BfsFileFactory.cpp" @@ -0,0 +1,166 @@ +/** + * 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 BfsFileFactory.cpp + * @author: kyonGuo + * @date 2022/10/18 + */ + +#include "BfsFileFactory.h" +#include +#include +#include +#include + +using namespace bcos::tool; +using namespace bcos::storage; + +void BfsFileFactory::buildAsync(bcos::storage::StorageInterface::Ptr const& _storage, + std::string_view fileName, FileType fileType, std::function _callback) +{ + _storage->asyncOpenTable(fileName, + [_callback = std::move(_callback), fileType](auto&& _error, std::optional
&& _table) { + if (_error) + { + _callback(std::forward(_error)); + return; + } + if (!_table.has_value()) + { + _callback(BCOS_ERROR_UNIQUE_PTR(-1, "Table not exist")); + return; + } + bool buildRet = false; + switch (fileType) + { + case DIRECTOR: + buildRet = buildDir(_table.value()); + break; + case LINK: + buildRet = buildLink(_table.value(), "", ""); + break; + case AUTH: + buildRet = buildAuth(_table.value(), ""); + break; + case CONTRACT: + buildRet = buildContract(_table.value()); + break; + } + _callback(buildRet ? nullptr : BCOS_ERROR_UNIQUE_PTR(-1, "Build BFS file error.")); + }); +} + +std::optional
BfsFileFactory::createDir( + const bcos::storage::StorageInterface::Ptr& _storage, std::string _table) +{ + std::promise>> createPromise; + _storage->asyncCreateTable(std::move(_table), std::string(FS_DIR_FIELDS), + [&createPromise](auto&& error, std::optional
&& _table) { + createPromise.set_value({std::forward(error), std::move(_table)}); + }); + auto [createError, table] = createPromise.get_future().get(); + if (createError) + { + BOOST_THROW_EXCEPTION(*createError); + } + return table; +} + +bool BfsFileFactory::buildDir(Table& _table) +{ + return false; +} + +void BfsFileFactory::buildDirEntry( + storage::Entry& _mutableEntry, std::variant fileType) +{ + std::string_view type; + std::visit( + [&type](const auto& _type) { + using T = std::decay_t; + if constexpr (std::is_same_v) + { + type = _type; + } + else if constexpr (std::is_same_v) + { + if (_type == FileType::DIRECTOR) + type = FS_TYPE_DIR; + else if (_type == FileType::LINK) + type = FS_TYPE_LINK; + else + type = FS_TYPE_CONTRACT; + } + }, + fileType); + _mutableEntry.setObject>({type.data(), "0", "0", "", "", ""}); +} + +bool BfsFileFactory::buildLink( + Table& _table, const std::string& _address, const std::string& _abi, const std::string& name) +{ + Entry tEntry; + tEntry.importFields({std::string(FS_TYPE_LINK)}); + _table.setRow(FS_KEY_TYPE, std::move(tEntry)); + + Entry linkEntry; + linkEntry.importFields({_address}); + _table.setRow(FS_LINK_ADDRESS, std::move(linkEntry)); + + if (!_abi.empty()) + { + BCOS_LOG(TRACE) << LOG_BADGE("BFS") << "buildLink with abi" + << LOG_KV("abiSize", _abi.size()); + Entry abiEntry; + abiEntry.importFields({_abi}); + _table.setRow(FS_LINK_ABI, std::move(abiEntry)); + } + + if (!name.empty()) + { + Entry nameEntry; + nameEntry.importFields({name}); + _table.setRow(FS_KEY_NAME, std::move(nameEntry)); + } + + return true; +} +bool BfsFileFactory::buildAuth(Table& _table, const std::string& _admin) +{ + Entry adminEntry; + adminEntry.importFields({_admin}); + _table.setRow(ADMIN_FIELD, std::move(adminEntry)); + + Entry statusEntry; + statusEntry.importFields({"normal"}); + _table.setRow(STATUS_FIELD, std::move(statusEntry)); + + Entry emptyType; + emptyType.importFields({""}); + _table.setRow(METHOD_AUTH_TYPE, std::move(emptyType)); + + Entry emptyWhite; + emptyWhite.importFields({""}); + _table.setRow(METHOD_AUTH_WHITE, std::move(emptyWhite)); + + Entry emptyBlack; + emptyBlack.importFields({""}); + _table.setRow(METHOD_AUTH_BLACK, std::move(emptyBlack)); + return true; +} +bool BfsFileFactory::buildContract(Table& _table) +{ + return false; +} diff --git "a/BFPL\345\243\271/bcos-tool/bcos-tool/BfsFileFactory.h" "b/BFPL\345\243\271/bcos-tool/bcos-tool/BfsFileFactory.h" new file mode 100644 index 00000000..c6c32844 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tool/bcos-tool/BfsFileFactory.h" @@ -0,0 +1,107 @@ +/** + * 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 BfsFileFactory.h + * @author: kyonGuo + * @date 2022/10/18 + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace bcos::tool +{ +/// BFS base dir +constexpr static const std::string_view FS_ROOT{"/"}; +constexpr static const std::string_view FS_APPS{"/apps"}; +constexpr static const std::string_view FS_USER{"/usr"}; +constexpr static const std::string_view FS_SYS_BIN{"/sys"}; +constexpr static const std::string_view FS_USER_TABLE{"/tables"}; +constexpr static const uint8_t FS_ROOT_SUB_COUNT = 5; +constexpr static const std::array FS_ROOT_SUBS = { + FS_ROOT, FS_APPS, FS_USER, FS_USER_TABLE, FS_SYS_BIN}; + +// not use in version > 3.1.0 +constexpr static const std::string_view FS_KEY_SUB{"sub"}; +constexpr static const std::string_view FS_KEY_NAME{"name"}; +constexpr static const std::string_view FS_KEY_TYPE{"type"}; +constexpr static const std::string_view FS_KEY_STAT{"status"}; +constexpr static const std::string_view FS_ACL_TYPE{"acl_type"}; +constexpr static const std::string_view FS_ACL_WHITE{"acl_white"}; +constexpr static const std::string_view FS_ACL_BLACK{"acl_black"}; +constexpr static const std::string_view FS_KEY_EXTRA{"extra"}; +constexpr static const std::array FS_FIELDS = { + FS_KEY_TYPE, FS_KEY_STAT, FS_ACL_TYPE, FS_ACL_WHITE, FS_ACL_BLACK, FS_KEY_EXTRA}; +// static const auto FS_DIR_FIELDS = boost::join( +// FS_FIELDS | RANGES::views::transform([](auto& str) -> std::string { return std::string(str); +// }), +// ","); +constexpr static const std::string_view FS_DIR_FIELDS{ + "type,status,acl_type,acl_white,acl_black,extra"}; + +/// BFS file type +constexpr static const std::string_view FS_TYPE_DIR{"directory"}; +constexpr static const std::string_view FS_TYPE_CONTRACT{"contract"}; +constexpr static const std::string_view FS_TYPE_LINK{"link"}; + +/// BFS link type +constexpr static const std::string_view FS_LINK_ADDRESS{"link_address"}; +constexpr static const std::string_view FS_LINK_ABI{"link_abi"}; + +/// auth +constexpr static const std::string_view CONTRACT_SUFFIX{"_accessAuth"}; +constexpr static const std::string_view ADMIN_FIELD{"admin"}; +constexpr static const std::string_view STATUS_FIELD{"status"}; +constexpr static const std::string_view METHOD_AUTH_TYPE{"method_auth_type"}; +constexpr static const std::string_view METHOD_AUTH_WHITE{"method_auth_white"}; +constexpr static const std::string_view METHOD_AUTH_BLACK{"method_auth_black"}; + +enum FileType : uint16_t +{ + DIRECTOR = 0, + LINK = 1, + AUTH = 2, + CONTRACT = 3, +}; +class BfsFileFactory +{ +public: + BfsFileFactory() = delete; + ~BfsFileFactory() = default; + BfsFileFactory(const BfsFileFactory&) = delete; + BfsFileFactory(BfsFileFactory&&) noexcept = delete; + BfsFileFactory& operator=(const BfsFileFactory&) = delete; + BfsFileFactory& operator=(BfsFileFactory&&) noexcept = delete; + static void buildAsync(bcos::storage::StorageInterface::Ptr const& _storage, + std::string_view fileName, FileType fileType, + std::function _callback); + + static bool buildDir(storage::Table& _table); + // sync create dir + static std::optional createDir( + bcos::storage::StorageInterface::Ptr const& _storage, std::string _table); + static void buildDirEntry( + storage::Entry& _mutableEntry, std::variant fileType); + static bool buildLink(storage::Table& _table, const std::string& _address, + const std::string& _abi, const std::string& name = ""); + static bool buildAuth(storage::Table& _table, const std::string& _admin); + static bool buildContract(storage::Table& _table); +}; +} // namespace bcos::tool diff --git "a/BFPL\345\243\271/bcos-tool/bcos-tool/ConsensusNode.h" "b/BFPL\345\243\271/bcos-tool/bcos-tool/ConsensusNode.h" new file mode 100644 index 00000000..f3e4fe4e --- /dev/null +++ "b/BFPL\345\243\271/bcos-tool/bcos-tool/ConsensusNode.h" @@ -0,0 +1,70 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::ledger +{ +struct ConsensusNode +{ + ConsensusNode(){}; + ConsensusNode(std::string _nodeID, u256 _weight, std::string _type, std::string _enableNumber) + : nodeID(std::move(_nodeID)), + weight(_weight), + type(std::move(_type)), + enableNumber(std::move(_enableNumber)) + {} + + std::string nodeID; + u256 weight; + std::string type; + std::string enableNumber; + + template + void serialize(Archive& ar, const unsigned int version) + { + boost::ignore_unused(version); + ar& nodeID; + ar& weight; + ar& type; + ar& enableNumber; + } +}; + +using ConsensusNodeList = std::vector; + +inline ConsensusNodeList decodeConsensusList(const std::string_view& value) +{ + boost::iostreams::stream inputStream( + value.data(), value.size()); + boost::archive::binary_iarchive archive(inputStream, + boost::archive::no_header | boost::archive::no_codecvt | boost::archive::no_tracking); + + ConsensusNodeList consensusList; + archive >> consensusList; + + return consensusList; +} + +inline std::string encodeConsensusList(const ConsensusNodeList& consensusList) +{ + std::string value; + boost::iostreams::stream> outputStream(value); + boost::archive::binary_oarchive archive(outputStream, + boost::archive::no_header | boost::archive::no_codecvt | boost::archive::no_tracking); + + archive << consensusList; + outputStream.flush(); + + return value; +} + +} // namespace bcos::ledger \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tool/bcos-tool/Exceptions.h" "b/BFPL\345\243\271/bcos-tool/bcos-tool/Exceptions.h" new file mode 100644 index 00000000..c70e1bb1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tool/bcos-tool/Exceptions.h" @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021 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. + * + * @brief exceptions for tool + * @file Exceptions.h + * @author: yujiechen + * @date: 2021-03-16 + */ +#pragma once +#include +namespace bcos +{ +namespace tool +{ +DERIVE_BCOS_EXCEPTION(LedgerConfigFetcherException); +DERIVE_BCOS_EXCEPTION(InvalidConfig); +DERIVE_BCOS_EXCEPTION(InvalidVersion); + +class ExceptionHolder +{ +public: + auto run(std::invocable auto&& func) noexcept + { + try + { + return func(); + } + catch (...) + { + m_exception = std::current_exception(); + } + } + + void rethrow() + { + if (m_exception) + { + std::rethrow_exception(m_exception); + } + } + +private: + std::exception_ptr m_exception; +}; +} // namespace tool +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-tool/bcos-tool/LedgerConfigFetcher.cpp" "b/BFPL\345\243\271/bcos-tool/bcos-tool/LedgerConfigFetcher.cpp" new file mode 100644 index 00000000..7e48b45a --- /dev/null +++ "b/BFPL\345\243\271/bcos-tool/bcos-tool/LedgerConfigFetcher.cpp" @@ -0,0 +1,204 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Public function to get information from Ledger + * @file LedgerConfigFetcher.cpp + * @author: yujiechen + * @date 2021-05-19 + */ +#include "LedgerConfigFetcher.h" +#include "Exceptions.h" +#include "VersionConverter.h" +#include +#include +#include +#include +using namespace bcos::protocol; +using namespace bcos::crypto; +using namespace bcos::consensus; +using namespace bcos::tool; +using namespace bcos::ledger; + +void LedgerConfigFetcher::fetchBlockNumberAndHash() +{ + std::promise> blockNumberPromise; + m_ledger->asyncGetBlockNumber([&blockNumberPromise](Error::Ptr _error, BlockNumber _number) { + blockNumberPromise.set_value(std::make_pair(_error, _number)); + }); + auto ret = blockNumberPromise.get_future().get(); + auto error = ret.first; + if (error) + { + TOOL_LOG(WARNING) << LOG_DESC("LedgerConfigFetcher: fetchBlockNumber failed") + << LOG_KV("errorCode", error->errorCode()) + << LOG_KV("errorMessage", error->errorMessage()); + BOOST_THROW_EXCEPTION(LedgerConfigFetcherException() + << errinfo_comment("LedgerConfigFetcher: fetchBlockNumber failed ")); + } + auto blockNumber = ret.second; + m_ledgerConfig->setBlockNumber(blockNumber); + TOOL_LOG(INFO) << LOG_DESC("LedgerConfigFetcher: fetchBlockNumber success") + << LOG_KV("blockNumber", blockNumber); + // fetch blockHash + auto hash = fetchBlockHash(blockNumber); + TOOL_LOG(INFO) << LOG_DESC("LedgerConfigFetcher: fetchBlockHash success") + << LOG_KV("blockNumber", blockNumber) << LOG_KV("hash", hash.abridged()); + m_ledgerConfig->setHash(hash); +} + +void LedgerConfigFetcher::fetchGenesisHash() +{ + m_genesisHash = fetchBlockHash(0); + TOOL_LOG(INFO) << LOG_DESC("fetchGenesisHash success") + << LOG_KV("genesisHash", m_genesisHash.abridged()); +} + +HashType LedgerConfigFetcher::fetchBlockHash(BlockNumber _blockNumber) +{ + std::promise> hashPromise; + m_ledger->asyncGetBlockHashByNumber( + _blockNumber, [&hashPromise](Error::Ptr _error, HashType _hash) { + hashPromise.set_value(std::make_pair(_error, _hash)); + }); + auto result = hashPromise.get_future().get(); + auto error = result.first; + if (error) + { + TOOL_LOG(WARNING) << LOG_DESC("LedgerConfigFetcher: fetchBlockHash failed") + << LOG_KV("errorCode", error->errorCode()) + << LOG_KV("errorMessage", error->errorMessage()) + << LOG_KV("number", _blockNumber); + BOOST_THROW_EXCEPTION(LedgerConfigFetcherException() + << errinfo_comment("LedgerConfigFetcher: fetchBlockHash failed ")); + } + return result.second; +} + + +std::string LedgerConfigFetcher::fetchSystemConfig(std::string_view _key) +{ + std::promise> systemConfigPromise; + m_ledger->asyncGetSystemConfigByKey(_key, + [&systemConfigPromise](Error::Ptr _error, std::string _sysValue, BlockNumber _blockNumber) { + systemConfigPromise.set_value(std::tuple(_error, _sysValue, _blockNumber)); + }); + auto ret = systemConfigPromise.get_future().get(); + auto error = std::get<0>(ret); + if (error) + { + TOOL_LOG(WARNING) << LOG_DESC("fetchSystemConfig failed") + << LOG_KV("errorCode", error->errorCode()) + << LOG_KV("errorMessage", error->errorMessage()) << LOG_KV("key", _key); + BOOST_THROW_EXCEPTION( + LedgerConfigFetcherException() + << errinfo_comment("LedgerConfigFetcher: fetchSystemConfig for " + std::string{_key} + " failed")); + } + return std::get<1>(ret); +} + +ConsensusNodeListPtr LedgerConfigFetcher::fetchNodeListByNodeType(std::string_view _type) +{ + std::promise> nodeListPromise; + m_ledger->asyncGetNodeListByType( + _type, [&nodeListPromise](Error::Ptr _error, ConsensusNodeListPtr _nodes) { + nodeListPromise.set_value(std::make_pair(_error, _nodes)); + }); + auto ret = nodeListPromise.get_future().get(); + auto error = ret.first; + if (error) + { + TOOL_LOG(WARNING) << LOG_DESC("fetchNodeListByNodeType failed") << LOG_KV("type", _type) + << LOG_KV("code", error->errorCode()) + << LOG_KV("msg", error->errorMessage()); + BOOST_THROW_EXCEPTION( + LedgerConfigFetcherException() << errinfo_comment( + "LedgerConfigFetcher: fetchNodeListByNodeType of type " + std::string{_type} + " failed")); + } + return ret.second; +} + +void LedgerConfigFetcher::fetchConsensusNodeList() +{ + auto consensusNodeList = fetchNodeListByNodeType(CONSENSUS_SEALER); + TOOL_LOG(INFO) << LOG_DESC("fetchConsensusNodeList success") + << LOG_KV("size", consensusNodeList->size()); + m_ledgerConfig->setConsensusNodeList(*consensusNodeList); +} + +void LedgerConfigFetcher::fetchObserverNodeList() +{ + auto observerList = fetchNodeListByNodeType(CONSENSUS_OBSERVER); + TOOL_LOG(INFO) << LOG_DESC("fetchObserverNodeList success") + << LOG_KV("size", observerList->size()); + m_ledgerConfig->setObserverNodeList(*observerList); +} + +void LedgerConfigFetcher::fetchConsensusLeaderPeriod() +{ + auto ret = fetchSystemConfig(SYSTEM_KEY_CONSENSUS_LEADER_PERIOD); + TOOL_LOG(INFO) << LOG_DESC("fetchConsensusLeaderPeriod success") << LOG_KV("value", ret); + m_ledgerConfig->setLeaderSwitchPeriod(boost::lexical_cast(ret)); +} + +void LedgerConfigFetcher::fetchBlockTxCountLimit() +{ + auto ret = fetchSystemConfig(SYSTEM_KEY_TX_COUNT_LIMIT); + TOOL_LOG(INFO) << LOG_DESC("fetchBlockTxCountLimit success") << LOG_KV("value", ret); + m_ledgerConfig->setBlockTxCountLimit(boost::lexical_cast(ret)); +} + +void LedgerConfigFetcher::fetchNonceList(BlockNumber _startNumber, int64_t _offset) +{ + std::promise>>> + noncePromise; + m_ledger->asyncGetNonceList(_startNumber, _offset, + [&noncePromise]( + Error::Ptr _error, std::shared_ptr> _nonceList) { + noncePromise.set_value(std::make_pair(_error, _nonceList)); + }); + auto ret = noncePromise.get_future().get(); + auto error = ret.first; + if (error) + { + TOOL_LOG(WARNING) << LOG_DESC("LedgerConfigFetcher: fetchNonceList failed") + << LOG_KV("errorCode", error->errorCode()) + << LOG_KV("errorMsg", error->errorMessage()) + << LOG_KV("startNumber", _startNumber) << LOG_KV("offset", _offset); + BOOST_THROW_EXCEPTION(LedgerConfigFetcherException() << errinfo_comment( + "LedgerConfigFetcher: fetchNonceList failed, start: " + + boost::lexical_cast(_startNumber) + + ", offset:" + boost::lexical_cast(_offset))); + } + m_nonceList = ret.second; +} + +void LedgerConfigFetcher::fetchCompatibilityVersion() +{ + TOOL_LOG(INFO) << LOG_DESC("fetchCompatibilityVersion"); + auto versionStr = fetchSystemConfig(SYSTEM_KEY_COMPATIBILITY_VERSION); + if (versionStr.empty()) + { + m_ledgerConfig->setCompatibilityVersion((uint32_t)(bcos::protocol::DEFAULT_VERSION)); + TOOL_LOG(INFO) << LOG_DESC("fetchCompatibilityVersion: empty version, use " + + bcos::protocol::V3_1_VERSION_STR + " as default version."); + return; + } + auto version = toVersionNumber(versionStr); + m_ledgerConfig->setCompatibilityVersion(version); + TOOL_LOG(INFO) << LOG_DESC("fetchCompatibilityVersion success") << LOG_KV("version", versionStr) + << LOG_KV("versionNumber", version) + << LOG_KV("minSupportedVersion", g_BCOSConfig.minSupportedVersion()) + << LOG_KV("maxSupportedVersion", g_BCOSConfig.maxSupportedVersion()); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tool/bcos-tool/LedgerConfigFetcher.h" "b/BFPL\345\243\271/bcos-tool/bcos-tool/LedgerConfigFetcher.h" new file mode 100644 index 00000000..8b7b4a1f --- /dev/null +++ "b/BFPL\345\243\271/bcos-tool/bcos-tool/LedgerConfigFetcher.h" @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Public function to get information from Ledger + * @file LedgerConfigFetcher.h + * @author: yujiechen + * @date 2021-05-19 + */ +#pragma once +#include "bcos-framework/ledger/LedgerConfig.h" +#include "bcos-framework/ledger/LedgerInterface.h" +#include + +#define TOOL_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("TOOL") + +namespace bcos +{ +namespace tool +{ +class LedgerConfigFetcher : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + explicit LedgerConfigFetcher(bcos::ledger::LedgerInterface::Ptr _ledger) + : m_ledger(_ledger), m_ledgerConfig(std::make_shared()) + {} + + virtual ~LedgerConfigFetcher() {} + + virtual void fetchBlockNumberAndHash(); + virtual void fetchConsensusNodeList(); + virtual void fetchObserverNodeList(); + virtual void fetchBlockTxCountLimit(); + virtual void fetchGenesisHash(); + virtual void fetchNonceList(protocol::BlockNumber _startNumber, int64_t _offset); + virtual void fetchConsensusLeaderPeriod(); + + // consensus_leader_period + virtual bcos::ledger::LedgerConfig::Ptr ledgerConfig() { return m_ledgerConfig; } + virtual std::shared_ptr> nonceList() + { + return m_nonceList; + } + virtual bcos::crypto::HashType const& genesisHash() const { return m_genesisHash; } + + virtual void fetchCompatibilityVersion(); + + virtual bcos::crypto::HashType fetchBlockHash(bcos::protocol::BlockNumber _blockNumber); + +protected: + virtual std::string fetchSystemConfig(std::string_view _key); + virtual bcos::consensus::ConsensusNodeListPtr fetchNodeListByNodeType(std::string_view _type); + + bcos::ledger::LedgerInterface::Ptr m_ledger; + bcos::ledger::LedgerConfig::Ptr m_ledgerConfig; + std::shared_ptr> m_nonceList; + bcos::crypto::HashType m_genesisHash; +}; +} // namespace tool +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tool/bcos-tool/NodeConfig.cpp" "b/BFPL\345\243\271/bcos-tool/bcos-tool/NodeConfig.cpp" new file mode 100644 index 00000000..832d2c6d --- /dev/null +++ "b/BFPL\345\243\271/bcos-tool/bcos-tool/NodeConfig.cpp" @@ -0,0 +1,874 @@ +/** + * Copyright (C) 2021 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. + * + * @brief configuration for the node + * @file NodeConfig.cpp + * @author: yujiechen + * @date 2021-06-10 + */ +#include "NodeConfig.h" +#include "VersionConverter.h" +#include "bcos-framework/bcos-framework/protocol/Protocol.h" +#include "bcos-framework/consensus/ConsensusNode.h" +#include "bcos-framework/ledger/LedgerTypeDef.h" +#include "bcos-framework/protocol/ServiceDesc.h" +#include "bcos-utilities/BoostLog.h" +#include "bcos-utilities/FileUtility.h" +#include "fisco-bcos-tars-service/Common/TarsUtils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_BLOCK_LIMIT 5000 + +using namespace bcos; +using namespace bcos::crypto; +using namespace bcos::tool; +using namespace bcos::consensus; +using namespace bcos::ledger; +using namespace bcos::protocol; + +NodeConfig::NodeConfig(KeyFactory::Ptr _keyFactory) + : m_keyFactory(_keyFactory), m_ledgerConfig(std::make_shared()) +{} + +void NodeConfig::loadConfig( + boost::property_tree::ptree const& _pt, bool _enforceMemberID, bool _enforceChainConfig) +{ + // if version < 3.1.0, config.ini include chainConfig + if (_enforceChainConfig || + (m_compatibilityVersion < (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION && + m_compatibilityVersion >= (uint32_t)bcos::protocol::BlockVersion::MIN_VERSION)) + { + loadChainConfig(_pt); + } + loadCertConfig(_pt); + loadRpcConfig(_pt); + loadGatewayConfig(_pt); + loadTxPoolConfig(_pt); + loadStorageSecurityConfig(_pt); + + loadFailOverConfig(_pt, _enforceMemberID); + loadSecurityConfig(_pt); + loadSealerConfig(_pt); + loadStorageConfig(_pt); + loadConsensusConfig(_pt); + loadOthersConfig(_pt); +} + +void NodeConfig::loadGenesisConfig(boost::property_tree::ptree const& _genesisConfig) +{ + // if version >= 3.1.0, genesisBlock include chainConfig + m_compatibilityVersionStr = _genesisConfig.get( + "version.compatibility_version", bcos::protocol::RC4_VERSION_STR); + m_compatibilityVersion = toVersionNumber(m_compatibilityVersionStr); + if (m_compatibilityVersion >= (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION) + { + loadChainConfig(_genesisConfig); + } + loadLedgerConfig(_genesisConfig); + loadExecutorConfig(_genesisConfig); + generateGenesisData(); +} + +std::string NodeConfig::getServiceName(boost::property_tree::ptree const& _pt, + std::string const& _configSection, std::string const& _objName, + std::string const& _defaultValue, bool _require) +{ + auto serviceName = _pt.get(_configSection, _defaultValue); + if (!_require) + { + return serviceName; + } + checkService(_configSection, serviceName); + return getPrxDesc(serviceName, _objName); +} + +void NodeConfig::loadRpcServiceConfig(boost::property_tree::ptree const& _pt) +{ + // rpc service name + m_rpcServiceName = getServiceName(_pt, "service.rpc", RPC_SERVANT_NAME); + NodeConfig_LOG(INFO) << LOG_DESC("loadServiceConfig") + << LOG_KV("rpcServiceName", m_rpcServiceName); +} + +void NodeConfig::loadGatewayServiceConfig(boost::property_tree::ptree const& _pt) +{ + // gateway service name + m_gatewayServiceName = getServiceName(_pt, "service.gateway", GATEWAY_SERVANT_NAME); + NodeConfig_LOG(INFO) << LOG_DESC("loadServiceConfig") + << LOG_KV("gatewayServiceName", m_gatewayServiceName); +} +void NodeConfig::loadServiceConfig(boost::property_tree::ptree const& _pt) +{ + loadGatewayServiceConfig(_pt); + loadRpcServiceConfig(_pt); + + /* + [service] + without_tars_framework = true + tars_proxy_conf = tars_proxy.ini + */ + + auto withoutTarsFramework = _pt.get("service.without_tars_framework", false); + m_withoutTarsFramework = withoutTarsFramework; + + NodeConfig_LOG(INFO) << LOG_DESC("loadServiceConfig") + << LOG_KV("withoutTarsFramework", m_withoutTarsFramework); + + if (m_withoutTarsFramework) + { + std::string tarsProxyConf = + _pt.get("service.tars_proxy_conf", "./tars_proxy.ini"); + loadTarsProxyConfig(tarsProxyConf); + } +} + +void NodeConfig::loadWithoutTarsFrameworkConfig(boost::property_tree::ptree const& _pt) +{ + /* + [service] + without_tars_framework = true + tars_proxy_conf = conf/tars_proxy.ini + */ + + auto withoutTarsFramework = _pt.get("service.without_tars_framework", false); + m_withoutTarsFramework = withoutTarsFramework; + + NodeConfig_LOG(INFO) << LOG_DESC("loadWithoutTarsFrameworkConfig") + << LOG_KV("withoutTarsFramework", m_withoutTarsFramework); +} + +void NodeConfig::loadNodeServiceConfig( + std::string const& _nodeID, boost::property_tree::ptree const& _pt, bool _require) +{ + auto nodeName = _pt.get("service.node_name", ""); + if (nodeName.size() == 0) + { + nodeName = _nodeID; + } + if (!isalNumStr(nodeName)) + { + BOOST_THROW_EXCEPTION( + InvalidConfig() << errinfo_comment("The node name must be number or digit")); + } + + /* + [service] + without_tars_framework = true + tars_proxy_conf = conf/tars_proxy.ini + */ + + auto withoutTarsFramework = _pt.get("service.without_tars_framework", false); + m_withoutTarsFramework = withoutTarsFramework; + + NodeConfig_LOG(INFO) << LOG_DESC("loadNodeServiceConfig") + << LOG_KV("withoutTarsFramework", m_withoutTarsFramework); + + if (m_withoutTarsFramework) + { + std::string tarsProxyConf = + _pt.get("service.tars_proxy_conf", "conf/tars_proxy.ini"); + loadTarsProxyConfig(tarsProxyConf); + } + + m_nodeName = nodeName; + m_schedulerServiceName = getServiceName(_pt, "service.scheduler", SCHEDULER_SERVANT_NAME, + getDefaultServiceName(nodeName, SCHEDULER_SERVICE_NAME), _require); + m_executorServiceName = getServiceName(_pt, "service.executor", EXECUTOR_SERVANT_NAME, + getDefaultServiceName(nodeName, EXECUTOR_SERVICE_NAME), _require); + m_txpoolServiceName = getServiceName(_pt, "service.txpool", TXPOOL_SERVANT_NAME, + getDefaultServiceName(nodeName, TXPOOL_SERVICE_NAME), _require); + + NodeConfig_LOG(INFO) << LOG_DESC("load node service") << LOG_KV("nodeName", m_nodeName) + << LOG_KV("withoutTarsFramework", m_withoutTarsFramework) + << LOG_KV("schedulerServiceName", m_schedulerServiceName) + << LOG_KV("executorServiceName", m_executorServiceName); +} + +void NodeConfig::loadTarsProxyConfig(const std::string& _tarsProxyConf) +{ + boost::property_tree::ptree pt; + try + { + boost::property_tree::read_ini(_tarsProxyConf, pt); + + loadServiceTarsProxyConfig("front", pt); + loadServiceTarsProxyConfig("rpc", pt); + loadServiceTarsProxyConfig("gateway", pt); + loadServiceTarsProxyConfig("executor", pt); + loadServiceTarsProxyConfig("txpool", pt); + loadServiceTarsProxyConfig("scheduler", pt); + loadServiceTarsProxyConfig("pbft", pt); + loadServiceTarsProxyConfig("ledger", pt); + + NodeConfig_LOG(INFO) << LOG_BADGE("loadTarsProxyConfig") + << LOG_KV("service endpoints size", m_tarsSN2EndPoints.size()); + } + catch (const std::exception& e) + { + NodeConfig_LOG(ERROR) << LOG_BADGE("loadTarsProxyConfig") + << LOG_DESC("load tars proxy config failed") << LOG_KV("e", e.what()) + << LOG_KV("tarsProxyConf", _tarsProxyConf); + + BOOST_THROW_EXCEPTION(InvalidParameter() << errinfo_comment( + "Load tars proxy config failed, e: " + std::string(e.what()))); + } +} + +void NodeConfig::loadServiceTarsProxyConfig( + const std::string& _serviceName, boost::property_tree::ptree const& _pt) +{ + if (!_pt.get_child_optional(_serviceName)) + { + NodeConfig_LOG(WARNING) << LOG_BADGE("loadServiceTarsProxyConfig") + << LOG_DESC("service name not exist") + << LOG_KV("serviceName", _serviceName); + return; + } + + for (auto const& it : _pt.get_child(_serviceName)) + { + if (it.first.find("proxy.") != 0) + { + continue; + } + + std::string data = it.second.data(); + + // string to endpoint + tars::TC_Endpoint endpoint = bcostars::string2TarsEndPoint(data); + m_tarsSN2EndPoints[_serviceName].push_back(endpoint); + + NodeConfig_LOG(INFO) << LOG_BADGE("loadTarsProxyConfig") << LOG_DESC("add element") + << LOG_KV("serviceName", _serviceName) + << LOG_KV("endpoint", endpoint.toString()); + } + + NodeConfig_LOG(INFO) << LOG_BADGE("loadTarsProxyConfig") << LOG_KV("serviceName", _serviceName) + << LOG_KV("endpoints size", m_tarsSN2EndPoints[_serviceName].size()); +} + +// +void NodeConfig::getTarsClientProxyEndpoints( + const std::string& _clientPrx, std::vector& _endpoints) +{ + if (!m_withoutTarsFramework) + { + NodeConfig_LOG(TRACE) << LOG_BADGE("getTarsClientProxyEndpoints") + << "not work with tars rpc" + << LOG_KV("withoutTarsFramework", m_withoutTarsFramework); + return; + } + + _endpoints.clear(); + + auto it = m_tarsSN2EndPoints.find(boost::to_lower_copy(_clientPrx)); + if (it != m_tarsSN2EndPoints.end()) + { + _endpoints = it->second; + + NodeConfig_LOG(INFO) << LOG_BADGE("getTarsClientProxyEndpoints") + << LOG_DESC("find tars client proxy endpoints") + << LOG_KV("serviceName", _clientPrx) + << LOG_KV("endpoints size", _endpoints.size()); + } + + if (_endpoints.empty()) + { + NodeConfig_LOG(WARNING) << LOG_BADGE("getTarsClientProxyEndpoints") + << LOG_DESC("can not find tars client proxy endpoints") + << LOG_KV("serviceName", _clientPrx); + + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment( + ("Can't find tars client proxy endpoints, serviceName : " + _clientPrx))); + } +} + +void NodeConfig::checkService(std::string const& _serviceType, std::string const& _serviceName) +{ + if (_serviceName.empty()) + { + BOOST_THROW_EXCEPTION( + InvalidConfig() << errinfo_comment("Must set service name for " + _serviceType + "!")); + } + std::vector serviceNameList; + boost::split(serviceNameList, _serviceName, boost::is_any_of(".")); + std::string errorMsg = + "Must set service name in format of application_name.server_name with only include letters " + "and numbers for " + + _serviceType + ", invalid config now is:" + _serviceName; + if (serviceNameList.size() != 2) + { + BOOST_THROW_EXCEPTION(InvalidConfig() << errinfo_comment(errorMsg)); + } + for (const auto& serviceName : serviceNameList) + { + if (!isalNumStr(serviceName)) + { + BOOST_THROW_EXCEPTION(InvalidConfig() << errinfo_comment(errorMsg)); + } + } +} + +void NodeConfig::loadRpcConfig(boost::property_tree::ptree const& _pt) +{ + /* + [rpc] + listen_ip=0.0.0.0 + listen_port=30300 + thread_count=16 + sm_ssl=false + disable_ssl=false + */ + 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); + bool smSsl = _pt.get("rpc.sm_ssl", false); + bool disableSsl = _pt.get("rpc.disable_ssl", false); + + m_rpcListenIP = listenIP; + m_rpcListenPort = listenPort; + m_rpcThreadPoolSize = threadCount; + m_rpcDisableSsl = disableSsl; + m_rpcSmSsl = smSsl; + + NodeConfig_LOG(INFO) << LOG_DESC("loadRpcConfig") << LOG_KV("listenIP", listenIP) + << LOG_KV("listenPort", listenPort) << LOG_KV("listenPort", listenPort) + << LOG_KV("smSsl", smSsl) << LOG_KV("disableSsl", disableSsl); +} + +void NodeConfig::loadGatewayConfig(boost::property_tree::ptree const& _pt) +{ + /* + [p2p] + listen_ip=0.0.0.0 + listen_port=30300 + sm_ssl=false + nodes_path=./ + nodes_file=nodes.json + */ + std::string listenIP = _pt.get("p2p.listen_ip", "0.0.0.0"); + int listenPort = _pt.get("p2p.listen_port", 30300); + std::string nodesDir = _pt.get("p2p.nodes_path", "./"); + std::string nodesFile = _pt.get("p2p.nodes_file", "nodes.json"); + bool smSsl = _pt.get("p2p.sm_ssl", false); + + m_p2pListenIP = listenIP; + m_p2pListenPort = listenPort; + m_p2pNodeDir = nodesDir; + m_p2pSmSsl = smSsl; + m_p2pNodeFileName = nodesFile; + + NodeConfig_LOG(INFO) << LOG_DESC("loadGatewayConfig") << LOG_KV("listenIP", listenIP) + << LOG_KV("listenPort", listenPort) << LOG_KV("listenPort", listenPort) + << LOG_KV("smSsl", smSsl) << LOG_KV("nodesFile", nodesFile); +} + +void NodeConfig::loadCertConfig(boost::property_tree::ptree const& _pt) +{ + /* + [cert] + ; directory the certificates located in + ca_path=./ + ; the ca certificate file + ca_cert=ca.crt + ; the node private key file + node_key=ssl.key + ; the node certificate file + node_cert=ssl.crt + + or + + [cert] + ; directory the certificates located in + ca_path=./ + ; the ca certificate file + sm_ca_cert=sm_ca.crt + ; the node private key file + sm_node_key=sm_ssl.key + ; the node certificate file + sm_node_cert=sm_ssl.crt + ; the node private key file + sm_ennode_key=sm_enssl.key + ; the node certificate file + sm_ennode_cert=sm_enssl.crt + */ + + // load sm cert + m_certPath = _pt.get("cert.ca_path", "./"); + + std::string smCaCertFile = + m_certPath + "/" + _pt.get("cert.sm_ca_cert", "sm_ca.crt"); + std::string smNodeCertFile = + m_certPath + "/" + _pt.get("cert.sm_node_cert", "sm_ssl.crt"); + std::string smNodeKeyFile = + m_certPath + "/" + _pt.get("cert.sm_node_key", "sm_ssl.key"); + std::string smEnNodeCertFile = + m_certPath + "/" + _pt.get("cert.sm_ennode_cert", "sm_enssl.crt"); + std::string smEnNodeKeyFile = + m_certPath + "/" + _pt.get("cert.sm_ennode_key", "sm_enssl.key"); + + m_smCaCert = smCaCertFile; + m_smNodeCert = smNodeCertFile; + m_smNodeKey = smNodeKeyFile; + m_enSmNodeCert = smEnNodeCertFile; + m_enSmNodeKey = smEnNodeKeyFile; + + NodeConfig_LOG(INFO) << LOG_DESC("loadCertConfig") << LOG_KV("ca_path", m_certPath) + << LOG_KV("sm_ca_cert", smCaCertFile) + << LOG_KV("sm_node_cert", smNodeCertFile) + << LOG_KV("sm_node_key", smNodeKeyFile) + << LOG_KV("sm_ennode_cert", smEnNodeCertFile) + << LOG_KV("sm_ennode_key", smEnNodeKeyFile); + + // load cert + std::string caCertFile = m_certPath + "/" + _pt.get("cert.ca_cert", "ca.crt"); + std::string nodeCertFile = m_certPath + "/" + _pt.get("cert.node_cert", "ssl.crt"); + std::string nodeKeyFile = m_certPath + "/" + _pt.get("cert.node_key", "ssl.key"); + + m_caCert = caCertFile; + m_nodeCert = nodeCertFile; + m_nodeKey = nodeKeyFile; + + NodeConfig_LOG(INFO) << LOG_DESC("loadCertConfig") << LOG_KV("ca_path", m_certPath) + << LOG_KV("ca_cert", caCertFile) << LOG_KV("node_cert", nodeCertFile) + << LOG_KV("node_key", nodeKeyFile); +} + +// load the txpool related params +void NodeConfig::loadTxPoolConfig(boost::property_tree::ptree const& _pt) +{ + m_txpoolLimit = checkAndGetValue(_pt, "txpool.limit", "15000"); + if (m_txpoolLimit <= 0) + { + BOOST_THROW_EXCEPTION( + InvalidConfig() << errinfo_comment("Please set txpool.limit to positive !")); + } + m_notifyWorkerNum = checkAndGetValue(_pt, "txpool.notify_worker_num", "2"); + if (m_notifyWorkerNum <= 0) + { + BOOST_THROW_EXCEPTION(InvalidConfig() << errinfo_comment( + "Please set txpool.notify_worker_num to positive !")); + } + m_verifierWorkerNum = checkAndGetValue( + _pt, "txpool.verify_worker_num", std::to_string(std::thread::hardware_concurrency())); + if (m_verifierWorkerNum <= 0) + { + BOOST_THROW_EXCEPTION(InvalidConfig() << errinfo_comment( + "Please set txpool.verify_worker_num to positive !")); + } + // the txs expiration time, in second + auto txsExpirationTime = checkAndGetValue(_pt, "txpool.txs_expiration_time", "600"); + if (txsExpirationTime * 1000 <= DEFAULT_MIN_CONSENSUS_TIME_MS) + { + NodeConfig_LOG(WARNING) + << LOG_DESC( + "loadTxPoolConfig: the configured txs_expiration_time is smaller than default " + "consensus time, reset to the consensus time") + << LOG_KV("txsExpirationTime(seconds)", txsExpirationTime) + << LOG_KV("defaultConsTime", DEFAULT_MIN_CONSENSUS_TIME_MS); + m_txsExpirationTime = DEFAULT_MIN_CONSENSUS_TIME_MS; + } + else + { + m_txsExpirationTime = txsExpirationTime * 1000; + } + NodeConfig_LOG(INFO) << LOG_DESC("loadTxPoolConfig") << LOG_KV("txpoolLimit", m_txpoolLimit) + << LOG_KV("notifierWorkers", m_notifyWorkerNum) + << LOG_KV("verifierWorkers", m_verifierWorkerNum) + << LOG_KV("txsExpirationTime(ms)", m_txsExpirationTime); +} + +void NodeConfig::loadChainConfig(boost::property_tree::ptree const& _pt) +{ + m_smCryptoType = _pt.get("chain.sm_crypto", false); + m_groupId = _pt.get("chain.group_id", "group"); + m_chainId = _pt.get("chain.chain_id", "chain"); + if (!isalNumStr(m_chainId)) + { + BOOST_THROW_EXCEPTION( + InvalidConfig() << errinfo_comment("The chainId must be number or digit")); + } + m_blockLimit = checkAndGetValue(_pt, "chain.block_limit", "1000"); + if (m_blockLimit <= 0 || m_blockLimit > MAX_BLOCK_LIMIT) + { + BOOST_THROW_EXCEPTION(InvalidConfig() << errinfo_comment( + "Please set chain.block_limit to positive and less than " + + std::to_string(MAX_BLOCK_LIMIT) + " !")); + } + NodeConfig_LOG(INFO) << METRIC << LOG_DESC("loadChainConfig") + << LOG_KV("smCrypto", m_smCryptoType) << LOG_KV("chainId", m_chainId) + << LOG_KV("groupId", m_groupId) << LOG_KV("blockLimit", m_blockLimit); +} + +void NodeConfig::loadSecurityConfig(boost::property_tree::ptree const& _pt) +{ + m_privateKeyPath = _pt.get("security.private_key_path", "node.pem"); + NodeConfig_LOG(INFO) << LOG_DESC("loadSecurityConfig") + << LOG_KV("privateKeyPath", m_privateKeyPath); +} + +void NodeConfig::loadSealerConfig(boost::property_tree::ptree const& _pt) +{ + m_minSealTime = checkAndGetValue(_pt, "consensus.min_seal_time", "500"); + if (m_minSealTime <= 0 || m_minSealTime > 3000) + { + BOOST_THROW_EXCEPTION(InvalidConfig() << errinfo_comment( + "Please set consensus.min_seal_time between 1 and 3000!")); + } + NodeConfig_LOG(INFO) << LOG_DESC("loadSealerConfig") << LOG_KV("minSealTime", m_minSealTime); +} + +void NodeConfig::loadStorageSecurityConfig(boost::property_tree::ptree const& _pt) +{ + m_storageSecurityEnable = _pt.get("storage_security.enable", false); + if (!m_storageSecurityEnable) + { + return; + } + std::string storageSecurityKeyCenterUrl = + _pt.get("storage_security.key_center_url", ""); + + std::vector values; + boost::split( + values, storageSecurityKeyCenterUrl, boost::is_any_of(":"), boost::token_compress_on); + if (2 != values.size()) + { + BOOST_THROW_EXCEPTION( + InvalidParameter() << errinfo_comment( + "initGlobalConfig storage_security failed! Invalid key_center_url!")); + } + + m_storageSecurityKeyCenterIp = values[0]; + m_storageSecurityKeyCenterPort = boost::lexical_cast(values[1]); + if (false == isValidPort(m_storageSecurityKeyCenterPort)) + { + BOOST_THROW_EXCEPTION( + InvalidConfig() << errinfo_comment( + "initGlobalConfig storage_security failed! Invalid key_manange_port!")); + } + + m_storageSecurityCipherDataKey = _pt.get("storage_security.cipher_data_key", ""); + if (true == m_storageSecurityCipherDataKey.empty()) + { + BOOST_THROW_EXCEPTION( + InvalidConfig() << errinfo_comment("Please provide cipher_data_key!")); + } + NodeConfig_LOG(INFO) << LOG_DESC("loadStorageSecurityConfig") + << LOG_KV("keyCenterUrl", storageSecurityKeyCenterUrl); +} + +void NodeConfig::loadStorageConfig(boost::property_tree::ptree const& _pt) +{ + m_storagePath = _pt.get("storage.data_path", "data/" + m_groupId); + m_storageType = _pt.get("storage.type", "RocksDB"); + m_keyPageSize = _pt.get("storage.key_page_size", 10240); + m_pdCaPath = _pt.get("storage.pd_ssl_ca_path", ""); + m_pdCertPath = _pt.get("storage.pd_ssl_cert_path", ""); + m_pdKeyPath = _pt.get("storage.pd_ssl_key_path", ""); + + if (m_keyPageSize < 4096 || m_keyPageSize > (1 << 25)) + { + BOOST_THROW_EXCEPTION( + InvalidConfig() << errinfo_comment("Please set storage.key_page_size in 4K~32M")); + } + auto pd_addrs = _pt.get("storage.pd_addrs", "127.0.0.1:2379"); + boost::split(m_pd_addrs, pd_addrs, boost::is_any_of(",")); + m_enableLRUCacheStorage = _pt.get("storage.enable_cache", true); + m_cacheSize = _pt.get("storage.cache_size", DEFAULT_CACHE_SIZE); + NodeConfig_LOG(INFO) << LOG_DESC("loadStorageConfig") << LOG_KV("storagePath", m_storagePath) + << LOG_KV("KeyPage", m_keyPageSize) << LOG_KV("storageType", m_storageType) + << LOG_KV("pd_addrs", pd_addrs) + << LOG_KV("enableLRUCacheStorage", m_enableLRUCacheStorage); +} + +// Note: In components that do not require failover, do not need to set member_id +void NodeConfig::loadFailOverConfig(boost::property_tree::ptree const& _pt, bool _enforceMemberID) +{ + // only enable leaderElection when using tikv + m_enableFailOver = _pt.get("failover.enable", false); + if (!m_enableFailOver) + { + return; + } + m_failOverClusterUrl = _pt.get("failover.cluster_url", "127.0.0.1:2379"); + m_memberID = _pt.get("failover.member_id", ""); + if (m_memberID.size() == 0 && _enforceMemberID) + { + BOOST_THROW_EXCEPTION( + InvalidConfig() << errinfo_comment("Please set failover.member_id must be non-empty ")); + } + m_leaseTTL = + checkAndGetValue(_pt, "failover.lease_ttl", std::to_string(DEFAULT_MIN_LEASE_TTL_SECONDS)); + if (m_leaseTTL < DEFAULT_MIN_LEASE_TTL_SECONDS) + { + BOOST_THROW_EXCEPTION(InvalidConfig() << errinfo_comment( + "Please set failover.lease_ttl to no less than " + + std::to_string(DEFAULT_MIN_LEASE_TTL_SECONDS) + " seconds!")); + } + + NodeConfig_LOG(INFO) << LOG_DESC("loadFailOverConfig") + << LOG_KV("failOverClusterUrl", m_failOverClusterUrl) + << LOG_KV("memberID", m_memberID.size() > 0 ? m_memberID : "not-set") + << LOG_KV("leaseTTL", m_leaseTTL) + << LOG_KV("enableFailOver", m_enableFailOver); +} + +void NodeConfig::loadOthersConfig(boost::property_tree::ptree const& _pt) +{ + // + m_sendTxTimeout = _pt.get("others.send_tx_timeout", -1); + + NodeConfig_LOG(INFO) << LOG_DESC("loadOthersConfig") + << LOG_KV("sendTxTimeout", m_sendTxTimeout); +} + +void NodeConfig::loadConsensusConfig(boost::property_tree::ptree const& _pt) +{ + m_checkPointTimeoutInterval = checkAndGetValue( + _pt, "consensus.checkpoint_timeout", std::to_string(DEFAULT_MIN_CONSENSUS_TIME_MS)); + if (m_checkPointTimeoutInterval < DEFAULT_MIN_CONSENSUS_TIME_MS) + { + BOOST_THROW_EXCEPTION(InvalidConfig() << errinfo_comment( + "Please set consensus.checkpoint_timeout to no less than " + + std::to_string(DEFAULT_MIN_CONSENSUS_TIME_MS) + "ms!")); + } + NodeConfig_LOG(INFO) << LOG_DESC("loadConsensusConfig") + << LOG_KV("checkPointTimeoutInterval", m_checkPointTimeoutInterval); +} + +void NodeConfig::loadLedgerConfig(boost::property_tree::ptree const& _genesisConfig) +{ + // consensus type + m_consensusType = _genesisConfig.get("consensus.consensus_type", "pbft"); + // blockTxCountLimit + auto blockTxCountLimit = + checkAndGetValue(_genesisConfig, "consensus.block_tx_count_limit", "1000"); + if (blockTxCountLimit <= 0) + { + BOOST_THROW_EXCEPTION(InvalidConfig() << errinfo_comment( + "Please set consensus.block_tx_count_limit to positive!")); + } + m_ledgerConfig->setBlockTxCountLimit(blockTxCountLimit); + // txGasLimit + auto txGasLimit = checkAndGetValue(_genesisConfig, "tx.gas_limit", "3000000000"); + if (txGasLimit <= TX_GAS_LIMIT_MIN) + { + BOOST_THROW_EXCEPTION( + InvalidConfig() << errinfo_comment( + "Please set tx.gas_limit to more than " + std::to_string(TX_GAS_LIMIT_MIN) + " !")); + } + + m_txGasLimit = txGasLimit; + // the compatibility version + m_compatibilityVersionStr = _genesisConfig.get( + "version.compatibility_version", bcos::protocol::RC4_VERSION_STR); + // must call here to check the compatibility_version + m_compatibilityVersion = toVersionNumber(m_compatibilityVersionStr); + // sealerList + auto consensusNodeList = parseConsensusNodeList(_genesisConfig, "consensus", "node."); + if (!consensusNodeList || consensusNodeList->empty()) + { + BOOST_THROW_EXCEPTION(InvalidConfig() << errinfo_comment("Must set sealerList!")); + } + m_ledgerConfig->setConsensusNodeList(*consensusNodeList); + + // leaderSwitchPeriod + auto consensusLeaderPeriod = checkAndGetValue(_genesisConfig, "consensus.leader_period", "1"); + if (consensusLeaderPeriod <= 0) + { + BOOST_THROW_EXCEPTION( + InvalidConfig() << errinfo_comment("Please set consensus.leader_period to positive!")); + } + m_ledgerConfig->setLeaderSwitchPeriod(consensusLeaderPeriod); + NodeConfig_LOG(INFO) << LOG_DESC("loadLedgerConfig") + << LOG_KV("consensus_type", m_consensusType) + << LOG_KV("block_tx_count_limit", m_ledgerConfig->blockTxCountLimit()) + << LOG_KV("gas_limit", m_txGasLimit) + << LOG_KV("leader_period", m_ledgerConfig->leaderSwitchPeriod()) + << LOG_KV("minSealTime", m_minSealTime) + << LOG_KV("compatibilityVersion", + (bcos::protocol::BlockVersion)m_compatibilityVersion); +} + +ConsensusNodeListPtr NodeConfig::parseConsensusNodeList(boost::property_tree::ptree const& _pt, + std::string const& _sectionName, std::string const& _subSectionName) +{ + if (!_pt.get_child_optional(_sectionName)) + { + NodeConfig_LOG(DEBUG) << LOG_DESC("parseConsensusNodeList return for empty config") + << LOG_KV("sectionName", _sectionName); + return nullptr; + } + auto nodeList = std::make_shared(); + for (auto const& it : _pt.get_child(_sectionName)) + { + if (it.first.find(_subSectionName) != 0) + { + continue; + } + std::string data = it.second.data(); + std::vector nodeInfo; + boost::split(nodeInfo, data, boost::is_any_of(":")); + if (nodeInfo.size() == 0) + { + BOOST_THROW_EXCEPTION( + InvalidConfig() << errinfo_comment( + "Uninitialized nodeInfo, key: " + it.first + ", value: " + data)); + } + std::string nodeId = nodeInfo[0]; + boost::to_lower(nodeId); + int64_t weight = 1; + if (nodeInfo.size() == 2) + { + auto& weightInfoStr = nodeInfo[1]; + boost::trim(weightInfoStr); + weight = boost::lexical_cast(weightInfoStr); + } + if (weight <= 0) + { + BOOST_THROW_EXCEPTION(InvalidConfig() << errinfo_comment( + "Please set weight for " + nodeId + " to positive!")); + } + auto consensusNode = std::make_shared( + m_keyFactory->createKey(*fromHexString(nodeId)), weight); + NodeConfig_LOG(INFO) << LOG_BADGE("parseConsensusNodeList") + << LOG_KV("sectionName", _sectionName) << LOG_KV("nodeId", nodeId) + << LOG_KV("weight", weight); + nodeList->push_back(consensusNode); + } + // only sort nodeList after rc3 version + std::sort(nodeList->begin(), nodeList->end(), bcos::consensus::ConsensusNodeComparator()); + NodeConfig_LOG(INFO) << LOG_BADGE("parseConsensusNodeList") + << LOG_KV("totalNodesSize", nodeList->size()); + return nodeList; +} + +void NodeConfig::generateGenesisData() +{ + std::string versionData = ""; + std::string executorConfig = ""; + std::string genesisdata = ""; + if (m_compatibilityVersion >= (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION) + { + auto genesisData = std::make_shared(m_smCryptoType, m_chainId, + m_groupId, m_consensusType, m_ledgerConfig->blockTxCountLimit(), + m_ledgerConfig->leaderSwitchPeriod(), m_compatibilityVersionStr, m_txGasLimit, m_isWasm, + m_isAuthCheck, m_authAdminAddress, m_isSerialExecute); + genesisdata = genesisData->genesisDataOutPut(); + size_t j = 0; + for (const auto& node : m_ledgerConfig->consensusNodeList()) + { + genesisdata = genesisdata + "node." + boost::lexical_cast(j) + ":" + + *toHexString(node->nodeID()->data()) + "," + + std::to_string(node->weight()) + "\n"; + ++j; + } + NodeConfig_LOG(INFO) << LOG_BADGE("generateGenesisData") + << LOG_KV("genesisData", genesisdata); + m_genesisData = genesisdata; + return; + } + + versionData = m_compatibilityVersionStr + "-"; + std::stringstream ss; + ss << m_isWasm << "-" << m_isAuthCheck << "-" << m_authAdminAddress << "-" << m_isSerialExecute; + executorConfig = ss.str(); + + ss.str(""); + ss << m_ledgerConfig->blockTxCountLimit() << "-" << m_ledgerConfig->leaderSwitchPeriod() << "-" + << m_txGasLimit << "-" << versionData << executorConfig; + for (const auto& node : m_ledgerConfig->consensusNodeList()) + { + ss << *toHexString(node->nodeID()->data()) << "," << node->weight() << ";"; + } + m_genesisData = ss.str(); + NodeConfig_LOG(INFO) << LOG_BADGE("generateGenesisData") + << LOG_KV("genesisData", m_genesisData); +} + +void NodeConfig::loadExecutorConfig(boost::property_tree::ptree const& _genesisConfig) +{ + m_isWasm = _genesisConfig.get("executor.is_wasm", false); + m_isAuthCheck = _genesisConfig.get("executor.is_auth_check", false); + m_isSerialExecute = _genesisConfig.get("executor.is_serial_execute", false); + if (m_isWasm && !m_isSerialExecute) + { + if (m_compatibilityVersion >= (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION) + { + BOOST_THROW_EXCEPTION(InvalidConfig() << errinfo_comment( + "loadExecutorConfig wasm only support serial executing, " + "please set is_serial_execute to true")); + } + NodeConfig_LOG(WARNING) + << METRIC + << LOG_DESC("loadExecutorConfig wasm with serial executing is not recommended"); + } + if (m_isWasm && m_isAuthCheck) + { + if (m_compatibilityVersion >= (uint32_t)bcos::protocol::BlockVersion::V3_1_VERSION) + { + BOOST_THROW_EXCEPTION(InvalidConfig() << errinfo_comment( + "loadExecutorConfig auth only support solidity, " + "please set is_auth_check to false or set is_wasm to false")); + } + NodeConfig_LOG(WARNING) << METRIC + << LOG_DESC( + "loadExecutorConfig wasm auth is not supported for now"); + } + + m_authAdminAddress = _genesisConfig.get("executor.auth_admin_account", ""); + NodeConfig_LOG(INFO) << METRIC << LOG_DESC("loadExecutorConfig") << LOG_KV("isWasm", m_isWasm) + << LOG_KV("isAuthCheck", m_isAuthCheck) + << LOG_KV("authAdminAccount", m_authAdminAddress) + << LOG_KV("ismSerialExecute", m_isSerialExecute); +} + +// Note: make sure the consensus param checker is consistent with the precompiled param checker +int64_t NodeConfig::checkAndGetValue(boost::property_tree::ptree const& _pt, + std::string const& _key, std::string const& _defaultValue) +{ + auto value = _pt.get(_key, _defaultValue); + try + { + return boost::lexical_cast(value); + } + catch (std::exception const& e) + { + BOOST_THROW_EXCEPTION(InvalidConfig() << errinfo_comment( + "Invalid value " + value + " for configuration " + _key + + ", please set the value with a valid number")); + } +} + +bool NodeConfig::isValidPort(int port) +{ + if (port <= 1024 || port > 65535) + return false; + return true; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tool/bcos-tool/NodeConfig.h" "b/BFPL\345\243\271/bcos-tool/bcos-tool/NodeConfig.h" new file mode 100644 index 00000000..d62ef493 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tool/bcos-tool/NodeConfig.h" @@ -0,0 +1,364 @@ +/** + * Copyright (C) 2021 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. + * + * @brief configuration for the node + * @file NodeConfig.h + * @author: yujiechen + * @date 2021-06-10 + */ +#pragma once +#include "Exceptions.h" +#include "bcos-framework/consensus/ConsensusNodeInterface.h" +#include "bcos-framework/ledger/LedgerConfig.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define NodeConfig_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("NodeConfig") +namespace bcos::tool +{ +class NodeConfig +{ +public: + constexpr static ssize_t DEFAULT_CACHE_SIZE = 32 * 1024 * 1024; + constexpr static ssize_t DEFAULT_MIN_CONSENSUS_TIME_MS = 3000; + constexpr static ssize_t DEFAULT_MIN_LEASE_TTL_SECONDS = 3; + + using Ptr = std::shared_ptr; + NodeConfig() : m_ledgerConfig(std::make_shared()) {} + + explicit NodeConfig(bcos::crypto::KeyFactory::Ptr _keyFactory); + virtual ~NodeConfig() = default; + + virtual void loadConfig(std::string const& _configPath, bool _enforceMemberID = true, + bool enforceChainConfig = false) + { + boost::property_tree::ptree iniConfig; + boost::property_tree::read_ini(_configPath, iniConfig); + loadConfig(iniConfig, _enforceMemberID, enforceChainConfig); + } + virtual void loadServiceConfig(boost::property_tree::ptree const& _pt); + virtual void loadRpcServiceConfig(boost::property_tree::ptree const& _pt); + virtual void loadGatewayServiceConfig(boost::property_tree::ptree const& _pt); + + virtual void loadWithoutTarsFrameworkConfig(boost::property_tree::ptree const& _pt); + + virtual void loadNodeServiceConfig( + std::string const& _nodeID, boost::property_tree::ptree const& _pt, bool _require = false); + + virtual void loadTarsProxyConfig(const std::string& _tarsProxyConf); + + virtual void loadServiceTarsProxyConfig( + const std::string& _serviceSectionName, boost::property_tree::ptree const& _pt); + + virtual void loadGenesisConfig(std::string const& _genesisConfigPath) + { + boost::property_tree::ptree genesisConfig; + boost::property_tree::read_ini(_genesisConfigPath, genesisConfig); + loadGenesisConfig(genesisConfig); + } + + virtual void loadConfigFromString(std::string const& _content) + { + boost::property_tree::ptree iniConfig; + std::stringstream contentStream(_content); + boost::property_tree::read_ini(contentStream, iniConfig); + loadConfig(iniConfig); + } + + virtual void loadGenesisConfigFromString(std::string const& _content) + { + boost::property_tree::ptree genesisConfig; + std::stringstream contentStream(_content); + boost::property_tree::read_ini(contentStream, genesisConfig); + loadGenesisConfig(genesisConfig); + } + + virtual void loadConfig(boost::property_tree::ptree const& _pt, bool _enforceMemberID = true, + bool _enforceChainConfig = false); + virtual void loadGenesisConfig(boost::property_tree::ptree const& _genesisConfig); + + // the txpool configurations + size_t txpoolLimit() const { return m_txpoolLimit; } + size_t notifyWorkerNum() const { return m_notifyWorkerNum; } + size_t verifierWorkerNum() const { return m_verifierWorkerNum; } + int64_t txsExpirationTime() const { return m_txsExpirationTime; } + + bool smCryptoType() const { return m_smCryptoType; } + std::string const& chainId() const { return m_chainId; } + std::string const& groupId() const { return m_groupId; } + size_t blockLimit() const { return m_blockLimit; } + + std::string const& privateKeyPath() const { return m_privateKeyPath; } + + size_t minSealTime() const { return m_minSealTime; } + size_t checkPointTimeoutInterval() const { return m_checkPointTimeoutInterval; } + + std::string const& storagePath() const { return m_storagePath; } + std::string const& storageType() const { return m_storageType; } + size_t keyPageSize() const { return m_keyPageSize; } + std::vector const& pdAddrs() const { return m_pd_addrs; } + std::string const& pdCaPath() const { return m_pdCaPath; } + std::string const& pdCertPath() const { return m_pdCertPath; } + std::string const& pdKeyPath() const { return m_pdKeyPath; } + std::string const& storageDBName() const { return m_storageDBName; } + std::string const& stateDBName() const { return m_stateDBName; } + + bcos::crypto::KeyFactory::Ptr keyFactory() { return m_keyFactory; } + + bcos::ledger::LedgerConfig::Ptr ledgerConfig() { return m_ledgerConfig; } + + std::string const& consensusType() const { return m_consensusType; } + size_t txGasLimit() const { return m_txGasLimit; } + std::string const& genesisData() const { return m_genesisData; } + + bool isWasm() const { return m_isWasm; } + bool isAuthCheck() const { return m_isAuthCheck; } + bool isSerialExecute() const { return m_isSerialExecute; } + std::string const& authAdminAddress() const { return m_authAdminAddress; } + + std::string const& rpcServiceName() const { return m_rpcServiceName; } + std::string const& gatewayServiceName() const { return m_gatewayServiceName; } + + std::string const& schedulerServiceName() const { return m_schedulerServiceName; } + std::string const& executorServiceName() const { return m_executorServiceName; } + std::string const& txpoolServiceName() const { return m_txpoolServiceName; } + + std::string const& nodeName() const { return m_nodeName; } + + std::string getDefaultServiceName(std::string const& _nodeName, std::string const& _serviceName) + { + return m_chainId + "." + _nodeName + _serviceName; + } + + // the rpc configurations + const std::string& rpcListenIP() const { return m_rpcListenIP; } + uint16_t rpcListenPort() const { return m_rpcListenPort; } + uint32_t rpcThreadPoolSize() const { return m_rpcThreadPoolSize; } + bool rpcSmSsl() const { return m_rpcSmSsl; } + bool rpcDisableSsl() const { return m_rpcDisableSsl; } + + // the gateway configurations + const std::string& p2pListenIP() const { return m_p2pListenIP; } + uint16_t p2pListenPort() const { return m_p2pListenPort; } + bool p2pSmSsl() const { return m_p2pSmSsl; } + const std::string& p2pNodeDir() const { return m_p2pNodeDir; } + const std::string& p2pNodeFileName() const { return m_p2pNodeFileName; } + + // config for cert + const std::string& certPath() { return m_certPath; } + void setCertPath(const std::string& _certPath) { m_certPath = _certPath; } + + const std::string& caCert() { return m_caCert; } + void setCaCert(const std::string& _caCert) { m_caCert = _caCert; } + + const std::string& nodeCert() { return m_nodeCert; } + void setNodeCert(const std::string& _nodeCert) { m_nodeCert = _nodeCert; } + + const std::string& nodeKey() { return m_nodeKey; } + void setNodeKey(const std::string& _nodeKey) { m_nodeKey = _nodeKey; } + + const std::string& smCaCert() const { return m_smCaCert; } + void setSmCaCert(const std::string& _smCaCert) { m_smCaCert = _smCaCert; } + + const std::string& smNodeCert() const { return m_smNodeCert; } + void setSmNodeCert(const std::string& _smNodeCert) { m_smNodeCert = _smNodeCert; } + + const std::string& smNodeKey() const { return m_smNodeKey; } + void setSmNodeKey(const std::string& _smNodeKey) { m_smNodeKey = _smNodeKey; } + + const std::string& enSmNodeCert() const { return m_enSmNodeCert; } + void setEnSmNodeCert(const std::string& _enSmNodeCert) { m_enSmNodeCert = _enSmNodeCert; } + + const std::string& enSmNodeKey() const { return m_enSmNodeKey; } + void setEnSmNodeKey(const std::string& _enSmNodeKey) { m_enSmNodeKey = _enSmNodeKey; } + + bool enableLRUCacheStorage() const { return m_enableLRUCacheStorage; } + ssize_t cacheSize() const { return m_cacheSize; } + + uint32_t compatibilityVersion() const { return m_compatibilityVersion; } + std::string const& compatibilityVersionStr() const { return m_compatibilityVersionStr; } + + std::string const& memberID() const { return m_memberID; } + unsigned leaseTTL() const { return m_leaseTTL; } + bool enableFailOver() const { return m_enableFailOver; } + std::string const& failOverClusterUrl() const { return m_failOverClusterUrl; } + + bool storageSecurityEnable() const { return m_storageSecurityEnable; } + std::string storageSecurityKeyCenterIp() const { return m_storageSecurityKeyCenterIp; } + unsigned short storageSecurityKeyCenterPort() const { return m_storageSecurityKeyCenterPort; } + std::string storageSecurityCipherDataKey() const { return m_storageSecurityCipherDataKey; } + + int sendTxTimeout() const { return m_sendTxTimeout; } + + bool withoutTarsFramework() const { return m_withoutTarsFramework; } + void setWithoutTarsFramework(bool _withoutTarsFramework) + { + m_withoutTarsFramework = _withoutTarsFramework; + } + void getTarsClientProxyEndpoints( + const std::string& _clientPrx, std::vector& _endPoints); + +protected: + virtual void loadChainConfig(boost::property_tree::ptree const& _pt); + virtual void loadRpcConfig(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); + virtual void loadSecurityConfig(boost::property_tree::ptree const& _pt); + virtual void loadSealerConfig(boost::property_tree::ptree const& _pt); + virtual void loadStorageSecurityConfig(boost::property_tree::ptree const& _pt); + + virtual void loadStorageConfig(boost::property_tree::ptree const& _pt); + virtual void loadConsensusConfig(boost::property_tree::ptree const& _pt); + virtual void loadFailOverConfig( + boost::property_tree::ptree const& _pt, bool _enforceMemberID = true); + virtual void loadOthersConfig(boost::property_tree::ptree const& _pt); + + virtual void loadLedgerConfig(boost::property_tree::ptree const& _genesisConfig); + + void loadExecutorConfig(boost::property_tree::ptree const& _pt); + + std::string getServiceName(boost::property_tree::ptree const& _pt, + std::string const& _configSection, std::string const& _objName, + std::string const& _defaultValue = "", bool _require = true); + void checkService(std::string const& _serviceType, std::string const& _serviceName); + + +private: + bcos::consensus::ConsensusNodeListPtr parseConsensusNodeList( + boost::property_tree::ptree const& _pt, std::string const& _sectionName, + std::string const& _subSectionName); + + void generateGenesisData(); + virtual int64_t checkAndGetValue(boost::property_tree::ptree const& _pt, + std::string const& _value, std::string const& _defaultValue); + + bool isValidPort(int port); + + bcos::crypto::KeyFactory::Ptr m_keyFactory; + // txpool related configuration + size_t m_txpoolLimit; + size_t m_notifyWorkerNum; + size_t m_verifierWorkerNum; + int64_t m_txsExpirationTime; + // TODO: the block sync module need some configurations? + + // chain configuration + bool m_smCryptoType; + std::string m_chainId; + std::string m_groupId; + size_t m_blockLimit; + + // sealer configuration + size_t m_minSealTime = 0; + size_t m_checkPointTimeoutInterval; + + // for security + std::string m_privateKeyPath; + + // storage security configuration + bool m_storageSecurityEnable; + std::string m_storageSecurityKeyCenterIp; + unsigned short m_storageSecurityKeyCenterPort; + std::string m_storageSecurityCipherDataKey; + + // ledger configuration + std::string m_consensusType; + bcos::ledger::LedgerConfig::Ptr m_ledgerConfig; + size_t m_txGasLimit; + std::string m_genesisData; + + // storage configuration + std::string m_storagePath; + std::string m_storageType = "RocksDB"; + size_t m_keyPageSize = 8192; + std::vector m_pd_addrs; + std::string m_pdCaPath; + std::string m_pdCertPath; + std::string m_pdKeyPath; + + std::string m_storageDBName = "storage"; + std::string m_stateDBName = "state"; + + // executor config + bool m_isWasm = false; + bool m_isAuthCheck = false; + bool m_isSerialExecute = false; + std::string m_authAdminAddress; + + // Pro and Max versions run do not apply to tars admin site + bool m_withoutTarsFramework = {false}; + + // service name to tars endpoints + std::unordered_map> m_tarsSN2EndPoints; + + std::string m_rpcServiceName; + std::string m_gatewayServiceName; + + // the serviceName of other modules + std::string m_schedulerServiceName; + std::string m_executorServiceName; + std::string m_txpoolServiceName; + std::string m_nodeName; + + // config for rpc + std::string m_rpcListenIP; + uint16_t m_rpcListenPort; + uint32_t m_rpcThreadPoolSize; + bool m_rpcSmSsl; + bool m_rpcDisableSsl = false; + + // config for gateway + std::string m_p2pListenIP; + uint16_t m_p2pListenPort; + bool m_p2pSmSsl; + std::string m_p2pNodeDir; + std::string m_p2pNodeFileName; + + // config for cert + std::string m_certPath; + + std::string m_caCert; + std::string m_nodeCert; + std::string m_nodeKey; + + std::string m_smCaCert; + std::string m_smNodeCert; + std::string m_smNodeKey; + std::string m_enSmNodeCert; + std::string m_enSmNodeKey; + + bool m_enableLRUCacheStorage = true; + ssize_t m_cacheSize = DEFAULT_CACHE_SIZE; // 32MB for default + uint32_t m_compatibilityVersion; + std::string m_compatibilityVersionStr; + + // failover config + std::string m_memberID; + unsigned m_leaseTTL = 0; + bool m_enableFailOver = false; + // etcd/zookeeper/consual url + std::string m_failOverClusterUrl; + + // others config + int m_sendTxTimeout = -1; +}; +} // namespace bcos::tool diff --git "a/BFPL\345\243\271/bcos-tool/bcos-tool/NodeTimeMaintenance.cpp" "b/BFPL\345\243\271/bcos-tool/bcos-tool/NodeTimeMaintenance.cpp" new file mode 100644 index 00000000..4d8e8ec8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tool/bcos-tool/NodeTimeMaintenance.cpp" @@ -0,0 +1,105 @@ +/** + * @brief : maintain the node time + * @file: NodeTimeMaintenance.cpp + * @author: yujiechen + * @date: 2020-06-12 + */ + +#include "NodeTimeMaintenance.h" + +#include +#include + +using namespace bcos::tool; + +void NodeTimeMaintenance::tryToUpdatePeerTimeInfo( + bcos::crypto::PublicPtr nodeID, const std::int64_t time) +{ + std::int64_t localTime = utcTime(); + auto peerTimeOffset = time - localTime; + + { + Guard l(x_mutex); + // The time information of the same node is within m_minTimeOffset, + // and the time information of the node is not updated + if (m_node2TimeOffset.count(nodeID)) + { + auto orgTimeOffset = m_node2TimeOffset[nodeID]; + if (std::abs(orgTimeOffset - peerTimeOffset) <= m_minTimeOffset) + return; + + m_node2TimeOffset[nodeID] = peerTimeOffset; + } + else + { + // update time information + m_node2TimeOffset.insert(std::make_pair(nodeID, peerTimeOffset)); + } + } + + // check remote time + if (std::abs(peerTimeOffset) > m_maxTimeOffset) + { + TIMESYNC_LOG(WARNING) + << LOG_DESC("Invalid remote peer time: too much difference from local time") + << LOG_KV("peer", nodeID->shortHex()) << LOG_KV("peerTime", time) + << LOG_KV("localTime", localTime) << LOG_KV("medianTimeOffset", m_medianTimeOffset); + } + updateTimeInfo(); + + TIMESYNC_LOG(INFO) << LOG_DESC("updateTimeInfo: update median time offset") + << LOG_KV("updatedMedianTimeOffset", m_medianTimeOffset) + << LOG_KV("peer", nodeID->shortHex()) << LOG_KV("peerTime", time) + << LOG_KV("peerOffset", peerTimeOffset) << LOG_KV("utcTime", utcTime()); +} + +void NodeTimeMaintenance::updateTimeInfo() +{ + // get median time offset + std::vector timeOffsetVec; + { + Guard l(x_mutex); + for (auto const& it : m_node2TimeOffset) + timeOffsetVec.emplace_back(it.second); + } + std::sort(timeOffsetVec.begin(), timeOffsetVec.end()); + + auto medianIndex = timeOffsetVec.size() >> 1; + std::int64_t medianTimeOffset{ 0 }; + if (timeOffsetVec.size() % 2 == 0) + { + medianTimeOffset = + (std::int64_t)(timeOffsetVec[medianIndex] + timeOffsetVec[medianIndex - 1]) >> 1; + } + else + { + medianTimeOffset = timeOffsetVec[medianIndex]; + } + if (std::abs(m_medianTimeOffset) >= m_maxTimeOffset) + { + checkLocalTimeAndWarning(timeOffsetVec); + } + m_medianTimeOffset = medianTimeOffset; +} + +void NodeTimeMaintenance::checkLocalTimeAndWarning(const std::vector& timeOffsetVec) +{ + // If nobody has a time different than ours but within m_minTimeOffset of ours, give a warning + for (auto rIter = timeOffsetVec.rbegin(); rIter != timeOffsetVec.rend(); ++rIter) + { + if (std::abs(*rIter) > m_minTimeOffset) + { + TIMESYNC_LOG(WARNING) << LOG_DESC( + "Please check that your node's date and time are correct!") + << LOG_KV("medianTimeOffset", m_medianTimeOffset) + << LOG_KV("peersSize", timeOffsetVec.size()); + } + else + break; + } +} + +std::int64_t NodeTimeMaintenance::getAlignedTime() const +{ + return (utcTime() + m_medianTimeOffset); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tool/bcos-tool/NodeTimeMaintenance.h" "b/BFPL\345\243\271/bcos-tool/bcos-tool/NodeTimeMaintenance.h" new file mode 100644 index 00000000..7e3eb509 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tool/bcos-tool/NodeTimeMaintenance.h" @@ -0,0 +1,44 @@ +/** + * @brief : maintain the node time + * @file: NodeTimeMaintenance.h + * @author: yujiechen + * @date: 2020-06-12 + */ +#pragma once + +#include + +#define TIMESYNC_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("TIMESYNC") + +namespace bcos +{ +namespace tool +{ + +class NodeTimeMaintenance +{ +public: + using Ptr = std::shared_ptr; + + void tryToUpdatePeerTimeInfo(bcos::crypto::PublicPtr nodeId, const std::int64_t time); + int64_t getAlignedTime() const; + int64_t medianTimeOffset() const { return m_medianTimeOffset; } + +private: + void updateTimeInfo(); + void checkLocalTimeAndWarning(const std::vector& timeOffsetVec); + +private: + // maps between nodeID and the timeOffset + mutable Mutex x_mutex; + std::map m_node2TimeOffset; + + std::atomic_int64_t m_medianTimeOffset{ 0 }; + + // 30min + std::int64_t m_maxTimeOffset{ 30 * 60 * 1000 }; + // 3min + std::int64_t m_minTimeOffset{ 3 * 60 * 1000 }; +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tool/bcos-tool/VersionConverter.h" "b/BFPL\345\243\271/bcos-tool/bcos-tool/VersionConverter.h" new file mode 100644 index 00000000..59bde8ec --- /dev/null +++ "b/BFPL\345\243\271/bcos-tool/bcos-tool/VersionConverter.h" @@ -0,0 +1,77 @@ + +/** + * Copyright (C) 2021 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. + * + * @brief Public function for version converter + * @file VersionConverter.h + * @author: yujiechen + * @date 2021-05-19 + */ +#pragma once +#include "Exceptions.h" +#include +#include +#include + +namespace bcos::tool +{ +inline uint32_t toVersionNumber(const std::string& _version) +{ + auto version = _version; + boost::to_lower(version); + // 3.0.0-rc{x} version + if (_version.starts_with(bcos::protocol::RC_VERSION_PREFIX)) + { + std::string versionNumber = _version.substr(bcos::protocol::RC_VERSION_PREFIX.length()); + return boost::lexical_cast(versionNumber); + } + std::vector versionFields; + boost::split(versionFields, version, boost::is_any_of(".")); + if (versionFields.size() < 2) + { + BOOST_THROW_EXCEPTION(InvalidVersion() << errinfo_comment( + "The version must be in format of MAJOR.MINOR.PATCH, " + "and the PATCH version is optional, current version is " + + _version)); + } + try + { + // MAJOR.MINOR.PATCH 0xMMmmpp00 every field is uint8_t, last byte is reserved + // v3.1.1 => 0x03010100 + auto majorVersion = boost::lexical_cast(versionFields[0]); + auto minorVersion = boost::lexical_cast(versionFields[1]); + auto patchVersion = 0; + if (versionFields.size() == 3) + { + patchVersion = boost::lexical_cast(versionFields[2]); + } + // check the major version + if (majorVersion > bcos::protocol::MAX_MAJOR_VERSION || + majorVersion < bcos::protocol::MIN_MAJOR_VERSION) + { + BOOST_THROW_EXCEPTION( + InvalidVersion() << errinfo_comment( + "The major version must between " + + std::to_string(bcos::protocol::MIN_MAJOR_VERSION) + " to " + + std::to_string(bcos::protocol::MAX_MAJOR_VERSION) + ", version:" + _version)); + } + return (majorVersion << 24) + (minorVersion << 16) + (patchVersion << 8); + } + catch (const boost::bad_lexical_cast& e) + { + BOOST_THROW_EXCEPTION(InvalidVersion() << errinfo_comment(_version)); + } +} +} // namespace bcos::tool \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tool/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-tool/test/CMakeLists.txt" new file mode 100644 index 00000000..49422c74 --- /dev/null +++ "b/BFPL\345\243\271/bcos-tool/test/CMakeLists.txt" @@ -0,0 +1,28 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of bcos-tool +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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(GLOB_RECURSE SOURCES "*.cpp") + +# cmake settings +set(TEST_BINARY_NAME test-bcos-tool) +find_package(Boost REQUIRED unit_test_framework) + +add_executable(${TEST_BINARY_NAME} ${SOURCES}) +target_include_directories(${TEST_BINARY_NAME} PRIVATE . ${CMAKE_SOURCE_DIR}) + +target_link_libraries(${TEST_BINARY_NAME} PUBLIC ${TOOL_TARGET} ${CRYPTO_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 diff --git "a/BFPL\345\243\271/bcos-tool/test/unittests/libtool/NodeTimeMaintenanceTest.cpp" "b/BFPL\345\243\271/bcos-tool/test/unittests/libtool/NodeTimeMaintenanceTest.cpp" new file mode 100644 index 00000000..56a85c1a --- /dev/null +++ "b/BFPL\345\243\271/bcos-tool/test/unittests/libtool/NodeTimeMaintenanceTest.cpp" @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::tool; +using namespace bcos::crypto; + +namespace bcos +{ +namespace test +{ +BOOST_AUTO_TEST_CASE(testNodeTimeMaintenance_doubleNode) +{ + // create four node + auto fixedSec1 = h256( + "4edbf97a0c6c3decde00ccd41f069dc30377f859fb1a9eb5759d0c9c995be244"); + auto sec1 = std::make_shared(fixedSec1.asBytes()); + auto pub1 = secp256k1PriToPub(sec1); + + auto fixedSec2 = h256( + "52ca4bd084c9d5a309dd5d5e08e6ddb3424168ee329e9a65cdf9f20c791dbe4d"); + auto sec2 = std::make_shared(fixedSec2.asBytes()); + auto pub2 = secp256k1PriToPub(sec2); + + auto fixedSec3 = h256( + "ba7699fcdc502b1ae4a7eb924ccc02db80e7d04056d2b3a114b2b2ccada4928d"); + auto sec3 = std::make_shared(fixedSec3.asBytes()); + auto pub3 = secp256k1PriToPub(sec3); + + auto fixedSec4 = h256( + "21b08860aa297501e51089e01631cc915d305d18c145136a55560277ad18b283"); + auto sec4 = std::make_shared(fixedSec4.asBytes()); + auto pub4 = secp256k1PriToPub(sec4); + + NodeTimeMaintenance nodeTimeMaintenance; + nodeTimeMaintenance.tryToUpdatePeerTimeInfo(pub1, utcTime()); + nodeTimeMaintenance.tryToUpdatePeerTimeInfo(pub2, utcTime() + 1 * 60 * 1000); + nodeTimeMaintenance.tryToUpdatePeerTimeInfo(pub3, utcTime() + 2 * 60 * 1000); + nodeTimeMaintenance.tryToUpdatePeerTimeInfo(pub4, utcTime() + 3 * 60 * 1000); + + BOOST_CHECK_EQUAL(1.5 * 60 * 1000, nodeTimeMaintenance.medianTimeOffset()); + BOOST_CHECK_EQUAL(utcTime() + 1.5 * 60 * 1000, nodeTimeMaintenance.getAlignedTime()); +} + +BOOST_AUTO_TEST_CASE(testNodeTimeMaintenance_singlarNode) +{ + // create five node +auto fixedSec1 = h256( + "4edbf97a0c6c3decde00ccd41f069dc30377f859fb1a9eb5759d0c9c995be244"); + auto sec1 = std::make_shared(fixedSec1.asBytes()); + auto pub1 = secp256k1PriToPub(sec1); + + auto fixedSec2 = h256( + "52ca4bd084c9d5a309dd5d5e08e6ddb3424168ee329e9a65cdf9f20c791dbe4d"); + auto sec2 = std::make_shared(fixedSec2.asBytes()); + auto pub2 = secp256k1PriToPub(sec2); + + auto fixedSec3 = h256( + "ba7699fcdc502b1ae4a7eb924ccc02db80e7d04056d2b3a114b2b2ccada4928d"); + auto sec3 = std::make_shared(fixedSec3.asBytes()); + auto pub3 = secp256k1PriToPub(sec3); + + auto fixedSec4 = h256( + "21b08860aa297501e51089e01631cc915d305d18c145136a55560277ad18b283"); + auto sec4 = std::make_shared(fixedSec4.asBytes()); + auto pub4 = secp256k1PriToPub(sec4); + + auto fixedSec5 = h256( + "badf6be7ea9865501aec46322b3ab2f0ddbd54e1c2c2c0502251eef85992ec1e"); + auto sec5 = std::make_shared(fixedSec5.asBytes()); + auto pub5 = secp256k1PriToPub(sec5); + + NodeTimeMaintenance nodeTimeMaintenance; + nodeTimeMaintenance.tryToUpdatePeerTimeInfo(pub1, utcTime()); + nodeTimeMaintenance.tryToUpdatePeerTimeInfo(pub2, utcTime() + 1 * 60 * 1000); + nodeTimeMaintenance.tryToUpdatePeerTimeInfo(pub3, utcTime() + 2 * 60 * 1000); + nodeTimeMaintenance.tryToUpdatePeerTimeInfo(pub4, utcTime() + 3 * 60 * 1000); + nodeTimeMaintenance.tryToUpdatePeerTimeInfo(pub5, utcTime() + 4 * 60 * 1000); + + BOOST_CHECK_EQUAL(2 * 60 * 1000, nodeTimeMaintenance.medianTimeOffset()); + BOOST_CHECK_EQUAL(utcTime() + 2 * 60 * 1000, nodeTimeMaintenance.getAlignedTime()); +} +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tool/test/unittests/libtool/VersionConvert.cpp" "b/BFPL\345\243\271/bcos-tool/test/unittests/libtool/VersionConvert.cpp" new file mode 100644 index 00000000..846fb0bd --- /dev/null +++ "b/BFPL\345\243\271/bcos-tool/test/unittests/libtool/VersionConvert.cpp" @@ -0,0 +1,24 @@ + +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::tool; + +namespace bcos +{ +namespace test +{ +BOOST_AUTO_TEST_CASE(testVersionConvert) +{ + BOOST_CHECK_EQUAL(toVersionNumber("3.2.3"), 0x03020300); + BOOST_CHECK_EQUAL(toVersionNumber("3.2"), 0x03020000); + BOOST_CHECK_THROW(toVersionNumber("1"), InvalidVersion); + BOOST_CHECK_THROW(toVersionNumber("2.1"), InvalidVersion); + BOOST_CHECK_THROW(toVersionNumber("256.1"), InvalidVersion); +} + +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-tool/test/unittests/main/main.cpp" "b/BFPL\345\243\271/bcos-tool/test/unittests/main/main.cpp" new file mode 100644 index 00000000..5029377e --- /dev/null +++ "b/BFPL\345\243\271/bcos-tool/test/unittests/main/main.cpp" @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 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 main.cpp + * @author: yujiechen, jimmyshi + * @date 2021-02-24 + */ +#define BOOST_TEST_MODULE FISCO_BCOS_Tests +#define BOOST_TEST_MAIN + +#include +#include diff --git "a/BFPL\345\243\271/bcos-txpool/CMakeLists.txt" "b/BFPL\345\243\271/bcos-txpool/CMakeLists.txt" new file mode 100644 index 00000000..bb545dc5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/CMakeLists.txt" @@ -0,0 +1,45 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for bcos-txpool +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 bcos-txpool +# 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. +#------------------------------------------------------------------------------ + +cmake_minimum_required(VERSION 3.10) +set(CMAKE_OSX_DEPLOYMENT_TARGET "11.3" CACHE STRING "Minimum OS X deployment version") + +include(Version) +project(bcos-txpool VERSION ${VERSION}) + +set(PROTO_PATH ${PROJECT_SOURCE_DIR}) +set(SYNC_PROTO_GENERATE_BASE_DIR ${CMAKE_CURRENT_BINARY_DIR}) +set(SYNC_PROTO_SUB_DIR "bcos-txpool/sync/protocol/proto") + +add_subdirectory(bcos-txpool) + +if (TESTS) + enable_testing() + set(CTEST_OUTPUT_ON_FAILURE TRUE) + add_subdirectory(test) +endif() + +# for doxygen +# include(BuildDocs) +# buildDoc(bcos-txpool-doc) + +# for code coverage +if (COVERAGE) + include(Coverage) + config_coverage("txpool-cov" "'/usr*' '${CMAKE_CURRENT_SOURCE_DIR}/bcos-cmake-scripts*' '${CMAKE_CURRENT_SOURCE_DIR}/test/bcos-test*'") +endif () diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/CMakeLists.txt" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/CMakeLists.txt" new file mode 100644 index 00000000..6e929bc4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/CMakeLists.txt" @@ -0,0 +1,32 @@ +file(GLOB SRC_LIST "*.cpp") +file(GLOB HEADERS "*.h") +aux_source_directory(. SRC_LIST) +include_directories(.) + +# generate sync protobuf files + +set(MESSAGES_PROTOS TxsSync.proto) +set(SYNC_PROTO_GENERATE_DIR ${SYNC_PROTO_GENERATE_BASE_DIR}/${SYNC_PROTO_SUB_DIR}) +include(GenerateSources) +protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${MESSAGES_PROTOS} ${PROTO_PATH} "sync/protocol/proto" ${SYNC_PROTO_GENERATE_BASE_DIR} ${SYNC_PROTO_GENERATE_DIR}) + +aux_source_directory(./txpool SRC_LIST) +include_directories(./txpool) +aux_source_directory(./txpool/storage SRC_LIST) +include_directories(./txpool/storage) +aux_source_directory(./txpool/validator SRC_LIST) +include_directories(./txpool/validator) + +aux_source_directory(./sync/ SRC_LIST) +include_directories(./sync/) + +aux_source_directory(./sync/protocol/PB SRC_LIST) +include_directories(./sync/protocol/PB) + +add_library(${TXPOOL_TARGET} ${SRC_LIST} ${PROTO_SRCS} ${HEADERS} ${PROTO_HDRS}) + +find_package(TBB CONFIG REQUIRED) +find_package(Protobuf CONFIG REQUIRED) +target_include_directories(${TXPOOL_TARGET} PUBLIC .) +target_include_directories(${TXPOOL_TARGET} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/bcos-txpool) +target_link_libraries(${TXPOOL_TARGET} PUBLIC TBB::tbb protobuf::libprotobuf bcos-protocol ${UTILITIES_TARGET} ${TOOL_TARGET}) diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/Sync.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/Sync.h" new file mode 100644 index 00000000..f6f9e258 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/Sync.h" @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include + +namespace bcos::txpool::sync +{ +class Sync +{ +public: + task::Task broadcastTransaction( + concepts::transaction::Transaction auto const& transaction) + { + // m_front->asyncSendBroadcastMessage(uint16_t _type, int _moduleID, bytesConstRef _data) + } + +private: + bcos::front::FrontServiceInterface::Ptr m_front; +}; +} // namespace bcos::txpool::sync \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPool.cpp" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPool.cpp" new file mode 100644 index 00000000..0b443fa0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPool.cpp" @@ -0,0 +1,498 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for txpool + * @file TxPool.cpp + * @author: yujiechen + * @date 2021-05-10 + */ +#include "TxPool.h" +#include "txpool/validator/LedgerNonceChecker.h" +#include "txpool/validator/TxValidator.h" +#include +#include +#include +using namespace bcos; +using namespace bcos::txpool; +using namespace bcos::protocol; +using namespace bcos::crypto; +using namespace bcos::sync; +using namespace bcos::consensus; +using namespace bcos::tool; +void TxPool::start() +{ + if (m_running) + { + TXPOOL_LOG(INFO) << LOG_DESC("The txpool has already been started!"); + return; + } + m_transactionSync->start(); + m_txpoolStorage->start(); + m_running = true; + TXPOOL_LOG(INFO) << LOG_DESC("Start the txpool."); +} + +void TxPool::stop() +{ + if (!m_running) + { + TXPOOL_LOG(INFO) << LOG_DESC("The txpool has already been stopped!"); + return; + } + if (m_worker) + { + m_worker->stop(); + } + if (m_txpoolStorage) + { + m_txpoolStorage->stop(); + } + m_transactionSync->stop(); + m_running = false; + TXPOOL_LOG(INFO) << LOG_DESC("Stop the txpool."); +} + + +task::Task TxPool::submitTransaction( + protocol::Transaction::Ptr transaction) +{ + co_return co_await m_txpoolStorage->submitTransaction(std::move(transaction)); +} + +bool TxPool::checkExistsInGroup(TxSubmitCallback _txSubmitCallback) +{ + auto syncConfig = m_transactionSync->config(); + if (!_txSubmitCallback || syncConfig->existsInGroup()) + { + return true; + } + auto txResult = m_config->txResultFactory()->createTxSubmitResult(); + txResult->setTxHash(HashType()); + txResult->setStatus((uint32_t)TransactionStatus::RequestNotBelongToTheGroup); + + auto errorMsg = "Do not send transactions to nodes that are not in the group"; + _txSubmitCallback(std::make_shared((int32_t)txResult->status(), errorMsg), txResult); + TXPOOL_LOG(WARNING) << LOG_DESC(errorMsg); + return false; +} + +void TxPool::asyncSealTxs(uint64_t _txsLimit, TxsHashSetPtr _avoidTxs, + std::function _sealCallback) +{ + // Note: not block seal new block here + auto self = weak_from_this(); + m_sealer->enqueue([self, _txsLimit, _avoidTxs, _sealCallback]() { + auto txpool = self.lock(); + if (!txpool) + { + return; + } + auto fetchedTxs = txpool->m_config->blockFactory()->createBlock(); + auto sysTxs = txpool->m_config->blockFactory()->createBlock(); + txpool->m_txpoolStorage->batchFetchTxs(fetchedTxs, sysTxs, _txsLimit, _avoidTxs, true); + _sealCallback(nullptr, fetchedTxs, sysTxs); + }); +} + +void TxPool::asyncNotifyBlockResult(BlockNumber _blockNumber, TransactionSubmitResultsPtr txsResult, + std::function _onNotifyFinished) +{ + m_worker->enqueue([this, txsResult = std::move(txsResult), _blockNumber]() { + m_txpoolStorage->batchRemove(_blockNumber, *txsResult); + }); + + if (_onNotifyFinished) + { + _onNotifyFinished(nullptr); + } +} + +void TxPool::asyncVerifyBlock(PublicPtr _generatedNodeID, bytesConstRef const& _block, + std::function _onVerifyFinished) +{ + auto block = m_config->blockFactory()->createBlock(_block); + auto blockHeader = block->blockHeader(); + TXPOOL_LOG(INFO) << LOG_DESC("begin asyncVerifyBlock") + << LOG_KV("consNum", blockHeader ? blockHeader->number() : -1) + << LOG_KV("hash", blockHeader ? blockHeader->hash().abridged() : "null"); + // Note: here must have thread pool for lock in the callback + // use single thread here to decrease thread competition + auto self = weak_from_this(); + m_verifier->enqueue([self, _generatedNodeID, blockHeader, block, _onVerifyFinished]() { + try + { + auto startT = utcTime(); + auto txpool = self.lock(); + if (!txpool) + { + if (_onVerifyFinished) + { + _onVerifyFinished(std::make_shared( + -1, "asyncVerifyBlock failed for lock txpool failed"), + false); + } + return; + } + auto txpoolStorage = txpool->m_txpoolStorage; + auto missedTxs = txpoolStorage->batchVerifyProposal(block); + auto onVerifyFinishedWrapper = + [txpool, txpoolStorage, _onVerifyFinished, block, blockHeader, missedTxs, startT]( + Error::Ptr _error, bool _ret) { + auto verifyRet = _ret; + auto verifyError = _error; + if (missedTxs->size() > 0) + { + // try to fetch the missed txs from the local txpool again + if (_error && _error->errorCode() == CommonError::TransactionsMissing) + { + verifyRet = txpoolStorage->batchVerifyProposal(missedTxs); + } + if (verifyRet) + { + verifyError = nullptr; + } + } + TXPOOL_LOG(INFO) + << METRIC << LOG_DESC("asyncVerifyBlock finished") + << LOG_KV("consNum", blockHeader ? blockHeader->number() : -1) + << LOG_KV("hash", blockHeader ? blockHeader->hash().abridged() : "null") + << LOG_KV("code", verifyError ? verifyError->errorCode() : 0) + << LOG_KV("msg", verifyError ? verifyError->errorMessage() : "success") + << LOG_KV("result", verifyRet) << LOG_KV("timecost", (utcTime() - startT)); + if (!_onVerifyFinished) + { + return; + } + _onVerifyFinished(verifyError, verifyRet); + // batchPreStore the proposal txs when verifySuccess in the case of not enable + // txsPreStore + if (!txpoolStorage->preStoreTxs() && !verifyError && verifyRet && block && + block->blockHeader()) + { + txpool->storeVerifiedBlock(block); + } + }; + + if (missedTxs->size() == 0) + { + TXPOOL_LOG(DEBUG) << LOG_DESC("asyncVerifyBlock: hit all transactions in txpool") + << LOG_KV("consNum", blockHeader ? blockHeader->number() : -1) + << LOG_KV("nodeId", + txpool->m_transactionSync->config()->nodeID()->shortHex()); + onVerifyFinishedWrapper(nullptr, true); + return; + } + TXPOOL_LOG(DEBUG) << LOG_DESC("asyncVerifyBlock") + << LOG_KV("consNum", blockHeader ? blockHeader->number() : -1) + << LOG_KV("totalTxs", block->transactionsHashSize()) + << LOG_KV("missedTxs", missedTxs->size()); + txpool->m_transactionSync->requestMissedTxs( + _generatedNodeID, missedTxs, block, onVerifyFinishedWrapper); + } + catch (std::exception const& e) + { + TXPOOL_LOG(WARNING) << LOG_DESC("asyncVerifyBlock exception") + << LOG_KV("fromNodeId", _generatedNodeID->shortHex()) + << LOG_KV("consNum", blockHeader ? blockHeader->number() : -1) + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +void TxPool::asyncNotifyTxsSyncMessage(Error::Ptr _error, std::string const& _uuid, + NodeIDPtr _nodeID, bytesConstRef _data, std::function _onRecv) +{ + auto self = weak_from_this(); + m_transactionSync->onRecvSyncMessage( + _error, _nodeID, _data, [self, _uuid, _nodeID](bytesConstRef _respData) { + try + { + auto txpool = self.lock(); + if (!txpool) + { + return; + } + txpool->m_sendResponseHandler( + _uuid, bcos::protocol::ModuleID::TxsSync, _nodeID, _respData); + } + catch (std::exception const& e) + { + TXPOOL_LOG(WARNING) << LOG_DESC("asyncNotifyTxsSyncMessage: sendResponse failed") + << LOG_KV("error", boost::diagnostic_information(e)) + << LOG_KV("uuid", _uuid) << LOG_KV("dst", _nodeID->shortHex()); + } + }); + if (!_onRecv) + { + return; + } + _onRecv(nullptr); +} + +void TxPool::notifyConsensusNodeList( + ConsensusNodeList const& _consensusNodeList, std::function _onRecvResponse) +{ + m_transactionSync->config()->setConsensusNodeList(_consensusNodeList); + if (!_onRecvResponse) + { + return; + } + _onRecvResponse(nullptr); +} + +void TxPool::notifyObserverNodeList( + ConsensusNodeList const& _observerNodeList, std::function _onRecvResponse) +{ + m_transactionSync->config()->setObserverList(_observerNodeList); + if (!_onRecvResponse) + { + return; + } + _onRecvResponse(nullptr); +} + +void TxPool::getTxsFromLocalLedger(HashListPtr _txsHash, HashListPtr _missedTxs, + std::function _onBlockFilled) +{ + // fetch from the local ledger + auto self = weak_from_this(); + m_worker->enqueue([self, _txsHash, _missedTxs, _onBlockFilled]() { + auto txpool = self.lock(); + if (!txpool) + { + _onBlockFilled( + std::make_shared(CommonError::TransactionsMissing, "TransactionsMissing"), + nullptr); + return; + } + auto sync = txpool->m_transactionSync; + sync->requestMissedTxs(nullptr, _missedTxs, nullptr, + [txpool, _txsHash, _onBlockFilled](Error::Ptr _error, bool _verifyResult) { + if (_error || !_verifyResult) + { + TXPOOL_LOG(WARNING) + << LOG_DESC("getTxsFromLocalLedger failed") + << LOG_KV("code", _error ? _error->errorCode() : 0) + << LOG_KV("msg", _error ? _error->errorMessage() : "fetchSucc") + << LOG_KV("verifyResult", _verifyResult); + _onBlockFilled(std::make_shared( + CommonError::TransactionsMissing, "TransactionsMissing"), + nullptr); + return; + } + TXPOOL_LOG(INFO) << LOG_DESC( + "asyncFillBlock miss and try to get the transaction from the ledger success"); + txpool->fillBlock(_txsHash, _onBlockFilled, false); + }); + }); +} + +// Note: the transaction must be all hit in local txpool +void TxPool::asyncFillBlock( + HashListPtr _txsHash, std::function _onBlockFilled) +{ + auto self = weak_from_this(); + m_filler->enqueue([self, _txsHash, _onBlockFilled]() { + auto txpool = self.lock(); + if (!txpool) + { + return; + } + txpool->fillBlock(_txsHash, _onBlockFilled, true); + }); +} + +void TxPool::fillBlock(HashListPtr _txsHash, + std::function _onBlockFilled, bool _fetchFromLedger) +{ + HashListPtr missedTxs = std::make_shared(); + auto txs = m_txpoolStorage->fetchTxs(*missedTxs, *_txsHash); + if (missedTxs->size() > 0) + { + TXPOOL_LOG(WARNING) << LOG_DESC("asyncFillBlock failed for missing some transactions") + << LOG_KV("missedTxsSize", missedTxs->size()); + if (_fetchFromLedger) + { + TXPOOL_LOG(INFO) << LOG_DESC("getTxsFromLocalLedger") + << LOG_KV("txsSize", _txsHash->size()) + << LOG_KV("missedSize", missedTxs->size()); + getTxsFromLocalLedger(_txsHash, missedTxs, _onBlockFilled); + } + else + { + _onBlockFilled( + std::make_shared(CommonError::TransactionsMissing, "TransactionsMissing"), + nullptr); + } + return; + } + _onBlockFilled(nullptr, txs); +} + + +void TxPool::asyncMarkTxs(HashListPtr _txsHash, bool _sealedFlag, + bcos::protocol::BlockNumber _batchId, bcos::crypto::HashType const& _batchHash, + std::function _onRecvResponse) +{ + m_txpoolStorage->batchMarkTxs(*_txsHash, _batchId, _batchHash, _sealedFlag); + if (!_onRecvResponse) + { + return; + } + _onRecvResponse(nullptr); +} + +void TxPool::asyncResetTxPool(std::function _onRecvResponse) +{ + // mark all the transactions as unsealed + m_txpoolStorage->batchMarkAllTxs(false); + if (!_onRecvResponse) + { + return; + } + TXPOOL_LOG(INFO) << LOG_DESC("asyncResetTxPool") << LOG_KV("txsSize", m_txpoolStorage->size()); + _onRecvResponse(nullptr); +} + +void TxPool::init() +{ + initSendResponseHandler(); + auto ledgerConfigFetcher = std::make_shared(m_config->ledger()); + TXPOOL_LOG(INFO) << LOG_DESC("fetch LedgerConfig information"); + ledgerConfigFetcher->fetchBlockNumberAndHash(); + ledgerConfigFetcher->fetchConsensusNodeList(); + ledgerConfigFetcher->fetchObserverNodeList(); + TXPOOL_LOG(INFO) << LOG_DESC("fetch LedgerConfig success"); + + auto blockLimit = m_config->blockLimit(); + auto ledgerConfig = ledgerConfigFetcher->ledgerConfig(); + auto startNumber = + (ledgerConfig->blockNumber() > blockLimit ? (ledgerConfig->blockNumber() - blockLimit + 1) : + 0); + if (startNumber > 0) + { + auto toNumber = ledgerConfig->blockNumber(); + auto fetchedSize = std::min(blockLimit, (toNumber - startNumber + 1)); + TXPOOL_LOG(INFO) << LOG_DESC("fetch history nonces information") + << LOG_KV("startNumber", startNumber) + << LOG_KV("fetchedSize", fetchedSize); + ledgerConfigFetcher->fetchNonceList(startNumber, fetchedSize); + } + TXPOOL_LOG(INFO) << LOG_DESC("fetch history nonces success"); + + // create LedgerNonceChecker and set it into the validator + TXPOOL_LOG(INFO) << LOG_DESC("init txs validator"); + auto ledgerNonceChecker = std::make_shared( + ledgerConfigFetcher->nonceList(), ledgerConfig->blockNumber(), blockLimit); + + auto validator = std::dynamic_pointer_cast(m_config->txValidator()); + validator->setLedgerNonceChecker(ledgerNonceChecker); + TXPOOL_LOG(INFO) << LOG_DESC("init txs validator success"); + + // init syncConfig + TXPOOL_LOG(INFO) << LOG_DESC("init sync config"); + auto txsSyncConfig = m_transactionSync->config(); + txsSyncConfig->setConsensusNodeList(ledgerConfig->consensusNodeList()); + txsSyncConfig->setObserverList(ledgerConfig->observerNodeList()); + TXPOOL_LOG(INFO) << LOG_DESC("init sync config success"); +} + +void TxPool::initSendResponseHandler() +{ + // set the sendResponse callback + std::weak_ptr weakFrontService = + m_transactionSync->config()->frontService(); + m_sendResponseHandler = [weakFrontService](std::string const& _id, int _moduleID, + NodeIDPtr _dstNode, bytesConstRef _data) { + try + { + auto frontService = weakFrontService.lock(); + if (!frontService) + { + return; + } + frontService->asyncSendResponse( + _id, _moduleID, _dstNode, _data, [_id, _moduleID, _dstNode](Error::Ptr _error) { + if (_error) + { + TXPOOL_LOG(WARNING) + << LOG_DESC("sendResponse failed") << LOG_KV("uuid", _id) + << LOG_KV("module", std::to_string(_moduleID)) + << LOG_KV("dst", _dstNode->shortHex()) + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + } + }); + } + catch (std::exception const& e) + { + TXPOOL_LOG(WARNING) << LOG_DESC("sendResponse exception") + << LOG_KV("error", boost::diagnostic_information(e)) + << LOG_KV("uuid", _id) << LOG_KV("moduleID", _moduleID) + << LOG_KV("peer", _dstNode->shortHex()); + } + }; +} + + +void TxPool::storeVerifiedBlock(bcos::protocol::Block::Ptr _block) +{ + auto blockHeader = _block->blockHeader(); + TXPOOL_LOG(INFO) << LOG_DESC("storeVerifiedBlock") << LOG_KV("consNum", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("txsSize", _block->transactionsHashSize()); + auto txsHashList = std::make_shared(); + for (size_t i = 0; i < _block->transactionsHashSize(); i++) + { + txsHashList->emplace_back(_block->transactionHash(i)); + } + + auto self = weak_from_this(); + auto startT = utcTime(); + asyncFillBlock( + txsHashList, [self, startT, blockHeader, _block](Error::Ptr _error, TransactionsPtr _txs) { + if (_error) + { + TXPOOL_LOG(WARNING) + << LOG_DESC("storeVerifiedBlock, fillBlock error") + << LOG_KV("consNum", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("msg", _error->errorMessage()) << LOG_KV("code", _error->errorCode()); + return; + } + auto txpool = self.lock(); + if (!txpool) + { + return; + } + txpool->m_config->ledger()->asyncPreStoreBlockTxs( + _txs, _block, [startT, blockHeader](Error::UniquePtr&& _error) { + if (_error) + { + TXPOOL_LOG(WARNING) + << LOG_DESC("storeVerifiedBlock: asyncPreStoreBlockTxs error") + << LOG_KV("consNum", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("msg", _error->errorMessage()) + << LOG_KV("code", _error->errorCode()); + return; + } + TXPOOL_LOG(INFO) << LOG_DESC("storeVerifiedBlock success") + << LOG_KV("consNum", blockHeader->number()) + << LOG_KV("hash", blockHeader->hash().abridged()) + << LOG_KV("timecost", (utcTime() - startT)); + }); + }); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPool.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPool.h" new file mode 100644 index 00000000..6910b9a4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPool.h" @@ -0,0 +1,172 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for txpool + * @file TxPool.h + * @author: yujiechen + * @date 2021-05-10 + */ +#pragma once +#include "TxPoolConfig.h" +#include "sync/interfaces/TransactionSyncInterface.h" +#include "txpool/interfaces/TxPoolStorageInterface.h" +#include +#include +#include +#include +namespace bcos::txpool +{ +class TxPool : public TxPoolInterface, public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + TxPool(TxPoolConfig::Ptr _config, TxPoolStorageInterface::Ptr _txpoolStorage, + bcos::sync::TransactionSyncInterface::Ptr _transactionSync, size_t _verifierWorkerNum = 1) + : m_config(std::move(_config)), + m_txpoolStorage(std::move(_txpoolStorage)), + m_transactionSync(std::move(_transactionSync)) + { + // threadpool for submit txs + m_worker = std::make_shared("submitter", _verifierWorkerNum); + // threadpool for verify block + m_verifier = std::make_shared("verifier", 4); + m_sealer = std::make_shared("txsSeal", 1); + m_filler = std::make_shared("txsFiller", std::thread::hardware_concurrency()); + TXPOOL_LOG(INFO) << LOG_DESC("create TxPool") + << LOG_KV("submitterWorkerNum", _verifierWorkerNum); + } + + ~TxPool() noexcept override { stop(); } + + void start() override; + void stop() override; + + task::Task submitTransaction( + protocol::Transaction::Ptr transaction) override; + + void asyncSealTxs(uint64_t _txsLimit, TxsHashSetPtr _avoidTxs, + std::function + _sealCallback) override; + + void asyncNotifyBlockResult(bcos::protocol::BlockNumber _blockNumber, + bcos::protocol::TransactionSubmitResultsPtr _txsResult, + std::function _onNotifyFinished) override; + + void asyncVerifyBlock(bcos::crypto::PublicPtr _generatedNodeID, bytesConstRef const& _block, + std::function _onVerifyFinished) override; + + void asyncNotifyTxsSyncMessage(bcos::Error::Ptr _error, std::string const& _uuid, + bcos::crypto::NodeIDPtr _nodeID, bytesConstRef _data, + std::function _onRecv) override; + + void notifyConsensusNodeList(bcos::consensus::ConsensusNodeList const& _consensusNodeList, + std::function _onRecvResponse) override; + + void asyncFillBlock(bcos::crypto::HashListPtr _txsHash, + std::function _onBlockFilled) override; + + void notifyObserverNodeList(bcos::consensus::ConsensusNodeList const& _observerNodeList, + std::function _onRecvResponse) override; + + void asyncMarkTxs(bcos::crypto::HashListPtr _txsHash, bool _sealedFlag, + bcos::protocol::BlockNumber _batchId, bcos::crypto::HashType const& _batchHash, + std::function _onRecvResponse) override; + + void asyncResetTxPool(std::function _onRecvResponse) override; + + TxPoolConfig::Ptr txpoolConfig() { return m_config; } + TxPoolStorageInterface::Ptr txpoolStorage() { return m_txpoolStorage; } + + bcos::sync::TransactionSyncInterface::Ptr transactionSync() { return m_transactionSync; } + void setTransactionSync(bcos::sync::TransactionSyncInterface::Ptr _transactionSync) + { + m_transactionSync = std::move(_transactionSync); + } + + virtual void init(); + virtual void registerUnsealedTxsNotifier( + std::function)> _unsealedTxsNotifier) + { + m_txpoolStorage->registerUnsealedTxsNotifier(std::move(_unsealedTxsNotifier)); + } + + void asyncGetPendingTransactionSize( + std::function _onGetTxsSize) override + { + if (!_onGetTxsSize) + { + return; + } + auto pendingTxsSize = m_txpoolStorage->size(); + _onGetTxsSize(nullptr, pendingTxsSize); + } + + void notifyConnectedNodes(bcos::crypto::NodeIDSet const& _connectedNodes, + std::function _onResponse) override + { + m_transactionSync->config()->notifyConnectedNodes(_connectedNodes, _onResponse); + if (m_txpoolStorage->size() > 0) + { + return; + } + // try to broadcast empty txsStatus and request txs from the connected nodes when the txpool + // is empty + m_transactionSync->onEmptyTxs(); + } + + // for UT + void setTxPoolStorage(TxPoolStorageInterface::Ptr _txpoolStorage) + { + m_txpoolStorage = _txpoolStorage; + m_transactionSync->config()->setTxPoolStorage(_txpoolStorage); + } + + void registerTxsCleanUpSwitch(std::function _txsCleanUpSwitch) override + { + m_txpoolStorage->registerTxsCleanUpSwitch(_txsCleanUpSwitch); + } + + void clearAllTxs() override { m_txpoolStorage->clear(); } + +protected: + virtual bool checkExistsInGroup(bcos::protocol::TxSubmitCallback _txSubmitCallback); + virtual void getTxsFromLocalLedger(bcos::crypto::HashListPtr _txsHash, + bcos::crypto::HashListPtr _missedTxs, + std::function _onBlockFilled); + + virtual void fillBlock(bcos::crypto::HashListPtr _txsHash, + std::function _onBlockFilled, + bool _fetchFromLedger = true); + + void initSendResponseHandler(); + + virtual void storeVerifiedBlock(bcos::protocol::Block::Ptr _block); + +private: + TxPoolConfig::Ptr m_config; + TxPoolStorageInterface::Ptr m_txpoolStorage; + bcos::sync::TransactionSyncInterface::Ptr m_transactionSync; + + std::function + m_sendResponseHandler; + + ThreadPool::Ptr m_worker; + ThreadPool::Ptr m_verifier; + ThreadPool::Ptr m_sealer; + ThreadPool::Ptr m_filler; + std::atomic_bool m_running = {false}; +}; +} // namespace bcos::txpool diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPoolConfig.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPoolConfig.h" new file mode 100644 index 00000000..6b09341c --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPoolConfig.h" @@ -0,0 +1,86 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Transaction pool configuration module, + * including transaction pool dependent modules and related configuration information + * @file TxPoolConfig.h + * @author: yujiechen + * @date 2021-05-08 + */ +#pragma once +#include "txpool/interfaces/NonceCheckerInterface.h" +#include "txpool/interfaces/TxPoolStorageInterface.h" +#include "txpool/interfaces/TxValidatorInterface.h" +#include +#include +#include +#include +namespace bcos +{ +namespace txpool +{ +class TxPoolConfig +{ +public: + using Ptr = std::shared_ptr; + TxPoolConfig(TxValidatorInterface::Ptr _txValidator, + bcos::protocol::TransactionSubmitResultFactory::Ptr _txResultFactory, + bcos::protocol::BlockFactory::Ptr _blockFactory, + std::shared_ptr _ledger, + NonceCheckerInterface::Ptr _txpoolNonceChecker, int64_t _blockLimit = 1000) + : m_txValidator(std::move(_txValidator)), + m_txResultFactory(std::move(_txResultFactory)), + m_blockFactory(std::move(_blockFactory)), + m_ledger(std::move(_ledger)), + m_txPoolNonceChecker(std::move(_txpoolNonceChecker)), + m_blockLimit(_blockLimit) + {} + + virtual ~TxPoolConfig() {} + virtual void setPoolLimit(size_t _poolLimit) { m_poolLimit = _poolLimit; } + virtual size_t poolLimit() const { return m_poolLimit; } + + NonceCheckerInterface::Ptr txPoolNonceChecker() { return m_txPoolNonceChecker; } + + TxValidatorInterface::Ptr txValidator() { return m_txValidator; } + bcos::protocol::TransactionSubmitResultFactory::Ptr txResultFactory() + { + return m_txResultFactory; + } + + bcos::protocol::BlockFactory::Ptr blockFactory() { return m_blockFactory; } + void setBlockFactory(bcos::protocol::BlockFactory::Ptr _blockFactory) + { + m_blockFactory = _blockFactory; + } + + bcos::protocol::TransactionFactory::Ptr txFactory() + { + return m_blockFactory->transactionFactory(); + } + std::shared_ptr ledger() { return m_ledger; } + int64_t blockLimit() const { return m_blockLimit; } + +private: + TxValidatorInterface::Ptr m_txValidator; + bcos::protocol::TransactionSubmitResultFactory::Ptr m_txResultFactory; + bcos::protocol::BlockFactory::Ptr m_blockFactory; + std::shared_ptr m_ledger; + NonceCheckerInterface::Ptr m_txPoolNonceChecker; + size_t m_poolLimit = 15000; + int64_t m_blockLimit = 1000; +}; +} // namespace txpool +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPoolFactory.cpp" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPoolFactory.cpp" new file mode 100644 index 00000000..29abf03d --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPoolFactory.cpp" @@ -0,0 +1,78 @@ +/** + * Copyright (C) 2021 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. + * + * @brief factory to create txpool + * @file TxPoolFactory.cpp + * @author: yujiechen + * @date 2021-05-19 + */ +#include "TxPoolFactory.h" +#include "bcos-txpool/sync/TransactionSync.h" +#include "bcos-txpool/sync/protocol/PB/TxsSyncMsgFactoryImpl.h" +#include "bcos-txpool/txpool/validator/TxValidator.h" +#include "txpool/storage/MemoryStorage.h" +#include "txpool/validator/TxPoolNonceChecker.h" +#include + +using namespace bcos; +using namespace bcos::txpool; +using namespace bcos::sync; +using namespace bcos::crypto; +using namespace bcos::protocol; + +TxPoolFactory::TxPoolFactory(NodeIDPtr _nodeId, CryptoSuite::Ptr _cryptoSuite, + TransactionSubmitResultFactory::Ptr _txResultFactory, BlockFactory::Ptr _blockFactory, + bcos::front::FrontServiceInterface::Ptr _frontService, + bcos::ledger::LedgerInterface::Ptr _ledger, std::string const& _groupId, + std::string const& _chainId, int64_t _blockLimit) + : m_nodeId(_nodeId), + m_cryptoSuite(_cryptoSuite), + m_txResultFactory(_txResultFactory), + m_blockFactory(_blockFactory), + m_frontService(_frontService), + m_ledger(_ledger), + m_groupId(_groupId), + m_chainId(_chainId), + m_blockLimit(_blockLimit) +{} + + +TxPool::Ptr TxPoolFactory::createTxPool(size_t _notifyWorkerNum, size_t _verifierWorkerNum, + int64_t _txsExpirationTime, bool _preStoreTxs) +{ + TXPOOL_LOG(INFO) << LOG_DESC("create transaction validator"); + auto txpoolNonceChecker = std::make_shared(); + auto validator = + std::make_shared(txpoolNonceChecker, m_cryptoSuite, m_groupId, m_chainId); + + TXPOOL_LOG(INFO) << LOG_DESC("create transaction config"); + auto txpoolConfig = std::make_shared( + validator, m_txResultFactory, m_blockFactory, m_ledger, txpoolNonceChecker, m_blockLimit); + + TXPOOL_LOG(INFO) << LOG_DESC("create transaction storage"); + auto txpoolStorage = std::make_shared( + txpoolConfig, _notifyWorkerNum, _txsExpirationTime, _preStoreTxs); + + auto syncMsgFactory = std::make_shared(); + TXPOOL_LOG(INFO) << LOG_DESC("create sync config"); + auto txsSyncConfig = std::make_shared( + m_nodeId, m_frontService, txpoolStorage, syncMsgFactory, m_blockFactory, m_ledger); + TXPOOL_LOG(INFO) << LOG_DESC("create sync engine"); + auto txsSync = std::make_shared(txsSyncConfig); + + TXPOOL_LOG(INFO) << LOG_DESC("create txpool") << LOG_KV("submitWorkerNum", _verifierWorkerNum) + << LOG_KV("notifyWorkerNum", _notifyWorkerNum); + return std::make_shared(txpoolConfig, txpoolStorage, txsSync, _verifierWorkerNum); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPoolFactory.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPoolFactory.h" new file mode 100644 index 00000000..e23d0d23 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/TxPoolFactory.h" @@ -0,0 +1,57 @@ +/** + * Copyright (C) 2021 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. + * + * @brief factory to create txpool + * @file TxPoolFactory.h + * @author: yujiechen + * @date 2021-05-19 + */ +#pragma once +#include "TxPool.h" +#include "TxPoolConfig.h" +#include "sync/TransactionSyncConfig.h" +#include +namespace bcos +{ +namespace txpool +{ +class TxPoolFactory +{ +public: + using Ptr = std::shared_ptr; + TxPoolFactory(bcos::crypto::NodeIDPtr _nodeId, bcos::crypto::CryptoSuite::Ptr _cryptoSuite, + bcos::protocol::TransactionSubmitResultFactory::Ptr _txResultFactory, + bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::front::FrontServiceInterface::Ptr _frontService, + std::shared_ptr _ledger, std::string const& _groupId, + std::string const& _chainId, int64_t _blockLimit); + + virtual ~TxPoolFactory() {} + TxPool::Ptr createTxPool(size_t _notifyWorkerNum = 2, size_t _verifierWorkerNum = 1, + int64_t _txsExpirationTime = 10 * 60 * 1000, bool _preStoreTxs = true); + +private: + bcos::crypto::NodeIDPtr m_nodeId; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + bcos::protocol::TransactionSubmitResultFactory::Ptr m_txResultFactory; + bcos::protocol::BlockFactory::Ptr m_blockFactory; + bcos::front::FrontServiceInterface::Ptr m_frontService; + std::shared_ptr m_ledger; + std::string m_groupId; + std::string m_chainId; + int64_t m_blockLimit = 1000; +}; +} // namespace txpool +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/TransactionSync.cpp" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/TransactionSync.cpp" new file mode 100644 index 00000000..ab42a49d --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/TransactionSync.cpp" @@ -0,0 +1,782 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for transaction sync + * @file TransactionSync.cpp + * @author: yujiechen + * @date 2021-05-11 + */ +#include "bcos-txpool/sync/TransactionSync.h" +#include "bcos-txpool/sync/utilities/Common.h" +#include +#include + +using namespace bcos; +using namespace bcos::sync; +using namespace bcos::crypto; +using namespace bcos::txpool; +using namespace bcos::protocol; +using namespace bcos::ledger; +using namespace bcos::consensus; +static unsigned const c_maxSendTransactions = 10000; + +void TransactionSync::start() +{ + startWorking(); + m_running.store(true); + SYNC_LOG(DEBUG) << LOG_DESC("start TransactionSync"); +} + +void TransactionSync::stop() +{ + if (!m_running) + { + SYNC_LOG(DEBUG) << LOG_DESC("TransactionSync already stopped"); + return; + } + m_running.store(false); + if (m_worker) + { + m_worker->stop(); + } + if (m_txsRequester) + { + m_txsRequester->stop(); + } + finishWorker(); + stopWorking(); + // will not restart worker, so terminate it + terminate(); + SYNC_LOG(DEBUG) << LOG_DESC("stop SyncTransaction"); +} + +void TransactionSync::executeWorker() +{ +#if FISCO_DEBUG + // TODO: remove this, now just for bug tracing + m_config->txpoolStorage()->printPendingTxs(); +#endif + if (!downloadTxsBufferEmpty()) + { + maintainDownloadingTransactions(); + } + if (m_config->existsInGroup() && downloadTxsBufferEmpty() && m_newTransactions.load()) + { + // TODO: Disable maintain transactions + maintainTransactions(); + } + if (!m_config->existsInGroup() || (!m_newTransactions && downloadTxsBufferEmpty())) + { + boost::unique_lock l(x_signalled); + m_signalled.wait_for(l, boost::chrono::milliseconds(10)); + } +} + +void TransactionSync::onRecvSyncMessage( + Error::Ptr _error, NodeIDPtr _nodeID, bytesConstRef _data, SendResponseCallback _sendResponse) +{ + try + { + if (_error != nullptr) + { + SYNC_LOG(WARNING) << LOG_DESC("onRecvSyncMessage error") + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMsg", _error->errorMessage()); + return; + } + // reject the message from the outside-group + if (!m_config->existsInGroup(_nodeID)) + { + return; + } + auto txsSyncMsg = m_config->msgFactory()->createTxsSyncMsg(_data); + // receive transactions + if (txsSyncMsg->type() == TxsSyncPacketType::TxsPacket) + { + txsSyncMsg->setFrom(_nodeID); + appendDownloadTxsBuffer(txsSyncMsg); + m_signalled.notify_all(); + return; + } + // receive txs request, and response the transactions + if (txsSyncMsg->type() == TxsSyncPacketType::TxsRequestPacket) + { + auto self = weak_from_this(); + m_worker->enqueue([self, txsSyncMsg, _sendResponse, _nodeID]() { + try + { + auto transactionSync = self.lock(); + if (!transactionSync) + { + return; + } + transactionSync->onReceiveTxsRequest(txsSyncMsg, _sendResponse, _nodeID); + } + catch (std::exception const& e) + { + SYNC_LOG(WARNING) << LOG_DESC("onRecvSyncMessage: send txs response exception") + << LOG_KV("error", boost::diagnostic_information(e)) + << LOG_KV("peer", _nodeID->shortHex()); + } + }); + } + if (txsSyncMsg->type() == TxsSyncPacketType::TxsStatusPacket) + { + auto self = weak_from_this(); + m_txsRequester->enqueue([self, _nodeID, txsSyncMsg]() { + try + { + auto transactionSync = self.lock(); + if (!transactionSync) + { + return; + } + transactionSync->onPeerTxsStatus(_nodeID, txsSyncMsg); + } + catch (std::exception const& e) + { + SYNC_LOG(WARNING) << LOG_DESC("onRecvSyncMessage: onPeerTxsStatus exception") + << LOG_KV("error", boost::diagnostic_information(e)) + << LOG_KV("peer", _nodeID->shortHex()); + } + }); + } + } + catch (std::exception const& e) + { + SYNC_LOG(WARNING) << LOG_DESC("onRecvSyncMessage exception") + << LOG_KV("error", boost::diagnostic_information(e)) + << LOG_KV("peer", _nodeID->shortHex()); + } +} + +void TransactionSync::onReceiveTxsRequest(TxsSyncMsgInterface::Ptr _txsRequest, + SendResponseCallback _sendResponse, bcos::crypto::PublicPtr _peer) +{ + auto const& txsHash = _txsRequest->txsHash(); + HashList missedTxs; + auto txs = m_config->txpoolStorage()->fetchTxs(missedTxs, txsHash); + // Note: here assume that all the transaction should be hit in the txpool + if (missedTxs.size() > 0) + { + SYNC_LOG(DEBUG) << LOG_DESC("onReceiveTxsRequest: transaction missing") + << LOG_KV("missedTxsSize", missedTxs.size()) + << LOG_KV("peer", _peer ? _peer->shortHex() : "unknown") + << LOG_KV("nodeId", m_config->nodeID()->shortHex()); +#if FISCO_DEBUG + // TODO: remove this, now just for bug tracing + for (auto txHash : missedTxs) + { + SYNC_LOG(WARNING) << LOG_DESC("miss tx") << txHash.abridged(); + } +#endif + } + // response the txs + auto block = m_config->blockFactory()->createBlock(); + for (auto constTx : *txs) + { + auto tx = std::const_pointer_cast(constTx); + block->appendTransaction(tx); + } + bytesPointer txsData = std::make_shared(); + block->encode(*txsData); + auto txsResponse = m_config->msgFactory()->createTxsSyncMsg( + TxsSyncPacketType::TxsResponsePacket, std::move(*txsData)); + auto packetData = txsResponse->encode(); + _sendResponse(ref(*packetData)); + SYNC_LOG(INFO) << LOG_DESC("onReceiveTxsRequest: response txs") + << LOG_KV("peer", _peer ? _peer->shortHex() : "unknown") + << LOG_KV("txsSize", txs->size()); +} + +void TransactionSync::requestMissedTxs(PublicPtr _generatedNodeID, HashListPtr _missedTxs, + Block::Ptr _verifiedProposal, std::function _onVerifyFinished) +{ + auto missedTxsSet = + std::make_shared>(_missedTxs->begin(), _missedTxs->end()); + auto startT = utcTime(); + auto self = weak_from_this(); + m_config->ledger()->asyncGetBatchTxsByHashList(_missedTxs, false, + [self, startT, _verifiedProposal, missedTxsSet, _generatedNodeID, _onVerifyFinished]( + Error::Ptr _error, TransactionsPtr _fetchedTxs, + std::shared_ptr>) { + auto txsSync = self.lock(); + if (!txsSync) + { + return; + } + // hit all the txs + auto missedTxsSize = txsSync->onGetMissedTxsFromLedger( + *missedTxsSet, _error, _fetchedTxs, _verifiedProposal, _onVerifyFinished); + if (missedTxsSize == 0) + { + return; + } + if (!_generatedNodeID || + _generatedNodeID->data() == txsSync->m_config->nodeID()->data()) + { + SYNC_LOG(WARNING) + << LOG_DESC("requestMissedTxs failed from the ledger for Transaction missing") + << LOG_KV("missedTxs", missedTxsSize); + _onVerifyFinished( + std::make_shared(CommonError::TransactionsMissing, + "requestMissedTxs failed from the ledger for Transaction missing"), + false); + return; + } + // fetch missed txs from the given peer + auto ledgerMissedTxs = + std::make_shared(missedTxsSet->begin(), missedTxsSet->end()); + SYNC_LOG(DEBUG) + << LOG_DESC("requestMissedTxs: missing txs from ledger and fetch from the peer") + << LOG_KV("txsSize", ledgerMissedTxs->size()) + << LOG_KV("peer", _generatedNodeID->shortHex()) + << LOG_KV("readDBTime", utcTime() - startT) + << LOG_KV("consNum", _verifiedProposal && _verifiedProposal->blockHeader() ? + _verifiedProposal->blockHeader()->number() : + -1) + << LOG_KV("hash", _verifiedProposal && _verifiedProposal->blockHeader() ? + _verifiedProposal->blockHeader()->hash().abridged() : + "null"); + txsSync->requestMissedTxsFromPeer( + _generatedNodeID, ledgerMissedTxs, _verifiedProposal, _onVerifyFinished); + }); +} + +size_t TransactionSync::onGetMissedTxsFromLedger(std::set& _missedTxs, Error::Ptr _error, + TransactionsPtr _fetchedTxs, Block::Ptr _verifiedProposal, + VerifyResponseCallback _onVerifyFinished) +{ + if (_error != nullptr) + { + SYNC_LOG(TRACE) << LOG_DESC("onGetMissedTxsFromLedger: get error response") + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + return _missedTxs.size(); + } + // import and verify the transactions + auto ret = importDownloadedTxs(m_config->nodeID(), _fetchedTxs, _verifiedProposal); + if (!ret) + { + SYNC_LOG(WARNING) << LOG_DESC("onGetMissedTxsFromLedger: verify tx failed"); + return _missedTxs.size(); + } + // fetch missed transactions from the local ledger + for (auto tx : *_fetchedTxs) + { + if (!_missedTxs.count(tx->hash())) + { + SYNC_LOG(WARNING) << LOG_DESC( + "onGetMissedTxsFromLedger: Encounter transaction that was " + "not expected to fetch from the ledger") + << LOG_KV("tx", tx->hash().abridged()); + continue; + } + // update the missedTxs + _missedTxs.erase(tx->hash()); + } + if (_missedTxs.size() == 0 && _onVerifyFinished) + { + SYNC_LOG(DEBUG) << LOG_DESC("onGetMissedTxsFromLedger: hit all transactions"); + _onVerifyFinished(nullptr, true); + } + SYNC_LOG(TRACE) << LOG_DESC("onGetMissedTxsFromLedger: missing txs") + << LOG_KV("missCount", _missedTxs.size()); + return _missedTxs.size(); +} + +void TransactionSync::requestMissedTxsFromPeer(PublicPtr _generatedNodeID, HashListPtr _missedTxs, + Block::Ptr _verifiedProposal, std::function _onVerifyFinished) +{ + BlockHeader::Ptr proposalHeader = nullptr; + if (_verifiedProposal) + { + proposalHeader = _verifiedProposal->blockHeader(); + } + if (_missedTxs->empty() && _onVerifyFinished) + { + _onVerifyFinished(nullptr, true); + return; + } + + + auto protocolID = _verifiedProposal ? ModuleID::ConsTxsSync : ModuleID::TxsSync; + + auto txsRequest = + m_config->msgFactory()->createTxsSyncMsg(TxsSyncPacketType::TxsRequestPacket, *_missedTxs); + auto encodedData = txsRequest->encode(); + auto startT = utcTime(); + auto self = weak_from_this(); + m_config->frontService()->asyncSendMessageByNodeID(protocolID, std::move(_generatedNodeID), + ref(*encodedData), m_config->networkTimeout(), + [self, startT, _missedTxs, _verifiedProposal, proposalHeader, _onVerifyFinished]( + Error::Ptr _error, NodeIDPtr _nodeID, bytesConstRef _data, const std::string&, + SendResponseCallback) { + try + { + auto transactionSync = self.lock(); + if (!transactionSync) + { + return; + } + auto networkT = utcTime() - startT; + auto recordT = utcTime(); + transactionSync->verifyFetchedTxs(_error, _nodeID, _data, _missedTxs, + _verifiedProposal, + [networkT, recordT, proposalHeader, _onVerifyFinished]( + Error::Ptr _error, bool _result) { + if (!_onVerifyFinished) + { + return; + } + _onVerifyFinished(_error, _result); + if (!(proposalHeader)) + { + return; + } + SYNC_LOG(DEBUG) + << LOG_DESC("requestMissedTxs: response verify result") + << LOG_KV("propIndex", proposalHeader->number()) + << LOG_KV("propHash", proposalHeader->hash().abridged()) + << LOG_KV("_result", _result) << LOG_KV("networkT", networkT) + << LOG_KV("verifyAndSubmitT", (utcTime() - recordT)); + }); + } + catch (std::exception const& e) + { + SYNC_LOG(WARNING) + << LOG_DESC( + "requestMissedTxs: verifyFetchedTxs when recv txs response exception") + << LOG_KV("error", boost::diagnostic_information(e)) + << LOG_KV("_peer", _nodeID->shortHex()); + } + }); +} + +void TransactionSync::verifyFetchedTxs(Error::Ptr _error, NodeIDPtr _nodeID, bytesConstRef _data, + HashListPtr _missedTxs, Block::Ptr _verifiedProposal, VerifyResponseCallback _onVerifyFinished) +{ + auto startT = utcTime(); + auto recordT = utcTime(); + if (_error != nullptr) + { + SYNC_LOG(INFO) << LOG_DESC("asyncVerifyBlock: fetch missed txs failed") + << LOG_KV("peer", _nodeID ? _nodeID->shortHex() : "unknown") + << LOG_KV("missedTxsSize", _missedTxs->size()) + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()) + << LOG_KV( + "propHash", (_verifiedProposal && _verifiedProposal->blockHeader()) ? + _verifiedProposal->blockHeader()->hash().abridged() : + "unknown") + << LOG_KV( + "propIndex", (_verifiedProposal && _verifiedProposal->blockHeader()) ? + _verifiedProposal->blockHeader()->number() : + -1); + _onVerifyFinished(_error, false); + return; + } + auto txsResponse = m_config->msgFactory()->createTxsSyncMsg(_data); + auto error = nullptr; + if (txsResponse->type() != TxsSyncPacketType::TxsResponsePacket) + { + SYNC_LOG(WARNING) << LOG_DESC("requestMissedTxs: receive invalid txsResponse") + << LOG_KV("peer", _nodeID->shortHex()) + << LOG_KV("expectedType", TxsSyncPacketType::TxsResponsePacket) + << LOG_KV("recvType", txsResponse->type()); + _onVerifyFinished(std::make_shared( + CommonError::FetchTransactionsFailed, "FetchTransactionsFailed"), + false); + return; + } + // verify missedTxs + auto transactions = m_config->blockFactory()->createBlock(txsResponse->txsData(), true, false); + auto decodeT = utcTime() - startT; + startT = utcTime(); + BlockHeader::Ptr proposalHeader = nullptr; + if (_verifiedProposal) + { + proposalHeader = _verifiedProposal->blockHeader(); + } + if (_missedTxs->size() != transactions->transactionsSize()) + { + SYNC_LOG(INFO) << LOG_DESC("verifyFetchedTxs failed") + << LOG_KV("expectedTxs", _missedTxs->size()) + << LOG_KV("fetchedTxs", transactions->transactionsSize()) + << LOG_KV("peer", _nodeID->shortHex()) + << LOG_KV("hash", + (proposalHeader) ? proposalHeader->hash().abridged() : "unknown") + << LOG_KV("consNum", (proposalHeader) ? proposalHeader->number() : -1); + // response to verify result + _onVerifyFinished( + std::make_shared(CommonError::TransactionsMissing, "TransactionsMissing"), + false); + // try to import the transactions even when verify failed + importDownloadedTxs(_nodeID, transactions); + return; + } + if (!importDownloadedTxs(_nodeID, transactions, _verifiedProposal)) + { + _onVerifyFinished(std::make_shared(CommonError::TxsSignatureVerifyFailed, + "invalid transaction for invalid signature or nonce or blockLimit"), + false); + return; + } + // check the transaction hash + for (size_t i = 0; i < _missedTxs->size(); i++) + { + if ((*_missedTxs)[i] != transactions->transaction(i)->hash()) + { + _onVerifyFinished(std::make_shared(CommonError::InconsistentTransactions, + "InconsistentTransactions"), + false); + return; + } + } + _onVerifyFinished(error, true); + SYNC_LOG(DEBUG) << METRIC << LOG_DESC("requestMissedTxs and verify success") + << LOG_KV( + "hash", (proposalHeader) ? proposalHeader->hash().abridged() : "unknown") + << LOG_KV("consNum", (proposalHeader) ? proposalHeader->number() : -1) + << LOG_KV("decodeT", decodeT) << LOG_KV("importT", (utcTime() - startT)) + << LOG_KV("timecost", (utcTime() - recordT)); +} + +void TransactionSync::maintainDownloadingTransactions() +{ + if (downloadTxsBufferEmpty()) + { + return; + } + auto localBuffer = swapDownloadTxsBuffer(); + if (!m_config->existsInGroup()) + { + SYNC_LOG(DEBUG) + << LOG_DESC( + "stop maintainDownloadingTransactions for the node is not belong to the group") + << LOG_KV("txpoolSize", m_config->txpoolStorage()->size()) + << LOG_KV("shardSize", m_downloadTxsBuffer->size()); + return; + } + auto self = weak_from_this(); + for (size_t i = 0; i < localBuffer->size(); ++i) + { + auto txsBuffer = (*localBuffer)[i]; + auto transactions = + m_config->blockFactory()->createBlock(txsBuffer->txsData(), true, false); + // async here to accelerate the txs process + m_worker->enqueue([self, txsBuffer, transactions]() { + auto txsSync = self.lock(); + if (!txsSync) + { + return; + } + txsSync->importDownloadedTxs(txsBuffer->from(), transactions); + }); + } +} + +bool TransactionSync::importDownloadedTxs( + NodeIDPtr _fromNode, Block::Ptr _txsBuffer, Block::Ptr _verifiedProposal) +{ + auto txs = std::make_shared(); + for (size_t i = 0; i < _txsBuffer->transactionsSize(); i++) + { + txs->emplace_back(std::const_pointer_cast(_txsBuffer->transaction(i))); + } + return importDownloadedTxs(std::move(_fromNode), txs, std::move(_verifiedProposal)); +} + +bool TransactionSync::importDownloadedTxs( + NodeIDPtr _fromNode, TransactionsPtr _txs, Block::Ptr _verifiedProposal) +{ + if (_txs->empty()) + { + return true; + } + auto txsSize = _txs->size(); + // Note: only need verify the signature for the transactions + bool enforceImport = false; + BlockHeader::Ptr proposalHeader = nullptr; + if (_verifiedProposal && _verifiedProposal->blockHeader()) + { + proposalHeader = _verifiedProposal->blockHeader(); + enforceImport = true; + } + auto recordT = utcTime(); + auto startT = utcTime(); + // verify the transactions + std::atomic_bool verifySuccess = {true}; + tbb::parallel_for( + tbb::blocked_range(0, txsSize), [&](const tbb::blocked_range& _range) { + for (size_t i = _range.begin(); i < _range.end(); i++) + { + auto tx = (*_txs)[i]; + if (!tx) + { + continue; + } + tx->appendKnownNode(_fromNode); + if (_verifiedProposal && proposalHeader) + { + tx->setBatchId(proposalHeader->number()); + tx->setBatchHash(proposalHeader->hash()); + } + if (m_config->txpoolStorage()->exist(tx->hash())) + { + continue; + } + try + { + tx->verify(); + } + catch (std::exception const& e) + { + tx->setInvalid(true); + SYNC_LOG(WARNING) << LOG_DESC("verify sender for tx failed") + << LOG_KV("reason", boost::diagnostic_information(e)) + << LOG_KV("hash", tx->hash().abridged()); + verifySuccess = false; + } + } + }); + if (enforceImport && !verifySuccess) + { + return false; + } + auto verifyT = utcTime() - startT; + startT = utcTime(); + // import the transactions into txpool + auto txpool = m_config->txpoolStorage(); + if (enforceImport) + { + if (!txpool->batchVerifyAndSubmitTransaction(proposalHeader, _txs)) + { + return false; + } + } + else + { + txpool->batchImportTxs(_txs); + } + SYNC_LOG(DEBUG) << LOG_DESC("importDownloadedTxs success") + << LOG_KV("hash", proposalHeader ? proposalHeader->hash().abridged() : "none") + << LOG_KV("number", proposalHeader ? proposalHeader->number() : -1) + << LOG_KV("totalTxs", txsSize) << LOG_KV("verifyT", verifyT) + << LOG_KV("submitT", (utcTime() - startT)) + << LOG_KV("timecost", (utcTime() - recordT)); + return true; +} + +void TransactionSync::maintainTransactions() +{ + auto consensusNodeList = m_config->consensusNodeList(); + auto connectedNodeList = m_config->connectedNodeList(); + // only one node + if (connectedNodeList.empty()) + { + m_newTransactions = false; + return; + } + if (consensusNodeList.size() == 1 && + consensusNodeList[0]->nodeID()->data() == m_config->nodeID()->data()) + { + m_newTransactions = false; + return; + } + auto txs = m_config->txpoolStorage()->fetchNewTxs(c_maxSendTransactions); + if (txs->empty()) + { + m_newTransactions = false; + return; + } + broadcastTxsFromRpc(connectedNodeList, consensusNodeList, txs); + forwardTxsFromP2P(connectedNodeList, consensusNodeList, txs); +} + +// Randomly select a number of nodes to forward the transaction status +void TransactionSync::forwardTxsFromP2P(bcos::crypto::NodeIDSet const& _connectedPeers, + bcos::consensus::ConsensusNodeList const& _consensusNodeList, ConstTransactionsPtr _txs) +{ + auto expectedPeers = (_connectedPeers.size() * m_config->forwardPercent() + 99) / 100; + std::map peerToForwardedTxs; + for (const auto& tx : *_txs) + { + // Note: in some cases the tx may be a empty shared_ptr with _vptr.Transaction to be 0x0 + // add determination here to in case of coredump + if (!tx || tx.get() == nullptr) + { + continue; + } + // TODO: Not forward txs status from the rpc directly + /*if (tx->submitCallback()) + { + continue; + }*/ + auto selectedPeers = selectPeers(tx, _connectedPeers, _consensusNodeList, expectedPeers); + for (const auto& peer : *selectedPeers) + { + if (!peerToForwardedTxs.count(peer)) + { + peerToForwardedTxs[peer] = std::make_shared(); + } + peerToForwardedTxs[peer]->emplace_back(tx->hash()); + } + } + // broadcast the txsStatus + for (auto const& it : peerToForwardedTxs) + { + auto peer = it.first; + auto txsHash = it.second; + if (txsHash->empty()) + { + continue; + } + auto txsStatus = + m_config->msgFactory()->createTxsSyncMsg(TxsSyncPacketType::TxsStatusPacket, *txsHash); + auto packetData = txsStatus->encode(); + m_config->frontService()->asyncSendMessageByNodeID( + ModuleID::TxsSync, peer, ref(*packetData), 0, nullptr); + SYNC_LOG(DEBUG) << LOG_DESC("txsStatus: forwardTxsFromP2P") + << LOG_KV("to", peer->shortHex()) << LOG_KV("txsSize", txsHash->size()) + << LOG_KV("packetSize", packetData->size()); + } +} + +NodeIDListPtr TransactionSync::selectPeers(Transaction::ConstPtr _tx, + NodeIDSet const& _connectedPeers, ConsensusNodeList const& _consensusNodeList, + size_t _expectedSize) +{ + auto selectedPeers = std::make_shared(); + for (const auto& consensusNode : _consensusNodeList) + { + auto nodeId = consensusNode->nodeID(); + // check connection + if (!_connectedPeers.contains(nodeId)) + { + continue; + } + // the node self or not + if (nodeId->data() == m_config->nodeID()->data()) + { + _tx->appendKnownNode(m_config->nodeID()); + continue; + } + // check tx existence + if (_tx->isKnownBy(nodeId)) + { + continue; + } + _tx->appendKnownNode(nodeId); + selectedPeers->emplace_back(nodeId); + if (selectedPeers->size() >= _expectedSize) + { + break; + } + } + return selectedPeers; +} + +void TransactionSync::broadcastTxsFromRpc(NodeIDSet const& _connectedPeers, + ConsensusNodeList const& _consensusNodeList, ConstTransactionsPtr _txs) +{ + auto block = m_config->blockFactory()->createBlock(); + // get the transactions from RPC + for (const auto& tx : *_txs) + { + if (!tx->submitCallback()) + { + continue; + } + for (auto const& node : _consensusNodeList) + { + if (!_connectedPeers.contains(node->nodeID())) + { + continue; + } + tx->appendKnownNode(node->nodeID()); + } + block->appendTransaction(std::const_pointer_cast(tx)); + } + if (block->transactionsSize() == 0) + { + return; + } + // broadcast the txs to all consensus node + auto encodedData = std::make_shared(); + block->encode(*encodedData); + auto txsPacket = m_config->msgFactory()->createTxsSyncMsg( + TxsSyncPacketType::TxsPacket, std::move(*encodedData)); + auto packetData = txsPacket->encode(); + m_config->frontService()->asyncSendBroadcastMessage( + bcos::protocol::NodeType::CONSENSUS_NODE, ModuleID::TxsSync, ref(*packetData)); + SYNC_LOG(DEBUG) << LOG_DESC("broadcastTxsFromRpc") + << LOG_KV("txsNum", block->transactionsSize()) + << LOG_KV("messageSize(B)", packetData->size()); +} + +void TransactionSync::onPeerTxsStatus(NodeIDPtr _fromNode, TxsSyncMsgInterface::Ptr _txsStatus) +{ + // insert all downloaded transaction into the txpool + while (!downloadTxsBufferEmpty()) + { + maintainDownloadingTransactions(); + } + if (_txsStatus->txsHash().empty()) + { + responseTxsStatus(_fromNode); + return; + } + auto requestTxs = m_config->txpoolStorage()->filterUnknownTxs(_txsStatus->txsHash(), _fromNode); + if (requestTxs->empty()) + { + return; + } + requestMissedTxsFromPeer(_fromNode, requestTxs, nullptr, nullptr); + SYNC_LOG(DEBUG) << LOG_DESC("onPeerTxsStatus") << LOG_KV("reqSize", requestTxs->size()) + << LOG_KV("peerTxsSize", _txsStatus->txsHash().size()) + << LOG_KV("peer", _fromNode->shortHex()); +} + +void TransactionSync::responseTxsStatus(NodeIDPtr _fromNode) +{ + auto txsHash = m_config->txpoolStorage()->getAllTxsHash(); + if (txsHash->empty()) + { + return; + } + auto txsStatus = + m_config->msgFactory()->createTxsSyncMsg(TxsSyncPacketType::TxsStatusPacket, *txsHash); + auto packetData = txsStatus->encode(); + m_config->frontService()->asyncSendMessageByNodeID( + ModuleID::TxsSync, _fromNode, ref(*packetData), 0, nullptr); + SYNC_LOG(DEBUG) << LOG_DESC("onPeerTxsStatus: receive empty txsStatus and responseTxsStatus") + << LOG_KV("to", _fromNode->shortHex()) << LOG_KV("txsSize", txsHash->size()) + << LOG_KV("packetSize", packetData->size()); +} + +void TransactionSync::onEmptyTxs() +{ + if (m_config->txpoolStorage()->size() > 0) + { + return; + } + SYNC_LOG(DEBUG) << LOG_DESC("onEmptyTxs: broadcast txs status to all consensus node list"); + auto txsStatus = + m_config->msgFactory()->createTxsSyncMsg(TxsSyncPacketType::TxsStatusPacket, HashList()); + auto packetData = txsStatus->encode(); + m_config->frontService()->asyncSendBroadcastMessage( + bcos::protocol::NodeType::CONSENSUS_NODE, ModuleID::TxsSync, ref(*packetData)); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/TransactionSync.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/TransactionSync.h" new file mode 100644 index 00000000..7cd6b1d1 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/TransactionSync.h" @@ -0,0 +1,154 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for transaction sync + * @file TransactionSync.h + * @author: yujiechen + * @date 2021-05-10 + */ +#pragma once + +#include "bcos-txpool/sync/TransactionSyncConfig.h" +#include "bcos-txpool/sync/interfaces/TransactionSyncInterface.h" +#include +#include +#include + +namespace bcos +{ +namespace sync +{ +class TransactionSync : public TransactionSyncInterface, + public Worker, + public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + explicit TransactionSync(TransactionSyncConfig::Ptr _config) + : TransactionSyncInterface(_config), + Worker("txsSync", 0), + m_downloadTxsBuffer(std::make_shared()), + m_worker( + std::make_shared("txsSyncWorker", std::thread::hardware_concurrency())), + m_txsRequester(std::make_shared("txsRequester", 4)), + m_forwardWorker(std::make_shared("txsForward", 1)) + { + m_txsSubmitted = m_config->txpoolStorage()->onReady([&]() { noteNewTransactions(); }); + } + + ~TransactionSync() override = default; + + void start() override; + void stop() override; + + using SendResponseCallback = std::function; + void onRecvSyncMessage(bcos::Error::Ptr _error, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, SendResponseCallback _sendResponse) override; + + using VerifyResponseCallback = std::function; + void requestMissedTxs(bcos::crypto::PublicPtr _generatedNodeID, + bcos::crypto::HashListPtr _missedTxs, bcos::protocol::Block::Ptr _verifiedProposal, + VerifyResponseCallback _onVerifyFinished) override; + + virtual void maintainTransactions(); + virtual void maintainDownloadingTransactions(); + void onEmptyTxs() override; + +protected: + virtual void responseTxsStatus(bcos::crypto::NodeIDPtr _fromNode); + void executeWorker() override; + + virtual void broadcastTxsFromRpc(bcos::crypto::NodeIDSet const& _connectedPeers, + bcos::consensus::ConsensusNodeList const& _consensusNodeList, + bcos::protocol::ConstTransactionsPtr _txs); + virtual void forwardTxsFromP2P(bcos::crypto::NodeIDSet const& _connectedPeers, + bcos::consensus::ConsensusNodeList const& _consensusNodeList, + bcos::protocol::ConstTransactionsPtr _txs); + virtual bcos::crypto::NodeIDListPtr selectPeers(bcos::protocol::Transaction::ConstPtr _tx, + bcos::crypto::NodeIDSet const& _connectedPeers, + bcos::consensus::ConsensusNodeList const& _consensusNodeList, size_t _expectedSize); + virtual void onPeerTxsStatus( + bcos::crypto::NodeIDPtr _fromNode, TxsSyncMsgInterface::Ptr _txsStatus); + + virtual void onReceiveTxsRequest(TxsSyncMsgInterface::Ptr _txsRequest, + SendResponseCallback _sendResponse, bcos::crypto::PublicPtr _peer); + + // functions called by requestMissedTxs + virtual void verifyFetchedTxs(Error::Ptr _error, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, bcos::crypto::HashListPtr _missedTxs, + bcos::protocol::Block::Ptr _verifiedProposal, VerifyResponseCallback _onVerifyFinished); + virtual void requestMissedTxsFromPeer(bcos::crypto::PublicPtr _generatedNodeID, + bcos::crypto::HashListPtr _missedTxs, bcos::protocol::Block::Ptr _verifiedProposal, + VerifyResponseCallback _onVerifyFinished); + + virtual size_t onGetMissedTxsFromLedger(std::set& _missedTxs, + Error::Ptr _error, bcos::protocol::TransactionsPtr _fetchedTxs, + bcos::protocol::Block::Ptr _verifiedProposal, VerifyResponseCallback _onVerifyFinished); + + + virtual bool downloadTxsBufferEmpty() + { + ReadGuard l(x_downloadTxsBuffer); + return (m_downloadTxsBuffer->size() == 0); + } + + virtual void appendDownloadTxsBuffer(TxsSyncMsgInterface::Ptr _txsBuffer) + { + WriteGuard l(x_downloadTxsBuffer); + m_downloadTxsBuffer->emplace_back(_txsBuffer); + } + + virtual TxsSyncMsgListPtr swapDownloadTxsBuffer() + { + UpgradableGuard l(x_downloadTxsBuffer); + auto localBuffer = m_downloadTxsBuffer; + UpgradeGuard ul(l); + m_downloadTxsBuffer = std::make_shared(); + return localBuffer; + } + virtual bool importDownloadedTxs(bcos::crypto::NodeIDPtr _fromNode, + bcos::protocol::Block::Ptr _txsBuffer, + bcos::protocol::Block::Ptr _verifiedProposal = nullptr); + + virtual bool importDownloadedTxs(bcos::crypto::NodeIDPtr _fromNode, + bcos::protocol::TransactionsPtr _txs, + bcos::protocol::Block::Ptr _verifiedProposal = nullptr); + + void noteNewTransactions() + { + m_newTransactions = true; + m_signalled.notify_all(); + } + +private: + TxsSyncMsgListPtr m_downloadTxsBuffer; + SharedMutex x_downloadTxsBuffer; + ThreadPool::Ptr m_worker; + ThreadPool::Ptr m_txsRequester; + ThreadPool::Ptr m_forwardWorker; + + bcos::Handler<> m_txsSubmitted; + + std::atomic_bool m_running = {false}; + + std::atomic_bool m_newTransactions = {false}; + + // signal to notify all thread to work + boost::condition_variable m_signalled; + // mutex to access m_signalled + boost::mutex x_signalled; +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/TransactionSyncConfig.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/TransactionSyncConfig.h" new file mode 100644 index 00000000..91265494 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/TransactionSyncConfig.h" @@ -0,0 +1,85 @@ +/** + * Copyright (C) 2021 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. + * + * @brief config for transaction sync + * @file TransactionSyncConfig.h + * @author: yujiechen + * @date 2021-05-11 + */ +#pragma once +#include "../txpool/interfaces/TxPoolStorageInterface.h" +#include "interfaces/TxsSyncMsgFactory.h" +#include +#include +#include +#include + +#include +namespace bcos +{ +namespace sync +{ +class TransactionSyncConfig : public SyncConfig +{ +public: + using Ptr = std::shared_ptr; + TransactionSyncConfig(bcos::crypto::NodeIDPtr _nodeId, + bcos::front::FrontServiceInterface::Ptr _frontService, + bcos::txpool::TxPoolStorageInterface::Ptr _txpoolStorage, + bcos::sync::TxsSyncMsgFactory::Ptr _msgFactory, + bcos::protocol::BlockFactory::Ptr _blockFactory, + std::shared_ptr _ledger) + : SyncConfig(std::move(_nodeId)), + m_frontService(std::move(_frontService)), + m_txpoolStorage(std::move(_txpoolStorage)), + m_msgFactory(std::move(_msgFactory)), + m_blockFactory(std::move(_blockFactory)), + m_ledger(std::move(_ledger)) + {} + + ~TransactionSyncConfig() override = default; + + bcos::front::FrontServiceInterface::Ptr frontService() { return m_frontService; } + bcos::txpool::TxPoolStorageInterface::Ptr txpoolStorage() { return m_txpoolStorage; } + bcos::sync::TxsSyncMsgFactory::Ptr msgFactory() { return m_msgFactory; } + + bcos::protocol::BlockFactory::Ptr blockFactory() { return m_blockFactory; } + + unsigned networkTimeout() const { return m_networkTimeout; } + void setNetworkTimeout(unsigned _networkTimeout) { m_networkTimeout = _networkTimeout; } + unsigned forwardPercent() const { return m_forwardPercent; } + void setForwardPercent(unsigned _forwardPercent) { m_forwardPercent = _forwardPercent; } + std::shared_ptr ledger() { return m_ledger; } + + // for ut + void setTxPoolStorage(bcos::txpool::TxPoolStorageInterface::Ptr _txpoolStorage) + { + m_txpoolStorage = _txpoolStorage; + } + +private: + bcos::front::FrontServiceInterface::Ptr m_frontService; + bcos::txpool::TxPoolStorageInterface::Ptr m_txpoolStorage; + bcos::sync::TxsSyncMsgFactory::Ptr m_msgFactory; + bcos::protocol::BlockFactory::Ptr m_blockFactory; + std::shared_ptr m_ledger; + + // set networkTimeout to 500ms + unsigned m_networkTimeout = 500; + + unsigned m_forwardPercent = 25; +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/interfaces/TransactionSyncInterface.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/interfaces/TransactionSyncInterface.h" new file mode 100644 index 00000000..e47b7f42 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/interfaces/TransactionSyncInterface.h" @@ -0,0 +1,54 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interfaces for transaction sync + * @file TransactionSyncInterface.h + * @author: yujiechen + * @date 2021-05-10 + */ +#pragma once +#include "../TransactionSyncConfig.h" +#include +#include + +namespace bcos::sync +{ +class TransactionSyncInterface +{ +public: + using Ptr = std::shared_ptr; + explicit TransactionSyncInterface(TransactionSyncConfig::Ptr _config) + : m_config(std::move(_config)) + {} + + virtual ~TransactionSyncInterface() = default; + + virtual void start() = 0; + virtual void stop() = 0; + + virtual void requestMissedTxs(bcos::crypto::PublicPtr _generatedNodeID, + bcos::crypto::HashListPtr _missedTxs, bcos::protocol::Block::Ptr _verifiedProposal, + std::function _onVerifyFinished) = 0; + + virtual void onRecvSyncMessage(bcos::Error::Ptr _error, bcos::crypto::NodeIDPtr _nodeID, + bytesConstRef _data, std::function _sendResponse) = 0; + + virtual TransactionSyncConfig::Ptr config() { return m_config; } + virtual void onEmptyTxs() = 0; + +protected: + TransactionSyncConfig::Ptr m_config; +}; +} // namespace bcos::sync \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/interfaces/TxsSyncMsgFactory.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/interfaces/TxsSyncMsgFactory.h" new file mode 100644 index 00000000..4d0cb273 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/interfaces/TxsSyncMsgFactory.h" @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2021 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. + * + * @brief factory for create the txs-sync-message + * @file TxsSyncMsgFactory.h + * @author: yujiechen + * @date 2021-05-11 + */ +#pragma once +#include "TxsSyncMsgInterface.h" + +namespace bcos::sync +{ +class TxsSyncMsgFactory +{ +public: + using Ptr = std::shared_ptr; + TxsSyncMsgFactory() = default; + virtual ~TxsSyncMsgFactory() = default; + + virtual TxsSyncMsgInterface::Ptr createTxsSyncMsg() = 0; + virtual TxsSyncMsgInterface::Ptr createTxsSyncMsg( + uint32_t _type, bytes&& _txsData, int32_t _version = 1) + { + auto txsSyncMsg = createTxsSyncMsg(); + txsSyncMsg->setType(_type); + txsSyncMsg->setTxsData(std::move(_txsData)); + txsSyncMsg->setVersion(_version); + return txsSyncMsg; + } + + virtual TxsSyncMsgInterface::Ptr createTxsSyncMsg( + uint32_t _type, bcos::crypto::HashList const& _txsHash, int32_t _version = 1) + { + auto txsSyncMsg = createTxsSyncMsg(); + txsSyncMsg->setType(_type); + txsSyncMsg->setTxsHash(_txsHash); + txsSyncMsg->setVersion(_version); + return txsSyncMsg; + } + + virtual TxsSyncMsgInterface::Ptr createTxsSyncMsg(bytesConstRef _data) = 0; +}; +} // namespace bcos::sync diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/interfaces/TxsSyncMsgInterface.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/interfaces/TxsSyncMsgInterface.h" new file mode 100644 index 00000000..4114fa07 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/interfaces/TxsSyncMsgInterface.h" @@ -0,0 +1,58 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interfaces for the txs-sync-message + * @file TxsSyncMsgInterface.h + * @author: yujiechen + * @date 2021-05-11 + */ +#pragma once +#include +#include +namespace bcos +{ +namespace sync +{ +class TxsSyncMsgInterface +{ +public: + using Ptr = std::shared_ptr; + TxsSyncMsgInterface() = default; + virtual ~TxsSyncMsgInterface() {} + + virtual bytesPointer encode() const = 0; + virtual void decode(bytesConstRef _data) = 0; + + virtual int32_t version() const = 0; + virtual int32_t type() const = 0; + virtual bytesConstRef txsData() const = 0; + virtual bcos::crypto::HashList const& txsHash() const = 0; + + virtual void setVersion(int32_t _version) = 0; + virtual void setType(int32_t _type) = 0; + virtual void setTxsData(bytes const& _txsData) = 0; + virtual void setTxsData(bytes&& _txsData) = 0; + virtual void setTxsHash(bcos::crypto::HashList const& _txsHash) = 0; + + virtual void setFrom(bcos::crypto::NodeIDPtr _from) { m_from = _from; } + virtual bcos::crypto::NodeIDPtr from() const { return m_from; } + +protected: + bcos::crypto::NodeIDPtr m_from; +}; +using TxsSyncMsgList = std::vector; +using TxsSyncMsgListPtr = std::shared_ptr; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/protocol/PB/TxsSyncMsg.cpp" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/protocol/PB/TxsSyncMsg.cpp" new file mode 100644 index 00000000..8eaa2cac --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/protocol/PB/TxsSyncMsg.cpp" @@ -0,0 +1,100 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for the txs-sync-message + * @file TxsSync.cpp + * @author: yujiechen + * @date 2021-05-11 + */ +#include "TxsSyncMsg.h" +#include "bcos-protocol/Common.h" + +using namespace bcos; +using namespace bcos::sync; +using namespace bcos::crypto; +using namespace bcos::protocol; + +bytesPointer TxsSyncMsg::encode() const +{ + return encodePBObject(m_rawSyncMessage); +} + +void TxsSyncMsg::decode(bytesConstRef _data) +{ + decodePBObject(m_rawSyncMessage, _data); + deserializeObject(); +} + +int32_t TxsSyncMsg::version() const +{ + return m_rawSyncMessage->version(); +} + +int32_t TxsSyncMsg::type() const +{ + return m_rawSyncMessage->type(); +} + +bytesConstRef TxsSyncMsg::txsData() const +{ + auto const& txsData = m_rawSyncMessage->txsdata(); + return bytesConstRef((byte const*)txsData.data(), txsData.size()); +} + +HashList const& TxsSyncMsg::txsHash() const +{ + return *m_txsHash; +} + +void TxsSyncMsg::setVersion(int32_t _version) +{ + m_rawSyncMessage->set_version(_version); +} + +void TxsSyncMsg::setType(int32_t _type) +{ + m_rawSyncMessage->set_type(_type); +} + +void TxsSyncMsg::setTxsData(bytes const& _txsData) +{ + m_rawSyncMessage->set_txsdata(_txsData.data(), _txsData.size()); +} +void TxsSyncMsg::setTxsData(bytes&& _txsData) +{ + auto dataSize = _txsData.size(); + m_rawSyncMessage->set_txsdata(std::move(_txsData).data(), dataSize); +} + +void TxsSyncMsg::setTxsHash(HashList const& _txsHash) +{ + *m_txsHash = _txsHash; + m_rawSyncMessage->clear_txshash(); + for (auto const& hash : _txsHash) + { + m_rawSyncMessage->add_txshash(hash.data(), HashType::SIZE); + } +} + +void TxsSyncMsg::deserializeObject() +{ + m_txsHash->clear(); + for (int i = 0; i < m_rawSyncMessage->txshash_size(); i++) + { + auto const& hashData = m_rawSyncMessage->txshash(i); + m_txsHash->emplace_back( + HashType((byte const*)hashData.c_str(), bcos::crypto::HashType::SIZE)); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/protocol/PB/TxsSyncMsg.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/protocol/PB/TxsSyncMsg.h" new file mode 100644 index 00000000..67ebe010 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/protocol/PB/TxsSyncMsg.h" @@ -0,0 +1,62 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for the txs-sync-message + * @file TxsSync.h + * @author: yujiechen + * @date 2021-05-11 + */ +#pragma once +#include +#include + +namespace bcos +{ +namespace sync +{ +class TxsSyncMsg : public TxsSyncMsgInterface +{ +public: + TxsSyncMsg() + : m_rawSyncMessage(std::make_shared()), + m_txsHash(std::make_shared()) + {} + + explicit TxsSyncMsg(bytesConstRef _data) : TxsSyncMsg() { decode(_data); } + ~TxsSyncMsg() override {} + + bytesPointer encode() const override; + void decode(bytesConstRef _data) override; + + int32_t version() const override; + int32_t type() const override; + bytesConstRef txsData() const override; + bcos::crypto::HashList const& txsHash() const override; + + void setVersion(int32_t _version) override; + void setType(int32_t _type) override; + void setTxsData(bytes const& _txsData) override; + void setTxsData(bytes&& _txsData) override; + void setTxsHash(bcos::crypto::HashList const& _txsHash) override; + +protected: + virtual void deserializeObject(); + +private: + std::shared_ptr m_rawSyncMessage; + bcos::crypto::HashListPtr m_txsHash; +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/protocol/PB/TxsSyncMsgFactoryImpl.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/protocol/PB/TxsSyncMsgFactoryImpl.h" new file mode 100644 index 00000000..79adf79f --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/protocol/PB/TxsSyncMsgFactoryImpl.h" @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2021 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. + * + * @brief factory for create the txs-sync-message + * @file TxsSyncMsgFactoryImpl.h + * @author: yujiechen + * @date 2021-05-11 + */ +#pragma once +#include "TxsSyncMsg.h" +#include "bcos-txpool/sync/interfaces/TxsSyncMsgFactory.h" + +namespace bcos +{ +namespace sync +{ +class TxsSyncMsgFactoryImpl : public TxsSyncMsgFactory +{ +public: + using Ptr = std::shared_ptr; + TxsSyncMsgFactoryImpl() = default; + ~TxsSyncMsgFactoryImpl() override {} + + TxsSyncMsgInterface::Ptr createTxsSyncMsg() override { return std::make_shared(); } + TxsSyncMsgInterface::Ptr createTxsSyncMsg(bytesConstRef _data) override + { + return std::make_shared(_data); + } +}; +} // namespace sync +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/protocol/proto/TxsSync.proto" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/protocol/proto/TxsSync.proto" new file mode 100644 index 00000000..c1221e08 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/protocol/proto/TxsSync.proto" @@ -0,0 +1,9 @@ +syntax = "proto3"; +package bcos.sync; +message TxsSyncMessage +{ + int32 version = 1; + int32 type = 2; + bytes txsData = 3; + repeated bytes txsHash = 4; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/utilities/Common.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/utilities/Common.h" new file mode 100644 index 00000000..6b2c4cfd --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/sync/utilities/Common.h" @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Common for the sync module + * @file Common.h + * @author: yujiechen + * @date 2021-05-11 + */ +#pragma once +#include +#include + +#define SYNC_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("SYNC") +namespace bcos +{ +namespace sync +{ +enum TxsSyncPacketType : int32_t +{ + TxsPacket = 0x00, + TxsStatusPacket = 0x01, + TxsRequestPacket = 0x02, + TxsResponsePacket = 0x03, + PacketCount +}; +} +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/interfaces/NonceCheckerInterface.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/interfaces/NonceCheckerInterface.h" new file mode 100644 index 00000000..4c6a114a --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/interfaces/NonceCheckerInterface.h" @@ -0,0 +1,54 @@ +/** + * Copyright (C) 2021 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. + * + * @brief interface for nonce check + * @file NonceCheckerInterface.h + * @author: yujiechen + * @date 2021-05-08 + */ +#pragma once +#include +#include +#include +#define TBB_PREVIEW_CONCURRENT_ORDERED_CONTAINERS 1 +#include + +#define NONCECHECKER_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("TXPOOL") << LOG_BADGE("NonceChecker") + +namespace bcos +{ +namespace txpool +{ +class NonceCheckerInterface +{ +public: + using Ptr = std::shared_ptr; + NonceCheckerInterface() = default; + virtual ~NonceCheckerInterface() {} + + virtual bcos::protocol::TransactionStatus checkNonce( + bcos::protocol::Transaction::ConstPtr _tx, bool _shouldUpdate = false) = 0; + virtual bool exists(bcos::protocol::NonceType const& _nonce) = 0; + virtual void batchInsert( + bcos::protocol::BlockNumber _batchId, bcos::protocol::NonceListPtr _nonceList) = 0; + virtual void batchRemove(bcos::protocol::NonceList const& _nonceList) = 0; + virtual void batchRemove(tbb::concurrent_set const& _nonceList) = 0; + virtual void insert(bcos::protocol::NonceType const& _nonce) = 0; + +protected: + virtual void remove(bcos::protocol::NonceType const& _nonce) = 0; +}; +} // namespace txpool +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/interfaces/TxPoolStorageInterface.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/interfaces/TxPoolStorageInterface.h" new file mode 100644 index 00000000..5b7764b2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/interfaces/TxPoolStorageInterface.h" @@ -0,0 +1,125 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Storage interface for TxPool + * @file TxPoolStorageInterface.h + * @author: yujiechen + * @date 2021-05-07 + */ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace txpool +{ +class TxPoolStorageInterface +{ +public: + using Ptr = std::shared_ptr; + TxPoolStorageInterface() = default; + virtual ~TxPoolStorageInterface() {} + + virtual task::Task submitTransaction( + protocol::Transaction::Ptr transaction) = 0; + + virtual bcos::protocol::TransactionStatus insert(bcos::protocol::Transaction::ConstPtr _tx) = 0; + virtual void batchInsert(bcos::protocol::Transactions const& _txs) = 0; + + virtual bcos::protocol::Transaction::ConstPtr remove(bcos::crypto::HashType const& _txHash) = 0; + virtual bcos::protocol::Transaction::ConstPtr removeSubmittedTx( + bcos::protocol::TransactionSubmitResult::Ptr _txSubmitResult) = 0; + virtual void batchRemove(bcos::protocol::BlockNumber _batchId, + bcos::protocol::TransactionSubmitResults const& _txsResult) = 0; + + // Note: the transactions may be missing from the transaction pool + virtual bcos::protocol::TransactionsPtr fetchTxs( + bcos::crypto::HashList& _missedTxs, bcos::crypto::HashList const& _txsList) = 0; + + + virtual bool batchVerifyAndSubmitTransaction( + bcos::protocol::BlockHeader::Ptr _header, bcos::protocol::TransactionsPtr _txs) = 0; + virtual void batchImportTxs(bcos::protocol::TransactionsPtr _txs) = 0; + + /** + * @brief Get newly inserted transactions from the txpool + * + * @param _txsLimit Maximum number of transactions that can be obtained at a time + * @return List of new transactions + */ + virtual bcos::protocol::ConstTransactionsPtr fetchNewTxs(size_t _txsLimit) = 0; + virtual void batchFetchTxs(bcos::protocol::Block::Ptr _txsList, + bcos::protocol::Block::Ptr _sysTxsList, size_t _txsLimit, TxsHashSetPtr _avoidTxs, + bool _avoidDuplicate = true) = 0; + + virtual bool exist(bcos::crypto::HashType const& _txHash) = 0; + + virtual bcos::crypto::HashListPtr filterUnknownTxs( + bcos::crypto::HashList const& _txsHashList, bcos::crypto::NodeIDPtr _peer) = 0; + + virtual size_t size() const = 0; + virtual void clear() = 0; + + // Register a handler that will be called once there is a new transaction imported + template + bcos::Handler<> onReady(T const& _t) + { + return m_onReady.add(_t); + } + + virtual void batchMarkTxs(bcos::crypto::HashList const& _txsHashList, + bcos::protocol::BlockNumber _batchId, bcos::crypto::HashType const& _batchHash, + bool _sealFlag) = 0; + virtual void batchMarkAllTxs(bool _sealFlag) = 0; + + virtual size_t unSealedTxsSize() = 0; + + virtual void registerUnsealedTxsNotifier( + std::function)> _unsealedTxsNotifier) + { + m_unsealedTxsNotifier = _unsealedTxsNotifier; + } + + virtual void stop() = 0; + virtual void start() = 0; + virtual void printPendingTxs() {} + + virtual std::shared_ptr batchVerifyProposal( + bcos::protocol::Block::Ptr _block) = 0; + + virtual bool batchVerifyProposal(std::shared_ptr _txsHashList) = 0; + virtual bcos::crypto::HashListPtr getAllTxsHash() = 0; + + void registerTxsCleanUpSwitch(std::function _txsCleanUpSwitch) + { + m_txsCleanUpSwitch = _txsCleanUpSwitch; + } + + virtual bool preStoreTxs() const { return true; } + +protected: + bcos::CallbackCollectionHandler<> m_onReady; + // notify the sealer the latest unsealed txs + std::function)> m_unsealedTxsNotifier; + // Determine to periodically clean up expired transactions or not + std::function m_txsCleanUpSwitch; +}; +} // namespace txpool +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/interfaces/TxValidatorInterface.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/interfaces/TxValidatorInterface.h" new file mode 100644 index 00000000..43dcb535 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/interfaces/TxValidatorInterface.h" @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Interface to verify the validity of the transaction + * @file TxValidatorInterface.h + * @author: yujiechen + * @date 2021-05-08 + */ +#pragma once +#include "bcos-txpool/txpool/interfaces/NonceCheckerInterface.h" +#include +#include +namespace bcos +{ +namespace txpool +{ +class TxValidatorInterface +{ +public: + using Ptr = std::shared_ptr; + TxValidatorInterface() = default; + virtual ~TxValidatorInterface() {} + + virtual bcos::protocol::TransactionStatus verify(bcos::protocol::Transaction::ConstPtr _tx) = 0; + virtual bcos::protocol::TransactionStatus submittedToChain( + bcos::protocol::Transaction::ConstPtr _tx) = 0; + virtual NonceCheckerInterface::Ptr ledgerNonceChecker() { return m_ledgerNonceChecker; } + virtual void setLedgerNonceChecker(NonceCheckerInterface::Ptr _ledgerNonceChecker) + { + m_ledgerNonceChecker = _ledgerNonceChecker; + } + +protected: + NonceCheckerInterface::Ptr m_ledgerNonceChecker; +}; +} // namespace txpool +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp" new file mode 100644 index 00000000..66b7d15c --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.cpp" @@ -0,0 +1,1147 @@ +/** + * Copyright (C) 2021 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. + * + * @brief an implementation of using memory to store transactions + * @file MemoryStorage.cpp + * @author: yujiechen + * @date 2021-05-07 + */ +#include "bcos-txpool/txpool/storage/MemoryStorage.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::txpool; +using namespace bcos::crypto; +using namespace bcos::protocol; + +MemoryStorage::MemoryStorage(TxPoolConfig::Ptr _config, size_t _notifyWorkerNum, + int64_t _txsExpirationTime, bool _preStoreTxs) + : m_config(_config), m_txsExpirationTime(_txsExpirationTime), m_preStoreTxs(_preStoreTxs) +{ + m_notifier = std::make_shared("txNotifier", _notifyWorkerNum); + m_worker = std::make_shared("txpoolWorker", 1); + m_blockNumberUpdatedTime = utcTime(); + // Trigger a transaction cleanup operation every 3s + m_cleanUpTimer = std::make_shared(3000, "txpoolTimer"); + m_cleanUpTimer->registerTimeoutHandler( + boost::bind(&MemoryStorage::cleanUpExpiredTransactions, this)); + TXPOOL_LOG(INFO) << LOG_DESC("init MemoryStorage of txpool") + << LOG_KV("txNotifierWorkerNum", _notifyWorkerNum) + << LOG_KV("txsExpriationTime", m_txsExpirationTime) + << LOG_KV("preStoreTxs", m_preStoreTxs); +} + +void MemoryStorage::start() +{ + if (m_cleanUpTimer) + { + m_cleanUpTimer->start(); + } +} + +void MemoryStorage::stop() +{ + if (m_notifier) + { + m_notifier->stop(); + } + if (m_worker) + { + m_worker->stop(); + } + if (m_cleanUpTimer) + { + m_cleanUpTimer->stop(); + } +} + +task::Task MemoryStorage::submitTransaction( + protocol::Transaction::Ptr transaction) +{ + transaction->setImportTime(utcTime()); + struct Awaitable + { + constexpr bool await_ready() { return false; } + void await_suspend(CO_STD::coroutine_handle<> handle) + { + try + { + auto result = m_self->verifyAndSubmitTransaction( + std::move(m_transaction), + [this, m_handle = handle](Error::Ptr error, + bcos::protocol::TransactionSubmitResult::Ptr result) mutable { + if (error) + { + m_submitResult.emplace(std::move(error)); + } + else + { + m_submitResult.emplace( + std::move(result)); + } + if (m_handle) + { + m_handle.resume(); + } + }, + true, true); + + if (result != TransactionStatus::None) + { + TXPOOL_LOG(ERROR) << "Submit transaction error! " << result; + m_submitResult.emplace( + BCOS_ERROR_PTR((int32_t)result, "Invalid transaction")); + handle.resume(); + } + } + catch (std::exception& e) + { + m_submitResult.emplace( + BCOS_ERROR_PTR((int32_t)TransactionStatus::Malform, "Invalid transaction")); + 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 = transaction, .m_self = shared_from_this(), .m_submitResult = {}}; + co_return co_await awaitable; +} + +TransactionStatus MemoryStorage::txpoolStorageCheck(Transaction::ConstPtr _tx) +{ + auto txHash = _tx->hash(); + if (m_txsTable.count(txHash)) + { + return TransactionStatus::AlreadyInTxPool; + } + return TransactionStatus::None; +} + +// Note: the signature of the tx has already been verified +TransactionStatus MemoryStorage::enforceSubmitTransaction(Transaction::Ptr _tx) +{ + auto txHash = _tx->hash(); + // the transaction has already onChain, reject it + auto result = m_config->txValidator()->submittedToChain(_tx); + auto it = m_txsTable.find(txHash); + Transaction::ConstPtr tx = nullptr; + if (it != m_txsTable.end()) + { + tx = it->second; + } + if (result == TransactionStatus::NonceCheckFail) + { + 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()); + } + return TransactionStatus::NonceCheckFail; + } + if (tx) + { + if (!tx->sealed() || tx->batchHash() == HashType()) + { + if (!tx->sealed()) + { + m_sealedTxsSize++; + tx->setSealed(true); + } + tx->setBatchId(_tx->batchId()); + tx->setBatchHash(_tx->batchHash()); + TXPOOL_LOG(TRACE) << LOG_DESC("enforce to seal:") << tx->hash().abridged() + << LOG_KV("num", tx->batchId()) + << LOG_KV("hash", tx->batchHash().abridged()); + return TransactionStatus::None; + } + // sealed for the same proposal + if (tx->batchId() == _tx->batchId() && tx->batchHash() == _tx->batchHash()) + { + return TransactionStatus::None; + } + TXPOOL_LOG(WARNING) << LOG_DESC("enforce to seal 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()); + // The transaction has already been sealed by another node + return TransactionStatus::AlreadyInTxPool; + } + auto status = insertWithoutLock(_tx); + if (status != TransactionStatus::None) + { + auto tx = m_txsTable.at(_tx->hash()); + TXPOOL_LOG(WARNING) << LOG_DESC("insertWithoutLock failed for already has the tx") + << LOG_KV("hash", tx->hash().abridged()) + << LOG_KV("status", tx->sealed()); + if (!tx->sealed()) + { + tx->setSealed(true); + m_sealedTxsSize++; + } + } + else + { + // avoid the sealed txs be sealed again + _tx->setSealed(true); + m_sealedTxsSize++; + } + return TransactionStatus::None; +} + +TransactionStatus MemoryStorage::verifyAndSubmitTransaction( + Transaction::Ptr _tx, TxSubmitCallback _txSubmitCallback, bool _checkPoolLimit, bool _lock) +{ + // start stat the tps when receive first new tx from the sdk + if (m_tpsStatstartTime.load() == 0 && m_txsTable.size() == 0) + { + m_tpsStatstartTime = utcTime(); + } + // Note: In order to ensure that transactions can reach all nodes, transactions from P2P are not + // restricted + if (_checkPoolLimit && m_txsTable.size() >= m_config->poolLimit()) + { + return TransactionStatus::TxPoolIsFull; + } + auto result = txpoolStorageCheck(_tx); + if (result != TransactionStatus::None) + { + return result; + } + // verify the transaction + result = m_config->txValidator()->verify(_tx); + if (result == TransactionStatus::None) + { + if (_txSubmitCallback) + { + _tx->setSubmitCallback(_txSubmitCallback); + } + if (_lock) + { + result = insert(_tx); + } + else + { + result = insertWithoutLock(_tx); + } + } + else + { + return result; + } + return result; +} + +void MemoryStorage::notifyInvalidReceipt( + HashType const& _txHash, TransactionStatus _status, TxSubmitCallback _txSubmitCallback) +{ + if (!_txSubmitCallback) + { + return; + } + // notify txResult + auto txResult = m_config->txResultFactory()->createTxSubmitResult(); + txResult->setTxHash(_txHash); + txResult->setStatus((uint32_t)_status); + std::stringstream errorMsg; + errorMsg << _status; + _txSubmitCallback(std::make_shared((int32_t)_status, errorMsg.str()), txResult); + TXPOOL_LOG(WARNING) << LOG_DESC("notifyReceipt: reject invalid tx") + << LOG_KV("tx", _txHash.abridged()) << LOG_KV("exception", _status); +} + +TransactionStatus MemoryStorage::insert(Transaction::ConstPtr _tx) +{ + ReadGuard l(x_txpoolMutex); + return insertWithoutLock(_tx); +} + +TransactionStatus MemoryStorage::insertWithoutLock(Transaction::ConstPtr _tx) +{ + // check again to ensure the same transaction not be imported many times + if (m_txsTable.count(_tx->hash())) + { + return TransactionStatus::AlreadyInTxPool; + } + auto result = m_txsTable.insert(std::make_pair(_tx->hash(), _tx)); + if (!result.second) + { + return TransactionStatus::AlreadyInTxPool; + } + m_onReady(); + if (m_preStoreTxs) + { + preCommitTransaction(_tx); + } + notifyUnsealedTxsSize(); +#if FISCO_DEBUG + // TODO: remove this, now just for bug tracing + TXPOOL_LOG(DEBUG) << LOG_DESC("submit tx:") << _tx->hash().abridged() + << LOG_KV("txPointer", _tx); +#endif + return TransactionStatus::None; +} + +void MemoryStorage::preCommitTransaction(Transaction::ConstPtr _tx) +{ + auto self = weak_from_this(); + m_worker->enqueue([self, _tx]() { + try + { + auto txpoolStorage = self.lock(); + if (!txpoolStorage) + { + return; + } + // the transaction has already been stored to backend + if (_tx->storeToBackend()) + { + return; + } + bcos::bytes encodeData; + _tx->encode(encodeData); + auto txsToStore = std::make_shared>(); + txsToStore->emplace_back(std::make_shared(std::move(encodeData))); + auto txsHash = std::make_shared(); + auto txHash = _tx->hash(); + txsHash->emplace_back(txHash); + txpoolStorage->m_config->ledger()->asyncStoreTransactions( + txsToStore, txsHash, [_tx, txHash](Error::Ptr _error) { + if (_error == nullptr) + { + _tx->setStoreToBackend(true); + return; + } + TXPOOL_LOG(WARNING) << LOG_DESC("asyncPreStoreTransaction failed") + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMsg", _error->errorMessage()) + << LOG_KV("tx", txHash.abridged()); + }); + } + catch (std::exception const& e) + { + TXPOOL_LOG(WARNING) << LOG_DESC("preCommitTransaction exception") + << LOG_KV("error", boost::diagnostic_information(e)) + << LOG_KV("tx", _tx->hash().abridged()); + } + }); +} + +void MemoryStorage::batchInsert(Transactions const& _txs) +{ + for (auto tx : _txs) + { + insert(tx); + } + WriteGuard l(x_missedTxs); + for (auto tx : _txs) + { + m_missedTxs.unsafe_erase(tx->hash()); + } +} + +Transaction::ConstPtr MemoryStorage::removeWithoutLock(HashType const& _txHash) +{ + auto it = m_txsTable.find(_txHash); + if (it == m_txsTable.end()) + { + return nullptr; + } + auto tx = std::move(it->second); + if (tx && tx->sealed()) + { + --m_sealedTxsSize; + } + m_txsTable.unsafe_erase(it); +#if FISCO_DEBUG + // TODO: remove this, now just for bug tracing + TXPOOL_LOG(DEBUG) << LOG_DESC("remove tx: ") << tx->hash().abridged() + << LOG_KV("index", tx->batchId()) + << LOG_KV("hash", tx->batchHash().abridged()) << LOG_KV("txPointer", tx); +#endif + return tx; +} + +Transaction::ConstPtr MemoryStorage::remove(HashType const& _txHash) +{ + WriteGuard l(x_txpoolMutex); + auto tx = removeWithoutLock(_txHash); + notifyUnsealedTxsSize(); + return tx; +} + +Transaction::ConstPtr MemoryStorage::removeSubmittedTxWithoutLock( + TransactionSubmitResult::Ptr txSubmitResult, bool _notify) +{ + auto tx = removeWithoutLock(txSubmitResult->txHash()); + if (!tx) + { + return nullptr; + } + if (_notify) + { + notifyTxResult(*tx, std::move(txSubmitResult)); + } + return tx; +} + +Transaction::ConstPtr MemoryStorage::removeSubmittedTx(TransactionSubmitResult::Ptr txSubmitResult) +{ + auto tx = remove(txSubmitResult->txHash()); + if (!tx) + { + return nullptr; + } + notifyTxResult(*tx, std::move(txSubmitResult)); + return tx; +} +void MemoryStorage::notifyTxResult( + Transaction const& transaction, TransactionSubmitResult::Ptr txSubmitResult) +{ + const auto& txSubmitCallback = transaction.submitCallback(); + if (!txSubmitCallback) + { + return; + } + + auto txHash = transaction.hash(); + txSubmitResult->setSender(std::string(transaction.sender())); + txSubmitResult->setTo(std::string(transaction.to())); + try + { + txSubmitCallback(nullptr, std::move(txSubmitResult)); + } + catch (std::exception const& e) + { + TXPOOL_LOG(WARNING) << LOG_DESC("notifyTxResult failed") << LOG_KV("tx", txHash.abridged()) + << LOG_KV("errorInfo", boost::diagnostic_information(e)); + } +} + +// TODO: remove this, now just for bug tracing +void MemoryStorage::printPendingTxs() +{ + if (m_printed) + { + return; + } + if (utcTime() - m_blockNumberUpdatedTime <= 1000 * 50) + { + return; + } + if (unSealedTxsSize() > 0 || m_txsTable.size() == 0) + { + return; + } + TXPOOL_LOG(DEBUG) << LOG_DESC("printPendingTxs for some txs unhandle") + << LOG_KV("pendingSize", m_txsTable.size()); + for (auto item : m_txsTable) + { + auto tx = item.second; + if (!tx) + { + continue; + } + TXPOOL_LOG(DEBUG) << LOG_KV("hash", tx->hash().abridged()) << LOG_KV("id", tx->batchId()) + << LOG_KV("hash", tx->batchHash().abridged()) + << LOG_KV("seal", tx->sealed()); + } + TXPOOL_LOG(DEBUG) << LOG_DESC("printPendingTxs for some txs unhandle finish"); + m_printed = true; +} +void MemoryStorage::batchRemove(BlockNumber batchId, TransactionSubmitResults const& txsResult) +{ + auto startT = utcTime(); + auto recordT = startT; + int64_t lockT = 0; + m_blockNumberUpdatedTime = recordT; + size_t succCount = 0; + NonceListPtr nonceList = std::make_shared(); + + std::vector> results; + results.reserve(txsResult.size()); + { + WriteGuard lock(x_txpoolMutex); + for (const auto& it : txsResult) + { + auto const& txResult = it; + auto tx = removeWithoutLock(txResult->txHash()); + if (!tx && txResult->nonce() != NonceType(-1)) + { + nonceList->emplace_back(txResult->nonce()); + } + else if (tx) + { + ++succCount; + nonceList->emplace_back(tx->nonce()); + } + results.emplace_back(std::tuple{std::move(tx), txResult}); + } + + if (batchId > m_blockNumber) + { + m_blockNumber = batchId; + } + lockT = utcTime() - startT; + } + + m_onChainTxsCount += txsResult.size(); + // stop stat the tps when there has no pending txs + if (m_tpsStatstartTime.load() > 0 && m_txsTable.size() == 0) + { + auto totalTime = (utcTime() - m_tpsStatstartTime); + if (totalTime > 0) + { + auto tps = (m_onChainTxsCount * 1000) / totalTime; + TXPOOL_LOG(INFO) << METRIC << LOG_DESC("StatTPS") << LOG_KV("tps", tps) + << LOG_KV("totalTime", totalTime); + } + m_tpsStatstartTime.store(0); + m_onChainTxsCount.store(0); + } + + auto removeT = utcTime() - startT; + startT = utcTime(); + notifyUnsealedTxsSize(); + // update the ledger nonce + m_config->txValidator()->ledgerNonceChecker()->batchInsert(batchId, nonceList); + auto updateLedgerNonceT = utcTime() - startT; + startT = utcTime(); + // update the txpool nonce + m_config->txPoolNonceChecker()->batchRemove(*nonceList); + auto updateTxPoolNonceT = utcTime() - startT; + + for (auto& [tx, txResult] : results) + { + if (tx) + { + notifyTxResult(*tx, std::move(txResult)); + } + } + + TXPOOL_LOG(INFO) << METRIC << LOG_DESC("batchRemove txs success") + << LOG_KV("expectedSize", txsResult.size()) << LOG_KV("succCount", succCount) + << LOG_KV("batchId", batchId) << LOG_KV("timecost", (utcTime() - recordT)) + << LOG_KV("lockT", lockT) << LOG_KV("removeT", removeT) + << LOG_KV("updateLedgerNonceT", updateLedgerNonceT) + << LOG_KV("updateTxPoolNonceT", updateTxPoolNonceT); +} + +TransactionsPtr MemoryStorage::fetchTxs(HashList& _missedTxs, HashList const& _txs) +{ + ReadGuard l(x_txpoolMutex); + auto fetchedTxs = std::make_shared(); + _missedTxs.clear(); + for (auto const& hash : _txs) + { + auto it = m_txsTable.find(hash); + if (it == m_txsTable.end()) + { + _missedTxs.emplace_back(hash); + continue; + } + auto tx = it->second; + fetchedTxs->emplace_back(std::const_pointer_cast(tx)); + } + if (c_fileLogLevel <= TRACE) + [[unlikely]] + { + for (auto const& tx : _missedTxs) + { + TXPOOL_LOG(TRACE) << "miss: " << tx.abridged(); + } + } + return fetchedTxs; +} + +ConstTransactionsPtr MemoryStorage::fetchNewTxs(size_t _txsLimit) +{ + ReadGuard l(x_txpoolMutex); + auto fetchedTxs = std::make_shared(); + fetchedTxs->reserve(_txsLimit); + + for (auto const& it : m_txsTable) + { + auto& tx = it.second; + // Note: When inserting data into tbb::concurrent_unordered_map while traversing, it.second + // will occasionally be a null pointer. + if (!tx || tx->synced()) + { + continue; + } + tx->setSynced(true); + fetchedTxs->emplace_back(tx); + if (fetchedTxs->size() >= _txsLimit) + { + break; + } + } + return fetchedTxs; +} + +void MemoryStorage::batchFetchTxs(Block::Ptr _txsList, Block::Ptr _sysTxsList, size_t _txsLimit, + TxsHashSetPtr _avoidTxs, bool _avoidDuplicate) +{ + TXPOOL_LOG(INFO) << LOG_DESC("begin batchFetchTxs") << LOG_KV("pendingTxs", m_txsTable.size()) + << LOG_KV("limit", _txsLimit); + auto blockFactory = m_config->blockFactory(); + auto recordT = utcTime(); + auto startT = utcTime(); + ReadGuard l(x_txpoolMutex); + auto lockT = utcTime() - startT; + startT = utcTime(); + int64_t currentTime = (int64_t)utcTime(); + size_t traverseCount = 0; + for (auto const& it : m_txsTable) + { + traverseCount++; + auto tx = it.second; + // Note: When inserting data into tbb::concurrent_unordered_map while traversing, + // it.second will occasionally be a null pointer. + if (!tx) + { + continue; + } + // only seal the txs have been stored to the backend + if (m_preStoreTxs && !tx->storeToBackend()) + { + continue; + } + auto txHash = tx->hash(); + if (m_invalidTxs.count(txHash)) + { + continue; + } + // the transaction has already been sealed for newer proposal + if (_avoidDuplicate && tx->sealed()) + { + continue; + } + if (currentTime > (tx->importTime() + m_txsExpirationTime)) + { + // add to m_invalidTxs to be deleted + m_invalidTxs.insert(txHash); + m_invalidNonces.insert(tx->nonce()); + continue; + } + /// check nonce again when obtain transactions + // since the invalid nonce has already been checked before the txs import into the + // txPool the txs with duplicated nonce here are already-committed, but have not been + // dropped + auto result = m_config->txValidator()->submittedToChain(tx); + if (result == TransactionStatus::NonceCheckFail) + { + // in case of the same tx notified more than once + auto transaction = std::const_pointer_cast(tx); + transaction->takeSubmitCallback(); + // add to m_invalidTxs to be deleted + m_invalidTxs.insert(txHash); + m_invalidNonces.insert(tx->nonce()); + continue; + } + // blockLimit expired + if (result == TransactionStatus::BlockLimitCheckFail) + { + m_invalidTxs.insert(txHash); + m_invalidNonces.insert(tx->nonce()); + continue; + } + if (_avoidTxs && _avoidTxs->count(txHash)) + { + continue; + } + auto txMetaData = m_config->blockFactory()->createTransactionMetaData(); + + txMetaData->setHash(tx->hash()); + txMetaData->setTo(std::string(tx->to())); + txMetaData->setAttribute(tx->attribute()); + if (tx->systemTx()) + { + _sysTxsList->appendTransactionMetaData(txMetaData); + } + else + { + _txsList->appendTransactionMetaData(txMetaData); + } + if (!tx->sealed()) + { + m_sealedTxsSize++; + } +#if FISCO_DEBUG + // TODO: remove this, now just for bug tracing + TXPOOL_LOG(INFO) << LOG_DESC("fetch ") << tx->hash().abridged() + << LOG_KV("sealed", tx->sealed()) << LOG_KV("batchId", tx->batchId()) + << LOG_KV("batchHash", tx->batchHash().abridged()) + << LOG_KV("txPointer", tx); +#endif + tx->setSealed(true); + tx->setBatchId(-1); + tx->setBatchHash(HashType()); + if ((_txsList->transactionsMetaDataSize() + _sysTxsList->transactionsMetaDataSize()) >= + _txsLimit) + { + break; + } + } + auto fetchTxsT = utcTime() - startT; + notifyUnsealedTxsSize(); + removeInvalidTxs(); + TXPOOL_LOG(INFO) << METRIC << LOG_DESC("batchFetchTxs success") + << LOG_KV("timecost", (utcTime() - recordT)) + << LOG_KV("txsSize", _txsList->transactionsMetaDataSize()) + << LOG_KV("sysTxsSize", _sysTxsList->transactionsMetaDataSize()) + << LOG_KV("pendingTxs", m_txsTable.size()) << LOG_KV("limit", _txsLimit) + << LOG_KV("fetchTxsT", fetchTxsT) << LOG_KV("lockT", lockT) + << LOG_KV("traverseCount", traverseCount); +} + +void MemoryStorage::removeInvalidTxs() +{ + auto self = weak_from_this(); + m_notifier->enqueue([self]() { + try + { + auto memoryStorage = self.lock(); + if (!memoryStorage) + { + return; + } + if (memoryStorage->m_invalidTxs.size() == 0) + { + return; + } + WriteGuard l(memoryStorage->x_txpoolMutex); + tbb::parallel_invoke( + [memoryStorage]() { + // remove invalid txs + for (auto const& txHash : memoryStorage->m_invalidTxs) + { + auto txResult = + memoryStorage->m_config->txResultFactory()->createTxSubmitResult(); + txResult->setTxHash(txHash); + txResult->setStatus((uint32_t)TransactionStatus::BlockLimitCheckFail); + // not notify receipt to the sdk when the txs has been removed by + // removeInvalidTxs in the cases the txs-expired + memoryStorage->removeSubmittedTxWithoutLock(txResult, false); + } + memoryStorage->notifyUnsealedTxsSize(); + }, + [memoryStorage]() { + // remove invalid nonce + memoryStorage->m_config->txPoolNonceChecker()->batchRemove( + memoryStorage->m_invalidNonces); + }); + TXPOOL_LOG(DEBUG) << LOG_DESC("removeInvalidTxs") + << LOG_KV("size", memoryStorage->m_invalidTxs.size()); + memoryStorage->m_invalidTxs.clear(); + memoryStorage->m_invalidNonces.clear(); + } + catch (std::exception const& e) + { + TXPOOL_LOG(WARNING) << LOG_DESC("removeInvalidTxs exception") + << LOG_KV("errorInfo", boost::diagnostic_information(e)); + } + }); +} + +void MemoryStorage::clear() +{ + WriteGuard l(x_txpoolMutex); + m_txsTable.clear(); + m_invalidTxs.clear(); + m_invalidNonces.clear(); + m_missedTxs.clear(); + notifyUnsealedTxsSize(); +} + +HashListPtr MemoryStorage::filterUnknownTxs(HashList const& _txsHashList, NodeIDPtr _peer) +{ + ReadGuard l(x_txpoolMutex); + for (auto txHash : _txsHashList) + { + auto it = m_txsTable.find(txHash); + if (it == m_txsTable.end()) + { + continue; + } + auto tx = it->second; + if (!tx) + { + continue; + } + tx->appendKnownNode(_peer); + } + auto unknownTxsList = std::make_shared(); + UpgradableGuard missedTxsLock(x_missedTxs); + for (auto const& txHash : _txsHashList) + { + if (m_txsTable.count(txHash)) + { + continue; + } + if (m_missedTxs.count(txHash)) + { + continue; + } + unknownTxsList->push_back(txHash); + m_missedTxs.insert(txHash); + } + if (m_missedTxs.size() >= m_config->poolLimit()) + { + UpgradeGuard ul(missedTxsLock); + m_missedTxs.clear(); + } + return unknownTxsList; +} + +void MemoryStorage::batchMarkTxs( + HashList const& _txsHashList, BlockNumber _batchId, HashType const& _batchHash, bool _sealFlag) +{ + if (_sealFlag) + { + ReadGuard l(x_txpoolMutex); + batchMarkTxsWithoutLock(_txsHashList, _batchId, _batchHash, _sealFlag); + return; + } + // Note: setting flag to false is pessimistic, use writeLock here in case of the same txs has + // been sealed twice + WriteGuard l(x_txpoolMutex); + batchMarkTxsWithoutLock(_txsHashList, _batchId, _batchHash, _sealFlag); + return; +} + +void MemoryStorage::batchMarkTxsWithoutLock( + HashList const& _txsHashList, BlockNumber _batchId, HashType const& _batchHash, bool _sealFlag) +{ + auto recordT = utcTime(); + auto startT = utcTime(); + ssize_t successCount = 0; + for (auto txHash : _txsHashList) + { + auto it = m_txsTable.find(txHash); + if (it == m_txsTable.end()) + { + TXPOOL_LOG(TRACE) << LOG_DESC("batchMarkTxs: missing transaction") + << LOG_KV("tx", txHash.abridged()) << LOG_KV("sealFlag", _sealFlag); + continue; + } + auto tx = it->second; + if (!tx) + { + continue; + } + // the tx has already been re-sealed, can not enforce unseal + if ((tx->batchId() != _batchId || tx->batchHash() != _batchHash) && tx->sealed() && + !_sealFlag) + { + continue; + } + if (_sealFlag && !tx->sealed()) + { + m_sealedTxsSize++; + } + if (!_sealFlag && tx->sealed()) + { + m_sealedTxsSize--; + } + tx->setSealed(_sealFlag); + successCount += 1; + // set the block information for the transaction + if (_sealFlag) + { + tx->setBatchId(_batchId); + tx->setBatchHash(_batchHash); + } +#if FISCO_DEBUG + // TODO: remove this, now just for bug tracing + TXPOOL_LOG(DEBUG) << LOG_DESC("mark ") << tx->hash().abridged() << ":" << _sealFlag + << LOG_KV("index", tx->batchId()) + << LOG_KV("hash", tx->batchHash().abridged()) << LOG_KV("txPointer", tx); +#endif + } + TXPOOL_LOG(DEBUG) << LOG_DESC("batchMarkTxs ") << LOG_KV("txsSize", _txsHashList.size()) + << LOG_KV("batchId", _batchId) << LOG_KV("hash", _batchHash.abridged()) + << LOG_KV("flag", _sealFlag) << LOG_KV("succ", successCount) + << LOG_KV("timecost", utcTime() - recordT) + << LOG_KV("markT", (utcTime() - startT)); + notifyUnsealedTxsSize(); +} + +void MemoryStorage::batchMarkAllTxs(bool _sealFlag) +{ + ReadGuard l(x_txpoolMutex); + for (auto item : m_txsTable) + { + auto tx = item.second; + if (!tx) + { + continue; + } + tx->setSealed(_sealFlag); + if (!_sealFlag) + { + tx->setBatchId(-1); + tx->setBatchHash(HashType()); + } + } + if (_sealFlag) + { + m_sealedTxsSize = m_txsTable.size(); + } + else + { + m_sealedTxsSize = 0; + } + notifyUnsealedTxsSize(); +} + +size_t MemoryStorage::unSealedTxsSize() +{ + ReadGuard l(x_txpoolMutex); + return unSealedTxsSizeWithoutLock(); +} + +size_t MemoryStorage::unSealedTxsSizeWithoutLock() +{ + if (m_txsTable.size() < m_sealedTxsSize) + { + m_sealedTxsSize = m_txsTable.size(); + return 0; + } + return (m_txsTable.size() - m_sealedTxsSize); +} + +void MemoryStorage::notifyUnsealedTxsSize(size_t _retryTime) +{ + // Note: must set the notifier + if (!m_unsealedTxsNotifier) + { + return; + } + + auto unsealedTxsSize = unSealedTxsSizeWithoutLock(); + auto self = weak_from_this(); + m_unsealedTxsNotifier(unsealedTxsSize, [_retryTime, self](Error::Ptr _error) { + if (_error == nullptr) + { + return; + } + TXPOOL_LOG(WARNING) << LOG_DESC("notifyUnsealedTxsSize failed") + << LOG_KV("errorCode", _error->errorCode()) + << LOG_KV("errorMsg", _error->errorMessage()); + auto memoryStorage = self.lock(); + if (!memoryStorage) + { + return; + } + if (_retryTime >= memoryStorage->c_maxRetryTime) + { + return; + } + memoryStorage->notifyUnsealedTxsSize((_retryTime + 1)); + }); +} + +std::shared_ptr MemoryStorage::batchVerifyProposal(Block::Ptr _block) +{ + auto missedTxs = std::make_shared(); + auto txsSize = _block->transactionsHashSize(); + if (txsSize == 0) + { + return missedTxs; + } + auto batchId = (_block && _block->blockHeader()) ? _block->blockHeader()->number() : -1; + auto batchHash = (_block && _block->blockHeader()) ? _block->blockHeader()->hash() : + bcos::crypto::HashType(); + auto startT = utcTime(); + ReadGuard l(x_txpoolMutex); + auto lockT = utcTime() - startT; + startT = utcTime(); + for (size_t i = 0; i < txsSize; i++) + { + auto txHash = _block->transactionHash(i); + if (!(m_txsTable.count(txHash))) + { + missedTxs->emplace_back(txHash); + } + } + TXPOOL_LOG(INFO) << LOG_DESC("batchVerifyProposal") << LOG_KV("consNum", batchId) + << LOG_KV("hash", batchHash.abridged()) << LOG_KV("txsSize", txsSize) + << LOG_KV("lockT", lockT) << LOG_KV("verifyT", (utcTime() - startT)); + return missedTxs; +} + +bool MemoryStorage::batchVerifyProposal(std::shared_ptr _txsHashList) +{ + ReadGuard l(x_txpoolMutex); + for (auto const& txHash : *_txsHashList) + { + if (!(m_txsTable.count(txHash))) + { + return false; + } + } + return true; +} + +HashListPtr MemoryStorage::getAllTxsHash() +{ + auto txsHash = std::make_shared(); + ReadGuard l(x_txpoolMutex); + for (auto const& it : m_txsTable) + { + auto tx = it.second; + if (!tx) + { + continue; + } + txsHash->emplace_back(it.first); + } + return txsHash; +} + +void MemoryStorage::cleanUpExpiredTransactions() +{ + m_cleanUpTimer->restart(); + + // Note: In order to minimize the impact of cleanUp on performance, + // the normal consensus node does not clear expired txs in m_clearUpTimer, but clears + // expired txs in the process of sealing txs + if (m_txsCleanUpSwitch && !m_txsCleanUpSwitch()) + { + return; + } + ReadGuard l(x_txpoolMutex); + if (m_txsTable.size() == 0) + { + return; + } + size_t traversedTxsNum = 0; + size_t erasedTxs = 0; + int64_t currentTime = utcTime(); + for (auto it = m_txsTable.begin(); + traversedTxsNum <= c_maxTraverseTxsNum && it != m_txsTable.end(); it++) + { + auto tx = it->second; + if (m_invalidTxs.count(tx->hash())) + { + continue; + } + if (tx->sealed() && tx->batchId() >= m_blockNumber) + { + continue; + } + // the txs expired or not + if (currentTime > (tx->importTime() + m_txsExpirationTime)) + { + m_invalidTxs.insert(tx->hash()); + m_invalidNonces.insert(tx->nonce()); + erasedTxs++; + } + traversedTxsNum++; + } + TXPOOL_LOG(INFO) << LOG_DESC("cleanUpExpiredTransactions") + << LOG_KV("pendingTxs", m_txsTable.size()) << LOG_KV("erasedTxs", erasedTxs); + removeInvalidTxs(); +} + + +void MemoryStorage::batchImportTxs(TransactionsPtr _txs) +{ + auto recordT = utcTime(); + ReadGuard l(x_txpoolMutex); + size_t successCount = 0; + for (auto const& tx : *_txs) + { + if (!tx || tx->invalid()) + { + continue; + } + // not checkLimit when receive txs from p2p + 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("error", ret); + continue; + } + successCount++; + } + notifyUnsealedTxsSize(); + TXPOOL_LOG(DEBUG) << LOG_DESC("batchImportTxs success") << LOG_KV("importTxs", successCount) + << LOG_KV("totalTxs", _txs->size()) << LOG_KV("pendingTxs", m_txsTable.size()) + << LOG_KV("timecost", (utcTime() - recordT)); +} + +bool MemoryStorage::batchVerifyAndSubmitTransaction( + bcos::protocol::BlockHeader::Ptr _header, TransactionsPtr _txs) +{ + // use writeGuard here in case of the transaction status will be modified by other + // interfaces + auto recordT = utcTime(); + // use writeGuard here in case of the transaction status will be modified by other + // interfaces + WriteGuard l(x_txpoolMutex); + auto lockT = utcTime() - recordT; + recordT = utcTime(); + for (auto const& tx : *_txs) + { + if (!tx || tx->invalid()) + { + continue; + } + auto result = enforceSubmitTransaction(tx); + if (result != TransactionStatus::None) + { + TXPOOL_LOG(WARNING) << LOG_BADGE("batchSubmitTransaction: verify proposal failed") + << LOG_KV("tx", tx->hash().abridged()) << LOG_KV("result", result) + << LOG_KV("txBatchID", tx->batchId()) + << LOG_KV("txBatchHash", tx->batchHash().abridged()) + << LOG_KV("consIndex", _header->number()) + << LOG_KV("propHash", _header->hash().abridged()); + return false; + } + } + notifyUnsealedTxsSize(); + TXPOOL_LOG(DEBUG) << LOG_DESC("batchVerifyAndSubmitTransaction success") + << LOG_KV("totalTxs", _txs->size()) << LOG_KV("lockT", lockT) + << LOG_KV("submitT", (utcTime() - recordT)); + return true; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.h" new file mode 100644 index 00000000..58f5ca85 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/storage/MemoryStorage.h" @@ -0,0 +1,172 @@ +/** + * Copyright (C) 2021 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. + * + * @brief an implementation of using memory to store transactions + * @file MemoryStorage.h + * @author: yujiechen + * @date 2021-05-07 + */ +#pragma once +#include "bcos-txpool/TxPoolConfig.h" +#include +#include +#include +#include +#define TBB_PREVIEW_CONCURRENT_ORDERED_CONTAINERS 1 +#include +namespace bcos +{ +namespace txpool +{ +class MemoryStorage : public TxPoolStorageInterface, + public std::enable_shared_from_this +{ +public: + // the default txsExpirationTime is 10 minutes + explicit MemoryStorage(TxPoolConfig::Ptr _config, size_t _notifyWorkerNum = 2, + int64_t _txsExpirationTime = 10 * 60 * 1000, bool _preStoreTxs = false); + ~MemoryStorage() override = default; + + task::Task submitTransaction( + protocol::Transaction::Ptr transaction) override; + + bcos::protocol::TransactionStatus insert(bcos::protocol::Transaction::ConstPtr _tx) override; + void batchInsert(bcos::protocol::Transactions const& _txs) override; + + bcos::protocol::Transaction::ConstPtr remove(bcos::crypto::HashType const& _txHash) override; + void batchRemove(bcos::protocol::BlockNumber _batchId, + bcos::protocol::TransactionSubmitResults const& _txsResult) override; + bcos::protocol::Transaction::ConstPtr removeSubmittedTx( + bcos::protocol::TransactionSubmitResult::Ptr _txSubmitResult) override; + + bcos::protocol::TransactionsPtr fetchTxs( + bcos::crypto::HashList& _missedTxs, bcos::crypto::HashList const& _txsList) override; + + bcos::protocol::ConstTransactionsPtr fetchNewTxs(size_t _txsLimit) override; + void batchFetchTxs(bcos::protocol::Block::Ptr _txsList, bcos::protocol::Block::Ptr _sysTxsList, + size_t _txsLimit, TxsHashSetPtr _avoidTxs, bool _avoidDuplicate = true) override; + + bool exist(bcos::crypto::HashType const& _txHash) override + { + ReadGuard l(x_txpoolMutex); + return m_txsTable.count(_txHash); + } + size_t size() const override + { + ReadGuard l(x_txpoolMutex); + return m_txsTable.size(); + } + void clear() override; + + bcos::crypto::HashListPtr filterUnknownTxs( + bcos::crypto::HashList const& _txsHashList, bcos::crypto::NodeIDPtr _peer) override; + + bcos::crypto::HashListPtr getAllTxsHash() override; + void batchMarkAllTxs(bool _sealFlag) override; + + size_t unSealedTxsSize() override; + + void stop() override; + void start() override; + void printPendingTxs() override; + + std::shared_ptr batchVerifyProposal( + bcos::protocol::Block::Ptr _block) override; + + bool batchVerifyProposal(std::shared_ptr _txsHashList) override; + + bool batchVerifyAndSubmitTransaction( + bcos::protocol::BlockHeader::Ptr _header, bcos::protocol::TransactionsPtr _txs) override; + void batchImportTxs(bcos::protocol::TransactionsPtr _txs) override; + + void batchMarkTxs(bcos::crypto::HashList const& _txsHashList, + bcos::protocol::BlockNumber _batchId, bcos::crypto::HashType const& _batchHash, + bool _sealFlag) override; + + bool preStoreTxs() const override { return m_preStoreTxs; } + +protected: + bcos::protocol::TransactionStatus insertWithoutLock(bcos::protocol::Transaction::ConstPtr _tx); + bcos::protocol::TransactionStatus enforceSubmitTransaction( + bcos::protocol::Transaction::Ptr _tx); + bcos::protocol::TransactionStatus verifyAndSubmitTransaction( + bcos::protocol::Transaction::Ptr _tx, bcos::protocol::TxSubmitCallback _txSubmitCallback, + bool _checkPoolLimit, bool _lock); + size_t unSealedTxsSizeWithoutLock(); + bcos::protocol::TransactionStatus txpoolStorageCheck(bcos::protocol::Transaction::ConstPtr _tx); + + virtual bcos::protocol::Transaction::ConstPtr removeWithoutLock( + bcos::crypto::HashType const& _txHash); + virtual bcos::protocol::Transaction::ConstPtr removeSubmittedTxWithoutLock( + bcos::protocol::TransactionSubmitResult::Ptr _txSubmitResult, bool _notify = true); + + virtual void notifyInvalidReceipt(bcos::crypto::HashType const& _txHash, + bcos::protocol::TransactionStatus _status, + bcos::protocol::TxSubmitCallback _txSubmitCallback); + + virtual void notifyTxResult(bcos::protocol::Transaction const& _tx, + bcos::protocol::TransactionSubmitResult::Ptr _txSubmitResult); + + virtual void removeInvalidTxs(); + + virtual void preCommitTransaction(bcos::protocol::Transaction::ConstPtr _tx); + + virtual void notifyUnsealedTxsSize(size_t _retryTime = 0); + virtual void cleanUpExpiredTransactions(); + + virtual void batchMarkTxsWithoutLock(bcos::crypto::HashList const& _txsHashList, + bcos::protocol::BlockNumber _batchId, bcos::crypto::HashType const& _batchHash, + bool _sealFlag); + +protected: + TxPoolConfig::Ptr m_config; + ThreadPool::Ptr m_notifier; + ThreadPool::Ptr m_worker; + + tbb::concurrent_unordered_map> + m_txsTable; + + mutable SharedMutex x_txpoolMutex; + + tbb::concurrent_set m_invalidTxs; + tbb::concurrent_set m_invalidNonces; + + tbb::concurrent_set m_missedTxs; + mutable SharedMutex x_missedTxs; + std::atomic m_sealedTxsSize = {0}; + + size_t c_maxRetryTime = 3; + + std::atomic m_blockNumber = {0}; + std::atomic_bool m_printed = {false}; + int64_t m_blockNumberUpdatedTime; + + // the txs expiration time, default is 10 minutes + int64_t m_txsExpirationTime = 10 * 60 * 1000; + // timer to clear up the expired txs in-period + std::shared_ptr m_cleanUpTimer; + // Maximum number of transactions traversed by m_cleanUpTimer, + // The limit set here is to minimize the impact of the cleanup operation on txpool performance + uint64_t c_maxTraverseTxsNum = 10000; + + // for tps stat + std::atomic m_tpsStatstartTime = {0}; + std::atomic m_onChainTxsCount = {0}; + + bool m_preStoreTxs = {true}; +}; +} // namespace txpool +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/LedgerNonceChecker.cpp" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/LedgerNonceChecker.cpp" new file mode 100644 index 00000000..55c3416f --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/LedgerNonceChecker.cpp" @@ -0,0 +1,99 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for ledger nonce-checker + * @file LedgerNonceChecker.cpp + * @author: yujiechen + * @date 2021-05-10 + */ +#include "LedgerNonceChecker.h" +using namespace bcos; +using namespace bcos::protocol; +using namespace bcos::txpool; + +void LedgerNonceChecker::initNonceCache( + std::map _initialNonces) +{ + for (auto const& it : _initialNonces) + { + m_blockNonceCache[it.first] = it.second; + TxPoolNonceChecker::batchInsert(it.first, it.second); + } +} + +TransactionStatus LedgerNonceChecker::checkNonce(Transaction::ConstPtr _tx, bool _shouldUpdate) +{ + // check nonce + auto status = TxPoolNonceChecker::checkNonce(_tx, _shouldUpdate); + if (status != TransactionStatus::None) + { + return status; + } + // check blockLimit + return checkBlockLimit(_tx); +} + +TransactionStatus LedgerNonceChecker::checkBlockLimit(bcos::protocol::Transaction::ConstPtr _tx) +{ + auto blockNumber = m_blockNumber.load(); + if (blockNumber >= _tx->blockLimit() || (blockNumber + m_blockLimit) < _tx->blockLimit()) + { + NONCECHECKER_LOG(WARNING) << LOG_DESC("InvalidBlockLimit") + << LOG_KV("blkLimit", _tx->blockLimit()) + << LOG_KV("blockLimit", m_blockLimit) + << LOG_KV("curBlk", m_blockNumber) + << LOG_KV("tx", _tx->hash().abridged()); + return TransactionStatus::BlockLimitCheckFail; + } + return TransactionStatus::None; +} + + +void LedgerNonceChecker::batchInsert(BlockNumber _batchId, NonceListPtr _nonceList) +{ + if (m_blockNumber < _batchId) + { + m_blockNumber.store(_batchId); + } + ssize_t batchToBeRemoved = (_batchId > m_blockLimit) ? (_batchId - m_blockLimit) : -1; + // insert the latest nonces + TxPoolNonceChecker::batchInsert(_batchId, _nonceList); + + WriteGuard l(x_blockNonceCache); + if (!m_blockNonceCache.count(_batchId)) + { + m_blockNonceCache[_batchId] = _nonceList; + NONCECHECKER_LOG(DEBUG) << LOG_DESC("batchInsert nonceList") << LOG_KV("batchId", _batchId) + << LOG_KV("nonceSize", _nonceList->size()); + } + // the genesis has no nonceList + if (batchToBeRemoved == -1) + { + return; + } + // remove the expired nonces + if (!m_blockNonceCache.count(batchToBeRemoved)) + { + NONCECHECKER_LOG(WARNING) << LOG_DESC("batchInsert: miss cache when remove expired cache") + << LOG_KV("batchToBeRemoved", batchToBeRemoved); + return; + } + auto nonceList = m_blockNonceCache[batchToBeRemoved]; + m_blockNonceCache.erase(batchToBeRemoved); + batchRemove(*nonceList); + NONCECHECKER_LOG(DEBUG) << LOG_DESC("batchInsert: remove expired nonce") + << LOG_KV("batchToBeRemoved", batchToBeRemoved) + << LOG_KV("nonceSize", nonceList->size()); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/LedgerNonceChecker.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/LedgerNonceChecker.h" new file mode 100644 index 00000000..ba4a2217 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/LedgerNonceChecker.h" @@ -0,0 +1,65 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for ledger nonce-checker + * @file LedgerNonceChecker.h + * @author: yujiechen + * @date 2021-05-10 + */ +#pragma once +#include "bcos-txpool/txpool/validator/TxPoolNonceChecker.h" +#include + +namespace bcos +{ +namespace txpool +{ +class LedgerNonceChecker : public TxPoolNonceChecker +{ +public: + LedgerNonceChecker( + std::shared_ptr > _initialNonces, + bcos::protocol::BlockNumber _blockNumber, int64_t _blockLimit) + : TxPoolNonceChecker(), m_blockNumber(_blockNumber), m_blockLimit(_blockLimit) + { + if (_initialNonces) + { + initNonceCache(*_initialNonces); + } + } + bcos::protocol::TransactionStatus checkNonce( + bcos::protocol::Transaction::ConstPtr _tx, bool _shouldUpdate = false) override; + + void batchInsert( + bcos::protocol::BlockNumber _batchId, bcos::protocol::NonceListPtr _nonceList) override; + +protected: + virtual bcos::protocol::TransactionStatus checkBlockLimit( + bcos::protocol::Transaction::ConstPtr _tx); + virtual void initNonceCache(std::map _initialNonces); + +private: + std::atomic m_blockNumber = {0}; + int64_t m_blockLimit; + + /// cache the block nonce to in case of accessing the DB to get nonces of given block frequently + /// key: block number + /// value: all the nonces of a given block + /// we cache at most m_blockLimit entries(occuppy about 32KB) + std::map m_blockNonceCache; + mutable SharedMutex x_blockNonceCache; +}; +} // namespace txpool +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/TxPoolNonceChecker.cpp" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/TxPoolNonceChecker.cpp" new file mode 100644 index 00000000..5f92deb5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/TxPoolNonceChecker.cpp" @@ -0,0 +1,92 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for txpool nonce-checker + * @file TxPoolNonceChecker.cpp + * @author: yujiechen + * @date 2021-05-10 + */ +#include "TxPoolNonceChecker.h" + +using namespace bcos; +using namespace bcos::protocol; +using namespace bcos::txpool; + +bool TxPoolNonceChecker::exists(NonceType const& _nonce) +{ + ReadGuard l(x_nonceCache); + if (m_nonceCache.count(_nonce)) + { + return true; + } + return false; +} + +TransactionStatus TxPoolNonceChecker::checkNonce(Transaction::ConstPtr _tx, bool _shouldUpdate) +{ + ReadGuard l(x_nonceCache); + auto nonce = _tx->nonce(); + if (m_nonceCache.count(nonce)) + { + return TransactionStatus::NonceCheckFail; + } + if (_shouldUpdate) + { + m_nonceCache.insert(nonce); + } + return TransactionStatus::None; +} + +void TxPoolNonceChecker::insert(NonceType const& _nonce) +{ + ReadGuard l(x_nonceCache); + m_nonceCache.insert(_nonce); +} + +void TxPoolNonceChecker::batchInsert(BlockNumber, NonceListPtr _nonceList) +{ + ReadGuard l(x_nonceCache); + for (auto const& nonce : *_nonceList) + { + m_nonceCache.insert(nonce); + } +} + +void TxPoolNonceChecker::remove(NonceType const& _nonce) +{ + if (m_nonceCache.count(_nonce)) + { + m_nonceCache.unsafe_erase(_nonce); + } +} + +void TxPoolNonceChecker::batchRemove(NonceList const& _nonceList) +{ + WriteGuard l(x_nonceCache); + for (auto const& nonce : _nonceList) + { + remove(nonce); + } +} + +void TxPoolNonceChecker::batchRemove( + tbb::concurrent_set const& _nonceList) +{ + WriteGuard l(x_nonceCache); + for (auto const& nonce : _nonceList) + { + remove(nonce); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/TxPoolNonceChecker.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/TxPoolNonceChecker.h" new file mode 100644 index 00000000..ccd31b23 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/TxPoolNonceChecker.h" @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for txpool nonce-checker + * @file TxPoolNonceChecker.h + * @author: yujiechen + * @date 2021-05-10 + */ +#pragma once +#include "bcos-txpool/txpool/interfaces/NonceCheckerInterface.h" +#include +namespace bcos +{ +namespace txpool +{ +class TxPoolNonceChecker : public NonceCheckerInterface +{ +public: + TxPoolNonceChecker() = default; + bcos::protocol::TransactionStatus checkNonce( + bcos::protocol::Transaction::ConstPtr _tx, bool _shouldUpdate = false) override; + void batchInsert( + bcos::protocol::BlockNumber _batchId, bcos::protocol::NonceListPtr _nonceList) override; + void batchRemove(bcos::protocol::NonceList const& _nonceList) override; + void batchRemove(tbb::concurrent_set const& _nonceList) override; + bool exists(bcos::protocol::NonceType const& _nonce) override; + + void insert(bcos::protocol::NonceType const& _nonce) override; + +protected: + void remove(bcos::protocol::NonceType const& _nonce) override; + + tbb::concurrent_unordered_set m_nonceCache; + mutable SharedMutex x_nonceCache; +}; +} // namespace txpool +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/TxValidator.cpp" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/TxValidator.cpp" new file mode 100644 index 00000000..c0890ae7 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/TxValidator.cpp" @@ -0,0 +1,84 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation of TxValidator + * @file TxValidator.cpp + * @author: yujiechen + * @date 2021-05-11 + */ +#include "TxValidator.h" + +using namespace bcos; +using namespace bcos::protocol; +using namespace bcos::txpool; + +TransactionStatus TxValidator::verify(bcos::protocol::Transaction::ConstPtr _tx) +{ + if (_tx->invalid()) + { + return TransactionStatus::InvalidSignature; + } + // check groupId and chainId + if (_tx->groupId() != m_groupId) + { + return TransactionStatus::InvalidGroupId; + } + if (_tx->chainId() != m_chainId) + { + return TransactionStatus::InvalidChainId; + } + // compare with nonces cached in memory + auto status = m_txPoolNonceChecker->checkNonce(_tx, false); + if (status != TransactionStatus::None) + { + return status; + } + status = submittedToChain(_tx); + if (status != TransactionStatus::None) + { + return status; + } + // check signature + try + { + _tx->verify(); + } + catch (std::exception const& e) + { + return TransactionStatus::InvalidSignature; + } + + if (isSystemTransaction(_tx)) + { + _tx->setSystemTx(true); + } + m_txPoolNonceChecker->insert(_tx->nonce()); + return TransactionStatus::None; +} + +TransactionStatus TxValidator::submittedToChain(bcos::protocol::Transaction::ConstPtr _tx) +{ + // compare with nonces stored on-chain + auto status = m_ledgerNonceChecker->checkNonce(_tx); + if (status != TransactionStatus::None) + { + return status; + } + if (isSystemTransaction(_tx)) + { + _tx->setSystemTx(true); + } + return TransactionStatus::None; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/TxValidator.h" "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/TxValidator.h" new file mode 100644 index 00000000..73254e40 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/bcos-txpool/txpool/validator/TxValidator.h" @@ -0,0 +1,63 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation of TxValidator + * @file TxValidator.h + * @author: yujiechen + * @date 2021-05-11 + */ +#pragma once +#include "bcos-txpool/txpool/interfaces/NonceCheckerInterface.h" +#include "bcos-txpool/txpool/interfaces/TxValidatorInterface.h" +#include +#include +namespace bcos +{ +namespace txpool +{ +class TxValidator : public TxValidatorInterface +{ +public: + using Ptr = std::shared_ptr; + TxValidator(NonceCheckerInterface::Ptr _txPoolNonceChecker, + bcos::crypto::CryptoSuite::Ptr _cryptoSuite, std::string const& _groupId, + std::string const& _chainId) + : m_txPoolNonceChecker(_txPoolNonceChecker), + m_cryptoSuite(_cryptoSuite), + m_groupId(_groupId), + m_chainId(_chainId) + {} + ~TxValidator() override {} + + bcos::protocol::TransactionStatus verify(bcos::protocol::Transaction::ConstPtr _tx) override; + bcos::protocol::TransactionStatus submittedToChain( + bcos::protocol::Transaction::ConstPtr _tx) override; + +protected: + virtual bool isSystemTransaction(bcos::protocol::Transaction::ConstPtr _tx) + { + auto txAddress = _tx->to(); + return bcos::precompiled::c_systemTxsAddress.count( + std::string(txAddress.begin(), txAddress.end())); + } + +private: + NonceCheckerInterface::Ptr m_txPoolNonceChecker; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + std::string m_groupId; + std::string m_chainId; +}; +} // namespace txpool +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-txpool/test/CMakeLists.txt" new file mode 100644 index 00000000..9eea4948 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/test/CMakeLists.txt" @@ -0,0 +1,33 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of bcos-txpool +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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(GLOB_RECURSE SOURCES "unittests/*.cpp" "unittests/*.h" "unittests/*.sol") +file(GLOB_RECURSE DEMO_SOURCES "demo/*.cpp" "demo/*.h" "demo/*.sol") + +# cmake settings +set(TEST_BINARY_NAME test-bcos-txpool) +set(DEMO_BINARY_NAME bcos-txpool-demo) + +add_executable(${TEST_BINARY_NAME} ${SOURCES}) +add_executable(${DEMO_BINARY_NAME} ${DEMO_SOURCES}) +target_include_directories(${TEST_BINARY_NAME} PRIVATE . ${CMAKE_SOURCE_DIR}) + +find_package(Boost REQUIRED unit_test_framework) + +target_link_libraries(${TEST_BINARY_NAME} ${TXPOOL_TARGET} bcos-crypto ${TARS_PROTOCOL_TARGET} Boost::unit_test_framework) +target_link_libraries(${DEMO_BINARY_NAME} ${TXPOOL_TARGET} bcos-crypto ${TARS_PROTOCOL_TARGET} Boost::unit_test_framework) +add_test(NAME test-txpool WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/test/demo/txpool_demo.cpp" "b/BFPL\345\243\271/bcos-txpool/test/demo/txpool_demo.cpp" new file mode 100644 index 00000000..fb90cd0e --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/test/demo/txpool_demo.cpp" @@ -0,0 +1,99 @@ +/** + * Copyright (C) 2021 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. + * + * @brief demo for bcos-txpool + * @file txpool_demo.cpp + * @date 2022.01.24 + * @author yujiechen + */ + +#include "bcos-crypto/hash/Keccak256.h" +#include "bcos-crypto/hash/SM3.h" +#include "bcos-protocol/TransactionSubmitResultImpl.h" +#include "bcos-txpool/test/unittests/txpool/TxPoolFixture.h" +#include +#include +#include +#include +#include +#include +#include +#include +using namespace bcos; +using namespace bcos::front; +using namespace bcos::protocol; +using namespace bcos::test; +using namespace bcos::txpool; +using namespace bcos::crypto; + +void testSubmitAndRemoveTransaction(bcos::crypto::CryptoSuite::Ptr _cryptoSuite, size_t _count) +{ + auto signatureImpl = _cryptoSuite->signatureImpl(); + auto hashImpl = _cryptoSuite->hashImpl(); + auto keyPair = signatureImpl->generateKeyPair(); + std::string groupId = "group_test_for_txpool"; + std::string chainId = "chain_test_for_txpool"; + int64_t blockLimit = 1000; + auto fakeGateWay = std::make_shared(); + auto faker = std::make_shared( + keyPair->publicKey(), _cryptoSuite, groupId, chainId, blockLimit, fakeGateWay); + faker->init(); + faker->appendSealer(faker->nodeID()); + auto ledger = faker->ledger(); + auto txpool = faker->txpool(); + + auto txsResult = std::make_shared(); + txsResult->resize(_count); + txpool->txpoolConfig()->setPoolLimit(_count + 1000); + tbb::parallel_for(tbb::blocked_range(0, _count), [&](const tbb::blocked_range& _r) { + for (auto i = _r.begin(); i < _r.end(); i++) + { + auto tx = fakeTransaction(_cryptoSuite, 1000 + i, + ledger->blockNumber() + blockLimit - 4, faker->chainId(), faker->groupId()); + ~txpool->submitTransaction(tx); + auto result = std::make_shared(); + result->setTxHash(tx->hash()); + (*txsResult)[i] = result; + } + }); + while (txpool->txpoolStorage()->size() < _count) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + std::cout << "### remove submitted txs, size:" << txpool->txpoolStorage()->size() << std::endl; + // remove the txs + txpool->asyncNotifyBlockResult(ledger->blockNumber() + 1, txsResult, [](Error::Ptr) {}); + faker.reset(); +} + +void Usage(std::string const& _appName) +{ + std::cout << _appName << " count" << std::endl; +} + +int main(int argc, char* argv[]) +{ + if (argc < 2) + { + Usage(argv[0]); + return -1; + } + size_t count = atoi(argv[1]); + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + testSubmitAndRemoveTransaction(cryptoSuite, count); + getchar(); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/test/unittests/main/main.cpp" "b/BFPL\345\243\271/bcos-txpool/test/unittests/main/main.cpp" new file mode 100644 index 00000000..5029377e --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/test/unittests/main/main.cpp" @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 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 main.cpp + * @author: yujiechen, jimmyshi + * @date 2021-02-24 + */ +#define BOOST_TEST_MODULE FISCO_BCOS_Tests +#define BOOST_TEST_MAIN + +#include +#include diff --git "a/BFPL\345\243\271/bcos-txpool/test/unittests/sync/FakeTxsSyncMsg.h" "b/BFPL\345\243\271/bcos-txpool/test/unittests/sync/FakeTxsSyncMsg.h" new file mode 100644 index 00000000..54122af4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/test/unittests/sync/FakeTxsSyncMsg.h" @@ -0,0 +1,65 @@ +/** + * Copyright (C) 2021 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. + * + * @brief fake the txsSyncMsg + * @file FakeTxsSyncMsg.h + */ +#pragma once +#include "bcos-txpool/sync/protocol/PB/TxsSyncMsg.h" +#include "bcos-txpool/sync/protocol/PB/TxsSyncMsgFactoryImpl.h" +#include + +using namespace bcos; +using namespace bcos::sync; +using namespace bcos::crypto; + +namespace bcos +{ +namespace test +{ +class FakeTxsSyncMsg +{ +public: + FakeTxsSyncMsg() { m_msgFactory = std::make_shared(); } + TxsSyncMsgInterface::Ptr fakeTxsMsg( + int32_t _type, int32_t _version, HashList const& _txsHash, bytes const& _txsData) + { + auto msg = m_msgFactory->createTxsSyncMsg(); + msg->setType(_type); + msg->setVersion(_version); + msg->setTxsHash(_txsHash); + msg->setTxsData(_txsData); + + // check encode/decode + auto encodedData = msg->encode(); + auto decodedMsg = m_msgFactory->createTxsSyncMsg(ref(*encodedData)); + BOOST_CHECK(msg->type() == decodedMsg->type()); + BOOST_CHECK(msg->version() == decodedMsg->version()); + BOOST_CHECK(msg->txsHash() == decodedMsg->txsHash()); + BOOST_CHECK(msg->txsData().toBytes() == decodedMsg->txsData().toBytes()); + + // compare with the origin data + BOOST_CHECK(_type == decodedMsg->type()); + BOOST_CHECK(_version == decodedMsg->version()); + BOOST_CHECK(_txsHash == decodedMsg->txsHash()); + BOOST_CHECK(_txsData == decodedMsg->txsData().toBytes()); + return msg; + } + +private: + TxsSyncMsgFactory::Ptr m_msgFactory; +}; +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/test/unittests/sync/TxsSyncMsgTest.cpp" "b/BFPL\345\243\271/bcos-txpool/test/unittests/sync/TxsSyncMsgTest.cpp" new file mode 100644 index 00000000..98d850fc --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/test/unittests/sync/TxsSyncMsgTest.cpp" @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2021 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. + * + * @brief unit test for TxsSyncMsg + * @file TxsSyncMsgTest.h + */ +#include "FakeTxsSyncMsg.h" +#include +#include +#include +#include +using namespace bcos; +using namespace bcos::sync; +using namespace bcos::crypto; +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(TxsSyncMsgTest, TestPromptFixture) +BOOST_AUTO_TEST_CASE(testTxsSyncMsg) +{ + auto faker = std::make_shared(); + int32_t type = 100; + int32_t version = 1000; + HashList hashList; + auto hashImpl = std::make_shared(); + for (int i = 0; i < 10; i++) + { + hashList.emplace_back(hashImpl->hash(std::to_string(i))); + } + std::string data = "adflwerjw39ewelrew"; + bytes txsData = bytes(data.begin(), data.end()); + faker->fakeTxsMsg(type, version, hashList, txsData); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/test/unittests/txpool/TxPoolFixture.h" "b/BFPL\345\243\271/bcos-txpool/test/unittests/txpool/TxPoolFixture.h" new file mode 100644 index 00000000..cdd7c6cb --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/test/unittests/txpool/TxPoolFixture.h" @@ -0,0 +1,269 @@ +/** + * Copyright (C) 2021 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. + * + * @brief fixture for the txpool + * @file TxPoolFixture.h + * @author: yujiechen + * @date 2021-05-25 + */ +#pragma once +#include +#include + +#include "bcos-txpool/TxPoolConfig.h" +#include "bcos-txpool/TxPoolFactory.h" +#include "bcos-txpool/sync/TransactionSync.h" +#include "bcos-txpool/txpool/storage/MemoryStorage.h" +#include "bcos-txpool/txpool/validator/TxValidator.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::protocol; +using namespace bcos::crypto; +using namespace bcos::ledger; +using namespace bcos::consensus; +using namespace bcos::front; +using namespace bcos::sync; + +namespace bcos +{ +namespace test +{ +class FakeTransactionSync1 : public TransactionSync +{ +public: + explicit FakeTransactionSync1(TransactionSyncConfig::Ptr _config) : TransactionSync(_config) {} + ~FakeTransactionSync1() override {} + void start() override {} +}; + +class FakeTransactionSync : public FakeTransactionSync1 +{ +public: + explicit FakeTransactionSync(TransactionSyncConfig::Ptr _config) : FakeTransactionSync1(_config) + {} + ~FakeTransactionSync() override {} + + // only broadcast txsStatus + void maintainTransactions() override + { + auto txs = config()->txpoolStorage()->fetchNewTxs(10000); + if (txs->size() == 0) + { + return; + } + auto connectedNodeList = m_config->connectedNodeList(); + auto consensusNodeList = m_config->consensusNodeList(); + + forwardTxsFromP2P(connectedNodeList, consensusNodeList, txs); + } +}; + +class FakeMemoryStorage : public MemoryStorage +{ +public: + FakeMemoryStorage(TxPoolConfig::Ptr _config, size_t _notifyWorkerNum = 2) + : MemoryStorage(_config, _notifyWorkerNum) + { + m_preStoreTxs = true; + } +}; + +class TxPoolFixture +{ +public: + using Ptr = std::shared_ptr; + TxPoolFixture(NodeIDPtr _nodeId, CryptoSuite::Ptr _cryptoSuite, std::string const& _groupId, + std::string const& _chainId, int64_t _blockLimit, FakeGateWay::Ptr _fakeGateWay) + : m_nodeId(_nodeId), + m_cryptoSuite(_cryptoSuite), + m_groupId(_groupId), + m_chainId(_chainId), + m_blockLimit(_blockLimit), + m_fakeGateWay(_fakeGateWay) + { + auto blockHeaderFactory = + std::make_shared(_cryptoSuite); + auto txFactory = std::make_shared(_cryptoSuite); + auto receiptFactory = + std::make_shared(_cryptoSuite); + m_blockFactory = std::make_shared( + _cryptoSuite, blockHeaderFactory, txFactory, receiptFactory); + m_txResultFactory = std::make_shared(); + m_ledger = std::make_shared(m_blockFactory, 20, 10, 10); + + m_frontService = std::make_shared(_nodeId); + auto txPoolFactory = + std::make_shared(_nodeId, _cryptoSuite, m_txResultFactory, + m_blockFactory, m_frontService, m_ledger, m_groupId, m_chainId, m_blockLimit); + m_txpool = txPoolFactory->createTxPool(); + auto fakeMemoryStorage = std::make_shared(m_txpool->txpoolConfig()); + m_txpool->setTxPoolStorage(fakeMemoryStorage); + + m_sync = std::dynamic_pointer_cast(m_txpool->transactionSync()); + auto syncConfig = m_sync->config(); + m_sync = std::make_shared(syncConfig); + m_txpool->setTransactionSync(m_sync); + m_txpool->start(); + + m_fakeGateWay->addTxPool(_nodeId, m_txpool); + m_frontService->setGateWay(m_fakeGateWay); + } + virtual ~TxPoolFixture() + { + std::cout << "#### TxPoolFixture de-constructor" << std::endl; + if (m_txpool) + { + m_txpool->stop(); + } + if (m_sync) + { + m_sync->stop(); + } + } + + BlockFactory::Ptr blockFactory() { return m_blockFactory; } + TxPool::Ptr txpool() { return m_txpool; } + FakeLedger::Ptr ledger() { return m_ledger; } + NodeIDPtr nodeID() { return m_nodeId; } + std::string const& chainId() { return m_chainId; } + std::string const& groupId() { return m_groupId; } + FakeFrontService::Ptr frontService() { return m_frontService; } + TransactionSync::Ptr sync() { return m_sync; } + void appendSealer(NodeIDPtr _nodeId) + { + auto consensusNode = std::make_shared(_nodeId); + m_ledger->ledgerConfig()->mutableConsensusNodeList().emplace_back(consensusNode); + m_txpool->notifyConsensusNodeList(m_ledger->ledgerConfig()->consensusNodeList(), nullptr); + updateConnectedNodeList(); + } + void init() + { + // init the txpool + m_txpool->init(); + } + + void resetToFakeTransactionSync() + { + auto syncConfig = m_sync->config(); + syncConfig->setForwardPercent(100); + m_sync = std::make_shared(syncConfig); + m_txpool->setTransactionSync(m_sync); + } + + void asyncNotifyBlockResult(BlockNumber _blockNumber, TransactionSubmitResultsPtr _txsResult, + std::function _onNotifyFinished) + { + m_txpool->txpoolStorage()->batchRemove(_blockNumber, *_txsResult); + if (_onNotifyFinished) + { + _onNotifyFinished(nullptr); + } + } + +private: + void updateConnectedNodeList() + { + NodeIDSet nodeIdSet; + for (auto node : m_ledger->ledgerConfig()->consensusNodeList()) + { + nodeIdSet.insert(node->nodeID()); + } + m_txpool->transactionSync()->config()->setConnectedNodeList(nodeIdSet); + m_txpool->transactionSync()->config()->notifyConnectedNodes(nodeIdSet, nullptr); + m_frontService->setNodeIDList(nodeIdSet); + } + +private: + NodeIDPtr m_nodeId; + CryptoSuite::Ptr m_cryptoSuite; + BlockFactory::Ptr m_blockFactory; + TransactionSubmitResultFactory::Ptr m_txResultFactory; + std::string m_groupId; + std::string m_chainId; + int64_t m_blockLimit; + + FakeLedger::Ptr m_ledger; + FakeFrontService::Ptr m_frontService; + FakeGateWay::Ptr m_fakeGateWay; + TxPool::Ptr m_txpool; + TransactionSync::Ptr m_sync; +}; + +inline void checkTxSubmit(TxPoolInterface::Ptr _txpool, TxPoolStorageInterface::Ptr _storage, + Transaction::Ptr _tx, HashType const& _expectedTxHash, uint32_t _expectedStatus, + size_t expectedTxSize, bool _needWaitResult = true, bool _waitNothing = false, + bool _maybeExpired = false) +{ + auto promise = std::make_shared>(); + auto future = promise->get_future(); + + bcos::task::wait(_txpool->submitTransaction(std::move(_tx)), + [_expectedTxHash = _expectedTxHash, _expectedStatus = _expectedStatus, + _maybeExpired = _maybeExpired, promise](auto&& submitResult) { + using ResultType = std::decay_t; + if constexpr (!std::is_same_v) + { + if (submitResult->txHash() != _expectedTxHash) + { + // do something + std::cout << "Mismatch!" << std::endl; + } + BOOST_CHECK_EQUAL(submitResult->txHash(), _expectedTxHash); + std::cout << "##### _expectedStatus: " << std::to_string(_expectedStatus) + << std::endl; + std::cout << "##### receiptStatus:" << std::to_string(submitResult->status()) + << std::endl; + if (_maybeExpired) + { + BOOST_CHECK((submitResult->status() == _expectedStatus) || + (submitResult->status() == + (int32_t)TransactionStatus::BlockLimitCheckFail)); + } + } + + promise->set_value(); + }); + + if (_waitNothing) + { + return; + } + + future.get(); + + if (!_needWaitResult) + { + while (_storage->size() != expectedTxSize) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + } + BOOST_CHECK(_storage->size() == expectedTxSize); +} +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/test/unittests/txpool/TxPoolTest.cpp" "b/BFPL\345\243\271/bcos-txpool/test/unittests/txpool/TxPoolTest.cpp" new file mode 100644 index 00000000..542a0e9a --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/test/unittests/txpool/TxPoolTest.cpp" @@ -0,0 +1,646 @@ +/** + * Copyright (C) 2021 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. + * + * @brief unit test for the txpool + * @file TxPoolTest.cpp + * @author: yujiechen + * @date 2021-05-26 + */ +#include "bcos-crypto/interfaces/crypto/KeyPairInterface.h" +#include "bcos-crypto/signature/sm2/SM2Crypto.h" +#include "bcos-tars-protocol/protocol/TransactionImpl.h" +#include "test/unittests/txpool/TxPoolFixture.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace bcos; +using namespace bcos::txpool; +using namespace bcos::protocol; +using namespace bcos::crypto; + +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(TxPoolTest, TestPromptFixture) +void testAsyncFillBlock(TxPoolFixture::Ptr _faker, TxPoolInterface::Ptr _txpool, + TxPoolStorageInterface::Ptr _txpoolStorage, CryptoSuite::Ptr _cryptoSuite) +{ + // case1: miss all transactions and verify failed + auto block = _faker->txpool()->txpoolConfig()->blockFactory()->createBlock(); + auto blockHeader = + _faker->txpool()->txpoolConfig()->blockFactory()->blockHeaderFactory()->createBlockHeader(); + block->setBlockHeader(blockHeader); + auto blockFactory = _faker->txpool()->txpoolConfig()->blockFactory(); + HashListPtr txsHash = std::make_shared(); + for (size_t i = 0; i < 10; i++) + { + auto txHash = _cryptoSuite->hashImpl()->hash(std::to_string(i)); + // auto txMetaData = transactionMetaDataFactory.createTransactionMetaData(txHash, + // txHash.abridged()); + auto txMetaData = _faker->blockFactory()->createTransactionMetaData(); + txMetaData->setHash(txHash); + txMetaData->setTo(txHash.abridged()); + + txsHash->emplace_back(txHash); + block->appendTransactionMetaData(txMetaData); + } + bool finish = false; + _txpool->asyncFillBlock(txsHash, [&](Error::Ptr _error, TransactionsPtr) { + BOOST_CHECK(_error->errorCode() == CommonError::TransactionsMissing); + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + + // verify block with invalid txsHash + auto blockData = std::make_shared(); + block->encode(*blockData); + finish = false; + _txpool->asyncVerifyBlock( + _faker->nodeID(), ref(*blockData), [&](Error::Ptr _error, bool _result) { + BOOST_CHECK(_error->errorCode() == CommonError::TransactionsMissing); + BOOST_CHECK(_result == false); + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + + // case2: hit all the transactions and verify success + auto txs = _txpoolStorage->fetchNewTxs(10000); + block = _faker->txpool()->txpoolConfig()->blockFactory()->createBlock(); + blockHeader = + _faker->txpool()->txpoolConfig()->blockFactory()->blockHeaderFactory()->createBlockHeader(); + block->setBlockHeader(blockHeader); + BOOST_CHECK(txs->size() > 0); + txsHash->clear(); + for (auto tx : *txs) + { + txsHash->emplace_back(tx->hash()); + // auto txMetaData = + // blockFactory->createTransactionMetaData(tx->hash(), tx->hash().abridged()); + auto txMetaData = _faker->blockFactory()->createTransactionMetaData(); + txMetaData->setHash(tx->hash()); + txMetaData->setTo(tx->hash().abridged()); + block->appendTransactionMetaData(txMetaData); + } + finish = false; + _txpool->asyncFillBlock(txsHash, [&](Error::Ptr _error, TransactionsPtr _fetchedTxs) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(txsHash->size() == _fetchedTxs->size()); + size_t i = 0; + for (auto tx : *_fetchedTxs) + { + BOOST_CHECK((*txsHash)[i++] == tx->hash()); + } + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + // verify the blocks + blockData = std::make_shared(); + block->encode(*blockData); + finish = false; + _txpool->asyncVerifyBlock( + _faker->nodeID(), ref(*blockData), [&](Error::Ptr _error, bool _result) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(_result == true); + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + + // case3: with some txs hitted + auto txHash = _cryptoSuite->hashImpl()->hash("test"); + txsHash->emplace_back(txHash); + // auto txMetaData = blockFactory->createTransactionMetaData(txHash, txHash.abridged()); + auto txMetaData = _faker->blockFactory()->createTransactionMetaData(); + txMetaData->setHash(txHash); + txMetaData->setTo(txHash.abridged()); + block->appendTransactionMetaData(txMetaData); + + finish = false; + _txpool->asyncFillBlock(txsHash, [&](Error::Ptr _error, TransactionsPtr) { + BOOST_CHECK(_error->errorCode() == CommonError::TransactionsMissing); + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + + finish = false; + blockData = std::make_shared(); + block->encode(*blockData); + std::cout << "#### test case3" << std::endl; + _txpool->asyncVerifyBlock( + _faker->nodeID(), ref(*blockData), [&](Error::Ptr _error, bool _result) { + BOOST_CHECK(_error->errorCode() == CommonError::TransactionsMissing); + BOOST_CHECK(_result == false); + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } +} + +void testAsyncSealTxs(TxPoolFixture::Ptr _faker, TxPoolInterface::Ptr _txpool, + TxPoolStorageInterface::Ptr _txpoolStorage, int64_t _blockLimit, CryptoSuite::Ptr _cryptoSuite) +{ + // asyncSealTxs + auto originTxsSize = _txpoolStorage->size(); + size_t txsLimit = 10; + HashListPtr sealedTxs = std::make_shared(); + bool finish = false; + _txpool->asyncSealTxs( + txsLimit, nullptr, [&](Error::Ptr _error, Block::Ptr _txsMetaDataList, Block::Ptr) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(_txsMetaDataList->transactionsMetaDataSize() == txsLimit); + for (size_t i = 0; i < _txsMetaDataList->transactionsMetaDataSize(); i++) + { + sealedTxs->emplace_back(_txsMetaDataList->transactionHash(i)); + } + BOOST_CHECK(_txpoolStorage->size() == originTxsSize); + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + // seal again to fetch all unsealed txs + finish = false; + _txpool->asyncSealTxs( + 100000, nullptr, [&](Error::Ptr _error, Block::Ptr _txsMetaDataList, Block::Ptr) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(_txsMetaDataList->transactionsMetaDataSize() == (originTxsSize - txsLimit)); + BOOST_CHECK(_txpoolStorage->size() == originTxsSize); + std::set txsSet(sealedTxs->begin(), sealedTxs->end()); + for (size_t i = 0; i < _txsMetaDataList->transactionsMetaDataSize(); i++) + { + auto const& hash = _txsMetaDataList->transactionHash(i); + BOOST_CHECK(!txsSet.count(hash)); + } + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + + finish = false; + _txpool->asyncMarkTxs(sealedTxs, false, -1, HashType(), [&](Error::Ptr _error) { + BOOST_CHECK(_error == nullptr); + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + + // seal again + finish = false; + _txpool->asyncSealTxs( + 100000, nullptr, [&](Error::Ptr _error, Block::Ptr _txsMetaDataList, Block::Ptr) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(_txsMetaDataList->transactionsMetaDataSize() == sealedTxs->size()); + BOOST_CHECK(_txsMetaDataList->transactionsHashSize() == sealedTxs->size()); + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + + // mark txs to given proposal as false, expect: mark failed + finish = false; + auto blockHash = _cryptoSuite->hashImpl()->hash("blockHash"); + auto blockNumber = 10; + _txpool->asyncMarkTxs(sealedTxs, false, blockNumber, blockHash, [&](Error::Ptr _error) { + BOOST_CHECK(_error == nullptr); + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + + // re-seal + finish = false; + _txpool->asyncSealTxs( + 100000, nullptr, [&](Error::Ptr _error, Block::Ptr _txsMetaDataList, Block::Ptr) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(_txsMetaDataList->transactionsMetaDataSize() == 0); + BOOST_CHECK(_txsMetaDataList->transactionsHashSize() == 0); + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + + // mark txs to as false, expect mark success + finish = false; + _txpool->asyncMarkTxs(sealedTxs, false, -1, HashType(), [&](Error::Ptr _error) { + BOOST_CHECK(_error == nullptr); + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + // re-seal success + finish = false; + _txpool->asyncSealTxs( + 100000, nullptr, [&](Error::Ptr _error, Block::Ptr _txsMetaDataList, Block::Ptr) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(_txsMetaDataList->transactionsMetaDataSize() == sealedTxs->size()); + BOOST_CHECK(_txsMetaDataList->transactionsHashSize() == sealedTxs->size()); + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + + // mark txs to given proposal as true + finish = false; + _txpool->asyncMarkTxs(sealedTxs, true, blockNumber, blockHash, [&](Error::Ptr _error) { + BOOST_CHECK(_error == nullptr); + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + + // reseal failed + finish = false; + _txpool->asyncSealTxs( + 100000, nullptr, [&](Error::Ptr _error, Block::Ptr _txsMetaDataList, Block::Ptr) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(_txsMetaDataList->transactionsMetaDataSize() == 0); + BOOST_CHECK(_txsMetaDataList->transactionsHashSize() == 0); + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + + // mark txs to given proposal as false, expect success + finish = false; + _txpool->asyncMarkTxs(sealedTxs, false, blockNumber, blockHash, [&](Error::Ptr _error) { + BOOST_CHECK(_error == nullptr); + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + // re-seal success + finish = false; + _txpool->asyncSealTxs( + 100000, nullptr, [&](Error::Ptr _error, Block::Ptr _txsMetaDataList, Block::Ptr) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(_txsMetaDataList->transactionsMetaDataSize() == sealedTxs->size()); + BOOST_CHECK(_txsMetaDataList->transactionsHashSize() == sealedTxs->size()); + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + + // test asyncNotifyBlockResult + blockNumber = _faker->ledger()->blockNumber() + _blockLimit; + auto txsResult = std::make_shared(); + for (auto txHash : *sealedTxs) + { + auto txResult = std::make_shared(); + txResult->setTxHash(txHash); + txResult->setStatus((uint32_t)TransactionStatus::None); + txsResult->emplace_back(txResult); + } + auto missedTxs = std::make_shared(); + auto notifiedTxs = _txpoolStorage->fetchTxs(*missedTxs, *sealedTxs); + BOOST_CHECK(missedTxs->size() == 0); + BOOST_CHECK(notifiedTxs->size() == sealedTxs->size()); + + finish = false; + _faker->asyncNotifyBlockResult(blockNumber, txsResult, [&](Error::Ptr _error) { + BOOST_CHECK(_error == nullptr); + finish = true; + }); + auto startT = utcTime(); + while ((!finish || (_txpoolStorage->size() != originTxsSize - sealedTxs->size())) && + (utcTime() - startT <= 10 * 1000)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + // check the txpool size + BOOST_CHECK(_txpoolStorage->size() == originTxsSize - sealedTxs->size()); + // check the txpoolNonce + auto txPoolNonceChecker = _faker->txpool()->txpoolConfig()->txPoolNonceChecker(); + auto validator = + std::dynamic_pointer_cast(_faker->txpool()->txpoolConfig()->txValidator()); + auto ledgerNonceChecker = validator->ledgerNonceChecker(); + for (auto tx : *notifiedTxs) + { + BOOST_CHECK(txPoolNonceChecker->checkNonce(tx) == TransactionStatus::None); + BOOST_CHECK(ledgerNonceChecker->checkNonce(tx) == TransactionStatus::NonceCheckFail); + } + // check the nonce of ledger->blockNumber() hash been removed from ledgerNonceChecker + auto const& blockData = _faker->ledger()->ledgerData(); + auto const& nonceList = blockData[_faker->ledger()->blockNumber()]->nonces(); + for (auto const& nonce : *nonceList) + { + BOOST_CHECK(ledgerNonceChecker->exists(nonce) == false); + } + + // case: the other left txs expired for invalid blockLimit + finish = false; + std::cout << "######### asyncSealTxs with invalid blocklimit" << std::endl; + std::cout << "##### origin txsSize:" << _txpoolStorage->size() << std::endl; + + _txpool->asyncResetTxPool(nullptr); + _txpool->asyncSealTxs( + 100000, nullptr, [&](Error::Ptr _error, Block::Ptr _txsMetaDataList, Block::Ptr) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(_txsMetaDataList->transactionsMetaDataSize() == 0); + BOOST_CHECK(_txsMetaDataList->transactionsHashSize() == 0); + finish = true; + }); + while (!finish || (_txpoolStorage->size() > 0)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + BOOST_CHECK(_txpoolStorage->size() == 0); +} + +void txPoolInitAndSubmitTransactionTest(bool _sm, CryptoSuite::Ptr _cryptoSuite) +{ + auto signatureImpl = _cryptoSuite->signatureImpl(); + auto hashImpl = _cryptoSuite->hashImpl(); + auto keyPair = signatureImpl->generateKeyPair(); + std::string groupId = "group_test_for_txpool"; + std::string chainId = "chain_test_for_txpool"; + int64_t blockLimit = 10; + auto fakeGateWay = std::make_shared(); + auto faker = std::make_shared( + keyPair->publicKey(), _cryptoSuite, groupId, chainId, blockLimit, fakeGateWay); + faker->init(); + + // check the txpool config + auto txpoolConfig = faker->txpool()->txpoolConfig(); + BOOST_CHECK(txpoolConfig->txPoolNonceChecker()); + BOOST_CHECK(txpoolConfig->txValidator()); + BOOST_CHECK(txpoolConfig->blockFactory()); + BOOST_CHECK(txpoolConfig->txFactory()); + BOOST_CHECK(txpoolConfig->ledger()); + + auto txpool = faker->txpool(); + auto txpoolStorage = txpool->txpoolStorage(); + // case1: the node is not in the consensus/observerList + auto tx = fakeTransaction(_cryptoSuite, utcTime()); + + checkTxSubmit(txpool, txpoolStorage, tx, HashType(), + (uint32_t)TransactionStatus::RequestNotBelongToTheGroup, 0); + + // case2: transaction with invalid blockLimit + faker->appendSealer(faker->nodeID()); + auto ledger = faker->ledger(); + tx = fakeTransaction(_cryptoSuite, utcTime() + 11000, ledger->blockNumber() + blockLimit + 1, + faker->chainId(), faker->groupId()); + checkTxSubmit( + txpool, txpoolStorage, tx, tx->hash(), (uint32_t)TransactionStatus::BlockLimitCheckFail, 0); + + // case3: transaction with invalid nonce(conflict with the ledger nonce) + auto const& blockData = ledger->ledgerData(); + auto duplicatedNonce = + blockData[ledger->blockNumber() - blockLimit + 1]->transaction(0)->nonce(); + tx = fakeTransaction(_cryptoSuite, duplicatedNonce, ledger->blockNumber() + blockLimit - 4, + faker->chainId(), faker->groupId()); + checkTxSubmit( + txpool, txpoolStorage, tx, tx->hash(), (uint32_t)TransactionStatus::NonceCheckFail, 0); + + // case4: invalid groupId + tx = fakeTransaction(_cryptoSuite, utcTime(), ledger->blockNumber() + blockLimit - 4, + faker->chainId(), "invalidGroup"); + checkTxSubmit( + txpool, txpoolStorage, tx, tx->hash(), (uint32_t)TransactionStatus::InvalidGroupId, 0); + + // case5: invalid chainId + tx = fakeTransaction(_cryptoSuite, utcTime(), ledger->blockNumber() + blockLimit - 4, + "invalidChainId", faker->groupId()); + checkTxSubmit( + txpool, txpoolStorage, tx, tx->hash(), (uint32_t)TransactionStatus::InvalidChainId, 0); + + // case6: invalid signature + tx = fakeTransaction(_cryptoSuite, utcTime() + 100000, ledger->blockNumber() + blockLimit - 4, + faker->chainId(), faker->groupId()); + auto pbTx = std::dynamic_pointer_cast(tx); + bcos::crypto::KeyPairInterface::Ptr invalidKeyPair = signatureImpl->generateKeyPair(); + auto invalidHash = hashImpl->hash(std::string("test")); + auto signatureData = signatureImpl->sign(*invalidKeyPair, invalidHash, true); + pbTx->setSignatureData(*signatureData); + pbTx->forceSender(bcos::bytes()); + size_t importedTxNum = 0; + if (!_sm) + { + importedTxNum++; + checkTxSubmit(txpool, txpoolStorage, pbTx, pbTx->hash(), (uint32_t)TransactionStatus::None, + importedTxNum, false, true, true); + } + else + { + checkTxSubmit(txpool, txpoolStorage, pbTx, pbTx->hash(), + (uint32_t)TransactionStatus::InvalidSignature, importedTxNum); + } + + // case7: submit success + importedTxNum++; + tx = fakeTransaction(_cryptoSuite, utcTime() + 2000000, ledger->blockNumber() + blockLimit - 4, + faker->chainId(), faker->groupId()); + checkTxSubmit(txpool, txpoolStorage, tx, tx->hash(), (uint32_t)TransactionStatus::None, + importedTxNum, false, true, true); + // case8: submit duplicated tx + checkTxSubmit(txpool, txpoolStorage, tx, tx->hash(), + (uint32_t)TransactionStatus::AlreadyInTxPool, importedTxNum); + + // batch import transactions with multiple thread + auto threadPool = std::make_shared("txpoolSubmitter", 8); + + Transactions transactions; + for (auto i = 0; i < 40; i++) + { + auto tmpTx = fakeTransaction(_cryptoSuite, utcTime() + 1000 + i, + ledger->blockNumber() + blockLimit - 4, faker->chainId(), faker->groupId()); + transactions.push_back(tmpTx); + } + + for (size_t i = 0; i < transactions.size(); i++) + { + auto tmpTx = transactions[i]; + checkTxSubmit(txpool, txpoolStorage, tmpTx, tmpTx->hash(), + (uint32_t)TransactionStatus::None, 0, false, true, true); + } + importedTxNum += transactions.size(); + auto startT = utcTime(); + while ((txpoolStorage->size() < importedTxNum) && (utcTime() - startT <= 10000)) + { + std::cout << "#### txpoolStorage->size:" << txpoolStorage->size() << std::endl; + std::cout << "#### importedTxNum:" << importedTxNum << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + std::cout << "#### txpoolStorage size:" << txpoolStorage->size() << std::endl; + std::cout << "#### importedTxNum:" << importedTxNum << std::endl; + // check txs submitted to the ledger + auto txsHash2Data = ledger->txsHashToData(); + for (size_t i = 0; i < transactions.size(); i++) + { + auto startT = utcTime(); + while (!txsHash2Data.count(transactions[i]->hash()) && (utcTime() - startT <= 5000)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + txsHash2Data = ledger->txsHashToData(); + } + std::cout << "### txsHash2Data size: " << txsHash2Data.size() << ", i: " << i + << ", transactions size:" << transactions.size() << std::endl; + } + // case9: the txpool is full + txpoolConfig->setPoolLimit(importedTxNum); + checkTxSubmit(txpool, txpoolStorage, tx, tx->hash(), (uint32_t)TransactionStatus::TxPoolIsFull, + importedTxNum); + + // case10: malformed transaction + bcos::bytes encodedData; + tx->encode(encodedData); + auto txData = std::make_shared(encodedData.begin(), encodedData.end()); + // fake invalid txData + for (size_t i = 0; i < txData->size(); i++) + { + (*txData)[i] += 100; + } + bool verifyFinish = false; + try + { + auto _result = ~txpool->submitTransaction(tx); + } + catch (bcos::Error& e) + { + // TODO: Put TransactionStatus::Malform into bcos::Error + // BOOST_CHECK(e.errorCode() == _result->status()); + std::cout << "#### error info:" << e.errorMessage() << std::endl; + // BOOST_CHECK(_result->txHash() == HashType()); + // BOOST_CHECK(_result->status() == (uint32_t)(TransactionStatus::Malform)); + } + + verifyFinish = true; + + while (!verifyFinish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + std::cout << "#### testAsyncFillBlock" << std::endl; + testAsyncFillBlock(faker, txpool, txpoolStorage, _cryptoSuite); + std::cout << "#### testAsyncSealTxs" << std::endl; + testAsyncSealTxs(faker, txpool, txpoolStorage, blockLimit, _cryptoSuite); + // clear all the txs before exit + txpool->txpoolStorage()->clear(); + std::cout << "#### txPoolInitAndSubmitTransactionTest finish" << std::endl; +} + +BOOST_AUTO_TEST_CASE(testTxPoolInitAndSubmitTransaction) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + txPoolInitAndSubmitTransactionTest(false, cryptoSuite); +} + +BOOST_AUTO_TEST_CASE(testSMTxPoolInitAndSubmitTransaction) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + txPoolInitAndSubmitTransactionTest(true, cryptoSuite); +} + +BOOST_AUTO_TEST_CASE(fillWithSubmit) +{ + // auto hashImpl = std::make_shared(); + // auto signatureImpl = std::make_shared(); + // auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + + // auto keyPair = signatureImpl->generateKeyPair(); + // std::string groupId = "group_test_for_txpool"; + // std::string chainId = "chain_test_for_txpool"; + // int64_t blockLimit = 10; + // auto fakeGateWay = std::make_shared(); + // auto faker = std::make_shared( + // keyPair->publicKey(), cryptoSuite, groupId, chainId, blockLimit, fakeGateWay); + // faker->init(); + + // // check the txpool config + // auto txpoolConfig = faker->txpool()->txpoolConfig(); + // BOOST_CHECK(txpoolConfig->txPoolNonceChecker()); + // BOOST_CHECK(txpoolConfig->txValidator()); + // BOOST_CHECK(txpoolConfig->blockFactory()); + // BOOST_CHECK(txpoolConfig->txFactory()); + // BOOST_CHECK(txpoolConfig->ledger()); + + // auto txpool = faker->txpool(); + // auto txpoolStorage = txpool->txpoolStorage(); + + // // case7: submit success + // auto tx = + // fakeTransaction(cryptoSuite, utcTime() + 2000000, 100, chainId, groupId); + + // tx->encode(); + // auto encodedPtr = std::make_shared(tx->takeEncoded()); + + // auto hashList = std::make_shared(); + // hashList->push_back(tx->hash()); + + // std::promise fillPromise; + // txpool->asyncFillBlock(hashList, + // [originTx = tx, &fillPromise](Error::Ptr error, bcos::protocol::TransactionsPtr tx) { + // BOOST_CHECK(!error); + // BOOST_CHECK(tx); + // BOOST_CHECK_EQUAL(tx->size(), 1); + // BOOST_CHECK_EQUAL((*tx)[0].get(), originTx.get()); + // fillPromise.set_value(); + // }); + // fillPromise.get_future().get(); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-txpool/test/unittests/txpool/TxsSyncTest.cpp" "b/BFPL\345\243\271/bcos-txpool/test/unittests/txpool/TxsSyncTest.cpp" new file mode 100644 index 00000000..1b2e2688 --- /dev/null +++ "b/BFPL\345\243\271/bcos-txpool/test/unittests/txpool/TxsSyncTest.cpp" @@ -0,0 +1,295 @@ +/** + * Copyright (C) 2021 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. + * + * @brief unit test for txs-fetching related logic of txpool + * @file TxsSyncTest.cpp.cpp + * @author: yujiechen + * @date 2021-05-26 + */ +#include "test/unittests/txpool/TxPoolFixture.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos::txpool; +using namespace bcos::sync; +using namespace bcos::crypto; + +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(txsSyncTest, TestPromptFixture) + +void importTransactions( + size_t _txsNum, CryptoSuite::Ptr _cryptoSuite, const TxPoolFixture::Ptr& _faker) +{ + auto txpool = _faker->txpool(); + auto ledger = _faker->ledger(); + Transactions transactions; + for (size_t i = 0; i < _txsNum; i++) + { + auto transaction = fakeTransaction(_cryptoSuite, utcTime() + 1000 + i, + ledger->blockNumber() + 1, _faker->chainId(), _faker->groupId()); + transactions.push_back(transaction); + task::wait(txpool->submitTransaction(transaction)); + } + auto startT = utcTime(); + while (txpool->txpoolStorage()->size() < _txsNum && (utcTime() - startT <= 10000)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } +} + +void importTransactionsNew( + size_t _txsNum, CryptoSuite::Ptr _cryptoSuite, const TxPoolFixture::Ptr& _faker) +{ + auto txpool = _faker->txpool(); + auto ledger = _faker->ledger(); + Transactions transactions; + for (size_t i = 0; i < _txsNum; i++) + { + auto transaction = fakeTransaction(_cryptoSuite, utcTime() + 1000 + i, + ledger->blockNumber() + 1, _faker->chainId(), _faker->groupId()); + transactions.push_back(transaction); + } + + // Test parallel submit + tbb::parallel_for( + tbb::blocked_range(0, transactions.size()), [&txpool, &transactions](auto& range) { + for (auto i = range.begin(); i < range.end(); ++i) + { + auto& transaction = transactions[i]; + task::wait(txpool->submitTransaction(transaction), [](auto&& result) { + using ResultType = std::decay_t; + if constexpr (std::is_same_v) + { + std::rethrow_exception(result); + } + else + { + BOOST_CHECK_EQUAL(result->status(), 0); + } + + TXPOOL_LOG(DEBUG) << "Submit transaction successed"; + }); + } + }); + + auto startT = utcTime(); + while (txpool->txpoolStorage()->size() < _txsNum && (utcTime() - startT <= 10000)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } +} + +void testTransactionSync(bool _onlyTxsStatus = false) +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto cryptoSuite = std::make_shared(hashImpl, signatureImpl, nullptr); + auto keyPair = cryptoSuite->signatureImpl()->generateKeyPair(); + std::string groupId = "test-group"; + std::string chainId = "test-chain"; + int64_t blockLimit = 15; + auto fakeGateWay = std::make_shared(); + auto faker = std::make_shared( + keyPair->publicKey(), cryptoSuite, groupId, chainId, blockLimit, fakeGateWay); + if (_onlyTxsStatus) + { + faker->resetToFakeTransactionSync(); + } + faker->appendSealer(keyPair->publicKey()); + // init the config + faker->init(); + auto txpool = faker->txpool(); + auto ledger = faker->ledger(); + // append sessions + size_t sessionSize = 8; + std::vector txpoolPeerList; + std::vector sessionNodeIDs; + sessionNodeIDs.emplace_back(keyPair->publicKey()); + for (size_t i = 0; i < sessionSize; i++) + { + auto nodeId = signatureImpl->generateKeyPair()->publicKey(); + auto sessionFaker = std::make_shared( + nodeId, cryptoSuite, groupId, chainId, blockLimit, fakeGateWay); + sessionFaker->init(); + if (_onlyTxsStatus) + { + sessionFaker->resetToFakeTransactionSync(); + } + sessionNodeIDs.emplace_back(nodeId); + faker->appendSealer(nodeId); + txpoolPeerList.push_back(sessionFaker); + } + for (auto& sessionFaker : txpoolPeerList) + { + for (auto const& nodeID : sessionNodeIDs) + { + // make sure the session in the group + sessionFaker->appendSealer(nodeID); + } + } + size_t txsNum = 10; + importTransactions(txsNum, cryptoSuite, faker); + + // check maintain transactions + faker->sync()->maintainTransactions(); + if (_onlyTxsStatus) + { + for (auto txpoolPeer : txpoolPeerList) + { + // all the peers has received the txsStatus, and fetch txs from other peers + BOOST_CHECK(faker->frontService()->getAsyncSendSizeByNodeID(txpoolPeer->nodeID()) >= 1); + auto startT = utcTime(); + while (txpoolPeer->txpool()->txpoolStorage()->size() < txsNum && + (utcTime() - startT <= 10000)) + { + // maintain the downloading txs + txpoolPeer->sync()->maintainDownloadingTransactions(); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + std::cout << "### txpoolSize: " << txpoolPeer->txpool()->txpoolStorage()->size() + << ", txsNum:" << txsNum << ", peer: " << txpoolPeer->nodeID()->shortHex() + << std::endl; + BOOST_CHECK(txpoolPeer->txpool()->txpoolStorage()->size() == txsNum); + } + // maintain transactions again + auto originSendSize = faker->frontService()->totalSendMsgSize(); + faker->sync()->maintainTransactions(); + BOOST_CHECK(faker->frontService()->totalSendMsgSize() == originSendSize); + return; + } + // check the transactions has been broadcasted to all the node + // maintainDownloadingTransactions and check the size + for (auto txpoolPeer : txpoolPeerList) + { + BOOST_CHECK(faker->frontService()->getAsyncSendSizeByNodeID(txpoolPeer->nodeID()) >= 1); + txpoolPeer->sync()->maintainDownloadingTransactions(); + auto startT = utcTime(); + while ( + txpoolPeer->txpool()->txpoolStorage()->size() < txsNum && (utcTime() - startT <= 10000)) + { + txpoolPeer->sync()->maintainDownloadingTransactions(); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + BOOST_CHECK(txpoolPeer->txpool()->txpoolStorage()->size() == txsNum); + } + // +1 for include the node self + auto forwardSize = + ((txpoolPeerList.size() + 1) * faker->sync()->config()->forwardPercent() + 99) / 100; + + // with requestMissedTxs request + auto maxSendSize = txpoolPeerList.size() + forwardSize + forwardSize; + BOOST_CHECK(faker->frontService()->totalSendMsgSize() <= maxSendSize); + auto originSendSize = faker->frontService()->totalSendMsgSize(); + faker->sync()->maintainTransactions(); + std::cout << "##### totalSendMsgSize: " << faker->frontService()->totalSendMsgSize() + << std::endl; + std::cout << "#### txpoolPeerList size:" << txpoolPeerList.size() << std::endl; + std::cout << "##### forwardSize:" << forwardSize << std::endl; + BOOST_CHECK(faker->frontService()->totalSendMsgSize() == originSendSize); + + // test forward txs status + auto syncPeer = txpoolPeerList[0]; + // update connected node list + originSendSize = syncPeer->frontService()->totalSendMsgSize(); + std::cout << "#### before maintainTransactions totalSendMsgSize: " << originSendSize + << std::endl; + for (auto txpoolPeer : txpoolPeerList) + { + syncPeer->appendSealer(txpoolPeer->nodeID()); + } + std::cout << "#### after maintainTransactions totalSendMsgSize: " + << syncPeer->frontService()->totalSendMsgSize() << std::endl; + syncPeer->sync()->maintainTransactions(); + // BOOST_CHECK(syncPeer->frontService()->totalSendMsgSize() == originSendSize + forwardSize); + + syncPeer->sync()->maintainTransactions(); + // BOOST_CHECK(syncPeer->frontService()->totalSendMsgSize() == originSendSize + forwardSize); + + // import new transaction to the syncPeer, but not broadcast the imported transaction + std::cout << "###### test fetch and verify block" << std::endl; + auto newTxsSize = 1000; + importTransactionsNew(newTxsSize, cryptoSuite, syncPeer); // Use new method to import + // transaction + // the syncPeer sealTxs + HashListPtr txsHash = std::make_shared(); + bool finish = false; + syncPeer->txpool()->asyncSealTxs( + 100000, nullptr, [&](Error::Ptr _error, Block::Ptr _fetchedTxs, Block::Ptr) { + BOOST_CHECK(_error == nullptr); + // BOOST_CHECK(_fetchedTxs->size() == syncPeer->txpool()->txpoolStorage()->size()); + for (size_t i = 0; i < _fetchedTxs->transactionsMetaDataSize(); i++) + { + txsHash->emplace_back(_fetchedTxs->transactionHash(i)); + } + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + // assume the faker verify the syncPeer generated proposal + auto blockFactory = faker->txpool()->txpoolConfig()->blockFactory(); + auto block = blockFactory->createBlock(); + for (auto const& txHash : *txsHash) + { + // auto txMetaData = blockFactory->createTransactionMetaData(txHash, txHash.abridged()); + auto txMetaData = blockFactory->createTransactionMetaData(); + txMetaData->setHash(txHash); + txMetaData->setTo(txHash.abridged()); + + block->appendTransactionMetaData(txMetaData); + } + auto encodedData = std::make_shared(); + block->encode(*encodedData); + finish = false; + faker->txpool()->asyncVerifyBlock( + syncPeer->nodeID(), ref(*encodedData), [&](Error::Ptr _error, bool _result) { + BOOST_CHECK(_error == nullptr); + BOOST_CHECK(_result == true); + finish = true; + }); + while (!finish) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } +} + +BOOST_AUTO_TEST_CASE(testMatainTransactions) +{ + testTransactionSync(false); +} + +BOOST_AUTO_TEST_CASE(testOnPeerTxsStatus) +{ + testTransactionSync(true); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/CMakeLists.txt" "b/BFPL\345\243\271/bcos-utilities/CMakeLists.txt" new file mode 100644 index 00000000..122cbba9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/CMakeLists.txt" @@ -0,0 +1,22 @@ +include(Version) +project(bcos-utilities VERSION ${VERSION}) + +aux_source_directory(bcos-utilities SRCS) + +find_package(Boost REQUIRED COMPONENTS log filesystem chrono thread serialization) +find_package(zstd CONFIG REQUIRED) + +add_library(bcos-utilities ${SRCS}) +target_include_directories(bcos-utilities PUBLIC + $ + $) +target_link_libraries(bcos-utilities PUBLIC Boost::log Boost::filesystem Boost::chrono Boost::thread Boost::serialization zstd::libzstd_static) + +if(TESTS) + enable_testing() + add_subdirectory(test) +endif() + +include(GNUInstallDirs) +install(TARGETS bcos-utilities EXPORT fiscobcosTargets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") +install(DIRECTORY "bcos-utilities" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/" FILES_MATCHING PATTERN "*.h") \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/Base64.cpp" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Base64.cpp" new file mode 100644 index 00000000..9836a9d0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Base64.cpp" @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2021 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 Base64.cpp + * @author: xingqiangbai + */ + +#include "Base64.h" +#include "Ranges.h" +#include +#include +#include +#include +#include + +using namespace boost::archive::iterators; + +std::string bcos::base64Encode(const byte* _begin, const size_t _dataSize) +{ + using It = base64_from_binary>; + auto tmp = std::string(It(_begin), It(_begin + _dataSize)); + return tmp.append((3 - _dataSize % 3) % 3, '='); +} + +std::string bcos::base64Encode(std::string const& _data) +{ + return base64Encode((const byte*)_data.data(), _data.size()); +} + +std::string bcos::base64Encode(bytesConstRef _data) +{ + return base64Encode(_data.data(), _data.size()); +} + +std::string bcos::base64Decode(std::string const& _data) +{ + size_t suffix = 0; + for (size_t i = _data.size() - 1; i >= _data.size() - 3; --i) + { + if (_data[i] == '=') + { + ++suffix; + } + else + { + break; + } + } + using It = transform_width, 8, 6>; + auto ret = std::string(It(_data.begin()), It(RANGES::end(_data))); + ret.resize(ret.size() - suffix); + return ret; +} + +std::shared_ptr bcos::base64DecodeBytes(std::string const& _data) +{ + auto s = base64Decode(_data); + return std::make_shared(s.begin(), s.end()); +} diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/Base64.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Base64.h" new file mode 100644 index 00000000..40722515 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Base64.h" @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2021 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 Base64.h + * @author: xingqiangbai + */ +#pragma once +#include "Common.h" + +namespace bcos +{ +std::string base64Encode(const byte* _begin, const size_t _dataSize); + +std::string base64Encode(std::string const& _data); +std::string base64Encode(bytesConstRef _data); + +std::shared_ptr base64DecodeBytes(std::string const& _data); +std::string base64Decode(std::string const& _data); +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/BoostLog.cpp" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/BoostLog.cpp" new file mode 100644 index 00000000..ffe54f1b --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/BoostLog.cpp" @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2021 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. + * + * @brief: define Log + * + * @file: Log.cpp + * @author: yujiechen + * @date 2021-02-24 + */ +#include "Log.h" +#include + +namespace bcos +{ +std::string const FileLogger = "FileLogger"; +boost::log::sources::severity_channel_logger_mt + FileLoggerHandler(boost::log::keywords::channel = FileLogger); + +std::string const StatFileLogger = "StatFileLogger"; +boost::log::sources::severity_channel_logger_mt + StatFileLoggerHandler(boost::log::keywords::channel = StatFileLogger); + +LogLevel c_fileLogLevel = LogLevel::TRACE; +LogLevel c_statLogLevel = LogLevel::INFO; + +void setFileLogLevel(LogLevel const& _level) +{ + c_fileLogLevel = _level; +} + +void setStatLogLevel(LogLevel const& _level) +{ + c_statLogLevel = _level; +} +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/BoostLog.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/BoostLog.h" new file mode 100644 index 00000000..ebd2bc09 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/BoostLog.h" @@ -0,0 +1,86 @@ +/** + * Copyright (C) 2021 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. + * + * @brief: define Log + * + * @file: Log.h + * @author: yujiechen + * @date 2021-02-24 + */ +#pragma once + +#include +#include +#include +#include + +// BCOS log format +#ifndef LOG_BADGE +#define LOG_BADGE(_NAME) "[" << (_NAME) << "]" +#endif + +#ifndef LOG_TYPE +#define LOG_TYPE(_TYPE) (_TYPE) << "|" +#endif + +#ifndef LOG_DESC +#define LOG_DESC(_DESCRIPTION) (_DESCRIPTION) +#endif + +#ifndef LOG_KV +#define LOG_KV(_K, _V) "," << (_K) << "=" << (_V) +#endif + +#ifdef ERROR +#undef ERROR +#endif + +namespace bcos +{ +extern std::string const FileLogger; +/// the file logger +extern boost::log::sources::severity_channel_logger_mt + FileLoggerHandler; + +// the statFileLogger +extern std::string const StatFileLogger; +extern boost::log::sources::severity_channel_logger_mt + StatFileLoggerHandler; + +enum LogLevel +{ + FATAL = boost::log::trivial::fatal, + ERROR = boost::log::trivial::error, + WARNING = boost::log::trivial::warning, + INFO = boost::log::trivial::info, + DEBUG = boost::log::trivial::debug, + TRACE = boost::log::trivial::trace +}; + +extern LogLevel c_fileLogLevel; +extern LogLevel c_statLogLevel; + +void setFileLogLevel(LogLevel const& _level); +void setStatLogLevel(LogLevel const& _level); + +#define BCOS_LOG(level) \ + if (bcos::LogLevel::level >= bcos::c_fileLogLevel) \ + BOOST_LOG_SEV( \ + bcos::FileLoggerHandler, (boost::log::trivial::severity_level)(bcos::LogLevel::level)) +// for block number log +#define BLOCK_NUMBER(NUMBER) "[blk-" << (NUMBER) << "]" +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/BoostLogInitializer.cpp" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/BoostLogInitializer.cpp" new file mode 100644 index 00000000..50dc8c69 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/BoostLogInitializer.cpp" @@ -0,0 +1,218 @@ +/** + * Copyright (C) 2021 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. + * + * @brief: setting log + * + * @file: BoostLogInitializer.cpp + * @author: yujiechen + */ +#include "BoostLogInitializer.h" +#include +#include +#include +#include +#include + +using namespace bcos; + +namespace logging = boost::log; +namespace expr = boost::log::expressions; + +/// handler to solve log rotate +bool BoostLogInitializer::canRotate(size_t const& _index) +{ + const boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); + int hour = (int)now.time_of_day().hours(); + if (hour != m_currentHourVec[_index]) + { + m_currentHourVec[_index] = hour; + return true; + } + return false; +} + +// init statLog +void BoostLogInitializer::initStatLog(boost::property_tree::ptree const& _pt, + std::string const& _logger, std::string const& _logPrefix) +{ + m_running.store(true); + // not set the log path before init + if (m_logPath.size() == 0) + { + m_logPath = _pt.get("log.log_path", "log"); + } + /// set log level + unsigned logLevel = getLogLevel(_pt.get("log.level", "info")); + auto sink = initLogSink(_pt, logLevel, m_logPath, _logPrefix, _logger); + + setStatLogLevel((LogLevel)logLevel); + + /// set file format + /// log-level|timestamp | message + sink->set_formatter(expr::stream + << boost::log::expressions::attr( + "Severity") + << "|" + << boost::log::expressions::format_date_time( + "TimeStamp", "%Y-%m-%d %H:%M:%S.%f") + << "|" << boost::log::expressions::smessage); +} + +boost::shared_ptr +BoostLogInitializer::initConsoleLogSink( + boost::property_tree::ptree const& _pt, unsigned const& _logLevel, std::string const& channel) +{ + boost::log::add_common_attributes(); + boost::shared_ptr consoleSink(new console_sink_t()); + consoleSink->locked_backend()->add_stream( + boost::shared_ptr(&std::cout, boost::null_deleter())); + + bool need_flush = _pt.get("log.flush", true); + consoleSink->locked_backend()->auto_flush(need_flush); + consoleSink->set_filter(boost::log::expressions::attr("Channel") == channel && + boost::log::trivial::severity >= _logLevel); + boost::log::core::get()->add_sink(consoleSink); + m_consoleSinks.push_back(consoleSink); + bool enable_log = _pt.get("log.enable", true); + boost::log::core::get()->set_logging_enabled(enable_log); + return consoleSink; +} +/** + * @brief: set log for specified channel + * + * @param pt: ptree that contains the log configuration + * @param channel: channel name + * @param logType: log prefix + */ +void BoostLogInitializer::initLog(boost::property_tree::ptree const& _pt, + std::string const& _logger, std::string const& _logPrefix) +{ + m_running.store(true); + // get log level + unsigned logLevel = getLogLevel(_pt.get("log.level", "info")); + bool consoleLog = _pt.get("log.enable_console_output", false); + if (consoleLog) + { + boost::shared_ptr sink = initConsoleLogSink(_pt, logLevel, _logger); + setLogFormatter(sink); + } + else + { + if (m_logPath.size() == 0) + { + m_logPath = _pt.get("log.log_path", "log"); + } + boost::shared_ptr sink = initLogSink(_pt, logLevel, m_logPath, _logPrefix, _logger); + setLogFormatter(sink); + } + setFileLogLevel((LogLevel)logLevel); +} + +boost::shared_ptr BoostLogInitializer::initLogSink( + boost::property_tree::ptree const& pt, unsigned const& _logLevel, std::string const& _logPath, + std::string const& _logPrefix, std::string const& channel) +{ + m_currentHourVec.push_back( + (int)boost::posix_time::second_clock::local_time().time_of_day().hours()); + /// set file name + std::string fileName = _logPath + "/" + _logPrefix + "_%Y%m%d%H.%M.log"; + boost::shared_ptr sink(new sink_t()); + + sink->locked_backend()->set_open_mode(std::ios::app); + sink->locked_backend()->set_time_based_rotation( + boost::bind(&BoostLogInitializer::canRotate, this, (m_currentHourVec.size() - 1))); + sink->locked_backend()->set_file_name_pattern(fileName); + /// set rotation size MB + uint64_t rotation_size = pt.get("log.max_log_file_size", 200) * 1048576; + sink->locked_backend()->set_rotation_size(rotation_size); + /// set auto-flush according to log configuration + bool need_flush = pt.get("log.flush", true); + sink->locked_backend()->auto_flush(need_flush); + + + sink->set_filter(boost::log::expressions::attr("Channel") == channel && + boost::log::trivial::severity >= _logLevel); + + + boost::log::core::get()->add_sink(sink); + m_sinks.push_back(sink); + bool enable_log = pt.get("log.enable", true); + boost::log::core::get()->set_logging_enabled(enable_log); + // add attributes + boost::log::add_common_attributes(); + return sink; +} + +/** + * @brief: get log level according to given string + * + * @param levelStr: the given string that should be transformed to boost log level + * @return unsigned: the log level + */ +unsigned BoostLogInitializer::getLogLevel(std::string const& levelStr) +{ + if (boost::iequals(levelStr, "trace")) + return boost::log::trivial::severity_level::trace; + if (boost::iequals(levelStr, "debug")) + return boost::log::trivial::severity_level::debug; + if (boost::iequals(levelStr, "warning")) + return boost::log::trivial::severity_level::warning; + if (boost::iequals(levelStr, "error")) + return boost::log::trivial::severity_level::error; + if (boost::iequals(levelStr, "fatal")) + return boost::log::trivial::severity_level::fatal; + /// default log level is info + return boost::log::trivial::severity_level::info; +} + +/// stop and remove all sinks after the program exit +void BoostLogInitializer::stopLogging() +{ + if (!m_running) + { + return; + } + m_running.store(false); + for (auto const& sink : m_sinks) + { + stopLogging(sink); + } + m_sinks.clear(); + + for (auto const& sink : m_consoleSinks) + { + stopLogging(sink); + } + m_consoleSinks.clear(); +} + +/// stop a single sink +void BoostLogInitializer::stopLogging(boost::shared_ptr sink) +{ + if (!sink) + { + return; + } + // remove the sink from the core, so that no records are passed to it + if (boost::log::core::get()) + { + boost::log::core::get()->remove_sink(sink); + } + // break the feeding loop + sink->stop(); + // flush all log records that may have left buffered + sink->flush(); + sink.reset(); +} diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/BoostLogInitializer.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/BoostLogInitializer.h" new file mode 100644 index 00000000..4eb40790 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/BoostLogInitializer.h" @@ -0,0 +1,150 @@ +/** + * Copyright (C) 2021 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. + * + * @brief: setting log + * + * @file: BoostLogInitializer.h + * @author: yujiechen + */ +#pragma once + +#include "Common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +class BoostLogInitializer +{ +public: + class Sink : public boost::log::sinks::text_file_backend + { + public: + void consume(const boost::log::record_view& rec, const std::string& str) + { + boost::log::sinks::text_file_backend::consume(rec, str); + auto severity = + rec.attribute_values()[boost::log::aux::default_attribute_names::severity()] + .extract(); + // bug fix: determine m_ptr before get the log level + // serverity.get() will call BOOST_ASSERT(m_ptr) + if (severity.get_ptr() && severity.get() == boost::log::trivial::severity_level::fatal) + { + // abort if encounter fatal, will generate coredump + // must make sure only use LOG(FATAL) when encounter the most serious problem + // forbid use LOG(FATAL) in the function that should exit normally + std::abort(); + } + } + }; + class ConsoleSink : public boost::log::sinks::text_ostream_backend + { + public: + void consume(const boost::log::record_view& rec, const std::string& str) + { + boost::log::sinks::text_ostream_backend::consume(rec, str); + auto severity = + rec.attribute_values()[boost::log::aux::default_attribute_names::severity()] + .extract(); + // bug fix: determine m_ptr before get the log level + // serverity.get() will call BOOST_ASSERT(m_ptr) + if (severity.get_ptr() && severity.get() == boost::log::trivial::severity_level::fatal) + { + // abort if encounter fatal, will generate coredump + // must make sure only use LOG(FATAL) when encounter the most serious problem + // forbid use LOG(FATAL) in the function that should exit normally + std::abort(); + } + } + }; + using Ptr = std::shared_ptr; + using sink_t = boost::log::sinks::asynchronous_sink; + using console_sink_t = boost::log::sinks::asynchronous_sink; + virtual ~BoostLogInitializer() { stopLogging(); } + BoostLogInitializer() {} + + void initLog(boost::property_tree::ptree const& _pt, + std::string const& _logger = bcos::FileLogger, std::string const& _logPrefix = "log"); + + void initStatLog(boost::property_tree::ptree const& _pt, + std::string const& _logger = bcos::StatFileLogger, std::string const& _logPrefix = "stat"); + + void stopLogging(); + + unsigned getLogLevel(std::string const& levelStr); + + void setLogPath(std::string const& _logPath) { m_logPath = _logPath; } + std::string logPath() const { return m_logPath; } + +private: + bool canRotate(size_t const& _index); + + boost::shared_ptr initLogSink(boost::property_tree::ptree const& _pt, + unsigned const& _logLevel, std::string const& _logPath, std::string const& _logPrefix, + std::string const& channel); + + boost::shared_ptr initConsoleLogSink(boost::property_tree::ptree const& _pt, + unsigned const& _logLevel, std::string const& channel); + + template + void setLogFormatter(T _sink) + { + /// set file format + /// log-level|timestamp | message + _sink->set_formatter( + boost::log::expressions::stream + << boost::log::expressions::attr("Severity") << "|" + << boost::log::expressions::format_date_time( + "TimeStamp", "%Y-%m-%d %H:%M:%S.%f") + << "|" << boost::log::expressions::smessage); + } + + template + void stopLogging(boost::shared_ptr sink) + { + if (!sink) + { + return; + } + // remove the sink from the core, so that no records are passed to it + boost::log::core::get()->remove_sink(sink); + // break the feeding loop + sink->stop(); + // flush all log records that may have left buffered + sink->flush(); + sink.reset(); + } + +private: + void stopLogging(boost::shared_ptr sink); + std::vector> m_sinks; + std::vector> m_consoleSinks; + + std::vector m_currentHourVec; + std::string m_logPath; + std::atomic_bool m_running = {false}; +}; +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/CallbackCollectionHandler.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/CallbackCollectionHandler.h" new file mode 100644 index 00000000..997ab1c3 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/CallbackCollectionHandler.h" @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2021 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. + * + * @author wheatli + * @date 2018.8.27 + * @modify add CallbackCollectionHandler.h + * + */ +#pragma once +#include "Common.h" +#include "DataConvertUtility.h" + +namespace bcos +{ +template +class CallbackCollectionHandler +{ +public: + using Callback = std::function; + + class SingleCallback + { + friend class CallbackCollectionHandler; + + public: + ~SingleCallback() + { + if (m_callbackCollectionHandler) + { + m_callbackCollectionHandler->m_callbackCollection.erase(m_iterator); + } + } + void reset() { m_callbackCollectionHandler = nullptr; } + void call(Args const&... _args) { m_callback(_args...); } + + private: + SingleCallback(unsigned _iterator, CallbackCollectionHandler* _callbackCollectionHandler, + Callback const& _callback) + : m_iterator(_iterator), + m_callbackCollectionHandler(_callbackCollectionHandler), + m_callback(_callback) + {} + + unsigned m_iterator = 0; + CallbackCollectionHandler* m_callbackCollectionHandler = nullptr; + Callback m_callback; + }; + + ~CallbackCollectionHandler() + { + for (auto const& item : m_callbackCollection) + { + if (auto callback = item.second.lock()) + { + callback->reset(); + } + } + } + + std::shared_ptr add(Callback const& _callback) + { + auto iterator = + m_callbackCollection.empty() ? 0 : (m_callbackCollection.rbegin()->first + 1); + auto callback = + std::shared_ptr(new SingleCallback(iterator, this, _callback)); + m_callbackCollection[iterator] = callback; + return callback; + } + + void operator()(Args const&... _args) + { + auto callbackCollection = convertMapToVector(m_callbackCollection); + for (auto const& singleCallback : *callbackCollection) + { + auto callback = singleCallback.lock(); + if (callback) + { + callback->call(_args...); + } + } + } + +private: + std::map> + m_callbackCollection; +}; + +template +using Handler = std::shared_ptr::SingleCallback>; +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/Common.cpp" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Common.cpp" new file mode 100644 index 00000000..17240f18 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Common.cpp" @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2021 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.cpp + * @author Asherli + * @date 2021-02-24 + */ + +#define NOMINMAX + +#include "Common.h" +#include "Exceptions.h" +#include +#if defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN32_) +#include +#else +#include +#endif +#ifdef __APPLE__ +#include +#endif + +using namespace std; + +namespace bcos +{ +bytes const NullBytes; +/// get utc time(ms) +uint64_t utcTime() +{ +#if defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN32_) + return std::chrono::steady_clock::now().time_since_epoch().count() / 1000000; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +#endif +} + +// getSteadyTime(ms) +uint64_t utcSteadyTime() +{ + // trans (ns) into (ms) + return std::chrono::steady_clock::now().time_since_epoch().count() / 1000000; +} + +/// get utc time(us) +uint64_t utcTimeUs() +{ +#if defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN32_) + return std::chrono::steady_clock::now().time_since_epoch().count() / 1000; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000 + tv.tv_usec; +#endif +} + +uint64_t utcSteadyTimeUs() +{ + return std::chrono::steady_clock::now().time_since_epoch().count() / 1000; +} + +std::string getCurrentDateTime() +{ + using std::chrono::system_clock; + char buffer[40]; + auto currentTime = system_clock::to_time_t(system_clock::now()); + strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", localtime(¤tTime)); + return std::string(buffer); +} + +void errorExit(std::stringstream& _exitInfo, Exception const& _exception) +{ + BCOS_LOG(WARNING) << _exitInfo.str(); + std::cerr << _exitInfo.str(); + raise(SIGTERM); + BOOST_THROW_EXCEPTION(_exception << errinfo_comment(_exitInfo.str())); +} + +thread_local std::string TimeRecorder::m_name; +thread_local std::chrono::steady_clock::time_point TimeRecorder::m_timePoint; +thread_local size_t TimeRecorder::m_heapCount = 0; +thread_local std::vector > + TimeRecorder::m_record; + +TimeRecorder::TimeRecorder(const std::string& function, const std::string& name) + : m_function(function) +{ + auto now = std::chrono::steady_clock::now(); + if (m_timePoint == std::chrono::steady_clock::time_point()) + { + m_name = name; + m_timePoint = now; + } + else + { + m_record.push_back(std::make_pair(m_name, m_timePoint)); + + m_name = name; + m_timePoint = now; + } + + ++m_heapCount; +} + +TimeRecorder::~TimeRecorder() +{ + --m_heapCount; + + if (!m_heapCount && m_timePoint != std::chrono::steady_clock::time_point()) + { + auto now = std::chrono::steady_clock::now(); + auto end = now; + m_record.push_back(std::make_pair(m_name, m_timePoint)); + + std::vector > elapseds; + elapseds.resize(m_record.size()); + std::stringstream ss; + for (auto i = m_record.size(); i > 0; --i) + { + std::chrono::duration elapsed = now - m_record[i - 1].second; + now = m_record[i - 1].second; + + elapseds[i - 1] = elapsed; + } + + for (size_t i = 0; i < m_record.size(); ++i) + { + ss << " [" << m_record[i].first << "]: " << std::setiosflags(std::ios::fixed) + << std::setprecision(4) << elapseds[i].count(); + } + + std::chrono::duration totalElapsed = end - m_record[0].second; + BCOS_LOG(DEBUG) << "[TIME RECORDER]-" << m_function + << ": [TOTAL]: " << std::setiosflags(std::ios::fixed) + << std::setprecision(4) << totalElapsed.count() << ss.str(); + + m_name = ""; + m_timePoint = std::chrono::steady_clock::time_point(); + m_record.clear(); + } +} + +std::string newSeq() +{ + static std::atomic seq; + size_t seqTmp = seq.fetch_add(1) + 1; + std::stringstream ss; + ss << std::setfill('0') << std::setw(32) << seqTmp; + return ss.str(); +} + +} // namespace bcos + +void bcos::pthread_setThreadName(std::string const& _n) +{ +#if defined(__GLIBC__) + pthread_setname_np(pthread_self(), _n.c_str()); +#elif defined(__APPLE__) + pthread_setname_np(_n.c_str()); +#endif +} diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/Common.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Common.h" new file mode 100644 index 00000000..8c01fa15 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Common.h" @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2021 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. + * + */ + +#pragma once + +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Log.h" +#include "RefDataContainer.h" +#include +#include +#include +#include +#include + +namespace bcos +{ +using namespace boost::multiprecision::literals; + +// vector of byte data +using byte = uint8_t; +using bytes = std::vector; +using bytesPointer = std::shared_ptr>; +using bytesConstPtr = std::shared_ptr; +using bytesRef = RefDataContainer; +using bytesConstRef = RefDataContainer; + +using smallBytes = boost::container::small_vector; + +// Numeric types. +using bigint = boost::multiprecision::number>; + +// unsigned int256 +using u256 = boost::multiprecision::number>; +// signed int256 +using s256 = boost::multiprecision::number>; +// unsigned int160 +using u160 = boost::multiprecision::number>; +// signed int160 +using s160 = boost::multiprecision::number>; +// unsigned int256 +using u512 = boost::multiprecision::number>; +// signed int256 +using s512 = boost::multiprecision::number>; + +// Map types. +using BytesMap = std::map; +// Fixed-length string types. +using string32 = std::array; +// Map types. +using HexMap = std::map; + +// Null/Invalid values for convenience. +extern bytes const NullBytes; +u256 constexpr Invalid256 = + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_cppui256; + + +using Mutex = std::mutex; +using RecursiveMutex = std::recursive_mutex; +using SharedMutex = boost::shared_mutex; + +using Guard = std::lock_guard; +using UniqueGuard = std::unique_lock; +using RecursiveGuard = std::lock_guard; +using ReadGuard = boost::shared_lock; +using UpgradableGuard = boost::upgrade_lock; +using UpgradeGuard = boost::upgrade_to_unique_lock; +using WriteGuard = boost::unique_lock; + +template +inline u256 exp10() +{ + return exp10() * u256(10); +} + +template <> +inline u256 exp10<0>() +{ + return u256(1); +} + +//------------ Type interprets and Convertions---------------- +/// Interprets @a _u as a two's complement signed number and returns the resulting s256. +inline s256 u2s(u256 _u) +{ + static const bigint c_end = bigint(1) << 256; + /// get the +/- symbols + if (boost::multiprecision::bit_test(_u, 255)) + return s256(-(c_end - _u)); + else + return s256(_u); +} + +/// @returns the two's complement signed representation of the signed number _u. +inline u256 s2u(s256 _u) +{ + static const bigint c_end = bigint(1) << 256; + if (_u >= 0) + return u256(_u); + else + return u256(c_end + _u); +} + +inline bool isalNumStr(std::string const& _stringData) +{ + for (auto ch : _stringData) + { + if (isalnum(ch)) + { + continue; + } + return false; + } + return true; +} + +enum class WithExisting : int +{ + Trust = 0, + Verify, + Rescue, + Kill +}; + +/// Get the current time in seconds since the epoch in UTC(ms) +uint64_t utcTime(); +uint64_t utcSteadyTime(); + +/// Get the current time in seconds since the epoch in UTC(us) +uint64_t utcTimeUs(); +uint64_t utcSteadyTimeUs(); + +// get the current datatime +std::string getCurrentDateTime(); + +struct Exception; +// callback when throw exceptions +void errorExit(std::stringstream& _exitInfo, Exception const& exception); + +/// Reference to a slice of buffer that also owns the buffer. +/// +/// This is extension to the concept C++ STL library names as array_view +/// (also known as gsl::span, array_ref, here RefDataContainer) -- reference to +/// continuous non-modifiable memory. The extension makes the object also owning +/// the referenced buffer. +/// +/// This type is used by VMs to return output coming from RETURN instruction. +/// To avoid memory copy, a VM returns its whole memory + the information what +/// part of this memory is actually the output. This simplifies the VM design, +/// because there are multiple options how the output will be used (can be +/// ignored, part of it copied, or all of it copied). The decision what to do +/// with it was moved out of VM interface making VMs "stateless". +/// +/// The type is movable, but not copyable. Default constructor available. +class owning_bytes_ref : public RefDataContainer +{ +public: + owning_bytes_ref() = default; + + /// @param _bytes The buffer. + /// @param _begin The index of the first referenced byte. + /// @param _size The number of referenced bytes. + owning_bytes_ref(bytes&& _bytes, size_t _begin, size_t _size) : m_bytes(std::move(_bytes)) + { + // Set the reference *after* the buffer is moved to avoid + // pointer invalidation. + retarget(&m_bytes[_begin], _size); + } + + owning_bytes_ref(owning_bytes_ref const&) = delete; + owning_bytes_ref(owning_bytes_ref&&) = default; + owning_bytes_ref& operator=(owning_bytes_ref const&) = delete; + owning_bytes_ref& operator=(owning_bytes_ref&&) = default; + + /// Moves the bytes vector out of here. The object cannot be used any more. + bytes&& takeBytes() + { + reset(); // Reset reference just in case. + return std::move(m_bytes); + } + +private: + bytes m_bytes; +}; + +template +class QueueSet +{ +public: + bool push(T const& _t) + { + if (m_set.count(_t) == 0) + { + m_set.insert(_t); + m_queue.push(_t); + return true; + } + return false; + } + bool pop() + { + if (m_queue.size() == 0) + return false; + auto t = m_queue.front(); + m_queue.pop(); + m_set.erase(t); + return true; + } + + void insert(T const& _t) { push(_t); } + size_t count(T const& _t) const { return exist(_t) ? 1 : 0; } + bool exist(T const& _t) const { return m_set.count(_t) > 0; } + size_t size() const { return m_set.size(); } + + void clear() + { + m_set.clear(); + while (!m_queue.empty()) + m_queue.pop(); + } + +private: + std::unordered_set m_set; + std::queue m_queue; +}; + +// do not use TIME_RECORD in tbb code block +#define __TIME_RECORD(name, var, line) ::bcos::TimeRecorder var##line(__FUNCTION__, name) +#define _TIME_RECORD(name, line) __TIME_RECORD(name, _time_anonymous, line) +#define TIME_RECORD(name) _TIME_RECORD(name, __LINE__) + +class TimeRecorder +{ +public: + TimeRecorder(const std::string& function, const std::string& name); + ~TimeRecorder(); + +private: + std::string m_function; + static thread_local std::string m_name; + static thread_local std::chrono::steady_clock::time_point m_timePoint; + static thread_local size_t m_heapCount; + static thread_local std::vector> + m_record; +}; + +template +class HolderForDestructor +{ +public: + HolderForDestructor(std::shared_ptr _elementsToDestroy) + : m_elementsToDestroy(std::move(_elementsToDestroy)) + {} + void operator()() {} + +private: + // Elements to be deconstructed + std::shared_ptr m_elementsToDestroy; +}; + +std::string newSeq(); +void pthread_setThreadName(std::string const& _n); + +/* +template +struct overloaded : Ts... +{ + using Ts::operator()...; +}; +// explicit deduction guide (not needed as of C++20) +template +overloaded(Ts...) -> overloaded; +*/ + +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/ConcurrentQueue.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/ConcurrentQueue.h" new file mode 100644 index 00000000..0cb675d8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/ConcurrentQueue.h" @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2021 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. + * + * @brief: Implement a queue with notification/consumption mechanism + * @file ConcurrentQueue.h + */ +#pragma once +#include +#include +#include + + +namespace bcos +{ +/// Concurrent queue. +/// You can push and pop elements to/from the queue. Pop will block until the queue is not empty. +/// The default backend (QueueT) is std::queue. It can be changed to any type that has +/// proper push(), pop(), empty() and front() methods. +template > +class ConcurrentQueue +{ +public: + template + void push(_U&& _elem) + { + { + std::lock_guard guard{x_mutex}; + m_queue.push(std::forward<_U>(_elem)); + } + m_cv.notify_one(); + } + + bool empty() + { + boost::unique_lock lock{x_mutex}; + return m_queue.empty(); + } + + _T pop() + { + boost::unique_lock lock{x_mutex}; + m_cv.wait(lock, [this] { return !m_queue.empty(); }); + auto item = std::move(m_queue.front()); + m_queue.pop(); + return item; + } + + std::pair tryPop(int milliseconds) + { + boost::unique_lock lock{x_mutex}; + // in consideration that when the system time has been changed, + // the process maybe stucked in 'wait_for' + auto ret = m_cv.wait_for( + lock, boost::chrono::milliseconds(milliseconds), [this] { return !m_queue.empty(); }); + if (!ret) + { + return std::make_pair(false, _T()); + } + auto item = std::move(m_queue.front()); + m_queue.pop(); + return std::make_pair(ret, item); + } + +private: + QueueT m_queue; + boost::mutex x_mutex; + boost::condition_variable m_cv; +}; +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/DataConvertUtility.cpp" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/DataConvertUtility.cpp" new file mode 100644 index 00000000..afecaaf0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/DataConvertUtility.cpp" @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2021 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 DataConvertUtility.cpp + */ + +#include "DataConvertUtility.h" +#include + +#include "Exceptions.h" + +using namespace std; +using namespace bcos; +/** + * @brief: convert the hex char into the hex number + * + * @param _hexChar: the hex char to be converted + * @return int : the converted hex number + */ +int convertCharToHexNumber(char _hexChar) +{ + if (_hexChar >= '0' && _hexChar <= '9') + return _hexChar - '0'; + if (_hexChar >= 'a' && _hexChar <= 'f') + return _hexChar - 'a' + 10; + if (_hexChar >= 'A' && _hexChar <= 'F') + return _hexChar - 'A' + 10; + return -1; +} + +bool bcos::isHexString(string const& _string) +{ + auto it = _string.begin(); + if (_string.find("0x") == 0) + { + it += 2; + } + for (; it != _string.end(); it++) + { + if (convertCharToHexNumber(*it) == -1) + { + return false; + } + } + return true; +} + +std::shared_ptr bcos::fromHexString(std::string const& _hexedString) +{ + unsigned startIndex = + (_hexedString.size() >= 2 && _hexedString[0] == '0' && _hexedString[1] == 'x') ? 2 : 0; + std::shared_ptr bytesData = std::make_shared(); + bytesData->reserve((_hexedString.size() - startIndex + 1) / 2); + + if (_hexedString.size() % 2) + { + int h = convertCharToHexNumber(_hexedString[startIndex++]); + if (h == -1) + { + BOOST_THROW_EXCEPTION(BadHexCharacter()); + } + bytesData->push_back(h); + } + for (unsigned i = startIndex; i < _hexedString.size(); i += 2) + { + int highValue = convertCharToHexNumber(_hexedString[i]); + int lowValue = convertCharToHexNumber(_hexedString[i + 1]); + if (highValue == -1 || lowValue == -1) + { + BOOST_THROW_EXCEPTION(BadHexCharacter()); + } + bytesData->push_back((bcos::byte)(highValue << 4) + lowValue); + } + return bytesData; +} + +std::string bcos::toString(string32 const& _s) +{ + std::string ret; + for (unsigned i = 0; i < 32 && _s[i]; ++i) + ret.push_back(_s[i]); + return ret; +} diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/DataConvertUtility.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/DataConvertUtility.h" new file mode 100644 index 00000000..38f771eb --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/DataConvertUtility.h" @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2021 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 DataConvertUtility.h + */ + +#pragma once + +#include "Common.h" +#include "Error.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +template +Out toHex(const Binary& binary, std::string_view prefix = std::string_view()) +{ + Out out; + + out.reserve(binary.size() * 2 + prefix.size()); + + if (!prefix.empty()) + { + out.insert(out.end(), prefix.begin(), prefix.end()); + } + + if (binary.empty()) + { + return out; + } + + boost::algorithm::hex_lower(binary.begin(), binary.end(), std::back_inserter(out)); + return out; +} + +template +Out fromHex(const Hex& hex, std::string_view prefix = std::string_view()) +{ + if (hex.empty()) + { + BOOST_THROW_EXCEPTION(BCOS_ERROR(-1, "Empty input hex string")); + } + + if ((hex.size() < prefix.size() + 2) || (hex.size() % 2 != 0)) + { + BOOST_THROW_EXCEPTION(BCOS_ERROR(-1, "Invalid input hex string size")); + } + + Out out; + out.reserve(hex.size() / 2); + + boost::algorithm::unhex(hex.begin() + prefix.size(), hex.end(), std::back_inserter(out)); + return out; +} + +/** + * @brief convert the specified bytes data into hex string + * + * @tparam Iterator: the iterator type of the data + * @param _begin : the begin pointer of the data that need to be converted to hex string + * @param _end : the end pointer of the data that need to be converted to the hex string + * @param _prefix: prefix of the converted hex string + * @return std::shared_ptr : the converted hex string + */ +template +std::shared_ptr toHexString( + Iterator _begin, Iterator _end, std::string const& _prefix = "") +{ + static_assert(sizeof(typename std::iterator_traits::value_type) == 1, + "only support byte-sized element type"); + auto hexStringSize = std::distance(_begin, _end) * 2 + _prefix.size(); + std::shared_ptr hexString = std::make_shared(hexStringSize, '0'); + // set the _prefix + memcpy((void*)hexString->data(), (const void*)_prefix.data(), _prefix.size()); + static char const* hexCharsCollection = "0123456789abcdef"; + // covert the bytes into hex chars + size_t offset = _prefix.size(); + for (auto it = _begin; it != _end; it++) + { + (*hexString)[offset++] = hexCharsCollection[(*it >> 4) & 0x0f]; + (*hexString)[offset++] = hexCharsCollection[*it & 0x0f]; + } + return hexString; +} + +/** + * @brief : convert the given data to hex string(without prefix) + * + * @tparam T : the type of the given data + * @param _data : the data need to be converted to hex + * @return std::shared_ptr : the pointer of the converted hex string + */ +template +std::shared_ptr toHexString(T const& _data) +{ + return toHexString(_data.begin(), _data.end()); +} + +/** + * @brief convert the bytes into hex string with 0x prefixed + * + * @tparam T : the type of data to be converted + * @param _data : the data to be converted + * @return std::string : the hex string + */ +template +std::string toHexStringWithPrefix(T const& _data) +{ + std::string out; + out.reserve(_data.size() * 2 + 2); + out = "0x"; + boost::algorithm::hex_lower(_data.begin(), _data.end(), std::back_inserter(out)); + + return out; +} + +/** + * @brief convert hex string to bytes + * + * @param _hexedString: the hex string need to be converted + * @return std::shared_ptr: the converted bytes + */ +std::shared_ptr fromHexString(std::string const& _hexedString); + +/** + * @brief determine the input string is hex string or not + * + * @param _string the string to be determined + * @return true : the input string is hex string + * @return false : the input string is not hex string + */ +bool isHexString(std::string const& _string); + +/// Converts byte array to a string containing the same (binary) data. Unless +/// the byte array happens to contain ASCII data, this won't be printable. +inline std::string asString(bytes const& _b) +{ + return std::string((char const*)_b.data(), (char const*)(_b.data() + _b.size())); +} + +/// Converts byte array ref to a string containing the same (binary) data. Unless +/// the byte array happens to contain ASCII data, this won't be printable. +inline std::string asString(bytesConstRef _b) +{ + return std::string((char const*)_b.data(), (char const*)(_b.data() + _b.size())); +} + +/// Converts a string to a byte array containing the string's (byte) data. +inline bytes asBytes(std::string const& _b) +{ + return bytes((byte const*)_b.data(), (byte const*)(_b.data() + _b.size())); +} + +// Big-endian to/from host endian conversion functions. + +/// Converts a templated integer value to the big-endian byte-stream represented on a templated +/// collection. The size of the collection object will be unchanged. If it is too small, it will not +/// represent the value properly, if too big then the additional elements will be zeroed out. +/// @a Out will typically be either std::string or bytes. +/// @a T will typically by unsigned, u160, u256 or bigint. +template +inline void toBigEndian(T _val, Out& o_out) +{ + static_assert(std::is_same::value || !std::numeric_limits::is_signed, + "only unsigned types or bigint supported"); // bigint does not carry sign bit on shift + for (auto i = o_out.size(); i != 0; _val >>= 8, i--) + { + T v = _val & (T)0xff; + o_out[i - 1] = (typename Out::value_type)(uint8_t)v; + } +} + +/// Converts a big-endian byte-stream represented on a templated collection to a templated integer +/// value. +/// @a _In will typically be either std::string or bytes. +/// @a T will typically by unsigned, u160, u256 or bigint. +template +inline T fromBigEndian(_In const& _bytes) +{ + T ret = (T)0; + for (auto i : _bytes) + ret = (T)((ret << 8) | (byte)(typename std::make_unsigned::type)i); + return ret; +} + +inline bytes toBigEndian(u256 _val) +{ + bytes ret(32); + toBigEndian(_val, ret); + return ret; +} +inline bytes toBigEndian(u160 _val) +{ + bytes ret(20); + toBigEndian(_val, ret); + return ret; +} + +/// Convenience function for toBigEndian. +/// @returns a byte array just big enough to represent @a _val. +template +inline bytes toCompactBigEndian(T _val, unsigned _min = 0) +{ + static_assert(std::is_same::value || !std::numeric_limits::is_signed, + "only unsigned types or bigint supported"); // bigint does not carry sign bit on shift + unsigned i = 0; + for (T v = _val; v; ++i, v >>= 8) + {} + bytes ret((std::max)(_min, i), 0); + toBigEndian(_val, ret); + return ret; +} +inline bytes toCompactBigEndian(byte _val, unsigned _min = 0) +{ + return (_min || _val) ? bytes{_val} : bytes{}; +} + +/// Convenience function for toBigEndian. +/// @returns a string just big enough to represent @a _val. +template +inline std::string toCompactBigEndianString(T _val, unsigned _min = 0) +{ + static_assert(std::is_same::value || !std::numeric_limits::is_signed, + "only unsigned types or bigint supported"); // bigint does not carry sign bit on shift + unsigned i = 0; + for (T v = _val; v; ++i, v >>= 8) + {} + std::string ret((std::max)(_min, i), '\0'); + toBigEndian(_val, ret); + return ret; +} + +// Algorithms for string and string-like collections. +// Concatenate two vectors of elements of POD types. +template +inline std::vector& operator+=( + std::vector::value, T>::type>& _a, + std::vector const& _b) +{ + auto s = _a.size(); + _a.resize(_a.size() + _b.size()); + memcpy(_a.data() + s, _b.data(), _b.size() * sizeof(T)); + return _a; +} + +/// Concatenate two vectors of elements. +template +inline std::vector& operator+=( + std::vector::value, T>::type>& _a, + std::vector const& _b) +{ + _a.reserve(_a.size() + _b.size()); + for (auto& i : _b) + _a.push_back(i); + return _a; +} + +/// Insert the contents of a container into a set +template +std::set& operator+=(std::set& _a, U const& _b) +{ + for (auto const& i : _b) + _a.insert(i); + return _a; +} + +/// Insert the contents of a container into an unordered_set +template +std::unordered_set& operator+=(std::unordered_set& _a, U const& _b) +{ + for (auto const& i : _b) + _a.insert(i); + return _a; +} + +/// Concatenate the contents of a container onto a vector +template +std::vector& operator+=(std::vector& _a, U const& _b) +{ + for (auto const& i : _b) + _a.push_back(i); + return _a; +} + +/// Insert the contents of a container into a set +template +std::set operator+(std::set _a, U const& _b) +{ + return _a += _b; +} + +/// Insert the contents of a container into an unordered_set +template +std::unordered_set operator+(std::unordered_set _a, U const& _b) +{ + return _a += _b; +} + +/// Concatenate the contents of a container onto a vector +template +std::vector operator+(std::vector _a, U const& _b) +{ + return _a += _b; +} + +/// Concatenate two vectors of elements. +template +inline std::vector operator+(std::vector const& _a, std::vector const& _b) +{ + std::vector ret(_a); + return ret += _b; +} + +template +inline std::ostream& operator<<(std::ostream& _out, std::vector const& _e) +{ + _out << "["; + for (auto const& element : _e) + { + _out << "," << element; + } + + _out << "]"; + return _out; +} + +template +std::shared_ptr> convertMapToVector(std::map const& _map) +{ + std::shared_ptr> convertedVec = std::make_shared>(); + for (auto const& it : _map) + { + convertedVec->push_back(it.second); + } + return convertedVec; +} + +/// Make normal string from fixed-length string. +std::string toString(string32 const& _s); + +/// Converts arbitrary value to string representation using std::stringstream. +template +inline std::string toString(_T const& _t) +{ + std::ostringstream o; + o << _t; + return o.str(); +} + +template <> +inline std::string toString(std::string const& _s) +{ + return _s; +} + +template <> +inline std::string toString(uint8_t const& _u) +{ + std::ostringstream o; + o << static_cast(_u); + return o.str(); +} +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/Error.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Error.h" new file mode 100644 index 00000000..a568ccd0 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Error.h" @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2021 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 Error.h + * @author: yujiechen + * @date: 2021-04-07 + */ +#pragma once +#include "Common.h" +#include "Exceptions.h" +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#define BCOS_ERROR(errorCode, errorMessage) \ + ::bcos::Error::buildError(BOOST_CURRENT_FUNCTION, __FILE__, __LINE__, errorCode, errorMessage) +#define BCOS_ERROR_WITH_PREV(errorCode, errorMessage, prev) \ + ::bcos::Error::buildError( \ + BOOST_CURRENT_FUNCTION, __FILE__, __LINE__, errorCode, errorMessage, prev) +#endif + +#define BCOS_ERROR(errorCode, errorMessage) \ + ::bcos::Error::buildError(BOOST_CURRENT_LOCATION.to_string(), errorCode, errorMessage) +#define BCOS_ERROR_WITH_PREV(errorCode, errorMessage, prev) \ + ::bcos::Error::buildError(BOOST_CURRENT_LOCATION.to_string(), errorCode, errorMessage, prev) + +#define BCOS_ERROR_PTR(code, message) std::make_shared(BCOS_ERROR(code, message)) +#define BCOS_ERROR_WITH_PREV_PTR(code, message, prev) \ + std::make_shared(BCOS_ERROR_WITH_PREV(code, message, prev)) + +#define BCOS_ERROR_UNIQUE_PTR(code, message) std::make_unique(BCOS_ERROR(code, message)) +#define BCOS_ERROR_WITH_PREV_UNIQUE_PTR(code, message, prev) \ + std::make_unique(BCOS_ERROR_WITH_PREV(code, message, prev)) + +namespace bcos +{ +class Error : public bcos::Exception +{ +public: + using Ptr = std::shared_ptr; + using ConstPtr = std::shared_ptr; + + using UniquePtr = std::unique_ptr; + using UniqueConstPtr = std::unique_ptr; + + using PrevStdError = boost::error_info; + + static Error buildError(char const* func, char const* file, int line, int32_t errorCode, + const std::string& errorMessage) + { + Error error(errorCode, errorMessage); + error << boost::throw_function(func); + error << boost::throw_file(file); + error << boost::throw_line(line); + + return error; + } + + static Error buildError(char const* func, char const* file, int line, int32_t errorCode, + const std::string& errorMessage, const Error& prev) + { + auto error = buildError(func, file, line, errorCode, errorMessage); + error << PrevStdError(boost::diagnostic_information(prev)); + return error; + } + + static Error buildError(char const* func, char const* file, int line, int32_t errorCode, + const std::string& errorMessage, const std::exception& prev) + { + auto error = buildError(func, file, line, errorCode, errorMessage); + error << PrevStdError(boost::diagnostic_information(prev)); + return error; + } + + static Error buildError( + const std::string& context, int32_t errorCode, const std::string& errorMessage) + { + Error error(errorCode, errorMessage); + error << boost::error_info(context); + return error; + } + + static Error buildError(const std::string& context, int32_t errorCode, + const std::string& errorMessage, const Error& prev) + { + auto error = buildError(context, errorCode, errorMessage); + error << PrevStdError(boost::diagnostic_information(prev)); + return error; + } + + static Error buildError(const std::string& context, int32_t errorCode, + const std::string& errorMessage, const std::exception& prev) + { + auto error = buildError(context, errorCode, errorMessage); + error << PrevStdError(boost::diagnostic_information(prev)); + return error; + } + + Error() = default; + Error(int64_t _errorCode, std::string _errorMessage) + : bcos::Exception(_errorMessage), + m_errorCode(_errorCode), + m_errorMessage(std::move(_errorMessage)) + {} + + virtual int64_t errorCode() const { return m_errorCode; } + virtual std::string const& errorMessage() const { return m_errorMessage; } + + virtual void setErrorCode(int64_t _errorCode) { m_errorCode = _errorCode; } + virtual void setErrorMessage(std::string const& _errorMessage) + { + m_errorMessage = _errorMessage; + } + +private: + int64_t m_errorCode = 0; + std::string m_errorMessage; +}; +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/Exceptions.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Exceptions.h" new file mode 100644 index 00000000..d8317bf4 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Exceptions.h" @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2021 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. + * + * @brief define basic Exceptions + * @file Exceptions.h + */ + +#pragma once +#include "Common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +/** + * @brief : Base class for all exceptions + */ +struct Exception : virtual std::exception, virtual boost::exception +{ + Exception(std::string _message = std::string()) : m_message(std::move(_message)) {} + const char* what() const noexcept override + { + return m_message.empty() ? std::exception::what() : m_message.c_str(); + } + +private: + std::string m_message; +}; + +/// construct a new exception class overriding Exception +#define DERIVE_BCOS_EXCEPTION(X) \ + struct X : virtual Exception \ + { \ + } +DERIVE_BCOS_EXCEPTION(ConstructFixedBytesFailed); +DERIVE_BCOS_EXCEPTION(BadCast); +DERIVE_BCOS_EXCEPTION(BadHexCharacter); +DERIVE_BCOS_EXCEPTION(InvalidAddress); +DERIVE_BCOS_EXCEPTION(InvalidParameter); + +using errinfo_invalidSymbol = boost::error_info; +using errinfo_comment = boost::error_info; +using errinfo_required = boost::error_info; +using errinfo_got = boost::error_info; +using RequirementError = boost::tuple; +using RequirementErrorComment = boost::tuple; +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/FileUtility.cpp" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/FileUtility.cpp" new file mode 100644 index 00000000..1904575d --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/FileUtility.cpp" @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 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 FileUtility.cpp + */ + +#include "FileUtility.h" +#include +#include +#include +#include +#include +using namespace std; + +namespace bcos +{ +template +inline std::shared_ptr genericReadContents(boost::filesystem::path const& _file) +{ + std::shared_ptr content = std::make_shared(); + size_t const c_elementSize = sizeof(typename T::value_type); + + boost::filesystem::ifstream fileStream(_file, std::ifstream::binary); + if (!fileStream) + { + return content; + } + fileStream.seekg(0, fileStream.end); + streamoff length = fileStream.tellg(); + + if (length == 0) + { + return content; + } + fileStream.seekg(0, fileStream.beg); + content->resize((length + c_elementSize - 1) / c_elementSize); + fileStream.read(const_cast(reinterpret_cast(content->data())), length); + return content; +} + +std::shared_ptr readContents(boost::filesystem::path const& _file) +{ + return genericReadContents(_file); +} + +std::shared_ptr readContentsToString(boost::filesystem::path const& _file) +{ + return genericReadContents(_file); +} +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/FileUtility.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/FileUtility.h" new file mode 100644 index 00000000..e3297ebf --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/FileUtility.h" @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 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 FileUtility.h + */ + +#pragma once + +#include "Common.h" +#include +namespace bcos +{ +/** + * @brief : read the specified file + * + * @param _file : the file to be read + * @return std::shared_ptr : the file content + * If the file doesn't exist or isn't readable, + * returns an empty bytes. + */ +std::shared_ptr readContents(boost::filesystem::path const& _file); + +/** + * @brief : read the content of the specified file, and return the content as string + * + * @param _file : the file + * @return std::shared_ptr : the content of the specified file + * If the file doesn't exist or isn't readable, + * returns an empty string + */ +std::shared_ptr readContentsToString(boost::filesystem::path const& _file); +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/FixedBytes.cpp" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/FixedBytes.cpp" new file mode 100644 index 00000000..22c1a45f --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/FixedBytes.cpp" @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2021 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. + * + * @brief: Implement the fixed-size bytes + * @file FixedBytes.cpp + * @date 2021-02-26 + */ + +#include "FixedBytes.h" +#include + +namespace bcos +{ +std::random_device s_fixedBytesEngine; +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/FixedBytes.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/FixedBytes.h" new file mode 100644 index 00000000..b34d1302 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/FixedBytes.h" @@ -0,0 +1,727 @@ +/* + * Copyright (C) 2021 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. + * + * @brief: Implement the fixed-size bytes + * @file FixedBytes.h + * @date 2021-02-26 + */ + +#pragma once + +#include "DataConvertUtility.h" +#include "Exceptions.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +/// Compile-time calculation of Log2 of constant values. +template +struct StaticLog2 +{ + enum + { + result = 1 + StaticLog2::result + }; +}; +template <> +struct StaticLog2<1> +{ + enum + { + result = 0 + }; +}; + +extern std::random_device s_fixedBytesEngine; + +/// Fixed-size raw-byte array container type, with an API optimised for storing hashes. +/// Transparently converts to/from the corresponding arithmetic type; this will +/// assume the data contained in the hash is big-endian. +template +class FixedBytes +{ +public: + /// The corresponding arithmetic type. + using ArithType = boost::multiprecision::number>; + enum + { + SIZE = N + }; + // construct FixedBytes from string + enum StringDataType + { + FromHex, + FromBinary + }; + + // align type when construct FixedBytes from the given bytes + enum DataAlignType + { + AlignLeft, + AlignRight, + AcquireEqual + }; + + enum ConstructorType + { + FromPointer, + }; + + /** + * @brief Construct a new empty Fixed Bytes object + */ + FixedBytes() { m_data.fill(0); } + + /// Explicitly construct, copying from a byte array. + explicit FixedBytes( + bytesConstRef _bytesRefData, DataAlignType _alignType = DataAlignType::AlignRight) + : FixedBytes() + { + constructFixedBytes(_bytesRefData, _alignType); + } + + explicit FixedBytes( + bytes const& _bytesData, DataAlignType _alignType = DataAlignType::AlignRight) + : FixedBytes() + { + constructFixedBytes(bytesConstRef(_bytesData.data(), _bytesData.size()), _alignType); + } + + explicit FixedBytes(std::string_view view, StringDataType type, + DataAlignType _alignType = DataAlignType::AlignRight) + { + if (view.size() >= 2 && (view[0] == '0' && view[1] == 'x')) + { + view = view.substr(2); + } + + if (type == FromHex) [[likely]] + { + if ((view.size() > static_cast(N * 2)) || + (view.size() % 2 != 0)) [[unlikely]] + { + BOOST_THROW_EXCEPTION(std::invalid_argument{"Invalid input string!"}); + } + + if (view.size() != N) + {} + + auto startIndex = 0; + if (_alignType == DataAlignType::AlignRight) [[likely]] + { + startIndex = N - (view.size() / 2); + std::fill(m_data.begin(), m_data.begin() + startIndex, 0); + } + else + { + auto endIndex = view.size() / 2; + std::fill(m_data.begin() + endIndex, m_data.end(), 0); + } + boost::algorithm::unhex(view.begin(), view.end(), m_data.begin() + startIndex); + } + else + { + constructFixedBytes( + bytesConstRef((const bcos::byte*)view.data(), view.size()), _alignType); + } + } + + /** + * @brief Construct a new Fixed Bytes object from the given FixedBytes according to the align + * type + * + * @tparam M: the size of the given FixedBytes + * @param _fixedBytes: the given FixedBytes that used to construct the new FixedBytes + * @param _alignType: the align type, support AlignLeft/AlignRight now + */ + template + explicit FixedBytes( + FixedBytes const& _fixedBytes, DataAlignType _alignType = DataAlignType::AlignLeft) + { + m_data.fill(0); + constructFixedBytes(_fixedBytes.ref(), _alignType); + } + + /** + * @brief Construct a new Fixed Bytes object according to the given number + * @param _arithNumber: the number used to construct the new FixedBytes + */ + FixedBytes(ArithType const& _arithNumber) : FixedBytes() { toBigEndian(_arithNumber, m_data); } + FixedBytes(unsigned _arithNumber) : FixedBytes() { toBigEndian(_arithNumber, m_data); } + + explicit FixedBytes(byte const* _data, size_t _dataSize) : FixedBytes() + { + memcpy(m_data.data(), _data, (std::min)(_dataSize, (size_t)N)); + } + + explicit FixedBytes(byte const* _bs, ConstructorType) : FixedBytes() + { + memcpy(m_data.data(), _bs, N); + } + + /// Explicitly construct, copying from a string. + explicit FixedBytes(std::string const& _s, StringDataType _t = FromHex, + DataAlignType _alignType = DataAlignType::AlignRight) + : FixedBytes(_t == FromHex ? *fromHexString(_s) : bcos::asBytes(_s), _alignType) + {} + + // Convert to Arith type. + operator ArithType() const { return fromBigEndian(m_data); } + explicit operator bool() const + { + return std::any_of(m_data.begin(), m_data.end(), [](byte _b) { return _b != 0; }); + } + bool operator==(FixedBytes const& _comparedFixedBytes) const + { + return m_data == _comparedFixedBytes.m_data; + } + bool operator!=(FixedBytes const& _comparedFixedBytes) const + { + return m_data != _comparedFixedBytes.m_data; + } + bool operator<(FixedBytes const& _comparedFixedBytes) const + { + for (unsigned index = 0; index < N; index++) + { + if (m_data[index] < _comparedFixedBytes[index]) + { + return true; + } + else if (m_data[index] > _comparedFixedBytes[index]) + { + return false; + } + } + return false; + } + bool operator>=(FixedBytes const& _comparedFixedBytes) const + { + return !operator<(_comparedFixedBytes); + } + bool operator<=(FixedBytes const& _comparedFixedBytes) const + { + return operator==(_comparedFixedBytes) || operator<(_comparedFixedBytes); + } + bool operator>(FixedBytes const& _comparedFixedBytes) const + { + return !operator<=(_comparedFixedBytes); + } + FixedBytes& operator^=(FixedBytes const& _rightFixedBytes) + { + for (unsigned index = 0; index < N; index++) + { + m_data[index] ^= _rightFixedBytes.m_data[index]; + } + return *this; + } + FixedBytes operator^(FixedBytes const& _rightFixedBytes) const + { + return FixedBytes(*this) ^= _rightFixedBytes; + } + FixedBytes& operator|=(FixedBytes const& _rightFixedBytes) + { + for (unsigned index = 0; index < N; index++) + { + m_data[index] |= _rightFixedBytes.m_data[index]; + } + return *this; + } + FixedBytes operator|(FixedBytes const& _rightFixedBytes) const + { + return FixedBytes(*this) |= _rightFixedBytes; + } + FixedBytes& operator&=(FixedBytes const& _rightFixedBytes) + { + for (unsigned index = 0; index < N; index++) + { + m_data[index] &= _rightFixedBytes.m_data[index]; + } + return *this; + } + FixedBytes operator&(FixedBytes const& _rightFixedBytes) const + { + return FixedBytes(*this) &= _rightFixedBytes; + } + FixedBytes operator~() const + { + FixedBytes result; + for (unsigned index = 0; index < N; index++) + { + result[index] = ~m_data[index]; + } + return result; + } + // @returns a specified byte from the FixedBytes + byte& operator[](unsigned _index) { return m_data[_index]; } + byte operator[](unsigned _index) const { return m_data[_index]; } + + // @returns an abridged version of the hash as a user-readable hex string + std::string abridged() const { return *toHexString(ref().getCroppedData(0, 4)) + "..."; } + /// @returns the hash as a user-readable hex string. + std::string hex() const { return *toHexString(ref()); } + /// @returns the hash as a user-readable hex string with 0x perfix. + std::string hexPrefixed() const { return toHexStringWithPrefix(ref()); } + + /// @returns a mutable byte RefDataContainer to the object's data. + bytesRef ref() { return bytesRef(m_data.data(), N); } + /// @returns a constant byte RefDataContainer to the object's data. + bytesConstRef ref() const { return bytesConstRef(m_data.data(), N); } + /// @returns a mutable byte pointer to the object's data. + byte* data() { return m_data.data(); } + /// @returns a constant byte pointer to the object's data. + byte const* data() const { return m_data.data(); } + + /// @returns begin iterator. + auto begin() const -> typename std::array::const_iterator { return m_data.begin(); } + /// @returns end iterator. + auto end() const -> typename std::array::const_iterator { return m_data.end(); } + /// @returns a copy of the object's data as a byte vector. + bytes asBytes() const { return bytes(data(), data() + N); } + + /// Populate with random data. + template + void generateRandomFixedBytesByEngine(Engine& _eng) + { + std::uniform_int_distribution dis(0, 255); + for (auto& element : m_data) + { + element = dis(_eng); + } + } + + /// @returns a random valued object. + static FixedBytes generateRandomFixedBytes() + { + FixedBytes randomFixedBytes; + randomFixedBytes.generateRandomFixedBytesByEngine(s_fixedBytesEngine); + return randomFixedBytes; + } + + struct hash + { + /// Make a hash of the object's data. + size_t operator()(FixedBytes const& _value) const + { + return boost::hash_range(_value.m_data.cbegin(), _value.m_data.cend()); + } + }; + + template + inline FixedBytes& shiftBloom(FixedBytes const& _FixedBytes) + { + return (*this |= _FixedBytes.template bloomPart()); + } + + template + inline FixedBytes bloomPart() const + { + unsigned const c_bloomBits = M * 8; + unsigned const c_mask = c_bloomBits - 1; + unsigned const c_bloomBytes = (StaticLog2::result + 7) / 8; + + static_assert((M & (M - 1)) == 0, "M must be power-of-two"); + static_assert(P * c_bloomBytes <= N, "out of range"); + + FixedBytes ret; + byte const* p = data(); + for (unsigned i = 0; i < P; ++i) + { + unsigned index = 0; + for (unsigned j = 0; j < c_bloomBytes; ++j, ++p) + index = (index << 8) | *p; + index &= c_mask; + ret[M - 1 - index / 8] |= (1 << (index % 8)); + } + return ret; + } + + /// Returns the index of the first bit set to one, or size() * 8 if no bits are set. + inline unsigned firstBitSet() const + { + unsigned ret = 0; + for (auto d : m_data) + { + if (d) + { + for (;; ++ret, d <<= 1) + { + if (d & 0x80) + { + return ret; + } + } + } + else + { + ret += 8; + } + } + return ret; + } + + auto begin() { return m_data.begin(); } + auto end() { return m_data.end(); } + auto size() const { return SIZE; } + void clear() { m_data.fill(0); } + +private: + void constructFixedBytes(bytesConstRef _bytesData, DataAlignType _alignType) + { + m_data.fill(0); + auto copyDataSize = (std::min)((unsigned)_bytesData.size(), N); + switch (_alignType) + { + case DataAlignType::AlignLeft: + { + memcpy(m_data.data(), _bytesData.data(), copyDataSize); + break; + } + case DataAlignType::AlignRight: + { + auto startIndex = N - copyDataSize; + memcpy(m_data.data() + startIndex, _bytesData.data(), copyDataSize); + break; + } + case DataAlignType::AcquireEqual: + { + if (_bytesData.size() != N) + { + BCOS_LOG(WARNING) << LOG_DESC("ConstructFixedBytesFailed") + << LOG_KV("requiredLen", N) + << LOG_KV("dataLen", _bytesData.size()); + BOOST_THROW_EXCEPTION(ConstructFixedBytesFailed() << errinfo_comment( + "Require " + std::to_string(N) + " length input data")); + } + memcpy(m_data.data(), _bytesData.data(), N); + break; + } + } + } + +private: + std::array m_data; ///< The binary data. +}; + +template +class SecureFixedBytes : private FixedBytes +{ +public: + using DataAlignType = typename FixedBytes::DataAlignType; + using StringDataType = typename FixedBytes::StringDataType; + using ConstructorType = typename FixedBytes::ConstructorType; + SecureFixedBytes() = default; + explicit SecureFixedBytes( + bytesConstRef _fixedBytesRef, DataAlignType _alignType = DataAlignType::AlignRight) + : FixedBytes(_fixedBytesRef, _alignType) + {} + + template + explicit SecureFixedBytes( + FixedBytes const& _fixedBytes, DataAlignType _alignType = DataAlignType::AlignLeft) + : FixedBytes(_fixedBytes, _alignType) + {} + template + explicit SecureFixedBytes(SecureFixedBytes const& _secureFixedBytes, + DataAlignType _alignType = DataAlignType::AlignLeft) + : FixedBytes(_secureFixedBytes.makeInsecure(), _alignType) + {} + explicit SecureFixedBytes(byte const* _bytesPtr, ConstructorType _type) + : FixedBytes(_bytesPtr, _type) + {} + explicit SecureFixedBytes(std::string const& _stringData, + StringDataType _stringType = FixedBytes::FromHex, + DataAlignType _alignType = DataAlignType::AlignRight) + : FixedBytes(_stringData, _stringType, _alignType) + {} + SecureFixedBytes(SecureFixedBytes const& _c) = default; + ~SecureFixedBytes() { ref().cleanMemory(); } + SecureFixedBytes& operator=(SecureFixedBytes const& _secureFixedBytes) + { + if (&_secureFixedBytes == this) + { + return *this; + } + ref().cleanMemory(); + FixedBytes::operator=(static_cast const&>(_secureFixedBytes)); + return *this; + } + using FixedBytes::size; + + FixedBytes const& makeInsecure() const { return static_cast const&>(*this); } + FixedBytes& writable() + { + clear(); + return static_cast&>(*this); + } + + using FixedBytes::operator bool; + + // The obvious comparison operators. + bool operator==(SecureFixedBytes const& _c) const + { + return static_cast const&>(*this).operator==( + static_cast const&>(_c)); + } + bool operator!=(SecureFixedBytes const& _c) const + { + return static_cast const&>(*this).operator!=( + static_cast const&>(_c)); + } + bool operator<(SecureFixedBytes const& _c) const + { + return static_cast const&>(*this).operator<( + static_cast const&>(_c)); + } + bool operator>=(SecureFixedBytes const& _c) const + { + return static_cast const&>(*this).operator>=( + static_cast const&>(_c)); + } + bool operator<=(SecureFixedBytes const& _c) const + { + return static_cast const&>(*this).operator<=( + static_cast const&>(_c)); + } + bool operator>(SecureFixedBytes const& _c) const + { + return static_cast const&>(*this).operator>( + static_cast const&>(_c)); + } + + using FixedBytes::operator==; + using FixedBytes::operator!=; + using FixedBytes::operator<; + using FixedBytes::operator>=; + using FixedBytes::operator<=; + using FixedBytes::operator>; + + // The obvious binary operators. + SecureFixedBytes& operator^=(FixedBytes const& _c) + { + static_cast&>(*this).operator^=(_c); + return *this; + } + SecureFixedBytes operator^(FixedBytes const& _c) const + { + return SecureFixedBytes(*this) ^= _c; + } + SecureFixedBytes& operator|=(FixedBytes const& _c) + { + static_cast&>(*this).operator^=(_c); + return *this; + } + SecureFixedBytes operator|(FixedBytes const& _c) const + { + return SecureFixedBytes(*this) |= _c; + } + SecureFixedBytes& operator&=(FixedBytes const& _c) + { + static_cast&>(*this).operator^=(_c); + return *this; + } + SecureFixedBytes operator&(FixedBytes const& _c) const + { + return SecureFixedBytes(*this) &= _c; + } + + SecureFixedBytes& operator^=(SecureFixedBytes const& _c) + { + static_cast&>(*this).operator^=(static_cast const&>(_c)); + return *this; + } + SecureFixedBytes operator^(SecureFixedBytes const& _c) const + { + return SecureFixedBytes(*this) ^= _c; + } + SecureFixedBytes& operator|=(SecureFixedBytes const& _c) + { + static_cast&>(*this).operator^=(static_cast const&>(_c)); + return *this; + } + SecureFixedBytes operator|(SecureFixedBytes const& _c) const + { + return SecureFixedBytes(*this) |= _c; + } + SecureFixedBytes& operator&=(SecureFixedBytes const& _c) + { + static_cast&>(*this).operator^=(static_cast const&>(_c)); + return *this; + } + SecureFixedBytes operator&(SecureFixedBytes const& _c) const + { + return SecureFixedBytes(*this) &= _c; + } + SecureFixedBytes operator~() const + { + auto r = ~static_cast const&>(*this); + return static_cast(r); + } + + using FixedBytes::abridged; + + bytesConstRef ref() const { return FixedBytes::ref(); } + byte const* data() const { return FixedBytes::data(); } + + static SecureFixedBytes generateRandomFixedBytes() + { + SecureFixedBytes randomFixedBytes; + randomFixedBytes.generateRandomFixedBytesByEngine(s_fixedBytesEngine); + return randomFixedBytes; + } + using FixedBytes::firstBitSet; + + void clear() { ref().cleanMemory(); } +}; + +/// Fast equality operator for h256. +template <> +inline bool FixedBytes<32>::operator==(FixedBytes<32> const& _other) const +{ + const uint64_t* hash1 = (const uint64_t*)data(); + const uint64_t* hash2 = (const uint64_t*)_other.data(); + return (hash1[0] == hash2[0]) && (hash1[1] == hash2[1]) && (hash1[2] == hash2[2]) && + (hash1[3] == hash2[3]); +} + +/// Fast std::hash compatible hash function object for h256. +template <> +inline size_t FixedBytes<32>::hash::operator()(FixedBytes<32> const& value) const +{ + uint64_t const* data = reinterpret_cast(value.data()); + return boost::hash_range(data, data + 4); +} + +/// Stream I/O for the FixedBytes class. +template +inline std::ostream& operator<<(std::ostream& _out, FixedBytes const& _h) +{ + _out << *toHexString(_h); + return _out; +} + +template +inline std::istream& operator>>(std::istream& _in, FixedBytes& o_h) +{ + std::string s; + _in >> s; + o_h = FixedBytes(s, FixedBytes::FromHex, FixedBytes::AlignRight); + return _in; +} + +/// Stream I/O for the SecureFixedBytes class. +template +inline std::ostream& operator<<(std::ostream& _out, SecureFixedBytes const& _h) +{ + _out << "SecureFixedBytes#" << std::hex << typename FixedBytes::hash()(_h.makeInsecure()) + << std::dec; + return _out; +} + +// Common types of FixedBytes. +using h2048 = FixedBytes<256>; +using h1024 = FixedBytes<128>; +using h520 = FixedBytes<65>; +using h512 = FixedBytes<64>; +using h256 = FixedBytes<32>; +using h160 = FixedBytes<20>; +using h128 = FixedBytes<16>; +using h64 = FixedBytes<8>; +using h512s = std::vector; +using h256s = std::vector; +using h160s = std::vector; +using h256Set = std::set; +using h160Set = std::set; +using h256Hash = std::unordered_set; +using h160Hash = std::unordered_set; + +using Address = h160; +/// A vector of addresses. +using Addresses = h160s; +/// A hash set of addresses. +using AddressHash = std::unordered_set; +Address const ZeroAddress; + +/// Convert the given value into h160 (160-bit unsigned integer) using the right 20 bytes. +inline h160 right160(h256 const& _t) +{ + h160 ret; + memcpy(ret.data(), _t.data() + 12, 20); + return ret; +} + +/// Convert the given value into h160 (160-bit unsigned integer) using the left 20 bytes. +inline h160 left160(h256 const& _t) +{ + h160 ret; + memcpy(&ret[0], _t.data(), 20); + return ret; +} + +inline std::string toString(h256s const& _bs) +{ + std::ostringstream out; + out << "[ "; + for (h256 const& i : _bs) + out << i.abridged() << ", "; + out << "]"; + return out.str(); +} +// Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash. +// Currently we just pull out the right (low-order in BE) 160-bits. +inline Address asAddress(u256 _item) +{ + return right160(h256(_item)); +} + +inline u256 fromAddress(Address _a) +{ + return (u160)_a; +} + +inline Address toAddress(std::string const& _address) +{ + auto address = fromHexString(_address); + if (address->size() == 20) + { + return Address(*address); + } + BOOST_THROW_EXCEPTION(InvalidAddress()); +} +} // namespace bcos + +namespace std +{ +template <> +struct hash : bcos::h160::hash +{ +}; +template <> +struct hash : bcos::h256::hash +{ +}; +template <> +struct hash : bcos::h512::hash +{ +}; +} // namespace std diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/IOServicePool.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/IOServicePool.h" new file mode 100644 index 00000000..f5f065f5 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/IOServicePool.h" @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2021 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 + * m_limitations under the License. + * + * @file IOServicePool.h + * @date 2022-06-14 + */ + +#pragma once +#include +#include +namespace bcos +{ +class IOServicePool +{ +public: + using Ptr = std::shared_ptr; + + using IOService = boost::asio::io_context; + using ExecutorType = boost::asio::io_context::executor_type; + using Work = boost::asio::executor_work_guard; + using WorkPtr = std::unique_ptr; + IOServicePool(size_t _workerNum = std::thread::hardware_concurrency()) + : m_works(_workerNum), m_nextIOService(0) + { + // create the ioservices + for (size_t i = 0; i < _workerNum; i++) + { + m_ioServices.emplace_back(std::make_shared()); + } + } + + IOServicePool(const IOServicePool&) = delete; + IOServicePool& operator=(const IOServicePool&) = delete; + + void start() + { + if (m_running) + { + return; + } + m_running = true; + for (size_t i = 0; i < m_ioServices.size(); ++i) + { + m_works[i] = std::unique_ptr(new Work(m_ioServices[i]->get_executor())); + } + + // one io_context per thread + for (size_t i = 0; i < m_ioServices.size(); ++i) + { + auto ioService = m_ioServices[i]; + m_threads.emplace_back([ioService]() { ioService->run(); }); + } + } + + std::shared_ptr getIOService() + { + auto selectedIoService = (m_nextIOService % m_ioServices.size()); + m_nextIOService++; + return m_ioServices.at(selectedIoService); + } + + void stop() + { + if (!m_running) + { + return; + } + m_running = false; + // stop the io service + for (auto& ioService : m_ioServices) + { + ioService->stop(); + } + // stop the work + for (auto& work : m_works) + { + work.reset(); + } + // stop the thread + for (auto& thread : m_threads) + { + if (thread.get_id() != std::this_thread::get_id()) + { + thread.join(); + } + else + { + thread.detach(); + } + } + } + +private: + std::vector> m_ioServices; + std::vector m_works; + std::vector m_threads; + size_t m_nextIOService; + bool m_running = false; +}; +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/JsonDataConvertUtility.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/JsonDataConvertUtility.h" new file mode 100644 index 00000000..7de1c71c --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/JsonDataConvertUtility.h" @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2021 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 JsonDataConvertUtility.h + */ + +#pragma once +#include "DataConvertUtility.h" +#include "Exceptions.h" +#include "FixedBytes.h" +#include + +namespace bcos +{ +inline std::string toJonString(byte _data) +{ + return "0x" + std::to_string(_data); +} +inline std::string toJonString(bytes const& _bytesData) +{ + return toHexStringWithPrefix(_bytesData); +} +template +std::string toJonString(boost::multiprecision::number> const& + _arithNumber) +{ + auto hexString = toHexString(toCompactBigEndian(_arithNumber, 1)); + return "0x" + ((*hexString)[0] == '0' ? (hexString->substr(1)) : *hexString); +} + +template +std::string toJonString(SecureFixedBytes const& _i) +{ + std::stringstream stream; + stream << "0x" << _i.makeInsecure().hex(); + return stream.str(); +} + +template +std::string toJonString(T const& _i) +{ + std::stringstream stream; + stream << "0x" << std::hex << _i; + return stream.str(); +} + +bytes jonStringToBytes(std::string const& _stringData) +{ + return *fromHexString(_stringData); +} + +template +FixedBytes jonStringToFixedBytes(std::string const& _s) +{ + if (_s.substr(0, 2) == "0x") + // Hex + return FixedBytes(_s.substr(2 + std::max(N * 2, (unsigned)(_s.size() - 2)) - N * 2)); + else if (_s.find_first_not_of("0123456789") == std::string::npos) + // Decimal + return (typename FixedBytes::ArithType)(_s); + else + // Binary + return FixedBytes(); +} + +template +boost::multiprecision::number> +jsonStringToInt(std::string const& _s) +{ + // Hex + if (_s.substr(0, 2) == "0x") + { + return fromBigEndian>>(*fromHexString(_s.substr(2))); + } + // Decimal + else if (_s.find_first_not_of("0123456789") == std::string::npos) + { + return boost::multiprecision::number>(_s); + } + else + { + BOOST_THROW_EXCEPTION( + bcos::BadCast() << errinfo_comment("can't convert " + _s + " to int")); + } +} + +inline u256 jonStringToU256(std::string const& _s) +{ + return jsonStringToInt<32>(_s); +} + +/// Convert a string representation of a number to an int +/// String can be a normal decimal number, or a hex prefixed by 0x or 0X, or an octal if prefixed by +/// 0 Returns 0 in case of failure +inline int64_t jsonStringToInt(std::string const& _s) +{ + return int64_t(jsonStringToInt<8>(_s)); +} + +inline Address jsonStringToAddress(std::string const& _s) +{ + return toAddress(_s); +} +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/Log.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Log.h" new file mode 100644 index 00000000..97dcf6a2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Log.h" @@ -0,0 +1,3 @@ +#pragma once + +#include "BoostLog.h" diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/Ranges.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Ranges.h" new file mode 100644 index 00000000..35a963d2 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Ranges.h" @@ -0,0 +1,9 @@ +#pragma once + +#ifdef USE_STD_RANGES +#include +namespace RANGES = ::std::ranges; +#else +#include +namespace RANGES = ::ranges::cpp20; +#endif \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/RefDataContainer.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/RefDataContainer.h" new file mode 100644 index 00000000..b950504d --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/RefDataContainer.h" @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2021 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. + * + * @brief: manager the STL data without copy overhead + * @file RefDataContainer.h + * @date 2021-02-24 + */ +#pragma once +#include +#include +#include +#include +#include + +namespace bcos +{ + +template +class RefDataContainer +{ +public: + RefDataContainer() = default; + RefDataContainer(T* _data, size_t _count) : m_dataPointer(_data), m_dataCount(_count) {} + using RequiredStringPointerType = + std::conditional::value, std::string const*, std::string*>; + using RequiredVecType = std::conditional::value, + std::vector::type> const*, std::vector*>; + using RequiredStringRefType = + std::conditional::value, std::string const&, std::string&>; + + RefDataContainer(typename RequiredStringPointerType::type _data) + : m_dataPointer(reinterpret_cast(_data->data())), m_dataCount(_data->size() / sizeof(T)) + {} + + RefDataContainer(typename RequiredVecType::type _data) + : m_dataPointer(reinterpret_cast(_data->data())), m_dataCount(_data->size()) + {} + + RefDataContainer(typename RequiredStringRefType::type _data) + : m_dataPointer(reinterpret_cast(_data.data())), m_dataCount(_data.size() / sizeof(T)) + {} + + operator RefDataContainer() const + { + return RefDataContainer(m_dataPointer, m_dataCount); + } + + explicit operator bool() { return m_dataPointer && m_dataCount; } + + T& operator[](size_t _index) + { + assert(_index < m_dataCount); + return m_dataPointer[_index]; + } + + T const& operator[](size_t _index) const + { + assert(_index < m_dataCount); + return m_dataPointer[_index]; + } + + bool operator==(RefDataContainer _comparedContainer) const + { + return (data() == _comparedContainer.data()) && (size() == _comparedContainer.size()); + } + + bool operator!=(RefDataContainer _comparedContainer) const + { + return !(operator==(_comparedContainer)); + } + + T* data() const { return m_dataPointer; } + T* begin() const { return m_dataPointer; } + T* end() const { return m_dataPointer + m_dataCount; } + + size_t count() const { return m_dataCount; } + size_t size() const { return m_dataCount; } + std::vector toBytes() const + { + unsigned const char* dataPointer = reinterpret_cast(m_dataPointer); + return std::vector(dataPointer, dataPointer + m_dataCount * sizeof(T)); + } + + std::string toString() const + { + return std::string( + (char const*)m_dataPointer, ((char const*)m_dataPointer) + m_dataCount * sizeof(T)); + } + + bool empty() { return (m_dataCount == 0); } + RefDataContainer getCroppedData(size_t _startIndex, size_t _count) const + { + if (m_dataPointer && _startIndex <= m_dataCount && _count <= m_dataCount && + _startIndex + _count <= m_dataCount) + { + return RefDataContainer(m_dataPointer + _startIndex, _count); + } + return RefDataContainer(); + } + + /** + * @brief Get the Cropped Data object + * + * @param _startIndex + * @return RefDataContainer + */ + RefDataContainer getCroppedData(size_t _startIndex) const + { + if (m_dataPointer && m_dataCount >= _startIndex) + { + return getCroppedData(_startIndex, (m_dataCount - _startIndex)); + } + return RefDataContainer(); + } + + bool dataOverlap(RefDataContainer _dataContainer) + { + // data overlap + if (begin() < _dataContainer.end() && _dataContainer.begin() < end()) + { + return true; + } + return false; + } + + // populate new RefDataContainer + void populate(RefDataContainer _dataContainer) + { + // data overlap + if (dataOverlap(_dataContainer)) + { + memmove((void*)_dataContainer.data(), (void*)data(), + (std::min)(_dataContainer.count(), count()) * sizeof(T)); + } + else + { + memcpy((void*)_dataContainer.data(), (void*)data(), + (std::min)(_dataContainer.count(), count()) * sizeof(T)); + } + // reset the remaining data to 0 + if (_dataContainer.count() > count()) + { + memset((void*)(_dataContainer.data() + count() * sizeof(T)), 0, + (_dataContainer.count() - count()) * sizeof(T)); + } + } + + void reset() + { + m_dataPointer = nullptr; + m_dataCount = 0; + } + + void retarget(T* _newDataPointer, size_t _newSize) + { + m_dataPointer = _newDataPointer; + m_dataCount = _newSize; + } + + void cleanMemory() + { + static std::atomic s_cleanCounter{0u}; + uint8_t* p = (uint8_t*)begin(); + size_t const len = (uint8_t*)end() - p; + size_t loop = len; + size_t count = s_cleanCounter; + while (loop--) + { + *(p++) = (uint8_t)count; + count += (17 + ((size_t)p & 0xf)); + } + p = (uint8_t*)memchr((uint8_t*)begin(), (uint8_t)count, len); + if (p) + count += (63 + (size_t)p); + s_cleanCounter = (uint8_t)count; + memset((uint8_t*)begin(), 0, len); + } + +private: + T* m_dataPointer = nullptr; + size_t m_dataCount = 0; +}; + +template +RefDataContainer ref(T& _data) +{ + return RefDataContainer(_data.data(), _data.size()); +} + +template +RefDataContainer ref(std::vector& _data) +{ + return RefDataContainer(_data.data(), _data.size()); +} + +template +RefDataContainer ref(std::vector const& _data) +{ + return RefDataContainer(_data.data(), _data.size()); +} +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/ThreadPool.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/ThreadPool.h" new file mode 100644 index 00000000..fcaf85fa --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/ThreadPool.h" @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2021 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. + * + * @brief: threadpool that can execute tasks asyncly + * + * @file ThreadPool.h + * @author: yujiechen + * @date 2021-02-26 + */ + +#pragma once +#include "Common.h" +#include +#include +#include +#include + +namespace bcos +{ +class ThreadPool +{ +public: + typedef std::shared_ptr Ptr; + + explicit ThreadPool(const std::string& threadName, size_t size) : m_work(_ioService) + { + _threadName = threadName; + + for (size_t i = 0; i < size; ++i) + { + _workers.create_thread([this] { + bcos::pthread_setThreadName(_threadName); + _ioService.run(); + }); + } + } + void stop() + { + _ioService.stop(); + if (!_workers.is_this_thread_in()) + { + _workers.join_all(); + } + } + + ~ThreadPool() { stop(); } + + // Add new work item to the pool. + template + void enqueue(F f) + { + _ioService.post(f); + } + + bool hasStopped() { return _ioService.stopped(); } + +private: + std::string _threadName; + boost::thread_group _workers; + boost::asio::io_service _ioService; + // m_work ensures that io_service's run() function will not exit while work is underway + boost::asio::io_service::work m_work; +}; + +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/Timer.cpp" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Timer.cpp" new file mode 100644 index 00000000..1370c23c --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Timer.cpp" @@ -0,0 +1,117 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for Timer + * @file Timer.cpp + * @author: yujiechen + * @date 2021-04-26 + */ +#include "Timer.h" +#include "Common.h" +#include "Log.h" +#include + +using namespace bcos; + +void Timer::start() +{ + if (!m_working) + { + return; + } + try + { + startTimer(); + } + catch (std::exception const& e) + { + BCOS_LOG(WARNING) << LOG_DESC("startTimer exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } +} + +void Timer::startTimer() +{ + if (m_running || !m_timer) + { + return; + } + m_timer->expires_from_now(std::chrono::milliseconds(adjustTimeout())); + auto timer = std::weak_ptr(shared_from_this()); + // calls the timeout handler + m_timer->async_wait([timer](const boost::system::error_code& error) { + // the timer has been cancelled + if (error == boost::asio::error::operation_aborted) + { + return; + } + if (error) + { + BCOS_LOG(WARNING) << LOG_DESC("Timer async_wait error") << LOG_KV("error", error); + return; + } + try + { + auto t = timer.lock(); + if (!t) + { + return; + } + t->run(); + } + catch (std::exception const& e) + { + BCOS_LOG(WARNING) << LOG_DESC("calls timeout handler failed") + << LOG_KV("errorInfo", boost::diagnostic_information(e)); + } + }); + m_running = true; +} + +// stop the timer +void Timer::stop() +{ + if (!m_working || !m_timer) + { + return; + } + if (!m_running) + { + return; + } + m_running = false; + // cancel the timer + m_timer->cancel(); +} + +void Timer::destroy() +{ + if (!m_working || !m_worker) + { + return; + } + m_working = false; + stop(); + m_ioService->stop(); + if (m_worker->get_id() != std::this_thread::get_id()) + { + m_worker->join(); + m_worker.reset(); + } + else + { + m_worker->detach(); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/Timer.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Timer.h" new file mode 100644 index 00000000..d44c2701 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Timer.h" @@ -0,0 +1,113 @@ +/** + * Copyright (C) 2021 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. + * + * @brief implementation for Timer + * @file Timer.h + * @author: yujiechen + * @date 2021-04-26 + */ +#pragma once +#include "Common.h" +#include +#include +namespace bcos +{ +class Timer : public std::enable_shared_from_this +{ +public: + explicit Timer(uint64_t _timeout, std::string const& _threadName = "timer") + : m_timeout(_timeout), + m_ioService(std::make_shared()), + m_timer(std::make_shared(*m_ioService)), + m_work(*m_ioService), + m_threadName(_threadName) + { + m_working = true; + m_worker.reset(new std::thread([&]() { + bcos::pthread_setThreadName(m_threadName); + while (m_working) + { + try + { + m_ioService->run(); + } + catch (std::exception const& e) + { + BCOS_LOG(WARNING) << LOG_DESC("Exception in Worker Thread of timer") + << LOG_KV("error", boost::diagnostic_information(e)); + } + m_ioService->reset(); + } + })); + } + + virtual ~Timer() { destroy(); } + + virtual void destroy(); + virtual void start(); + virtual void stop(); + virtual void restart() + { + if (!m_working) + { + return; + } + stop(); + start(); + } + + virtual void reset(uint64_t _timeout) + { + m_timeout = _timeout; + restart(); + } + + virtual bool running() { return m_running; } + virtual int64_t timeout() { return m_timeout; } + + virtual void registerTimeoutHandler(std::function _timeoutHandler) + { + m_timeoutHandler = _timeoutHandler; + } + +protected: + virtual void startTimer(); + + // invoked everytime when it reaches the timeout + virtual void run() + { + if (m_timeoutHandler) + { + m_timeoutHandler(); + } + } + // adjust the timeout + virtual uint64_t adjustTimeout() { return m_timeout; } + std::atomic m_timeout = {0}; + + std::atomic_bool m_running = {false}; + std::atomic_bool m_working = {false}; + + std::shared_ptr m_ioService; + std::shared_ptr m_timer; + std::unique_ptr m_worker; + + std::function m_timeoutHandler; + // m_work ensures that io_service's run() function will not exit while work is underway + boost::asio::io_service::work m_work; + + std::string m_threadName; +}; +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/Worker.cpp" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Worker.cpp" new file mode 100644 index 00000000..381352ea --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Worker.cpp" @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2021 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 Worker.cpp + */ +#include "Worker.h" + +#if defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN32_) +#include +#else +#include +#endif + +#include +#include +#include +#include +using namespace std; +using namespace bcos; + +void setThreadName(const char* _threadName) +{ +#if defined(__GLIBC__) + pthread_setname_np(pthread_self(), _threadName); +#elif defined(__APPLE__) + pthread_setname_np(_threadName); +#endif +} + +void Worker::startWorking() +{ + boost::unique_lock l(x_work); + if (m_workerThread) + { + WorkerState workerState = WorkerState::Stopped; + m_workerState.compare_exchange_strong(workerState, WorkerState::Starting); + m_workerStateNotifier.notify_all(); + } + else + { + m_workerState = WorkerState::Starting; + m_workerStateNotifier.notify_all(); + m_workerThread.reset(new thread([&]() { + setThreadName(m_threadName.c_str()); + while (m_workerState != WorkerState::Killing) + { + WorkerState ex = WorkerState::Starting; + { + // the condition variable-related lock + boost::unique_lock l(x_work); + m_workerState = WorkerState::Started; + } + + m_workerStateNotifier.notify_all(); + + try + { + initWorker(); + workerProcessLoop(); + finishWorker(); + } + catch (std::exception const& e) + { + BCOS_LOG(WARNING) << LOG_DESC("Exception thrown in Worker thread") + << LOG_KV("threadName", m_threadName) + << LOG_KV("errorMsg", boost::diagnostic_information(e)); + } + + { + // the condition variable-related lock + boost::unique_lock l(x_work); + ex = m_workerState.exchange(WorkerState::Stopped); + if (ex == WorkerState::Killing || ex == WorkerState::Starting) + m_workerState.exchange(ex); + } + m_workerStateNotifier.notify_all(); + + { + boost::unique_lock l(x_work); + TIME_RECORD("Worker stopping"); + while (m_workerState == WorkerState::Stopped) + m_workerStateNotifier.wait_for(l, boost::chrono::milliseconds(100)); + } + } + })); + } + + TIME_RECORD("Start worker"); + while (m_workerState == WorkerState::Starting) + m_workerStateNotifier.wait_for(l, boost::chrono::milliseconds(100)); +} + +void Worker::stopWorking() +{ + boost::unique_lock l(x_work); + if (m_workerThread) + { + WorkerState ex = WorkerState::Started; + if (!m_workerState.compare_exchange_strong(ex, WorkerState::Stopping)) + return; + m_workerStateNotifier.notify_all(); + TIME_RECORD("Stop worker"); + while (m_workerState != WorkerState::Stopped) + { + m_workerStateNotifier.wait_for(l, boost::chrono::milliseconds(100)); + } + } +} + +void Worker::terminate() +{ + boost::unique_lock l(x_work); + if (m_workerThread) + { + if (m_workerState.exchange(WorkerState::Killing) == WorkerState::Killing) + return; // Somebody else is doing this + l.unlock(); + m_workerStateNotifier.notify_all(); + TIME_RECORD("Terminate worker"); + m_workerThread->join(); + + l.lock(); + m_workerThread.reset(); + } +} + +void Worker::workerProcessLoop() +{ + while (m_workerState == WorkerState::Started) + { + if (m_idleWaitMs) + this_thread::sleep_for(chrono::milliseconds(m_idleWaitMs)); + executeWorker(); + } +} diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/Worker.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Worker.h" new file mode 100644 index 00000000..62326f06 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/Worker.h" @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2021 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 Worker.h + */ + +#pragma once + +#include "Common.h" +#include +#include +#include + +namespace bcos +{ +enum class WorkerState +{ + Starting, + Started, + Stopping, + Stopped, + Killing +}; + +class Worker +{ +protected: + Worker(std::string const& _threadName = "worker", unsigned _idleWaitMs = 30) + : m_threadName(_threadName), m_idleWaitMs(_idleWaitMs) + {} + virtual ~Worker() { terminate(); } + + /** + * @brief Set thread name for the worker + * @param _threadName: the thread name + */ + void setName(std::string const& _threadName) + { + if (!isWorking()) + m_threadName = _threadName; + } + + std::string const& threadName() const { return m_threadName; } + + // Starts worker thread by calling startedWorking + void startWorking(); + + // Stop worker thread + void stopWorking(); + + // Return true if the worker thread is working + bool isWorking() const + { + boost::unique_lock l(x_work); + return m_workerState == WorkerState::Started; + } + + // Called after thread is started from startWorking to init the worker + virtual void initWorker() {} + + // worker execute logic (Called continuously following sleep for m_idleWaitMs) + virtual void executeWorker() {} + + // schedule the task inner a loop before stop the worker thread + virtual void workerProcessLoop(); + bool shouldStop() const { return m_workerState != WorkerState::Started; } + + // Called when is to be stopped, just prior to thread being joined. + virtual void finishWorker() {} + // stop the worker + void terminate(); + + std::atomic& workerState() { return m_workerState; } + unsigned idleWaitMs() { return m_idleWaitMs; } + +private: + std::string m_threadName; + + unsigned m_idleWaitMs = 0; + + mutable boost::mutex x_work; + // the worker thread + std::unique_ptr m_workerThread; + // Notification when m_workerState changes + mutable boost::condition_variable m_workerStateNotifier; + std::atomic m_workerState = {WorkerState::Starting}; +}; + +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/ZstdCompress.cpp" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/ZstdCompress.cpp" new file mode 100644 index 00000000..22d39902 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/ZstdCompress.cpp" @@ -0,0 +1,94 @@ +/** + * @CopyRight: + * FISCO-BCOS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FISCO-BCOS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FISCO-BCOS. If not, see + * (c) 2016-2018 fisco-dev contributors. + * + * @brief : complement compress and uncompress with zstd + * + * @file ZstdCompress.cpp + * @author: lucasli + * @date 2022-09-22 + */ +#include "ZstdCompress.h" + +namespace bcos +{ +bool ZstdCompress::compress(bytesConstRef inputData, bytes& compressedData, int compressionLevel) +{ + // auto start_t = utcTimeUs(); + size_t const cBuffSize = ZSTD_compressBound(inputData.size()); + compressedData.resize(cBuffSize); + auto compressedDataPtr = const_cast(static_cast(&compressedData[0])); + auto inputDataPtr = static_cast(inputData.data()); + size_t const compressedSize = ZSTD_compress( + compressedDataPtr, cBuffSize, inputDataPtr, inputData.size(), compressionLevel); + auto code = ZSTD_isError(compressedSize); + if (code) + { + // if code == 1, means compress failed + BCOS_LOG(ERROR) << LOG_BADGE("ZstdCompress") + << LOG_DESC("compress failed, error code check failed") + << LOG_KV("code", code); + return false; + } + compressedData.resize(compressedSize); +#if 0 + BCOS_LOG(DEBUG) << LOG_BADGE("ZstdCompress") << LOG_DESC("Compress") + << LOG_KV("org_len", inputData.size()) << LOG_KV("compressed_len", compressedSize) + << LOG_KV("ratio", (float)inputData.size() / (float)compressedData.size()) + << LOG_KV("timecost", (utcTimeUs() - start_t)); +#endif + + return true; +} + +bool ZstdCompress::uncompress(bytesConstRef compressedData, bytes& uncompressedData) +{ + // auto start_t = utcTimeUs(); + size_t const cBuffSize = ZSTD_getFrameContentSize(compressedData.data(), compressedData.size()); + if (0 == cBuffSize || ZSTD_CONTENTSIZE_UNKNOWN == cBuffSize || + ZSTD_CONTENTSIZE_ERROR == cBuffSize) + { + BCOS_LOG(ERROR) << LOG_BADGE("ZstdUncompress") + << LOG_DESC("compress failed, compressedData size error") + << LOG_KV("compressedData size", cBuffSize); + return false; + } + + uncompressedData.resize(cBuffSize); + auto uncompressedDataPtr = const_cast(static_cast(&uncompressedData[0])); + auto compressedDataPtr = static_cast(compressedData.data()); + size_t const uncompressSize = + ZSTD_decompress(uncompressedDataPtr, cBuffSize, compressedDataPtr, compressedData.size()); + auto code = ZSTD_isError(uncompressSize); + if (code) + { + // if code == 1, means uncompress failed + BCOS_LOG(ERROR) << LOG_BADGE("ZstdUncompress") + << LOG_DESC("uncompress failed, error code check failed") + << LOG_KV("code", code); + return false; + } + uncompressedData.resize(uncompressSize); +#if 0 + BCOS_LOG(DEBUG) << LOG_BADGE("ZstdUncompress") << LOG_DESC("uncompress") + << LOG_KV("org_len", uncompressSize) + << LOG_KV("compress_len", compressedData.size()) + << LOG_KV("ratio", (float)uncompressSize / (float)compressedData.size()) + << LOG_KV("timecost", (utcTimeUs() - start_t)); +#endif + + return true; +} +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/bcos-utilities/ZstdCompress.h" "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/ZstdCompress.h" new file mode 100644 index 00000000..a0853615 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/bcos-utilities/ZstdCompress.h" @@ -0,0 +1,37 @@ +/** + * @CopyRight: + * FISCO-BCOS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FISCO-BCOS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FISCO-BCOS. If not, see + * (c) 2016-2018 fisco-dev contributors. + * + * @brief : complement compress and uncompress with zstd + * + * @file ZstdCompress.h + * @author: lucasli + * @date 2022-09-22 + */ +#pragma once +#include "Common.h" +#include "zstd.h" + +namespace bcos +{ + +class ZstdCompress +{ +public: + static bool compress(bytesConstRef inputData, bytes& compressedData, int compressionLevel); + static bool uncompress(bytesConstRef compressedData, bytes& uncompressedData); +}; + +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/test/CMakeLists.txt" "b/BFPL\345\243\271/bcos-utilities/test/CMakeLists.txt" new file mode 100644 index 00000000..73b0776a --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/test/CMakeLists.txt" @@ -0,0 +1,28 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for ut of bcos-utilities +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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(GLOB_RECURSE SOURCES "*.cpp") + +# cmake settings +set(TEST_BINARY_NAME test-bcos-utilities) +find_package(Boost REQUIRED COMPONENTS unit_test_framework) + +add_executable(${TEST_BINARY_NAME} ${SOURCES}) +target_include_directories(${TEST_BINARY_NAME} PRIVATE . ${CMAKE_SOURCE_DIR}) + +target_link_libraries(${TEST_BINARY_NAME} bcos-utilities Boost::unit_test_framework) +add_test(NAME ${TEST_BINARY_NAME} WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${TEST_BINARY_NAME}) diff --git "a/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/Base64Test.cpp" "b/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/Base64Test.cpp" new file mode 100644 index 00000000..1f8ffd71 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/Base64Test.cpp" @@ -0,0 +1,98 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Unit tests for the Base64 + * @file Base64.cpp + */ +#include "bcos-utilities/Base64.h" +#include "bcos-utilities/DataConvertUtility.h" +#include "bcos-utilities/testutils/TestPromptFixture.h" +#include +#include +#include + +using namespace bcos; +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(Base64, TestPromptFixture) +BOOST_AUTO_TEST_CASE(testBase64DecodeBytes) +{ + const std::string encodeStr = "MTIzNEFCY2Q="; + auto decodeStr = base64DecodeBytes(encodeStr); + std::string oriStr; + for (size_t i = 0; i < decodeStr->size(); i++) + { + oriStr += char((*decodeStr)[i]); + } + BOOST_CHECK(oriStr == "1234ABcd"); + + std::string originString = "1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b"; + bytes originBytes = *fromHexString(originString); + std::string base64Str = base64Encode(bytesConstRef(originBytes.data(), originBytes.size())); + std::cout << "base64Encode for " << *toHexString(originBytes) << " is: " << base64Str + << std::endl; + // decode the base64Str + auto decodedBytes = base64DecodeBytes(base64Str); + std::cout << "decodedBytes is: " << *toHexString(*decodedBytes) << std::endl; + BOOST_CHECK(*toHexString(*decodedBytes) == originString); + + auto encodedStr = base64Encode(originString); + std::string decodedString = base64Decode(encodedStr); + std::cout << "encodedStr for " << originString << " is " << encodedStr << std::endl; + std::cout << "decodedString for " << originString << " is " << decodedString << std::endl; + BOOST_CHECK(decodedString == originString); +} + +BOOST_AUTO_TEST_CASE(testBase64Encode) +{ + const std::string decodeStr = "1234ABcd"; + BOOST_CHECK(base64Encode(decodeStr) == "MTIzNEFCY2Q="); +} + +BOOST_AUTO_TEST_CASE(testBase64Codec) +{ + std::string org = + "aabbddeejlaskdjfaksldfjaksdjflkasdjfkasldjfkasdjfkalsdjfkaljfdk98qe9r8eq-" + "9r0qw0eriq0wepotlasadf"; + auto toBase64Length = [](size_t l) -> size_t { return (l + 2) / 3 * 4; }; + for (int i = 0; i < 100; ++i) + { + std::string s = org + std::to_string(i); + auto base64 = base64Encode(s); + BOOST_CHECK_EQUAL(base64.size(), toBase64Length(s.size())); + auto s0 = base64Decode(base64); + BOOST_CHECK_EQUAL(s0.size(), s.size()); + BOOST_CHECK_EQUAL(s, s0); + auto bytes = base64DecodeBytes(base64); + BOOST_CHECK_EQUAL(bytes->size(), s.size()); + auto s1 = std::string(bytes->begin(), bytes->end()); + BOOST_CHECK_EQUAL(s1.size(), s.size()); + BOOST_CHECK_EQUAL(s, s1); + } +} + +BOOST_AUTO_TEST_CASE(testBase64CodecZero) +{ + std::vector v(100, '\0'); + auto ev = base64Encode((uint8_t*)v.data(), v.size()); + auto ov = base64Decode(ev); + BOOST_CHECK_EQUAL(ov.size(), 100); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/CommonTest.cpp" "b/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/CommonTest.cpp" new file mode 100644 index 00000000..f2b6766f --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/CommonTest.cpp" @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2021 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. + * + * @brief: unit test for Common.* of bcos-utilities + * + * @file CommonTest.cpp + * @author: yujiechen + * @date 2021-02-24 + */ + + +#include "bcos-utilities/Common.h" +#include "bcos-utilities/Error.h" +#include "bcos-utilities/Exceptions.h" +#include "bcos-utilities/Ranges.h" +#include "bcos-utilities/testutils/TestPromptFixture.h" +#include +#include +#include + + +using namespace bcos; +using namespace std; +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(DevcoreCommonTest, TestPromptFixture) +/// test Arith Calculations +BOOST_AUTO_TEST_CASE(testArithCal) +{ + ///=========test u2s================== + u256 u_bigint("343894723987432"); + bigint c_end = bigint(1) << 256; + BOOST_CHECK(u2s(u_bigint) == u_bigint); + u_bigint = Invalid256; + BOOST_CHECK(u2s(u_bigint) < s256(0)); + u_bigint = u256("0xa170d8e0ae1b57d7ecc121f6fe5ceb03c1267801ff720edd2f8463e7effac6c6"); + BOOST_CHECK(u2s(u_bigint) < s256(0)); + BOOST_CHECK(u2s(u_bigint) == s256(-(c_end - u_bigint))); + u_bigint = u256("0x7170d8e0ae1b57d7ecc121f6fe5ceb03c1267801ff720edd2f8463e7effac6c6"); + BOOST_CHECK(u2s(u_bigint) == u_bigint); + ///=========test s2u================== + s256 s_bigint("0x7170d8e0ae1b57d7ecc121f6fe5ceb03c1267801ff720edd2f8463e7effac6c6"); + BOOST_CHECK(s2u(s_bigint) == s_bigint); + s_bigint = s256("0xf170d8e0ae1b57d7ecc121f6fe5ceb03c1267801ff720edd2f8463e7effac6c6"); + BOOST_CHECK(s2u(s_bigint) == u256(c_end + s_bigint)); + ///=========test exp10================== + BOOST_CHECK(exp10<1>() == u256(10)); + BOOST_CHECK(exp10<9>() == u256(1000000000)); + BOOST_CHECK(exp10<0>() == u256(1)); +} +/// test utcTime +BOOST_AUTO_TEST_CASE(testUtcTime) +{ + uint64_t old_time = utcTime(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + BOOST_CHECK(old_time < utcTime()); +} + +BOOST_AUTO_TEST_CASE(testGuards) +{ + Mutex mutex; + int count = 0; + int max = 8; + + auto f = [&]() { + Guard l(mutex); + count++; + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + }; + + // auto begin = std::chrono::high_resolution_clock::now(); + + thread* t = new thread[max]; + for (int i = 0; i < max; i++) + { + t[i] = thread(f); + } + + for (int i = 0; i < max; i++) + { + t[i].join(); + } + + // auto duration = std::chrono::duration_cast( + // std::chrono::high_resolution_clock::now() - begin); + + BOOST_CHECK(count == max); + + // uint64_t end_time = end.tv_sec * 1000000 + end.tv_usec; + // uint64_t begin_time = begin.tv_sec * 1000000 + begin.tv_usec; + // BOOST_CHECK((end_time - begin_time) >= (uint64_t(max) * 1000)); + delete[] t; +} + +BOOST_AUTO_TEST_CASE(testWriteGuard) +{ + SharedMutex mutex; + int count = 0; + int max = 8; + + auto f = [&]() { + WriteGuard l(mutex); + count++; + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + }; + + // struct timeval begin; + // gettimeofday(&begin, NULL); + + thread* t = new thread[max]; + for (int i = 0; i < max; i++) + { + t[i] = thread(f); + } + + for (int i = 0; i < max; i++) + { + t[i].join(); + } + + // struct timeval end; + // gettimeofday(&end, NULL); + + // uint64_t end_time = end.tv_sec * 1000000 + end.tv_usec; + // uint64_t begin_time = begin.tv_sec * 1000000 + begin.tv_usec; + + // BOOST_CHECK((end_time - begin_time) > (uint64_t(max) * 1000)); + BOOST_CHECK(count == max); + delete[] t; +} + +BOOST_AUTO_TEST_CASE(testRecursiveGuard) +{ + RecursiveMutex mutex; + int count = 0; + int max = 8; + + auto f0 = [&]() { + RecursiveGuard l(mutex); + count++; + }; + + auto f1 = [&]() { + RecursiveGuard l(mutex); + count++; + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + f0(); // recursive + }; + + // struct timeval begin; + // gettimeofday(&begin, NULL); + + thread* t = new thread[max]; + for (int i = 0; i < max; i++) + { + t[i] = thread(f1); + } + + for (int i = 0; i < max; i++) + { + t[i].join(); + } + + // struct timeval end; + // gettimeofday(&end, NULL); + + // uint64_t end_time = end.tv_sec * 1000000 + end.tv_usec; + // uint64_t begin_time = begin.tv_sec * 1000000 + begin.tv_usec; + // BOOST_CHECK((end_time - begin_time) >= (uint64_t(max) * 1000)); + BOOST_CHECK(count == 2 * max); + delete[] t; +} +BOOST_AUTO_TEST_CASE(testError) +{ + std::string errorMessage = " test error"; + int64_t errorCode = -100042; + Error::Ptr error = std::make_unique(errorCode, errorMessage); + BOOST_CHECK(error->errorCode() == errorCode); + BOOST_CHECK(error->errorMessage() == errorMessage); + + errorMessage = " test error234"; + errorCode = 100044; + error->setErrorCode(errorCode); + error->setErrorMessage(errorMessage); + BOOST_CHECK(error->errorCode() == errorCode); + BOOST_CHECK(error->errorMessage() == errorMessage); +} + +void testRange(RANGES::range auto range) +{ + BOOST_CHECK(RANGES::size(range) > 0); +} + +BOOST_AUTO_TEST_CASE(range) +{ + std::vector list = {1, 2, 3, 4, 5}; + + testRange(list); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/DataConvertUtilityTest.cpp" "b/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/DataConvertUtilityTest.cpp" new file mode 100644 index 00000000..5edb3ffb --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/DataConvertUtilityTest.cpp" @@ -0,0 +1,131 @@ +/** + * Copyright (C) 2021 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. + * + * @brief unit test for DataConvertUtility.*(mainly type covert) + * + * @file DataConvertUtility.cpp + * @author: yujiechen + */ +#include "bcos-utilities/DataConvertUtility.h" +#include "bcos-utilities/Exceptions.h" +#include "bcos-utilities/testutils/TestPromptFixture.h" +#include +#include +#include +#include + +using namespace bcos; +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(CommonDataTests, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(testHex) +{ + // toHex && fromHexString + unsigned int round = 10; + unsigned int size = 10; + bytes hexVec(size, 0); + std::string hexStr; + std::string hexStrWithPrefix; + std::srand(std::time(nullptr)); + for (unsigned int i = 0; i < round; i++) + { + for (unsigned int j = 0; j < size; j++) + { + hexVec[j] = std::rand() % 16; + } + hexStr = *toHexString(hexVec); + hexStrWithPrefix = *toHexString(hexVec); + BOOST_CHECK(*fromHexString(hexStr) == hexVec); + BOOST_CHECK(*fromHexString(hexStrWithPrefix) == hexVec); + } + // fromHexString Exception + BOOST_CHECK_THROW(fromHexString("0934xyz"), BadHexCharacter); + BOOST_CHECK(isHexString("0934xyz") == false); + + BOOST_CHECK(isHexString("0x000abc") == true); + + BOOST_CHECK(isHexString("000123123") == true); +} + +/// test asString && asBytes +BOOST_AUTO_TEST_CASE(testStringTrans) +{ + std::string tmp_str = "abcdef012343"; + BOOST_CHECK(asString(asBytes(tmp_str)) == tmp_str); + BOOST_CHECK(asString(asBytes(tmp_str)) == tmp_str); + // construct random vector + unsigned int round = 10; + unsigned int size = 10; + std::string tmp_str_from_ref; + bytes tmp_bytes(size, 0); + for (unsigned int i = 0; i < round; i++) + { + for (unsigned int j = 0; j < size; j++) + { + tmp_bytes[j] = std::rand(); + } + tmp_str = asString(tmp_bytes); + tmp_str_from_ref = asString(ref(tmp_bytes)); + BOOST_CHECK(tmp_str == tmp_str_from_ref); + BOOST_CHECK(asBytes(tmp_str) == tmp_bytes); + } +} + +/// test toBigEndian && fromBigEndian +BOOST_AUTO_TEST_CASE(testBigEndian) +{ + // check u256 + u256 number("9832989324908234742342343243243234324324243432432234324"); + u160 number_u160("983298932"); + bytes big_endian_bytes = toBigEndian(number); + BOOST_CHECK(fromBigEndian(big_endian_bytes) == number); + BOOST_CHECK(fromBigEndian(toBigEndian(number_u160)) == number_u160); +} +/// test operator+ +BOOST_AUTO_TEST_CASE(testOperators) +{ + // test is_pod operator+ + std::string a_str = "abcdef"; + std::string b_str = "01234"; + std::vector a_vec(a_str.begin(), a_str.end()); + std::vector b_vec(b_str.begin(), b_str.end()); + std::vector result = a_vec + b_vec; + BOOST_CHECK(std::string(result.begin(), result.end()) == (a_str + b_str)); + // test common operator+ + std::vector total_array; + std::vector a_str_array; + a_str_array.push_back("11"); + a_str_array.push_back("22"); + total_array = a_str_array; + std::vector b_str_array; + b_str_array.push_back("aa"); + b_str_array.push_back("cc"); + total_array.push_back("aa"); + total_array.push_back("cc"); + std::vector c_str_array = a_str_array + b_str_array; + BOOST_CHECK(c_str_array == total_array); + + // test toString + string32 s_32 = {{'a', 'b', 'c'}}; + std::string s = toString(s_32); + BOOST_CHECK(s == "abc"); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/JsonDataConvertUtilityTest.cpp" "b/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/JsonDataConvertUtilityTest.cpp" new file mode 100644 index 00000000..178f5022 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/JsonDataConvertUtilityTest.cpp" @@ -0,0 +1,110 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Unit tests for the FixedBytes + * @file FixedBytesTest.cpp + * @author: chaychen + */ + +#include "bcos-utilities/JsonDataConvertUtility.h" +#include "bcos-utilities/DataConvertUtility.h" +#include "bcos-utilities/FixedBytes.h" +#include "bcos-utilities/testutils/TestPromptFixture.h" +#include +#include + +using namespace bcos; +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(FixedBytes, TestPromptFixture) +BOOST_AUTO_TEST_CASE(testLeft160) +{ + const std::string str = "0x067150c07dab4facb7160e075548007e067150c07dab4facb7160e075548007e"; + h256 h = jonStringToFixedBytes<32>(str); + BOOST_CHECK("0x067150c07dab4facb7160e075548007e067150c0" == toJonString(left160(h))); +} + +BOOST_AUTO_TEST_CASE(testRight160) +{ + const std::string str = "0x067150c07dab4facb7160e075548007e067150c07dab4facb7160e075548007e"; + h256 h = jonStringToFixedBytes<32>(str); + BOOST_CHECK("0x5548007e067150c07dab4facb7160e075548007e" == toJonString(right160(h))); + // test u256 + h256 h256Data( + *fromHexString("12b5155eda010a5b7ae26a4a268e466a4b8d31547ad875fce9ab298c639a1b2f")); + // trans h256Data to u256 + u256 value(h256Data); + // trans value to h256 again + h256 convertedH256Data = value; + std::cout << "### value: " << value << ", h256Data:" << *toHexString(h256Data) + << "convertedH256Data" << *toHexString(convertedH256Data) << std::endl; + BOOST_CHECK(convertedH256Data == h256Data); +} + +BOOST_AUTO_TEST_CASE(testToJonString) +{ + h64 a("0xbaadf00ddeadbeef"); + uint64_t c = 38990234243; + bytes d = {0xff, 0x0, 0xef, 0xbc}; + + BOOST_CHECK(toJonString(a) == "0xbaadf00ddeadbeef"); + BOOST_CHECK(toJonString(c) == "0x913ffc283"); + BOOST_CHECK(toJonString(d) == "0xff00efbc"); +} + +BOOST_AUTO_TEST_CASE(testJonStringToBytes) +{ + bytes a = {0xff, 0xaa, 0xbb, 0xcc}; + bytes b = {0x03, 0x89, 0x90, 0x23, 0x42, 0x43}; + BOOST_CHECK(a == jonStringToBytes("0xffaabbcc")); + BOOST_CHECK(b == jonStringToBytes("38990234243")); + BOOST_CHECK(bytes() == jonStringToBytes("")); + BOOST_CHECK_THROW(jonStringToBytes("Invalid hex chars"), bcos::BadHexCharacter); +} + +BOOST_AUTO_TEST_CASE(testJonStringToFixedBytes) +{ + h256 a("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + BOOST_CHECK(a == jonStringToFixedBytes<32>( + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); + h256 b("0x000000000000000000000000000000000000000000000000000000740c54b42f"); + BOOST_CHECK(b == jonStringToFixedBytes<32>("498423084079")); + BOOST_CHECK(h256() == jonStringToFixedBytes<32>("NotAHexadecimalOrDecimal")); +} + +BOOST_AUTO_TEST_CASE(testJsonStringToInt) +{ + BOOST_CHECK(43832124 == jsonStringToInt("43832124")); + BOOST_CHECK(1342356623 == jsonStringToInt("0x5002bc8f")); + BOOST_CHECK(3483942 == jsonStringToInt("015224446")); + BOOST_CHECK_THROW(jsonStringToInt("NotAHexadecimalOrDecimal"), std::exception); + + BOOST_CHECK(u256("983298932490823474234") == jsonStringToInt<32>("983298932490823474234")); + BOOST_CHECK(u256("983298932490823474234") == jsonStringToInt<32>("0x354e03915c00571c3a")); + BOOST_CHECK_THROW(jsonStringToInt<32>("NotAHexadecimalOrDecimal"), std::exception); + BOOST_CHECK_THROW(jsonStringToInt<16>("NotAHexadecimalOrDecimal"), bcos::BadCast); +} + +BOOST_AUTO_TEST_CASE(testJonStringToU256) +{ + BOOST_CHECK(u256("983298932490823474234") == jonStringToU256("983298932490823474234")); + BOOST_CHECK_THROW(jonStringToU256("NotAHexadecimalOrDecimal"), bcos::BadCast); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/RefDataContainerTest.cpp" "b/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/RefDataContainerTest.cpp" new file mode 100644 index 00000000..5e79f9b9 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/RefDataContainerTest.cpp" @@ -0,0 +1,178 @@ +/** + * Copyright (C) 2021 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 RefDataContainerTest.cpp + * @date 2021-02-26 + */ + +#include "bcos-utilities/RefDataContainer.h" +#include "bcos-utilities/Common.h" +#include "bcos-utilities/testutils/TestPromptFixture.h" +#include +#include +#include + + +using namespace bcos; + +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(VectorRefTests, TestPromptFixture) + +/// test constructor of RefDataContainer: +/// case1: empty RefDataContainer +/// case2: pointing to part data of given object +BOOST_AUTO_TEST_CASE(testConstructor) +{ + // case1: empty RefDataContainer + RefDataContainer ref; + BOOST_CHECK(ref.data() == nullptr); + BOOST_CHECK(ref.count() == 0); + BOOST_CHECK(ref.empty() == true); + + // case2: pointing to part data of given object + unsigned int size = 10; + char* test_char = new char[size]; + RefDataContainer char_ref(test_char, size); + BOOST_CHECK(char_ref.empty() == false); + BOOST_CHECK(char_ref.data() == test_char); + BOOST_CHECK(char_ref.count() == size); + for (unsigned int i = 0; i < size - 1; i++) + { + test_char[i] = '0' + i; + BOOST_CHECK(char_ref[i] == test_char[i]); + } + if (size > 4) + { + char_ref[3] = 'a'; + BOOST_CHECK(test_char[3] == 'a'); + } + delete[] test_char; +} + +/// case3: create a new RefDataContainer pointing to the data part of a string (given as reference). +/// case4: create a new RefDataContainer pointing to the data part of a string (given as pointer). +BOOST_AUTO_TEST_CASE(testConstructString) +{ + // test reference + std::string str = "abcd"; + std::string str2 = "abcd"; + RefDataContainer string_ref(str); + BOOST_CHECK_EQUAL(string_ref.data(), str); + BOOST_CHECK_EQUAL(string_ref.size(), str.length()); + BOOST_CHECK_EQUAL(str, string_ref.toString()); + BOOST_CHECK((RefDataContainer)(&str) == string_ref); + BOOST_CHECK((RefDataContainer)(&str2) != string_ref); + // test retarget + string_ref.retarget(str2.c_str(), str2.length()); + BOOST_CHECK((RefDataContainer)(&str) != string_ref); + BOOST_CHECK((RefDataContainer)(&str2) == string_ref); + + // test pointer + std::string* pstr = &str; + RefDataContainer pstring_ref(pstr); + BOOST_CHECK_EQUAL(pstring_ref.data(), *pstr); + BOOST_CHECK_EQUAL(pstring_ref.size(), pstr->length()); + BOOST_CHECK_EQUAL(*pstr, pstring_ref.toString()); + BOOST_CHECK((RefDataContainer)(pstr) == pstring_ref); + pstr = &str2; + BOOST_CHECK((RefDataContainer)(pstr) != pstring_ref); + // to vector + // test retarget + pstring_ref.retarget(str2.c_str(), str2.length()); + BOOST_CHECK((RefDataContainer)(pstr) == pstring_ref); + pstr = &str; + BOOST_CHECK((RefDataContainer)(pstr) != pstring_ref); +} + +/// case4: create a new RefDataContainer pointing to the data part of a vector (given as pointer). +/// case5: create a new RefDataContainer pointing to the data part of a string (given as reference). +BOOST_AUTO_TEST_CASE(testConstructVector) +{ + unsigned int size = 10; + std::vector int_v1(size); + std::vector int_v2(size); + RefDataContainer ref_v1(&int_v1); + for (unsigned i = 0; i < size; i++) + { + ref_v1[i] = i; + int_v2[i] = i; + BOOST_CHECK(ref_v1[i] == (signed)(i)); + } + // vector to RefDataContainer + BOOST_CHECK(RefDataContainer(&int_v1) == ref_v1); +} + +BOOST_AUTO_TEST_CASE(testContentsEqual) +{ + std::vector v1(10, 19); + std::vector v2(10, 19); + RefDataContainer ref_v1(&v1); + BOOST_CHECK(RefDataContainer(&v2) != ref_v1); +} + +BOOST_AUTO_TEST_CASE(testEmptyCropped) +{ + std::vector v1(1, 19); + RefDataContainer origin = ref(v1); + RefDataContainer ret = origin.getCroppedData(2, 10); + BOOST_CHECK(ret.empty() == true); +} + +// test function "Overlap" +BOOST_AUTO_TEST_CASE(testOverlap) +{ + /// ====test string Overlap ==== + unsigned int size = 10; + std::string str(size, 0); + for (unsigned i = 0; i < size; i++) + { + str[i] = '0' + i; + } + RefDataContainer str_ref(str.c_str()); + // all overlap + RefDataContainer sub_str_ref1( + str_ref.data() + 1, (size_t)(std::min)((size_t)str.length() - 2, (size_t)5) / sizeof(char)); + // part overlap + RefDataContainer sub_str_ref2( + str_ref.data() + 3, (size_t)(std::min)((size_t)str.length() - 2, (size_t)5) / sizeof(char)); + // std::string tmp_str = "abcd"; + // RefDataContainer non_overlap_ref(tmp_str); + BOOST_CHECK(str_ref.dataOverlap(sub_str_ref1) == true); + BOOST_CHECK(str_ref.dataOverlap(sub_str_ref2) == true); + BOOST_CHECK(sub_str_ref1.dataOverlap(sub_str_ref2) == true); + + /// ====test vector Overlap ==== + std::vector v1_int(size); + RefDataContainer v1_ref(&v1_int); + for (unsigned i = 0; i < size; i++) + { + v1_ref[i] = i; + } + + RefDataContainer v2_ref(&(*v1_int.begin()) + (size_t)(std::min)(v1_int.size(), (size_t)(1)), + (std::min)(v1_int.size(), (size_t)(5))); + // test overlap + BOOST_CHECK(v2_ref.dataOverlap(v1_ref)); + std::vector tmp_v; + // test no-overlap + BOOST_CHECK(v2_ref.dataOverlap(RefDataContainer(&tmp_v)) == false); +} +BOOST_AUTO_TEST_SUITE_END() + +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/TestFixedBytes.cpp" "b/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/TestFixedBytes.cpp" new file mode 100644 index 00000000..d4ec9152 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/TestFixedBytes.cpp" @@ -0,0 +1,56 @@ +#include "../../../bcos-utilities/FixedBytes.h" +#include +#include +#include +#include + +class TestFixedBytesFixture +{ +public: +}; + +constexpr static size_t LENGTH = 32; + +BOOST_FIXTURE_TEST_SUITE(TestFixedBytes, TestFixedBytesFixture) + +BOOST_AUTO_TEST_CASE(fromStringView) +{ + std::string hex = "abc"; + BOOST_CHECK_THROW( + bcos::FixedBytes(std::string_view(hex), bcos::FixedBytes<32>::FromHex), + std::invalid_argument); + + hex = "abcd"; + bcos::FixedBytes rightBytes(std::string_view{hex}, bcos::FixedBytes<32>::FromHex); + + BOOST_CHECK_EQUAL(rightBytes.size(), LENGTH); + std::string outHex; + boost::algorithm::hex_lower(rightBytes.begin(), rightBytes.end(), std::back_inserter(outHex)); + BOOST_CHECK_NE(outHex, hex); + auto expect = std::string(30 * 2, '0') + hex; + BOOST_CHECK_EQUAL(outHex, expect); + + outHex.clear(); + bcos::FixedBytes leftBytes(std::string_view{hex}, bcos::FixedBytes<32>::FromHex, + bcos::FixedBytes<32>::DataAlignType::AlignLeft); + boost::algorithm::hex_lower(leftBytes.begin(), leftBytes.end(), std::back_inserter(outHex)); + BOOST_CHECK_NE(outHex, hex); + expect = hex + std::string(30 * 2, '0'); + BOOST_CHECK_EQUAL(outHex, expect); + + outHex.clear(); + hex = "0x50d0902a2f0da872d528fb09f6102362919e017496d62eb28f2a9bcac5ca370b"; + bcos::FixedBytes prefixRightBytes(std::string_view{hex}, bcos::FixedBytes<32>::FromHex, + bcos::FixedBytes<32>::DataAlignType::AlignLeft); + boost::algorithm::hex_lower( + prefixRightBytes.begin(), prefixRightBytes.end(), std::back_inserter(outHex)); + BOOST_CHECK_EQUAL("0x" + outHex, hex); + + outHex.clear(); + bcos::FixedBytes prefixLeftBytes(std::string_view{hex}, bcos::FixedBytes<32>::FromHex, + bcos::FixedBytes<32>::DataAlignType::AlignLeft); + boost::algorithm::hex_lower( + prefixLeftBytes.begin(), prefixLeftBytes.end(), std::back_inserter(outHex)); + BOOST_CHECK_EQUAL("0x" + outHex, hex); +} +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/WorkerTest.cpp" "b/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/WorkerTest.cpp" new file mode 100644 index 00000000..4fc68ccc --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/WorkerTest.cpp" @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Construct a new boost auto test case object for Worker + * + * @file Worker.cpp + * @author: tabsu + */ + +#include "bcos-utilities/Worker.h" +#include "bcos-utilities/Timer.h" +#include "bcos-utilities/testutils/TestPromptFixture.h" +#include +#include +#include +using namespace bcos; +using namespace std; + +namespace bcos +{ +namespace test +{ +class TestWorkerImpl : public Worker +{ +public: + TestWorkerImpl() : Worker("TestWorkerImpl", 1) + { + m_timer = std::make_shared(1); + m_timer->registerTimeoutHandler([]() { std::cout << "#### call timer" << std::endl; }); + } + void run() { startWorking(); } + void stop() { stopWorking(); } + +protected: + virtual void initWorker() { cout << "initWorker..." << endl; } + virtual void executeWorker() + { + cout << "count:" << count << endl; + count++; + } + virtual void finishWorker() { cout << "finishWorker..." << endl; } + +private: + int count = 0; + std::shared_ptr m_timer; +}; + +BOOST_FIXTURE_TEST_SUITE(Worker, TestPromptFixture) + +BOOST_AUTO_TEST_CASE(testWorker) +{ + TestWorkerImpl workerImpl; + workerImpl.run(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + workerImpl.stop(); +} +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/ZstdCompressTest.cpp" "b/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/ZstdCompressTest.cpp" new file mode 100644 index 00000000..6065bd45 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/test/unittests/libutilities/ZstdCompressTest.cpp" @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Unit tests for the ZstdCompress + * @file ZstdCompressTest.cpp + */ +#include "bcos-utilities/ZstdCompress.h" +#include "bcos-utilities/testutils/TestPromptFixture.h" +#include +#include +#include + +using namespace bcos; +namespace bcos +{ +namespace test +{ +BOOST_FIXTURE_TEST_SUITE(ZstdCompressTests, TestPromptFixture) +BOOST_AUTO_TEST_CASE(testZstdCompress) +{ + auto payload = std::make_shared(10000, 'a'); + auto wrongCompressedData = std::make_shared(10000, 'b'); + std::shared_ptr compressData = std::make_shared(); + std::shared_ptr uncompressData = std::make_shared(); + + // compress uncompress success + bool retCompress = ZstdCompress::compress(ref(*payload), *compressData, 1); + BOOST_CHECK(retCompress); + ssize_t retUncompress = ZstdCompress::uncompress(ref(*compressData), *uncompressData); + BOOST_CHECK(retUncompress); + BOOST_CHECK_EQUAL(std::string(uncompressData->begin(), uncompressData->end()), + std::string(payload->begin(), payload->end())); + + // uncompress fail + bool retUncompressFail = ZstdCompress::uncompress(ref(*wrongCompressedData), *uncompressData); + BOOST_CHECK(!retUncompressFail); +} + +BOOST_AUTO_TEST_SUITE_END() +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/bcos-utilities/test/unittests/main/main.cpp" "b/BFPL\345\243\271/bcos-utilities/test/unittests/main/main.cpp" new file mode 100644 index 00000000..bcf6dad8 --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/test/unittests/main/main.cpp" @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 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 main.cpp + * @author: yujiechen, jimmyshi + * @date 2021-02-24 + */ +#define BOOST_TEST_MODULE FISCO_BCOS_Tests +#define BOOST_TEST_MAIN + +#include diff --git "a/BFPL\345\243\271/bcos-utilities/testutils/TestPromptFixture.h" "b/BFPL\345\243\271/bcos-utilities/testutils/TestPromptFixture.h" new file mode 100644 index 00000000..6f5c2f9d --- /dev/null +++ "b/BFPL\345\243\271/bcos-utilities/testutils/TestPromptFixture.h" @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2021 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: TestPromptFixture.h + * fixture class for test case prompt + */ + +#pragma once +#include "bcos-utilities/Common.h" +#include +#include + +namespace bcos +{ +namespace test +{ +class TestPrompt +{ +public: + static TestPrompt& get() + { + static TestPrompt instance; + return instance; + } + TestPrompt(TestPrompt const&) = delete; + void operator=(TestPrompt const&) = delete; + /** + * @brief : init every test-suite by printting the name of cases + * @param _maxTests + */ + void initTest(size_t _maxTests = 1) + { + m_currentTestName = "unknown"; + m_currentTestFileName = std::string(); + m_startTime = utcTime(); + m_currentTestCaseName = boost::unit_test::framework::current_test_case().p_name; + std::cout << "===== BCOS Test Case : " + m_currentTestCaseName << "=====" << std::endl; + m_maxTests = _maxTests; + m_currTest = 0; + }; + + /** + * @brief : release resources when test-suite finished + */ + void finishTest() + { + execTimeName res; + res.first = (double)(utcTime() - m_startTime); + res.second = caseName(); + std::cout << "#### Run " << res.second << " time elapsed: " << res.first << std::endl; + m_execTimeResults.push_back(res); + } + + void setCurrentTestFile(boost::filesystem::path const& _name) { m_currentTestFileName = _name; } + void setCurrentTestName(std::string const& _name) { m_currentTestName = _name; } + std::string const& testName() { return m_currentTestName; } + std::string const& caseName() { return m_currentTestCaseName; } + boost::filesystem::path const& testFile() { return m_currentTestFileName; } + +private: + TestPrompt() {} + size_t m_currTest; + size_t m_maxTests; + std::string m_currentTestName; + std::string m_currentTestCaseName; + boost::filesystem::path m_currentTestFileName; + typedef std::pair execTimeName; + std::vector m_execTimeResults; + int64_t m_startTime; +}; + +class TestPromptFixture +{ +public: + // init test-suite fixture + TestPromptFixture() { TestPrompt::get().initTest(); } + // release test-suite fixture + ~TestPromptFixture() { TestPrompt::get().finishTest(); } +}; +} // namespace test +} // namespace bcos diff --git "a/BFPL\345\243\271/benchmark/CMakeLists.txt" "b/BFPL\345\243\271/benchmark/CMakeLists.txt" new file mode 100644 index 00000000..9f5db574 --- /dev/null +++ "b/BFPL\345\243\271/benchmark/CMakeLists.txt" @@ -0,0 +1,4 @@ +find_package(Boost REQUIRED program_options) + +add_executable(merkleBench merkleBench.cpp) +target_link_libraries(merkleBench ${TOOL_TARGET} ${PROTOCOL_TARGET} bcos-crypto Boost::program_options) \ No newline at end of file diff --git "a/BFPL\345\243\271/benchmark/merkleBench.cpp" "b/BFPL\345\243\271/benchmark/merkleBench.cpp" new file mode 100644 index 00000000..804c6adb --- /dev/null +++ "b/BFPL\345\243\271/benchmark/merkleBench.cpp" @@ -0,0 +1,128 @@ +#include "bcos-crypto/interfaces/crypto/CryptoSuite.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Hasher = bcos::crypto::hasher::openssl::OpenSSL_SM3_Hasher; + +std::string generateTestData(int count) +{ + std::string buffer; + buffer.resize(count * Hasher::HASH_SIZE); + tbb::enumerable_thread_specific hasher; + tbb::parallel_for(tbb::blocked_range(0, count), [&hasher, &buffer](auto const& range) { + auto& localHasher = hasher.local(); + for (size_t i = range.begin(); i < range.end(); ++i) + { + localHasher.update(i); + std::span view(&buffer[i * Hasher::HASH_SIZE], Hasher::HASH_SIZE); + localHasher.final(view); + } + }); + + return buffer; +} + +void testOldMerkle(const std::vector& datas) +{ + auto cryptoSuite = std::make_shared( + std::make_shared(), nullptr, nullptr); + auto timePoint = std::chrono::high_resolution_clock::now(); + + auto root = bcos::protocol::calculateMerkleProofRoot(cryptoSuite, datas); + + auto duration = std::chrono::high_resolution_clock::now() - timePoint; + std::cout << "Root[old]: " << root << " " + << std::chrono::duration_cast(duration).count() << "ms" + << std::endl; +} + +void testNewMerkle(const std::vector& datas) +{ + auto timePoint = std::chrono::high_resolution_clock::now(); + std::vector out; + + bcos::crypto::merkle::Merkle merkle; + merkle.generateMerkle(datas, out); + + auto root = *(out.rbegin()); + std::string rootString; + boost::algorithm::hex_lower( + (char*)root.data(), (char*)(root.data() + root.size()), std::back_inserter(rootString)); + + auto duration = std::chrono::high_resolution_clock::now() - timePoint; + std::cout << "Root[new]: " << rootString << " " + << std::chrono::duration_cast(duration).count() << "ms" + << std::endl; +} + +int main(int argc, char* argv[]) +{ + boost::program_options::options_description options("Merkle benchmark"); + + // clang-format off + options.add_options() + ("type,t", boost::program_options::value()->default_value(0), "0 for old merkle, 1 for new merkle") + ("prepare,p", boost::program_options::value()->default_value(0), "Prepare test data, count of hashes") + ("filename,f", boost::program_options::value()->default_value("merkle_test.data"), "Test data file name") + ; + // clang-format on + boost::program_options::variables_map vm; + boost::program_options::store( + boost::program_options::parse_command_line(argc, argv, options), vm); + + if (vm.empty()) + { + options.print(std::cout); + return -1; + } + + auto filename = vm["filename"].as(); + auto count = vm["prepare"].as(); + if (count) + { + auto data = generateTestData(count); + std::ofstream fileOutput( + filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); + fileOutput << count; + fileOutput << data; + fileOutput.close(); + + std::cout << "Write " << count << " data successed!" << std::endl; + + return 0; + } + + std::ifstream fileInput(filename, std::ios_base::in | std::ios_base::binary); + fileInput >> count; + std::vector inputDatas; + inputDatas.reserve(count); + for (auto i = 0; i < count; ++i) + { + bcos::bytes data(Hasher::HASH_SIZE); + fileInput.read((char*)data.data(), (std::streamsize)data.size()); + + inputDatas.emplace_back(std::move(data)); + } + + auto type = vm["type"].as(); + if (type) + { + testNewMerkle(inputDatas); + } + else + { + testOldMerkle(inputDatas); + } + fileInput.close(); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/cmake/CompilerSettings.cmake" "b/BFPL\345\243\271/cmake/CompilerSettings.cmake" new file mode 100644 index 00000000..dd208304 --- /dev/null +++ "b/BFPL\345\243\271/cmake/CompilerSettings.cmake" @@ -0,0 +1,185 @@ +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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: CompilerSettings.cmake +# Function: Common cmake file for setting compilation environment variables +# ------------------------------------------------------------------------------ + +#add_definitions(-Wno-unused-value -Wunused-parameter) + +set(CMAKE_CXX_STANDARD 20) +set(Boost_NO_WARN_NEW_VERSIONS ON) +message(STATUS "COMPILER_ID: ${CMAKE_CXX_COMPILER_ID}") +if(("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) + find_program(CCACHE_PROGRAM ccache) + + if(CCACHE_PROGRAM) + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}") + set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_PROGRAM}") + endif() + + add_compile_options(-Werror) + add_compile_options(-Wall) + add_compile_options(-pedantic) + add_compile_options(-Wextra) + + # Ignore warnings + add_compile_options(-Wno-unused-parameter) + add_compile_options(-Wno-unused-variable) + add_compile_options(-Wno-unknown-pragmas) + + add_compile_options(-fno-omit-frame-pointer) + + if(NOT APPLE) + set(CMAKE_CXX_VISIBILITY_PRESET hidden) + add_compile_options(-fvisibility=hidden) + add_compile_options(-fvisibility-inlines-hidden) + endif() + + # for boost json spirit + add_compile_options(-DBOOST_SPIRIT_THREADSAFE) + + # for tbb, TODO: https://software.intel.com/sites/default/files/managed/b2/d2/TBBRevamp.pdf + add_compile_options(-DTBB_SUPPRESS_DEPRECATED_MESSAGES=1) + + # build deps lib Release + set(_only_release_configuration "-DCMAKE_BUILD_TYPE=Release") + + if(BUILD_STATIC) + SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a") + SET(BUILD_SHARED_LIBRARIES OFF) + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") + + # Note: If bring the -static option, apple will fail to link + if(NOT APPLE) + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") + endif() + + # SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Bdynamic -ldl -lpthread -Wl,-Bstatic -static-libstdc++ ") + endif() + + if(TESTS) + add_compile_options(-DBOOST_TEST_THREAD_SAFE) + endif() + + # Configuration-specific compiler settings. + set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") + set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") + set(CMAKE_CXX_FLAGS_RELEASE "-O3 -g -DNDEBUG") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -DNDEBUG") + + if(USE_LD_GOLD) + execute_process(COMMAND ${CMAKE_C_COMPILER} -fuse-ld=gold -Wl,--version ERROR_QUIET OUTPUT_VARIABLE LD_VERSION) + if("${LD_VERSION}" MATCHES "GNU gold") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=gold") + endif() + endif() + + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") + # Check that we've got GCC 10.0 or newer. + set(GCC_MIN_VERSION "10.0") + execute_process( + COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MARCH_TYPE}") + set(CMAKE_C_FLAGS "-std=c99 -fexceptions ${CMAKE_C_FLAGS} ${MARCH_TYPE}") + + add_compile_options(-fstack-protector-strong) + add_compile_options(-fstack-protector) + + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11.0) + add_compile_options(-fcoroutines) + endif() + + add_compile_options(-fPIC) + add_definitions(-DUSE_STD_RANGES) + elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.0) + set(CMAKE_CXX_FLAGS_DEBUG "-O -g") + endif() + + add_compile_options(-fstack-protector) + add_compile_options(-Winconsistent-missing-override) + + # Some Linux-specific Clang settings. We don't want these for OS X. + if("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") + # Tell Boost that we're using Clang's libc++. Not sure exactly why we need to do. + add_definitions(-DBOOST_ASIO_HAS_CLANG_LIBCXX) + + # Use fancy colors in the compiler diagnostics + add_compile_options(-fcolor-diagnostics) + endif() + endif() + + if(SANITIZE_ADDRESS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined -fno-sanitize=alignment -fsanitize-address-use-after-scope -fsanitize-recover=all") + endif() + + if(SANITIZE_THREAD) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -fno-omit-frame-pointer -fsanitize=thread") + endif() + + if(COVERAGE) + set(TESTS ON) + + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") + set(CMAKE_CXX_FLAGS "-g --coverage ${CMAKE_CXX_FLAGS}") + set(CMAKE_C_FLAGS "-g --coverage ${CMAKE_C_FLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS "--coverage ${CMAKE_SHARED_LINKER_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "--coverage ${CMAKE_EXE_LINKER_FLAGS}") + elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + add_compile_options(-Wno-unused-command-line-argument) + set(CMAKE_CXX_FLAGS "-g -fprofile-arcs -ftest-coverage ${CMAKE_CXX_FLAGS}") + set(CMAKE_C_FLAGS "-g -fprofile-arcs -ftest-coverage ${CMAKE_C_FLAGS}") + endif() + endif() +elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC") + add_definitions(-DUSE_STD_RANGES) + add_compile_options(/std:c++latest) + add_compile_options(-bigobj) + + # MSVC only support static build + set(CMAKE_CXX_FLAGS_DEBUG "/MTd /DEBUG") + set(CMAKE_CXX_FLAGS_MINSIZEREL "/MT /Os") + set(CMAKE_CXX_FLAGS_RELEASE "/MT") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MT /DEBUG") + link_libraries(ws2_32 Crypt32 userenv) +else() + message(WARNING "Your compiler is not tested, if you run into any issues, we'd welcome any patches.") +endif() + +if(ALLOCATOR STREQUAL "tcmalloc") + # include(FindPkgConfig) + # pkg_check_modules(tcmalloc REQUIRED libtcmalloc) + # link_libraries(${tcmalloc_LINK_LIBRARIES}) +elseif(ALLOCATOR STREQUAL "jemalloc") + find_package(jemalloc REQUIRED) + link_libraries(jemalloc) +elseif(ALLOCATOR STREQUAL "mimalloc") + find_package(mimalloc REQUIRED) + link_libraries(mimalloc) +endif() + +# rust static library linking requirements for macos +if(APPLE) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Security -framework CoreFoundation") +else() + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-as-needed -ldl") +endif() + +message("CMAKE_EXE_LINKER_FLAGS: ${CMAKE_EXE_LINKER_FLAGS}") +message("CMAKE_SHARED_LINKER_FLAGS: ${CMAKE_SHARED_LINKER_FLAGS}") +set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY ON) diff --git "a/BFPL\345\243\271/cmake/Coverage.cmake" "b/BFPL\345\243\271/cmake/Coverage.cmake" new file mode 100644 index 00000000..02551cc9 --- /dev/null +++ "b/BFPL\345\243\271/cmake/Coverage.cmake" @@ -0,0 +1,31 @@ +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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: Coverage.cmake +# Function: Define coverage related functions +# ------------------------------------------------------------------------------ +# REMOVE_FILE_PATTERN eg.: '/usr*' '${CMAKE_SOURCE_DIR}/deps**' '${CMAKE_SOURCE_DIR}/evmc*' ‘${CMAKE_SOURCE_DIR}/fisco-bcos*’ +function(config_coverage TARGET REMOVE_FILE_PATTERN) + find_program(LCOV_TOOL lcov) + message(STATUS "lcov tool: ${LCOV_TOOL}") + if (LCOV_TOOL) + add_custom_target(${TARGET} + COMMAND ${LCOV_TOOL} -o ${CMAKE_BINARY_DIR}/coverage.info.in -c -d ${CMAKE_BINARY_DIR}/ + COMMAND ${LCOV_TOOL} -r ${CMAKE_BINARY_DIR}/coverage.info.in ${REMOVE_FILE_PATTERN} -o ${CMAKE_BINARY_DIR}/coverage.info + COMMAND genhtml -q -o ${CMAKE_BINARY_DIR}/CodeCoverage ${CMAKE_BINARY_DIR}/coverage.info) + else () + message(FATAL_ERROR "Can't find lcov tool. Please install lcov") + endif() +endfunction() \ No newline at end of file diff --git "a/BFPL\345\243\271/cmake/GenerateSources.cmake" "b/BFPL\345\243\271/cmake/GenerateSources.cmake" new file mode 100644 index 00000000..93ccfedd --- /dev/null +++ "b/BFPL\345\243\271/cmake/GenerateSources.cmake" @@ -0,0 +1,21 @@ +function(protobuf_generate_cpp PROTO_SRCS PROTO_HDRS MESSAGES_PROTOS PROTO_PATH PROTO_DIR GENERATED_BASE_DIR GENERATED_DIR) + set(MESSAGES_SRCS) + set(MESSAGES_HDRS) + foreach(proto_file ${MESSAGES_PROTOS}) + get_filename_component(proto_dir_abs ${PROTO_DIR} ABSOLUTE) + set(proto_file_abs ${proto_dir_abs}/${proto_file}) + get_filename_component(basename ${proto_file} NAME_WE) + set(generated_files ${GENERATED_DIR}/${basename}.pb.cc ${GENERATED_DIR}/${basename}.pb.h) + + add_custom_command( + OUTPUT ${generated_files} + COMMAND protobuf::protoc --cpp_out ${GENERATED_BASE_DIR} -I ${PROTO_PATH} ${proto_file_abs} + COMMENT "Generating ${generated_files} from ${proto_file_abs}" + VERBATIM + ) + list(APPEND MESSAGES_SRCS ${GENERATED_DIR}/${basename}.pb.cc) + list(APPEND MESSAGES_HDRS ${GENERATED_DIR}/${basename}.pb.h) + endforeach() + set(PROTO_SRCS ${MESSAGES_SRCS} PARENT_SCOPE) + set(PROTO_HDRS ${MESSAGES_HDRS} PARENT_SCOPE) +endfunction(protobuf_generate_cpp) \ No newline at end of file diff --git "a/BFPL\345\243\271/cmake/IncludeDirectories.cmake" "b/BFPL\345\243\271/cmake/IncludeDirectories.cmake" new file mode 100644 index 00000000..5301f32d --- /dev/null +++ "b/BFPL\345\243\271/cmake/IncludeDirectories.cmake" @@ -0,0 +1,15 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +# include_directories(${CMAKE_CURRENT_BINARY_DIR}) +# include_directories(${CMAKE_CURRENT_BINARY_DIR}/bcos-protocol) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/bcos-protocol) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/bcos-txpool) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/bcos-txpool) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/bcos-front) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/bcos-sealer) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/bcos-pbft) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/bcos-sync) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/bcos-gateway) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/bcos-rpc) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/bcos-security) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/bcos-tool) +# include_directories(${CMAKE_CURRENT_SOURCE_DIR}/fisco-bcos-tars-service) \ No newline at end of file diff --git "a/BFPL\345\243\271/cmake/Options.cmake" "b/BFPL\345\243\271/cmake/Options.cmake" new file mode 100644 index 00000000..219b3dcf --- /dev/null +++ "b/BFPL\345\243\271/cmake/Options.cmake" @@ -0,0 +1,144 @@ +# ------------------------------------------------------------------------------ + # Copyright (C) 2021 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: Options.cmake +# Function: Define common compilation options +# ------------------------------------------------------------------------------ + +macro(default_option O DEF) + if (DEFINED ${O}) + if (${${O}}) + set(${O} ON) + else () + set(${O} OFF) + endif() + else () + set(${O} ${DEF}) + endif() +endmacro() + +set(MARCH_TYPE "-mtune=generic") +if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") + message(FATAL "The ${PROJECT_NAME} does not support compiling on 32-bit systems") +endif() + +EXECUTE_PROCESS(COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE ARCHITECTURE) +macro(configure_project) + set(NAME ${PROJECT_NAME}) + + if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING + "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) + endif() + + default_option(BUILD_STATIC OFF) + + default_option(NATIVE OFF) + set(USE_LD_GOLD ON) + if ("${ARCHITECTURE}" MATCHES "aarch64" OR "${ARCHITECTURE}" MATCHES "arm64") + set(NATIVE ON) + set(USE_LD_GOLD OFF) + endif() + if(NATIVE) + set(MARCH_TYPE "-march=native -mtune=native") + endif() + default_option(SANITIZE_ADDRESS OFF) + default_option(SANITIZE_THREAD OFF) + default_option(IPO OFF) + if(IPO) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + endif() + + default_option(TESTS OFF) + default_option(COVERAGE OFF) + + default_option(DEBUG OFF) + if (DEBUG) + add_definitions(-DFISCO_DEBUG) + endif() + + default_option(FULLNODE ON) + default_option(WITH_LIGHTNODE ON) + default_option(WITH_TIKV ON) + default_option(WITH_TARS_SERVICES ON) + default_option(WITH_SM2_OPTIMIZE ON) + default_option(WITH_CPPSDK ON) + default_option(WITH_BENCHMARK ON) + + if(FULLNODE) + list(APPEND VCPKG_MANIFEST_FEATURES "fullnode") + endif() + if(WITH_LIGHTNODE) + list(APPEND VCPKG_MANIFEST_FEATURES "lightnode") + add_compile_definitions(WITH_LIGHTNODE) + endif() + if(WITH_TIKV) + list(APPEND VCPKG_MANIFEST_FEATURES "etcd") + add_compile_definitions(WITH_TIKV) + endif() + if(WITH_SM2_OPTIMIZE) + add_compile_definitions(WITH_SM2_OPTIMIZE) + endif() + + if(NOT ALLOCATOR) + set(ALLOCATOR "tcmalloc") + endif() + + if(ALLOCATOR STREQUAL "tcmalloc") + include(ProjectTCMalloc) + link_libraries(TCMalloc) + elseif(ALLOCATOR STREQUAL "jemalloc") + list(APPEND VCPKG_MANIFEST_FEATURES "jemalloc") + elseif(ALLOCATOR STREQUAL "mimalloc") + list(APPEND VCPKG_MANIFEST_FEATURES "mimalloc") + else() + set(ALLOCATOR "default") + endif() + + if (NOT DEFINED VERSION_SUFFIX) + set(VERSION_SUFFIX "") + endif() +endmacro() + +macro(print_config NAME) + message("") + message("------------------------------------------------------------------------") + message("-- Configuring ${NAME} ${PROJECT_VERSION}${VERSION_SUFFIX}") + message("------------------------------------------------------------------------") + message("-- CMAKE Cmake version and location ${CMAKE_VERSION} (${CMAKE_COMMAND})") + message("-- COMPILER C++ compiler version ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}") + message("-- CMAKE_BUILD_TYPE Build type ${CMAKE_BUILD_TYPE}") + message("-- TARGET_PLATFORM Target platform ${CMAKE_SYSTEM_NAME} ${ARCHITECTURE}") + message("-- BUILD_STATIC Build static ${BUILD_STATIC}") + message("-- COVERAGE Build code coverage ${COVERAGE}") + message("-- TESTS Build tests ${TESTS}") + message("-- NATIVE Build native binary ${NATIVE}") + message("-- IPO Enable IPO optimization ${IPO}") + message("-- SANITIZE_ADDRESS Enable sanitize ${SANITIZE_ADDRESS}") + message("-- SANITIZE_THREAD Enable sanitize ${SANITIZE_THREAD}") + message("-- TOOLCHAIN_FILE CMake toolchain file ${CMAKE_TOOLCHAIN_FILE}") + message("-- ALLOCATOR Allocator ${ALLOCATOR}") + message("------------------------------------------------------------------------") + message("-- Components") + message("------------------------------------------------------------------------") + message("-- FULLNODE Build full node ${FULLNODE}") + message("-- WITH_LIGHTNODE Enable light node ${WITH_LIGHTNODE}") + message("-- WITH_TIKV Enable tikv ${WITH_TIKV}") + message("-- WITH_TARS_SERVICES Enable tars services ${WITH_TARS_SERVICES}") + message("-- WITH_SM2_OPTIMIZE Enable sm2 optimize ${WITH_SM2_OPTIMIZE}") + message("-- WITH_CPPSDK Enable cpp sdk ${WITH_CPPSDK}") + message("------------------------------------------------------------------------") + message("") +endmacro() \ No newline at end of file diff --git "a/BFPL\345\243\271/cmake/ProjectBCOSWASM.cmake" "b/BFPL\345\243\271/cmake/ProjectBCOSWASM.cmake" new file mode 100644 index 00000000..72ac8567 --- /dev/null +++ "b/BFPL\345\243\271/cmake/ProjectBCOSWASM.cmake" @@ -0,0 +1,69 @@ +include(ExternalProject) +include(GNUInstallDirs) + +if(CMAKE_HOST_WIN32) + set(USER_HOME "$ENV{USERPROFILE}") +else() + set(USER_HOME "$ENV{HOME}") +endif() + +message(${USER_HOME}) + +find_program(CARGO_COMMAND NAMES cargo REQUIRED PATHS "${USER_HOME}\\.cargo\\bin") +find_program(RUSTUP_COMMAND NAMES rustup REQUIRED PATHS "${USER_HOME}\\.cargo\\bin") + +if(NOT CARGO_COMMAND OR NOT RUSTUP_COMMAND) + message(FATAL_ERROR "cargo/rustup is not installed") +endif() + +find_program(RUSTC_COMMAND NAMES rustc REQUIRED PATHS "${USER_HOME}\\.cargo\\bin") +if(NOT CARGO_COMMAND OR NOT RUSTC_COMMAND) + message(FATAL_ERROR "rustc is not installed") +endif() + +execute_process(COMMAND ${RUSTC_COMMAND} -V OUTPUT_VARIABLE RUSTC_VERSION_INFO OUTPUT_STRIP_TRAILING_WHITESPACE) +STRING(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" RUSTC_VERSION ${RUSTC_VERSION_INFO}) +# if (${RUSTC_VERSION} VERSION_LESS "1.53.0") +# message(STATUS "rustc ${RUSTC_VERSION} is too old, executing `rustup update` to update it.") +# execute_process(COMMAND rustup update) +# endif() + +# same as https://github.com/WeBankBlockchain/WeDPR-Lab-Crypto/blob/main/rust-toolchain +if(NOT RUSTC_VERSION_REQUIRED) + set(RUSTC_VERSION_REQUIRED "nightly-2022-07-28") +endif() +message(STATUS "set rustc to ${RUSTC_VERSION_REQUIRED} of path ${CMAKE_CURRENT_SOURCE_DIR}/deps/src/") +file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/deps/src/) +execute_process(COMMAND rustup override set ${RUSTC_VERSION_REQUIRED} --path ${CMAKE_CURRENT_SOURCE_DIR}/deps/src/ OUTPUT_QUIET ERROR_QUIET) + +if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") + set(BCOS_WASM_BUILD_MODE "debug") +else() + set(BCOS_WASM_BUILD_MODE "release") +endif() + +ExternalProject_Add(bcos_wasm_project + PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/deps + # DOWNLOAD_NO_PROGRESS 1 + GIT_REPOSITORY https://${URL_BASE}/FISCO-BCOS/bcos-wasm.git + GIT_TAG d5d58e5bf4ec9a7a6f397b36d0aadf9805d5aa29 + GIT_SHALLOW false + BUILD_IN_SOURCE 1 + CONFIGURE_COMMAND "" + BUILD_COMMAND ${CARGO_COMMAND} build && ${CARGO_COMMAND} build --release + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 + LOG_CONFIGURE 1 + LOG_BUILD 0 + LOG_INSTALL 1 + BUILD_BYPRODUCTS /target/${BCOS_WASM_BUILD_MODE}/libbcos_wasm.a /FBWASM.h +) + +ExternalProject_Get_Property(bcos_wasm_project SOURCE_DIR) +set(HERA_INCLUDE_DIRS ${SOURCE_DIR}/include) +file(MAKE_DIRECTORY ${HERA_INCLUDE_DIRS}) # Must exist. + +add_library(fbwasm STATIC IMPORTED) +set_property(TARGET fbwasm PROPERTY IMPORTED_LOCATION ${SOURCE_DIR}/target/release/libbcos_wasm.a) +set_property(TARGET fbwasm PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${SOURCE_DIR}/include) +add_dependencies(fbwasm bcos_wasm_project evmc::instructions) diff --git "a/BFPL\345\243\271/cmake/ProjectGroupSig.cmake" "b/BFPL\345\243\271/cmake/ProjectGroupSig.cmake" new file mode 100644 index 00000000..1e412031 --- /dev/null +++ "b/BFPL\345\243\271/cmake/ProjectGroupSig.cmake" @@ -0,0 +1,56 @@ +include(ExternalProject) +include(GNUInstallDirs) +ExternalProject_Add(GroupSigLib + PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/deps + DOWNLOAD_NAME group_sig_lib-b8b9164.tar.gz + DOWNLOAD_NO_PROGRESS 1 + URL https://${URL_BASE}/FISCO-BCOS/group-signature-lib/archive/9da5a205cb372b721a8581161e92580673a36a2d.tar.gz + URL_HASH SHA256=7749932bda2eb311ad41f0b8da21b1f4861317e98387e6d88fcb9415f698ec7a + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} + LOG_CONFIGURE 1 + LOG_BUILD 1 + LOG_INSTALL 1 + BUILD_IN_SOURCE 1 +) + +ExternalProject_Get_Property(GroupSigLib SOURCE_DIR) +set(LIB_SUFFIX .a) +set(DEPS_INCLUDE_DIR ${SOURCE_DIR}/deps/include) +file(MAKE_DIRECTORY ${DEPS_INCLUDE_DIR}) + +find_library(GMP_LIBRARIES NAMES "libgmp.a") +find_path(GMP_INCLUDE_DIR "gmp.h") +if(NOT GMP_INCLUDE_DIR) + message(FATAL_ERROR "Please install libgmp first") +endif() +add_library(Gmp UNKNOWN IMPORTED GLOBAL) +set_property(TARGET Gmp PROPERTY IMPORTED_LOCATION ${GMP_LIBRARIES}) +set_property(TARGET Gmp PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${GMP_INCLUDE_DIR}) + +add_library(Pbc STATIC IMPORTED GLOBAL) +set_property(TARGET Pbc PROPERTY IMPORTED_LOCATION ${SOURCE_DIR}/deps/lib/libpbc${LIB_SUFFIX}) +set_property(TARGET Pbc PROPERTY INTERFACE_LINK_LIBRARIES Gmp) +set_property(TARGET Pbc PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${DEPS_INCLUDE_DIR} ${GMP_INCLUDE_DIR}) +add_dependencies(Pbc GroupSigLib) + +add_library(PbcSig STATIC IMPORTED GLOBAL) +set_property(TARGET PbcSig PROPERTY IMPORTED_LOCATION ${SOURCE_DIR}/deps/lib/libpbc_sig${LIB_SUFFIX}) +set_property(TARGET PbcSig PROPERTY INTERFACE_LINK_LIBRARIES Pbc) +set_property(TARGET PbcSig PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${DEPS_INCLUDE_DIR}) +add_dependencies(PbcSig GroupSigLib) + +add_library(GroupSig STATIC IMPORTED GLOBAL) +set(GROUPSIG_LIBRARY ${CMAKE_CURRENT_SOURCE_DIR}/deps/lib/libgroup_sig${LIB_SUFFIX}) +set(GROUPSIG_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps/include) + +file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/deps/lib) # Must exist. +file(MAKE_DIRECTORY ${GROUPSIG_INCLUDE_DIR}) # Must exist. + +find_package(cryptopp CONFIG REQUIRED) +set_property(TARGET GroupSig PROPERTY IMPORTED_LOCATION ${GROUPSIG_LIBRARY}) +set_property(TARGET GroupSig PROPERTY INTERFACE_LINK_LIBRARIES PbcSig Pbc Gmp cryptopp-static) +set_property(TARGET GroupSig PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${GROUPSIG_INCLUDE_DIR} ${DEPS_INCLUDE_DIR}) +add_dependencies(GroupSig GroupSigLib) +unset(SOURCE_DIR) diff --git "a/BFPL\345\243\271/cmake/ProjectTCMalloc.cmake" "b/BFPL\345\243\271/cmake/ProjectTCMalloc.cmake" new file mode 100644 index 00000000..702c0d0f --- /dev/null +++ "b/BFPL\345\243\271/cmake/ProjectTCMalloc.cmake" @@ -0,0 +1,43 @@ +include(ExternalProject) + +set(GPERFTOOLS_OPTIONS --disable-shared --disable-cpu-profiler --disable-heap-profiler --disable-heap-checker --disable-debugalloc --enable-minimal) +set(TCMALLOC_LIB_NAME "libtcmalloc_minimal.a") +if (DEBUG) + set(GPERFTOOLS_OPTIONS "") + set(TCMALLOC_LIB_NAME "libtcmalloc${CMAKE_STATIC_LIBRARY_SUFFIX}") +endif() + +set(TCMALLOC_CONFIG ./configure CXXFLAGS=-DHAVE_POSIX_MEMALIGN_SYMBOL=1 --enable-frame-pointers ${GPERFTOOLS_OPTIONS} --prefix=${CMAKE_SOURCE_DIR}/deps/src/gperftools) + +set(TCMALLOC_MAKE make install) + +ExternalProject_Add(gperftools + PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/deps/ + DOWNLOAD_NAME gperftools-2.10.tar.gz + DOWNLOAD_NO_PROGRESS 1 + URL https://github.com/gperftools/gperftools/releases/download/gperftools-2.10/gperftools-2.10.tar.gz + # https://osp-1257653870.cos.ap-guangzhou.myqcloud.com/FISCO-BCOS/FISCO-BCOS/deps/gperftools-2.7.tar.gz + # https://raw.githubusercontent.com/FISCO-BCOS/LargeFiles/master/libs/gperftools-2.7.tar.gz + URL_HASH SHA256=83e3bfdd28b8bcf53222c3798d4d395d52dadbbae59e8730c4a6d31a9c3732d8 + BUILD_IN_SOURCE 1 + LOG_CONFIGURE 1 + LOG_BUILD 1 + LOG_INSTALL 1 + CONFIGURE_COMMAND ${TCMALLOC_CONFIG} + BUILD_COMMAND ${TCMALLOC_MAKE} + INSTALL_COMMAND "" + BUILD_BYPRODUCTS /.libs/${TCMALLOC_LIB_NAME} +) + +ExternalProject_Get_Property(gperftools SOURCE_DIR) +add_library(TCMalloc STATIC IMPORTED GLOBAL) + +set(TCMALLOC_INCLUDE_DIR ${SOURCE_DIR}/include/) +set(TCMALLOC_LIBRARY ${SOURCE_DIR}/.libs/${TCMALLOC_LIB_NAME}) +file(MAKE_DIRECTORY ${TCMALLOC_INCLUDE_DIR}) # Must exist. + +set_property(TARGET TCMalloc PROPERTY IMPORTED_LOCATION ${TCMALLOC_LIBRARY}) +set_property(TARGET TCMalloc PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${TCMALLOC_INCLUDE_DIR}) + +add_dependencies(TCMalloc gperftools) +unset(SOURCE_DIR) diff --git "a/BFPL\345\243\271/cmake/ProjectTiKVClient.cmake" "b/BFPL\345\243\271/cmake/ProjectTiKVClient.cmake" new file mode 100644 index 00000000..64efd7d5 --- /dev/null +++ "b/BFPL\345\243\271/cmake/ProjectTiKVClient.cmake" @@ -0,0 +1,37 @@ +include(ExternalProject) +include(GNUInstallDirs) + +if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") + set(TIKV_BUILD_MODE "debug") +else() + set(TIKV_BUILD_MODE "release") +endif() + +find_program(CARGO_COMMAND NAMES cargo REQUIRED PATHS "${USER_HOME}\\.cargo\\bin") + +# FIXME: when release 3.1.0 modify tikv-client log level to info +ExternalProject_Add(tikv_client_project + PREFIX ${CMAKE_SOURCE_DIR}/deps + GIT_REPOSITORY https://${URL_BASE}/FISCO-BCOS/tikv-client-cpp.git + GIT_TAG 20634f10560b9cf528ccff4c941ac0c4ef4c8ed2 + BUILD_IN_SOURCE true + # SOURCE_DIR ${CMAKE_SOURCE_DIR}/deps/src/ + CONFIGURE_COMMAND ${CARGO_COMMAND} install cxxbridge-cmd@1.0.75 + BUILD_COMMAND ${CARGO_COMMAND} build && ${CARGO_COMMAND} build --release && make target/${TIKV_BUILD_MODE}/libtikv_client.a + INSTALL_COMMAND "" + BUILD_BYPRODUCTS /target/${TIKV_BUILD_MODE}/libtikv_client.a + # LOG_BUILD true +) + +ExternalProject_Get_Property(tikv_client_project SOURCE_DIR) +ExternalProject_Get_Property(tikv_client_project BINARY_DIR) +set(KVCLIENT_INCLUDE_DIRS ${SOURCE_DIR}/include) +file(MAKE_DIRECTORY ${KVCLIENT_INCLUDE_DIRS}) # Must exist. + +find_package(OpenSSL REQUIRED) + +add_library(kv_client INTERFACE IMPORTED) +set_property(TARGET kv_client PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${KVCLIENT_INCLUDE_DIRS}) +set_property(TARGET kv_client PROPERTY INTERFACE_LINK_LIBRARIES ${SOURCE_DIR}/target/${TIKV_BUILD_MODE}/libtikv_client.a OpenSSL::SSL OpenSSL::Crypto) + +add_dependencies(kv_client tikv_client_project) diff --git "a/BFPL\345\243\271/cmake/ProjectWABT.cmake" "b/BFPL\345\243\271/cmake/ProjectWABT.cmake" new file mode 100644 index 00000000..57112fdb --- /dev/null +++ "b/BFPL\345\243\271/cmake/ProjectWABT.cmake" @@ -0,0 +1,40 @@ +include(ExternalProject) + +if (APPLE) + set(PATCH_COMMAND "") +else() + set(PATCH_COMMAND COMMAND sed -i "53a if (NOT \"\${CMAKE_PROJECT_VERSION}\")" CMakeLists.txt COMMAND sed -i "54a set(CMAKE_PROJECT_VERSION \${PROJECT_VERSION})" CMakeLists.txt COMMAND sed -i "55a endif()" CMakeLists.txt) +endif() + +ExternalProject_Add(wabt_project + PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/deps + DOWNLOAD_NAME wabt_1.0.23.tar.gz + DOWNLOAD_NO_PROGRESS 1 + URL https://codeload.github.com/WebAssembly/wabt/tar.gz/1.0.23 + URL_HASH SHA256=925f47020705cd2cc00a4ff6a36ab08f8adf6d08c7eac5057db0db38b6b2f16d + # PATCH_COMMAND ${PATCH_COMMAND} + BUILD_IN_SOURCE 0 + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + -DBUILD_TESTS=OFF -DBUILD_TOOLS=OFF -DBUILD_LIBWASM=OFF + LOG_CONFIGURE 1 + LOG_DOWNLOAD 1 + LOG_UPDATE 1 + LOG_BUILD 1 + LOG_INSTALL 1 + BUILD_BYPRODUCTS /libwabt.a +) + +ExternalProject_Get_Property(wabt_project SOURCE_DIR) +ExternalProject_Get_Property(wabt_project BINARY_DIR) +add_library(wabt STATIC IMPORTED GLOBAL) + +set(WABT_INCLUDE_DIR ${SOURCE_DIR}/ ${BINARY_DIR}/) +set(WABT_LIBRARY ${BINARY_DIR}/libwabt.a) +file(MAKE_DIRECTORY ${WABT_INCLUDE_DIR}) # Must exist. + +set_property(TARGET wabt PROPERTY IMPORTED_LOCATION ${WABT_LIBRARY}) +set_property(TARGET wabt PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${WABT_INCLUDE_DIR}) + +add_dependencies(wabt wabt_project) +unset(SOURCE_DIR) diff --git "a/BFPL\345\243\271/cmake/SearchTestCases.cmake" "b/BFPL\345\243\271/cmake/SearchTestCases.cmake" new file mode 100644 index 00000000..436d364b --- /dev/null +++ "b/BFPL\345\243\271/cmake/SearchTestCases.cmake" @@ -0,0 +1,65 @@ +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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: SearchTestCases.cmake +# Function: cmake to help search test cases +# ------------------------------------------------------------------------------ +function(config_test_cases TEST_ARGS SOURCES TEST_BINARY_PATH EXCLUDE_SUITES) + foreach(file ${SOURCES}) + file(STRINGS ${file} test_list_raw REGEX "BOOST_.*TEST_(SUITE|CASE|SUITE_END)") + set(TestSuite "DEFAULT") + set(TestSuitePath "") + foreach(test_raw ${test_list_raw}) + string(REGEX REPLACE ".*TEST_(SUITE|CASE)\\(([^ ,\\)]*).*" "\\1 \\2" test ${test_raw}) + + #skip disabled + if (";${EXCLUDE_SUITES};" MATCHES ";${TestSuite};") + continue() + endif() + + if(test MATCHES "^SUITE .*") + string(SUBSTRING ${test} 6 -1 TestSuite) + set(TestSuitePath "${TestSuitePath}/${TestSuite}") + + if(FASTCTEST) + if (";${EXCLUDE_SUITES};" MATCHES ";${TestSuite};") + continue() + endif() + if (NOT ";${allSuites};" MATCHES ";${TestSuite};") + string(SUBSTRING ${TestSuitePath} 1 -1 TestSuitePathFixed) + list(APPEND allSuites ${TestSuite}) + separate_arguments(TEST_ARGS) + set(TestArgs -t ${TestSuitePathFixed} -- ${TEST_ARGS}) + add_test(NAME ${TestSuitePathFixed} WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${TEST_BINARY_PATH} ${TestArgs}) + endif() + endif() + elseif(test MATCHES "^CASE .*") + if(NOT FASTCTEST) + if(NOT test MATCHES "^CASE &createRandom.*") + string(SUBSTRING ${test} 5 -1 TestCase) + string(SUBSTRING ${TestSuitePath} 1 -1 TestSuitePathFixed) + separate_arguments(TEST_ARGS) + set(TestArgs -t ${TestSuitePathFixed}/${TestCase} -- ${TEST_ARGS}) + add_test(NAME ${TestSuitePathFixed}/${TestCase} WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${TEST_BINARY_PATH} ${TestArgs}) + endif() + endif() + elseif (";${test_raw};" MATCHES "BOOST_AUTO_TEST_SUITE_END()") + #encountered SUITE_END block. remove one test suite from the suite path. + string(FIND ${TestSuitePath} "/" Position REVERSE) + string(SUBSTRING ${TestSuitePath} 0 ${Position} TestSuitePath) + endif() + endforeach(test_raw) + endforeach(file) +endfunction() diff --git "a/BFPL\345\243\271/cmake/TargetSettings.cmake" "b/BFPL\345\243\271/cmake/TargetSettings.cmake" new file mode 100644 index 00000000..19f5ab8c --- /dev/null +++ "b/BFPL\345\243\271/cmake/TargetSettings.cmake" @@ -0,0 +1,72 @@ +# bcos-utilities +set(UTILITIES_TARGET "bcos-utilities") + +# bcos-table +set(TABLE_TARGET "table") + +# bcos-crypto +set(CRYPTO_TARGET "bcos-crypto") +set(WEDPR_CRYPTO_TARGET "wedpr-crypto::crypto") +set(WEDPR_EXTEND_LIB "wedpr-crypto::extend-crypto") + +# bcos-codec +set(CODEC_TARGET "codec") + +# bcos-protocol +set(PROTOCOL_TARGET "protocol") + +# bcos-tars-protocol +set(TARS_PROTOCOL_TARGET "protocol-tars") + +# bcos-txpool +set(TXPOOL_TARGET "txpool") + +# bcos-sealer +set(SEALER_TARGET "sealer") + +#bcos-security +set(SECURITY_TARGET "security") + +# bcos-sync +set(SYNC_TARGET "sync") + +# bcos-pbft +set(PBFT_TARGET "pbft") + +# bcos-storage +set(STORAGE_TARGET "storage") + +# bcos-ledger +set(LEDGER_TARGET "ledger") + +# bcos-executor +set(EXECUTOR_TARGET "executor") + +# bcos-scheduler +set(SCHEDULER_TARGET "scheduler") + +# bcos-front +SET(FRONT_TARGET "front") + +# bcos-gateway +set(GATEWAY_TARGET "gateway") + +# bcos-rcp +set(RPC_TARGET "rpc") + +# bcos-tool +set(TOOL_TARGET "tool") + +# bcos-leader-election +set(LEADER_ELECTION_TARGET "leader_election") + +# light node +set(LIGHTNODE_TARGET "bcos-lightnode") + +# libinitializer +set(PROTOCOL_INIT_LIB protocol_init) +set(FRONTSERVICE_INIT_LIB front_init) +set(PBFT_INIT_LIB pbft_init) +set(TXPOOL_INIT_LIB txpool_init) +set(COMMAND_HELPER_LIB command) +set(INIT_LIB init) diff --git "a/BFPL\345\243\271/cmake/Version.cmake" "b/BFPL\345\243\271/cmake/Version.cmake" new file mode 100644 index 00000000..4e4bf6e8 --- /dev/null +++ "b/BFPL\345\243\271/cmake/Version.cmake" @@ -0,0 +1,2 @@ +set(VERSION "3.1.0") +set(VERSION_SUFFIX "") \ No newline at end of file diff --git "a/BFPL\345\243\271/cmake/fiscobcos-config.cmake.in" "b/BFPL\345\243\271/cmake/fiscobcos-config.cmake.in" new file mode 100644 index 00000000..51670bbd --- /dev/null +++ "b/BFPL\345\243\271/cmake/fiscobcos-config.cmake.in" @@ -0,0 +1,5 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/fiscobcosTargets.cmake") + +check_required_components(fiscobcos) \ No newline at end of file diff --git "a/BFPL\345\243\271/cmake/shell/check-commit.sh" "b/BFPL\345\243\271/cmake/shell/check-commit.sh" new file mode 100644 index 00000000..8c07dc05 --- /dev/null +++ "b/BFPL\345\243\271/cmake/shell/check-commit.sh" @@ -0,0 +1,110 @@ +#!/bin/bash +# "Copyright [2018] " +# @ function: check code format of {.h, .hpp and .cpp} files +# @ require : Make sure your machine is linux (centos/ubuntu), yum or apt is ready +# @ author : wheatli +# @ file : check-commit.sh +# @ date : 2018 + +SHELL_FOLDER=$( + cd $(dirname $0) + pwd +) + +check_script="clang-format-9" +commit_limit=6 +file_limit=35 +insert_limit=$1 +# eg: https://api.github.com/repos/FISCO-BCOS/FISCO-BCOS +repo_api=${2} +new_file_header_length=35 + +skip_check_words="sync code" + +LOG_ERROR() { + content=${1} + echo -e "\033[31m${content}\033[0m" +} + +LOG_INFO() { + content=${1} + echo -e "\033[32m${content}\033[0m" +} + +execute_cmd() { + command="${1}" + eval ${command} + ret=$? + if [ $ret -ne 0 ]; then + LOG_ERROR "FAILED of command: ${command}" + exit 1 + else + LOG_INFO "SUCCESS of command: ${command}" + fi +} + +function check_codeFormat() { + # Redirect output to stderr. + exec 1>&2 + sum=0 + for file in $(git diff-index --name-status HEAD^ -- | grep -v D | grep -E '\.[ch](pp)?$' | awk '{print $2}'); do + execute_cmd "$check_script -style=file -i $file" + sum=$(expr ${sum} + $?) + done + + if [ ${sum} -ne 0 ]; then + echo "######### ERROR: Format check failed, please adjust them before commit" + exit 1 + fi +} + +function check_PR_limit() { + if [ "${TRAVIS_PULL_REQUEST}" != "false" ]; then + local skip=$(curl -s ${repo_api}/pulls/${TRAVIS_PULL_REQUEST} | grep "title\"" | grep "${skip_check_words}") + if [ ! -z "${skip}" ]; then + LOG_INFO "sync code PR, skip PR limit check!" + exit 0 + else + LOG_INFO "PR-${TRAVIS_PULL_REQUEST}, checking PR limit..." + fi + fi + local files=$(git diff --shortstat HEAD^ | awk -F ' ' '{print $1}') + # if [ ${file_limit} -lt ${files} ]; then + # LOG_ERROR "modify ${files} files, limit is ${file_limit}" + # exit 1 + # fi + local new_files=$(git diff HEAD^ | grep "new file" | wc -l) + local test_insertions=$(git diff --numstat HEAD^ | grep "test/" | awk -F ' ' '{sum+=$1}END{print sum}') + local insertions=$(git diff --shortstat HEAD^ | awk -F ' ' '{print $4}') + local valid_insertions=$((insertions - new_files * new_file_header_length - test_insertions)) + + if [ ${insert_limit} -lt ${valid_insertions} ]; then + LOG_ERROR "insert ${insertions} lines, valid is ${valid_insertions}, limit is ${insert_limit}" + exit 1 + fi + + local deletions=$(git diff --shortstat HEAD^ | awk -F ' ' '{print $6}') + #if [ ${delete_limit} -lt ${deletions} ];then + # LOG_ERROR "delete ${deletions} lines, limit is ${delete_limit}" + # exit 1 + #fi + local commits=$(git rev-list --count HEAD^..HEAD) + if [ ${commit_limit} -lt ${commits} ]; then + LOG_ERROR "${commits} commits, limit is ${commit_limit}" + exit 1 + fi + local unique_commit=$(git log --format=%s HEAD^..HEAD | sort -u | wc -l) + if [ ${unique_commit} -ne ${commits} ]; then + LOG_ERROR "${commits} != ${unique_commit}, please make commit message unique!" + exit 1 + fi + local merges=$(git log --format=%s HEAD^..HEAD | grep -i merge | wc -l) + if [ ${merges} -gt 2 ]; then + LOG_ERROR "PR contain merge : ${merges}, Please rebase!" + exit 1 + fi + LOG_INFO "modify ${files} files, insert ${insertions} lines, valid insertion ${valid_insertions}, delete ${deletions} lines. Total ${commits} commits." +} + +check_codeFormat +check_PR_limit diff --git "a/BFPL\345\243\271/cmake/test/CMakeLists.txt" "b/BFPL\345\243\271/cmake/test/CMakeLists.txt" new file mode 100644 index 00000000..86f23aba --- /dev/null +++ "b/BFPL\345\243\271/cmake/test/CMakeLists.txt" @@ -0,0 +1,36 @@ +#------------------------------------------------------------------------------ +# Top-level CMake file for bcos-cmake-scripts. +# ------------------------------------------------------------------------------ +# Copyright (C) 2021 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. +#------------------------------------------------------------------------------ +cmake_minimum_required(VERSION 3.10) +list(APPEND CMAKE_MODULE_PATH ../) +set(BCOS_DOXYGEN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../") + +#init hunter +include(HunterGate) +HunterGate( + URL "https://github.com/cpp-pm/hunter/archive/v0.23.295.tar.gz" + SHA1 "8a3447594fa5948cc7c6888dd86a9e823259c69f" +) +project(bcos-cmake-scripts-test VERSION "3.0") +# basic settings +include(Options) +configure_project() +include(CompilerSettings) +include(BuildDocs) + +include(Coverage) +config_coverage("test_cov" "") diff --git "a/BFPL\345\243\271/concepts/CMakeLists.txt" "b/BFPL\345\243\271/concepts/CMakeLists.txt" new file mode 100644 index 00000000..49239134 --- /dev/null +++ "b/BFPL\345\243\271/concepts/CMakeLists.txt" @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.17) +project(bcos-concepts VERSION ${VERSION}) + +add_library(bcos-concepts INTERFACE) +target_include_directories(bcos-concepts INTERFACE + $ + $) +target_link_libraries(bcos-concepts INTERFACE bcos-framework bcos-crypto) + +include(GNUInstallDirs) +install(TARGETS bcos-concepts EXPORT fiscobcosTargets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") +install(DIRECTORY "bcos-concepts" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" FILES_MATCHING PATTERN "*.h") \ No newline at end of file diff --git "a/BFPL\345\243\271/concepts/bcos-concepts/Basic.h" "b/BFPL\345\243\271/concepts/bcos-concepts/Basic.h" new file mode 100644 index 00000000..9aaca4d6 --- /dev/null +++ "b/BFPL\345\243\271/concepts/bcos-concepts/Basic.h" @@ -0,0 +1,62 @@ +#pragma once +#include "Exception.h" +#include +#include +#include +#include +#include + +namespace bcos::concepts +{ + +// clang-format off +struct NoEnoughSpace : public bcos::error::Exception {}; +// clang-format on + +template +concept ByteBuffer = RANGES::contiguous_range && + std::is_trivial_v>> && + std::is_standard_layout_v>> && + (sizeof(RANGES::range_value_t>) == 1); + +template +concept PointerLike = requires(Pointer p) +{ + *p; + p.operator->(); +}; + +template +auto& getRef(Input& input) +{ + if constexpr (PointerLike) + { + return *input; + } + else + { + return input; + } +} + +template +concept DynamicRange = requires(Range range, size_t newSize) +{ + RANGES::range; + range.resize(newSize); + range.reserve(newSize); +}; + +void resizeTo(RANGES::range auto& out, std::integral auto size) +{ + if ((size_t)RANGES::size(out) < (size_t)size) + { + if constexpr (bcos::concepts::DynamicRange>) + { + out.resize(size); + return; + } + BOOST_THROW_EXCEPTION(NoEnoughSpace{}); + } +} +} // namespace bcos::concepts diff --git "a/BFPL\345\243\271/concepts/bcos-concepts/ByteBuffer.h" "b/BFPL\345\243\271/concepts/bcos-concepts/ByteBuffer.h" new file mode 100644 index 00000000..5e65e38b --- /dev/null +++ "b/BFPL\345\243\271/concepts/bcos-concepts/ByteBuffer.h" @@ -0,0 +1,45 @@ +#pragma once + +#include "Basic.h" +#include +#include +#include +#include +#include + +namespace bcos::concepts::bytebuffer +{ + +template +concept ByteBuffer = RANGES::contiguous_range && + std::is_trivial_v>> && + std::is_standard_layout_v>> && + (sizeof(RANGES::range_value_t>) == 1); + +template +concept Hash = ByteBuffer; + +std::string_view toView(ByteBuffer auto const& buffer) +{ + return std::string_view((const char*)RANGES::data(buffer), RANGES::size(buffer)); +} + +void assignTo(ByteBuffer auto const& from, ByteBuffer auto& to) +{ + resizeTo(to, RANGES::size(from)); + std::copy_n(reinterpret_cast(RANGES::data(from)), RANGES::size(from), + reinterpret_cast(RANGES::data(to))); +} + +bool equalTo(ByteBuffer auto const& left, ByteBuffer auto const& right) +{ + if (RANGES::size(left) != RANGES::size(right)) + { + return false; + } + + return std::equal(reinterpret_cast(RANGES::data(left)), + reinterpret_cast(RANGES::data(left)) + RANGES::size(left), + reinterpret_cast(RANGES::data(right))); +} +} // namespace bcos::concepts::bytebuffer \ No newline at end of file diff --git "a/BFPL\345\243\271/concepts/bcos-concepts/Exception.h" "b/BFPL\345\243\271/concepts/bcos-concepts/Exception.h" new file mode 100644 index 00000000..56537088 --- /dev/null +++ "b/BFPL\345\243\271/concepts/bcos-concepts/Exception.h" @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include + +namespace bcos::error +{ +struct Exception : public virtual std::exception, public virtual boost::exception +{ +}; + +using ErrorCode = boost::error_info; +using ErrorMessage = boost::error_info; +} // namespace bcos::error \ No newline at end of file diff --git "a/BFPL\345\243\271/concepts/bcos-concepts/Hash.h" "b/BFPL\345\243\271/concepts/bcos-concepts/Hash.h" new file mode 100644 index 00000000..4b8d37a5 --- /dev/null +++ "b/BFPL\345\243\271/concepts/bcos-concepts/Hash.h" @@ -0,0 +1,26 @@ +#pragma once +#include "Basic.h" +#include +#include +#include + +namespace bcos::concepts::hash +{ + +// ADL-based customization points +template +concept Hashable = requires(ObjectType object, std::string out1, std::vector out2, + std::vector out3, std::vector out4) +{ + impl_calculate(object, out1); + impl_calculate(object, out2); + impl_calculate(object, out3); + impl_calculate(object, out4); +}; + +template +auto calculate(auto const& obj, ByteBuffer auto& out) +{ + return impl_calculate(obj, out); +} +} // namespace bcos::concepts::hash diff --git "a/BFPL\345\243\271/concepts/bcos-concepts/Serialize.h" "b/BFPL\345\243\271/concepts/bcos-concepts/Serialize.h" new file mode 100644 index 00000000..04bd1e14 --- /dev/null +++ "b/BFPL\345\243\271/concepts/bcos-concepts/Serialize.h" @@ -0,0 +1,30 @@ +#pragma once +#include "Basic.h" +#include "ByteBuffer.h" +#include + +namespace bcos::concepts::serialize +{ + +// ADL-based customization points +template +concept Serializable = requires( + ObjectType object, std::vector out, std::vector in) +{ + impl_encode(object, out); + impl_decode(in, object); +}; + +template +auto encode(ObjectType const& in, bcos::concepts::bytebuffer::ByteBuffer auto& out) +{ + impl_encode(in, out); +} + +template +void decode(bcos::concepts::bytebuffer::ByteBuffer auto const& in, ObjectType& out) +{ + impl_decode(in, out); +} + +} // namespace bcos::concepts::serialize \ No newline at end of file diff --git "a/BFPL\345\243\271/concepts/bcos-concepts/front/Front.h" "b/BFPL\345\243\271/concepts/bcos-concepts/front/Front.h" new file mode 100644 index 00000000..f7628c8b --- /dev/null +++ "b/BFPL\345\243\271/concepts/bcos-concepts/front/Front.h" @@ -0,0 +1,45 @@ +#pragma once +#include "../Basic.h" +#include "../Serialize.h" +#include +#include +#include + +namespace bcos::concepts::front +{ + +template +class FrontBase +{ +public: + using NodeType = uint16_t; + using ModuleID = int; + + template + task::Task nodeIDs() + { + return impl().impl_nodeIDs(); + } + + task::Task sendMessageByNodeID(ModuleID moduleID, auto const& nodeID, + bcos::concepts::serialize::Serializable auto const& request, + bcos::concepts::serialize::Serializable auto& response) + { + return impl().impl_sendMessageByNodeID(moduleID, nodeID, request, response); + } + + task::Task broadcastMessage(NodeType type, ModuleID moduleID, + bcos::concepts::serialize::Serializable auto const& request) + { + return impl().impl_broadcastMessage(type, moduleID, request); + } + +private: + friend Impl; + auto& impl() { return static_cast(*this); } +}; + +template +concept Front = std::derived_from> || + std::derived_from>; +} // namespace bcos::concepts::front \ No newline at end of file diff --git "a/BFPL\345\243\271/concepts/bcos-concepts/ledger/Ledger.h" "b/BFPL\345\243\271/concepts/bcos-concepts/ledger/Ledger.h" new file mode 100644 index 00000000..083a109e --- /dev/null +++ "b/BFPL\345\243\271/concepts/bcos-concepts/ledger/Ledger.h" @@ -0,0 +1,122 @@ +#pragma once +#include "../protocol/Block.h" +#include "../storage/Storage.h" +#include +#include + +namespace bcos::concepts::ledger +{ + +template +concept TransactionOrReceipt = bcos::concepts::transaction::Transaction || + bcos::concepts::receipt::TransactionReceipt; + +// clang-format off +struct DataFlagBase {}; +struct ALL: public DataFlagBase {}; +struct HEADER: public DataFlagBase {}; +struct TRANSACTIONS_METADATA: public DataFlagBase {}; +struct TRANSACTIONS: public DataFlagBase {}; +struct RECEIPTS: public DataFlagBase {}; +struct NONCES: public DataFlagBase {}; +// clang-format on +template +concept DataFlag = std::derived_from; + +struct Status +{ + int64_t total = 0; + int64_t failed = 0; + int64_t blockNumber = 0; +}; + +// All method in ledger is uncacheed +template +class LedgerBase +{ +public: + template + auto getBlock(bcos::concepts::block::BlockNumber auto blockNumber, + bcos::concepts::block::Block auto& block) + { + return impl().template impl_getBlock(blockNumber, block); + } + + template + auto setBlock(bcos::concepts::block::Block auto block) + { + return impl().template impl_setBlock(std::move(block)); + } + + auto getBlockNumberByHash( + bcos::concepts::bytebuffer::ByteBuffer auto const& hash, std::integral auto& number) + { + return impl().impl_getBlockNumberByHash(hash, number); + } + + auto getBlockHashByNumber( + std::integral auto number, bcos::concepts::bytebuffer::ByteBuffer auto& hash) + { + return impl().impl_getBlockHashByNumber(number, hash); + } + + auto getTransactions(RANGES::range auto const& hashes, RANGES::range auto& out) requires + TransactionOrReceipt>> + { + return impl().impl_getTransactions(hashes, out); + } + + auto getStatus() { return impl().impl_getStatus(); } + + template + auto setTransactions(RANGES::range auto const& inputs) requires bcos::concepts::ledger:: + TransactionOrReceipt>> + { + auto hashesRange = inputs | RANGES::views::transform([](auto const& input) { + std::array hash; + bcos::concepts::hash::calculate(input, hash); + return hash; + }); + auto buffersRange = inputs | RANGES::views::transform([](auto const& input) { + std::vector buffer; + bcos::concepts::serialize::encode(input, buffer); + return buffer; + }); + + constexpr auto isTransaction = + bcos::concepts::transaction::Transaction>; + return setTransactionBuffers( + std::move(hashesRange), std::move(buffersRange)); + } + + template + auto setTransactionBuffers(RANGES::range auto hashes, RANGES::range auto buffers) + { + return impl().template impl_setTransactions( + std::move(hashes), std::move(buffers)); + } + + template + requires std::derived_from> || + std::derived_from> + auto sync(LedgerType& source, bool onlyHeader) + { + return impl().template impl_sync(source, onlyHeader); + } + + auto setupGenesisBlock(bcos::concepts::block::Block auto block) + { + return impl().template impl_setupGenesisBlock(std::move(block)); + } + +private: + friend Impl; + auto& impl() { return static_cast(*this); } +}; + +template +concept Ledger = std::derived_from> || + std::derived_from>; + +} // namespace bcos::concepts::ledger \ No newline at end of file diff --git "a/BFPL\345\243\271/concepts/bcos-concepts/protocol/Block.h" "b/BFPL\345\243\271/concepts/bcos-concepts/protocol/Block.h" new file mode 100644 index 00000000..6d446e7b --- /dev/null +++ "b/BFPL\345\243\271/concepts/bcos-concepts/protocol/Block.h" @@ -0,0 +1,78 @@ +#pragma once +#include "Receipt.h" +#include "Transaction.h" +#include +#include +#include + +namespace bcos::concepts::block +{ + +template +concept BlockNumber = std::integral; + +template +concept ParentInfo = requires(ParentInfoType parentInfo) +{ + std::integral; + parentInfo.blockHash; +}; + +template +concept Signature = requires(SignatureType signature) +{ + std::integral; + signature.signature; +}; + +template +concept BlockHeaderData = requires(BlockHeaderDataType blockHeaderData) +{ + std::integral; + requires RANGES::range && + ParentInfo>; + blockHeaderData.txRoot; + blockHeaderData.receiptRoot; + blockHeaderData.stateRoot; + BlockNumber; + std::unsigned_integral; + std::unsigned_integral; + std::integral; + RANGES::range; + blockHeaderData.extraData; + RANGES::range; +}; + +template +concept BlockHeader = requires(BlockHeaderType block) +{ + serialize::Serializable; + hash::Hashable; + BlockHeaderType{}; + BlockHeaderData; + block.dataHash; + requires RANGES::range && + Signature>; +}; + +template +concept Block = requires(BlockType block) +{ + serialize::Serializable; + hash::Hashable; + BlockType{}; + std::integral; + std::integral; + BlockHeader; + requires RANGES::range && + bcos::concepts::transaction::Transaction< + RANGES::range_value_t>; + requires RANGES::range && bcos::concepts::receipt::TransactionReceipt< + RANGES::range_value_t>; + requires RANGES::range; // TODO: add metadata concept + requires RANGES::range && + ByteBuffer>; + requires RANGES::range && + ByteBuffer>; +}; +} // namespace bcos::concepts::block \ No newline at end of file diff --git "a/BFPL\345\243\271/concepts/bcos-concepts/protocol/Receipt.h" "b/BFPL\345\243\271/concepts/bcos-concepts/protocol/Receipt.h" new file mode 100644 index 00000000..cc15c573 --- /dev/null +++ "b/BFPL\345\243\271/concepts/bcos-concepts/protocol/Receipt.h" @@ -0,0 +1,38 @@ +#pragma once +#include "../Basic.h" +#include "../Hash.h" +#include "../Serialize.h" + +namespace bcos::concepts::receipt +{ +template +concept LogEntry = requires(LogEntryType logEntry) +{ + logEntry.address; + RANGES::range; + logEntry.data; +}; + +template +concept TransactionReceiptData = requires(TransactionReceiptDataType transactionReceiptData) +{ + std::integral; + transactionReceiptData.gasUsed; + transactionReceiptData.contractAddress; + std::integral; + transactionReceiptData.output; + requires RANGES::range && + LogEntry>; + std::integral; +}; + +template +concept TransactionReceipt = requires(TransactionReceiptType transactionReceipt) +{ + bcos::concepts::hash::Hashable; + bcos::concepts::serialize::Serializable; + TransactionReceiptData; + transactionReceipt.dataHash; + transactionReceipt.message; +}; +} // namespace bcos::concepts::receipt \ No newline at end of file diff --git "a/BFPL\345\243\271/concepts/bcos-concepts/protocol/Transaction.h" "b/BFPL\345\243\271/concepts/bcos-concepts/protocol/Transaction.h" new file mode 100644 index 00000000..585390ed --- /dev/null +++ "b/BFPL\345\243\271/concepts/bcos-concepts/protocol/Transaction.h" @@ -0,0 +1,39 @@ +#pragma once + +#include "../Basic.h" +#include "../Hash.h" +#include "../Serialize.h" +#include +#include +#include + +namespace bcos::concepts::transaction +{ +template +concept TransactionData = requires(TransactionDataType transactionData) +{ + std::integral; + transactionData.chainID; + transactionData.groupID; + std::integral; + transactionData.nonce; + transactionData.to; + transactionData.input; + transactionData.abi; +}; + +template +concept Transaction = requires(TransactionType transaction) +{ + bcos::concepts::hash::Hashable; + bcos::concepts::serialize::Serializable; + TransactionType{}; + TransactionData; + transaction.dataHash; + transaction.signature; + transaction.sender; + std::integral; + std::integral; + transaction.source; +}; +} // namespace bcos::concepts::transaction \ No newline at end of file diff --git "a/BFPL\345\243\271/concepts/bcos-concepts/scheduler/Scheduler.h" "b/BFPL\345\243\271/concepts/bcos-concepts/scheduler/Scheduler.h" new file mode 100644 index 00000000..78a4d709 --- /dev/null +++ "b/BFPL\345\243\271/concepts/bcos-concepts/scheduler/Scheduler.h" @@ -0,0 +1,27 @@ +#pragma once + +#include "../Basic.h" +#include "../protocol/Receipt.h" +#include "../protocol/Transaction.h" + +namespace bcos::concepts::scheduler +{ +template +class SchedulerBase +{ +public: + auto call(bcos::concepts::transaction::Transaction auto const& transaction, + bcos::concepts::receipt::TransactionReceipt auto& receipt) + { + return impl().impl_call(transaction, receipt); + } + +private: + friend Impl; + auto& impl() { return static_cast(*this); } +}; + +template +concept Scheduler = std::derived_from> || + std::derived_from>; +} // namespace bcos::concepts::scheduler \ No newline at end of file diff --git "a/BFPL\345\243\271/concepts/bcos-concepts/storage/Storage.h" "b/BFPL\345\243\271/concepts/bcos-concepts/storage/Storage.h" new file mode 100644 index 00000000..5860b8ac --- /dev/null +++ "b/BFPL\345\243\271/concepts/bcos-concepts/storage/Storage.h" @@ -0,0 +1,42 @@ +#pragma once + +#include "../Basic.h" +#include +#include +#include + +namespace bcos::concepts::storage +{ + +template +class StorageBase +{ +public: + std::optional getRow(std::string_view table, std::string_view key) + { + return impl().impl_getRow(table, key); + } + + std::vector> getRows( + std::string_view table, RANGES::range auto&& keys) + { + return impl().impl_getRows(table, std::forward(keys)); + } + + void setRow(std::string_view table, std::string_view key, bcos::storage::Entry entry) + { + impl().impl_setRow(table, key, std::move(entry)); + } + + void createTable(std::string tableName) { impl().impl_createTable(std::move(tableName)); } + +private: + friend Impl; + StorageBase() = default; + auto& impl() { return static_cast(*this); } +}; + +template +concept Storage = std::derived_from>; + +} // namespace bcos::concepts::storage \ No newline at end of file diff --git "a/BFPL\345\243\271/concepts/bcos-concepts/transaction_pool/TransactionPool.h" "b/BFPL\345\243\271/concepts/bcos-concepts/transaction_pool/TransactionPool.h" new file mode 100644 index 00000000..24161b72 --- /dev/null +++ "b/BFPL\345\243\271/concepts/bcos-concepts/transaction_pool/TransactionPool.h" @@ -0,0 +1,28 @@ +#pragma once + +#include "../Basic.h" +#include "../protocol/Receipt.h" +#include "../protocol/Transaction.h" + +namespace bcos::concepts::transacton_pool +{ +template +class TransactionPoolBase +{ +public: + auto submitTransaction(bcos::concepts::transaction::Transaction auto transaction, + bcos::concepts::receipt::TransactionReceipt auto& receipt) + { + return impl().impl_submitTransaction(transaction, receipt); + } + +private: + friend Impl; + auto& impl() { return static_cast(*this); } +}; + +template +concept TransactionPool = std::derived_from> || + std::derived_from>; +} // namespace bcos::concepts::transacton_pool \ No newline at end of file diff --git "a/BFPL\345\243\271/concepts/tests/testTask.cpp" "b/BFPL\345\243\271/concepts/tests/testTask.cpp" new file mode 100644 index 00000000..e69de29b diff --git "a/BFPL\345\243\271/docs/FISCO_BCOS_Logo.svg" "b/BFPL\345\243\271/docs/FISCO_BCOS_Logo.svg" new file mode 100644 index 00000000..7bb0bb47 --- /dev/null +++ "b/BFPL\345\243\271/docs/FISCO_BCOS_Logo.svg" @@ -0,0 +1,15 @@ + + + + logo/横/原色 + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git "a/BFPL\345\243\271/docs/README_EN.md" "b/BFPL\345\243\271/docs/README_EN.md" new file mode 100644 index 00000000..823e915b --- /dev/null +++ "b/BFPL\345\243\271/docs/README_EN.md" @@ -0,0 +1,57 @@ +[中文](../README.md) / English + +![](./FISCO_BCOS_Logo.svg) + +[![codecov](https://codecov.io/gh/FISCO-BCOS/FISCO-BCOS/branch/master/graph/badge.svg)](https://codecov.io/gh/FISCO-BCOS/FISCO-BCOS) +[![CodeFactor](https://www.codefactor.io/repository/github/fisco-bcos/FISCO-BCOS/badge)](https://www.codefactor.io/repository/github/fisco-bcos/FISCO-BCOS) +[![GitHub All Releases](https://img.shields.io/github/downloads/FISCO-BCOS/FISCO-BCOS/total.svg)](https://github.com/FISCO-BCOS/FISCO-BCOS) + +FISCO BCOS is a secure and robust enterprise-level open source financial consortium blockchain platform, which was jointly created by the FISCO open source working group and officially launched in December 2017. + +In a single-chain configuration, the performance can reach more than 20k TPS. FISCO BCOS provides rich features including group architecture, parallel computing, distributed storage, pluggable consensus mechanism, privacy protection algorithms, and OSCCA-approved cryptography modules. After a long period of practical testing in the production environment by industry partners, it has financial-grade high performance, high availability and high security. + +FISCO BCOS 3.0 is the latest major upgrade with the following new features: + +- **Microservice Architecture**: The modular design architecture of microservices supports independent and parallel expansion of computing, storage, and network. + +- **Deterministic multi-contract parallel algorithm**: The intelligent parallel scheduling engine can automate contract-level parallelism and greatly increase transaction throughput. + +- **Pipelined PBFT consensus mechanism**: A new upgrade and revision of PBFT to pipeline block packaging, verification, and consensus stages, which significantly improves system efficiency. + +- **WeBankBlockchain Liquid and WASM virtual machine**: Besides Solidity, it also supports WeBankBlockchain Liquid contract, which makes the contract more powerful. + +- **Blockchain File System**: Supports the "what you see is what you get" blockchain resource management method, which improve user experience for blockchain contract writers and data governance users. + +- **Scenario-optimized service models**: Supports different service models for service deployment, including Air (lightweight) version, Pro (professional) version and Max (massive cluster) version to meet the needs of diversified scenarios. + +## Documentation + +- **[FISCO BCOS 3.x Documentation(latest)](https://fisco-bcos-doc.readthedocs.io/zh_CN/latest/)** + +- **[FISCO BCOS 2.x Documentation(stable)](https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/)** + +## Featured applications + +**FISCO BCOS** has been adopted in over hundreds of applications in areas like government affairs, finances, charity, health care, education, transport, copyright, product tracing, supply chain, recruitment, agriculture, social communication, and entertainment. + +- Finance: inter-institutional reconciliation, supply chain finance, tourism finance, etc. +- Judicial services: arbitration chain, digital IOUs, etc. +- Copyright: copyright registration and trading, etc. +- Social management: real-estate registration, etc. + +Featured use cases are provided [here](https://mp.weixin.qq.com/s/RJwRMChWt6mhJrysyBLAmA). + +## Contributing code + +- Your contributions are most welcome and appreciated. Please read the [contribution instructions](https://mp.weixin.qq.com/s/_w_auH8X4SQQWO3lhfNrbQ). +- If this project is useful to you, please star us on GitHub project page! + +## Join our community + +The FISCO BCOS community is one of the most active open-source blockchain communities in China. It provides long-term technical support for both institutional and individual developers and users of FISCO BCOS. Thousands of technical enthusiasts from numerous industry sectors have joined this community, studying and using FISCO BCOS platform. If you are also interested, you are most welcome to join us for more support and fun. + +![](https://raw.githubusercontent.com/FISCO-BCOS/LargeFiles/master/images/QR_image_en.png) + +## License + +All contributions are made under the Apache License 2.0, see [LICENSE](../LICENSE) for details. diff --git "a/BFPL\345\243\271/fisco-bcos-air/AirNodeInitializer.cpp" "b/BFPL\345\243\271/fisco-bcos-air/AirNodeInitializer.cpp" new file mode 100644 index 00000000..87f12ba5 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-air/AirNodeInitializer.cpp" @@ -0,0 +1,132 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Initializer for all the modules + * @file LocalInitializer.cpp + * @author: yujiechen + * @date 2021-10-28 + */ +#include "AirNodeInitializer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos::node; +using namespace bcos::initializer; +using namespace bcos::gateway; +using namespace bcos::rpc; +using namespace bcos::tool; + +void AirNodeInitializer::init(std::string const& _configFilePath, std::string const& _genesisFile) +{ + g_BCOSConfig.setCodec(std::make_shared()); + + boost::property_tree::ptree pt; + boost::property_tree::read_ini(_configFilePath, pt); + + m_logInitializer = std::make_shared(); + m_logInitializer->initLog(pt); + INITIALIZER_LOG(INFO) << LOG_DESC("initGlobalConfig"); + + // load nodeConfig + // Note: this NodeConfig is used to create Gateway which not init the nodeName + auto keyFactory = std::make_shared(); + auto nodeConfig = std::make_shared(keyFactory); + nodeConfig->loadGenesisConfig(_genesisFile); + nodeConfig->loadConfig(_configFilePath); + + m_nodeInitializer = std::make_shared(); + m_nodeInitializer->initConfig(_configFilePath, _genesisFile, "", true); + + // create gateway + // DataEncryption will be inited in ProtocolInitializer when storage_security.enable = true, + // otherwise dataEncryption() will return nullptr + GatewayFactory gatewayFactory(nodeConfig->chainId(), "localRpc", + m_nodeInitializer->protocolInitializer()->dataEncryption()); + auto gateway = gatewayFactory.buildGateway(_configFilePath, true, nullptr, "localGateway"); + m_gateway = gateway; + + // create the node + m_nodeInitializer->init(bcos::protocol::NodeArchitectureType::AIR, _configFilePath, + _genesisFile, m_gateway, true, m_logInitializer->logPath()); + + auto pbftInitializer = m_nodeInitializer->pbftInitializer(); + auto groupInfo = m_nodeInitializer->pbftInitializer()->groupInfo(); + auto nodeService = + std::make_shared(m_nodeInitializer->ledger(), m_nodeInitializer->scheduler(), + m_nodeInitializer->txPoolInitializer()->txpool(), pbftInitializer->pbft(), + pbftInitializer->blockSync(), m_nodeInitializer->protocolInitializer()->blockFactory()); + + // create rpc + RpcFactory rpcFactory(nodeConfig->chainId(), m_gateway, keyFactory, + m_nodeInitializer->protocolInitializer()->dataEncryption()); + rpcFactory.setNodeConfig(nodeConfig); + m_rpc = rpcFactory.buildLocalRpc(groupInfo, nodeService); + auto topicManager = + std::dynamic_pointer_cast(gateway->amop()->topicManager()); + topicManager->setLocalClient(m_rpc); + m_nodeInitializer->initNotificationHandlers(m_rpc); + + // NOTE: this should be last called + m_nodeInitializer->initSysContract(); +} + +void AirNodeInitializer::start() +{ + if (m_nodeInitializer) + { + m_nodeInitializer->start(); + } + + if (m_gateway) + { + m_gateway->start(); + } + + if (m_rpc) + { + m_rpc->start(); + } +} + +void AirNodeInitializer::stop() +{ + try + { + if (m_rpc) + { + m_rpc->stop(); + } + if (m_gateway) + { + m_gateway->stop(); + } + if (m_nodeInitializer) + { + m_nodeInitializer->stop(); + } + } + catch (std::exception const& e) + { + std::cout << "stop bcos-node failed for " << boost::diagnostic_information(e); + exit(-1); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-air/AirNodeInitializer.h" "b/BFPL\345\243\271/fisco-bcos-air/AirNodeInitializer.h" new file mode 100644 index 00000000..d20dfa0a --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-air/AirNodeInitializer.h" @@ -0,0 +1,57 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Initializer for all the modules + * @file LocalInitializer.h + * @author: yujiechen + * @date 2021-10-28 + */ + +#pragma once +#include "libinitializer/Initializer.h" +#include +#include +namespace bcos +{ +namespace node +{ +class AirNodeInitializer +{ +public: + AirNodeInitializer() = default; + virtual ~AirNodeInitializer() { stop(); } + + virtual void init(std::string const& _configFilePath, std::string const& _genesisFile); + virtual void start(); + virtual void stop(); + +protected: + virtual void initAirNode(std::string const& _configFilePath, std::string const& _genesisFile, + bcos::gateway::GatewayInterface::Ptr _gateway) + { + m_nodeInitializer = std::make_shared(); + m_nodeInitializer->initAirNode( + _configFilePath, _genesisFile, _gateway, m_logInitializer->logPath()); + } + +private: + BoostLogInitializer::Ptr m_logInitializer; + bcos::initializer::Initializer::Ptr m_nodeInitializer; + + bcos::gateway::GatewayInterface::Ptr m_gateway; + bcos::rpc::RPCInterface::Ptr m_rpc; +}; +} // namespace node +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-air/CMakeLists.txt" "b/BFPL\345\243\271/fisco-bcos-air/CMakeLists.txt" new file mode 100644 index 00000000..c77adce7 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-air/CMakeLists.txt" @@ -0,0 +1,5 @@ +aux_source_directory(. SRC_LIST) + +set(BINARY_NAME fisco-bcos) +add_executable(${BINARY_NAME} ${SRC_LIST}) +target_link_libraries(${BINARY_NAME} PUBLIC ${INIT_LIB} ${PBFT_INIT_LIB} ${TOOL_TARGET} ${COMMAND_HELPER_LIB} ${RPC_TARGET} ${GATEWAY_TARGET}) diff --git "a/BFPL\345\243\271/fisco-bcos-air/Common.h" "b/BFPL\345\243\271/fisco-bcos-air/Common.h" new file mode 100644 index 00000000..3be56db1 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-air/Common.h" @@ -0,0 +1,59 @@ +/** + * Copyright (C) 2021 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: yujiechen + * @date 2021-06-11 + */ + +#pragma once +#include "bcos-utilities/Common.h" +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace node +{ +class ExitHandler +{ +public: + void exit() { exitHandler(0); } + static void exitHandler(int signal) + { + std::cout << "[" << bcos::getCurrentDateTime() << "] " + << "exit because receive signal " << signal << std::endl; + ExitHandler::c_shouldExit.store(true); + } + bool shouldExit() const { return ExitHandler::c_shouldExit.load(); } + + static std::atomic_bool c_shouldExit; +}; +std::atomic_bool ExitHandler::c_shouldExit = {false}; + +void setDefaultOrCLocale() +{ +#if __unix__ + if (!std::setlocale(LC_ALL, "")) + { + setenv("LC_ALL", "C", 1); + } +#endif +} +} // namespace node +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-air/main.cpp" "b/BFPL\345\243\271/fisco-bcos-air/main.cpp" new file mode 100644 index 00000000..768a9933 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-air/main.cpp" @@ -0,0 +1,86 @@ +/** + * Copyright (C) 2021 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. + * + * @brief main for the fisco-bcos + * @file main.cpp + * @author: yujiechen + * @date 2021-07-26 + * @brief main for the fisco-bcos + * @file main.cpp + * @author: ancelmo + * @date 2021-10-14 + */ +#include "AirNodeInitializer.h" +#include "Common.h" +#include "libinitializer/CommandHelper.h" +#include +#include +#include + +using namespace bcos::node; +using namespace bcos::initializer; +using namespace bcos::node; + +int main(int argc, const char* argv[]) +{ + /// set LC_ALL + setDefaultOrCLocale(); + std::set_terminate([]() { + std::cerr << "terminate handler called, print stacks" << std::endl; + void* trace_elems[20]; + int trace_elem_count(backtrace(trace_elems, 20)); + char** stack_syms(backtrace_symbols(trace_elems, trace_elem_count)); + for (int i = 0; i < trace_elem_count; ++i) + { + std::cout << stack_syms[i] << "\n"; + } + free(stack_syms); + std::cerr << "terminate handler called, print stack end" << std::endl; + abort(); + }); + // get datetime and output welcome info + ExitHandler exitHandler; + signal(SIGTERM, &ExitHandler::exitHandler); + signal(SIGABRT, &ExitHandler::exitHandler); + signal(SIGINT, &ExitHandler::exitHandler); + + // Note: the initializer must exist in the life time of the whole program + auto initializer = std::make_shared(); + try + { + auto param = bcos::initializer::initAirNodeCommandLine(argc, argv, false); + initializer->init(param.configFilePath, param.genesisFilePath); + bcos::initializer::showNodeVersionMetric(); + initializer->start(); + } + catch (std::exception const& e) + { + bcos::initializer::printVersion(); + std::cout << "[" << bcos::getCurrentDateTime() << "] "; + std::cout << "start fisco-bcos failed, error:" << boost::diagnostic_information(e) + << std::endl; + return -1; + } + bcos::initializer::printVersion(); + std::cout << "[" << bcos::getCurrentDateTime() << "] "; + std::cout << "The fisco-bcos is running..." << std::endl; + while (!exitHandler.shouldExit()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + initializer.reset(); + std::cout << "[" << bcos::getCurrentDateTime() << "] "; + std::cout << "fisco-bcos program exit normally." << std::endl; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-demo/CMakeLists.txt" "b/BFPL\345\243\271/fisco-bcos-demo/CMakeLists.txt" new file mode 100644 index 00000000..09b8fc96 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-demo/CMakeLists.txt" @@ -0,0 +1,14 @@ +file(GLOB SRC_LIST "*.cpp") +file(GLOB HEADERS "*.h") + +find_package(tarscpp CONFIG REQUIRED) + +include_directories(${CMAKE_SOURCE_DIR}) +include_directories(${CMAKE_SOURCE_DIR}/bcos-framework) + +add_executable(echo_client_sample echo_client_sample.cpp) +target_link_libraries(echo_client_sample PUBLIC ${TOOL_TARGET} ${GATEWAY_TARGET} ${TARS_PROTOCOL_TARGET} ${CRYPTO_TARGET} ${UTILITIES_TARGET}) + + +add_executable(echo_server_sample echo_server_sample.cpp) +target_link_libraries(echo_server_sample PUBLIC ${TOOL_TARGET} ${GATEWAY_TARGET} ${TARS_PROTOCOL_TARGET} ${CRYPTO_TARGET} ${UTILITIES_TARGET}) \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-demo/distributed_ratelimiter_checker.cpp" "b/BFPL\345\243\271/fisco-bcos-demo/distributed_ratelimiter_checker.cpp" new file mode 100644 index 00000000..d3f5957b --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-demo/distributed_ratelimiter_checker.cpp" @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2021 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 distributed_ratlimiter_checker.cpp + * @author: octopuswang + * @date 2022-10-12 + */ +#include "bcos-utilities/BoostLog.h" +#include "bcos-utilities/Common.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::gateway; +using namespace bcos::gateway::ratelimiter; + +void usage() +{ + std::cerr << "Usage: ./distributed-ratelimiter-checker {redis host} {redis port} {pool size} " + "{client count} {rate} " + "{limit token}" + "{interval}\n" + << std::endl; + std::cerr << "eg:" << std::endl; + std::cerr << "\t./distributed-ratelimiter-checker 127.0.0.1 6379 16 16 limittoken 1 2\n " + << std::endl; + exit(0); +} + +struct StatData +{ + std::atomic totalFailedC{0}; + std::atomic totalSucC{0}; + + std::atomic totalFailedData{0}; + std::atomic totalSucData{0}; + + std::atomic lastFailedC{0}; + std::atomic lastSucC{0}; + + std::atomic lastFailedData{0}; + std::atomic lastSucData{0}; + + void resetLast() + { + lastFailedC = 0; + lastSucC = 0; + lastFailedData = 0; + lastSucData = 0; + } + + void update(int64_t _data, bool _suc) + { + if (_suc) + { + totalSucC++; + lastSucC++; + totalSucData += _data; + lastSucData += _data; + } + else + { + totalFailedC++; + lastFailedC++; + totalFailedData += _data; + lastFailedData += _data; + } + } +}; + +// TODO: add latency check + +int main(int argc, char** argv) +{ + if (argc < 8) + { + usage(); + } + + std::string redisIP = argv[1]; + uint16_t redisPort = atoi(argv[2]); + int32_t redisPoolSize = std::stol(argv[3]); + uint32_t clientCount = std::stoul(argv[4]); + std::string limitToken = argv[5]; + uint32_t rate = std::stoul(argv[6]) * 1024 * 1024 * 8; + uint32_t rateInterval = std::stoul(argv[7]); + + std::cout << " [distributed-ratelimiter-checker] parameters:" << redisIP << std::endl; + std::cout << "\t### redisIP:" << redisIP << std::endl; + std::cout << "\t### redisPort:" << redisPort << std::endl; + std::cout << "\t### redisPoolSize:" << redisPoolSize << std::endl; + std::cout << "\t### clientCount:" << clientCount << std::endl; + std::cout << "\t### limitToken:" << limitToken << std::endl; + std::cout << "\t### rate:" << rate << std::endl; + std::cout << "\t### interval:" << rateInterval << std::endl; + + auto factory = std::make_shared("", ""); + + GatewayConfig::RedisConfig redisConfig; + redisConfig.host = redisIP; + redisConfig.port = redisPort; + redisConfig.connectionPoolSize = redisPoolSize; + redisConfig.timeout = 1000; + + // init all redis instance as client + std::vector threads(clientCount); + // + bool allThreadsInitSuc = false; + + // stat statistics + StatData stateData; + + // init random + srand(time(NULL)); + + for (uint32_t i = 0; i < clientCount; i++) + { + auto redis = factory->initRedis(redisConfig); + auto rateLimiter = + std::make_shared(redis, limitToken, rate, rateInterval); + threads.emplace_back([rateLimiter, rate, rateInterval, &allThreadsInitSuc, &stateData]() { + while (true) + { + if (!allThreadsInitSuc) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + continue; + } + + auto start = utcTimeUs(); + + // tryAcquire (0, 1/10 * rate)] once time + auto acquire = random() % (rate / 10); + acquire = (acquire > 0 ? acquire : 1); + + bool result = rateLimiter->tryAcquire(acquire); + + // auto end = utcTimeUs(); + // if (end - start >= 500) + // { + // std::cerr << " [distributed ratelimiter][timeout]" + // << LOG_KV("tid", std::this_thread::get_id()) + // << LOG_KV("acquire", acquire) << LOG_KV("result", result) + // << LOG_KV("elapsedUS", (end - start)) << std::endl; + // } + + // state suc + stateData.update(acquire, result); + + // sleep ms + auto sleepMS = random() % (2 * rateInterval * 10); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + }); + }; + + // stat thread + threads.emplace_back([&allThreadsInitSuc, rate, rateInterval, &stateData]() { + while (true) + { + if (!allThreadsInitSuc) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + continue; + } + + // reset last stat + stateData.resetLast(); + + std::this_thread::sleep_for(std::chrono::seconds(rateInterval)); + + // stat report + std::cerr << " [distributed ratelimiter][stat]" << LOG_DESC(" stat -> ") + << LOG_KV("interval", rateInterval) + << LOG_KV("totalSucC", stateData.totalSucC) + << LOG_KV("totalFailedC", stateData.totalFailedC) + << LOG_KV("totalSucData", stateData.totalSucData) + << LOG_KV("totalFailedData", stateData.totalFailedData) + << LOG_KV("lastSucC", stateData.lastSucC) + << LOG_KV("lastFailedC", stateData.lastFailedC) + << LOG_KV("lastSucData", stateData.lastSucData) + << LOG_KV("lastFailedData", stateData.lastFailedData) << LOG_KV("rate", rate) + << LOG_KV(" STATUS ", stateData.lastSucData <= (rate + int64_t(0.05 * rate)) ? + "OK" : + "OverFlow") + << std::endl; + } + }); + + // init completely + allThreadsInitSuc = true; + + // join all thread + std::for_each(threads.begin(), threads.end(), [](std::thread& _t) mutable { + if (_t.joinable()) + { + _t.join(); + } + }); + + return 0; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-demo/echo_client_sample.cpp" "b/BFPL\345\243\271/fisco-bcos-demo/echo_client_sample.cpp" new file mode 100644 index 00000000..c95e167b --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-demo/echo_client_sample.cpp" @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2021 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 echo_client_sample.cpp + * @author: yujiechen + * @date 2022-06-10 + */ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::gateway; +using namespace bcos::tool; + +void usage() +{ + std::cerr << "Usage: ./echo-client-sample qps(MBit/s) ${server_address} ${port} " + "payloadSize(KBytes, default is 1MBytes)\n" + << std::endl; + exit(0); +} + +void sendMessage(NodeIPEndpoint const& _endPoint, std::shared_ptr _msg, + std::shared_ptr _service, std::shared_ptr _rateLimiter) +{ + while (true) + { + _rateLimiter->acquire(1, true); + auto seq = _service->messageFactory()->newSeq(); + _msg->setSeq(seq); + auto startT = utcTime(); + auto msgSize = _msg->payload()->size(); + _service->asyncSendMessageByEndPoint(_endPoint, _msg, + [msgSize, startT](NetworkException _e, std::shared_ptr _session, + std::shared_ptr) { + if (_e.errorCode()) + { + BCOS_LOG(WARNING) << LOG_DESC("asyncSendMessage network error") + << LOG_KV("code", _e.errorCode()) << LOG_KV("msg", _e.what()); + return; + } + BCOS_LOG(INFO) << LOG_DESC("receiveResponse, timecost:") << (utcTime() - startT) + << LOG_KV("msgSize", msgSize); + }); + } +} + +int main(int argc, char** argv) +{ + if (argc <= 3) + { + usage(); + } + std::string hostIp = argv[1]; + uint16_t port = atoi(argv[2]); + NodeIPEndpoint serverEndPoint(hostIp, port); + uint64_t qps = (atol(argv[3])) * 1024 * 1024; + // default payLoadSize is 1MB + uint64_t payLoadSize = 1024 * 1024; + if (argc > 3) + { + payLoadSize = (atol(argv[4]) * 1024); + } + std::cout << "### payLoad:" << payLoadSize << std::endl; + std::cout << "### qps: " << qps << std::endl; + int64_t packetQPS = (qps) / (payLoadSize * 8); + std::cout << "### packetQPS: " << packetQPS << std::endl; + + g_BCOSConfig.setCodec(std::make_shared()); + auto keyFactory = std::make_shared(); + auto nodeConfig = std::make_shared(); + std::string configFilePath = "config.ini"; + nodeConfig->loadConfig(configFilePath); + + auto logInitializer = std::make_shared(); + boost::property_tree::ptree pt; + boost::property_tree::read_ini(configFilePath, pt); + logInitializer->initLog(pt); + + GatewayFactory gatewayFactory(nodeConfig->chainId(), "localClient", nullptr); + auto gateway = gatewayFactory.buildGateway(configFilePath, true, nullptr, "localClient"); + auto service = std::dynamic_pointer_cast(gateway->p2pInterface()); + + gateway->start(); + // construct message + auto msg = std::dynamic_pointer_cast(service->messageFactory()->buildMessage()); + msg->setPacketType(999); + std::string randStr(payLoadSize, 'a'); + msg->setPayload(std::make_shared(randStr.begin(), randStr.end())); + auto rateLimiter = std::make_shared(packetQPS); + sendMessage(serverEndPoint, msg, service, rateLimiter); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-demo/echo_server_sample.cpp" "b/BFPL\345\243\271/fisco-bcos-demo/echo_server_sample.cpp" new file mode 100644 index 00000000..b19dab57 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-demo/echo_server_sample.cpp" @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2021 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 echo_server_sample.cpp + * @author: yujiechen + * @date 2022-06-10 + */ +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::gateway; +using namespace bcos::tool; + +void usage() +{ + std::cerr << "Usage: echo-server-sample \n" + << "Example:\n" + << "./echo-server-sample\n"; + std::exit(0); +} + +int main(int argc, char** argv) +{ + g_BCOSConfig.setCodec(std::make_shared()); + auto keyFactory = std::make_shared(); + auto nodeConfig = std::make_shared(); + std::string configFilePath = "config.ini"; + nodeConfig->loadConfig(configFilePath); + + auto logInitializer = std::make_shared(); + boost::property_tree::ptree pt; + boost::property_tree::read_ini(configFilePath, pt); + logInitializer->initLog(pt); + + GatewayFactory gatewayFactory(nodeConfig->chainId(), "localClient", nullptr); + auto gateway = gatewayFactory.buildGateway(configFilePath, true, nullptr, "localClient"); + auto service = std::dynamic_pointer_cast(gateway->p2pInterface()); + + gateway->start(); + + + service->registerHandlerByMsgType( + 999, [](NetworkException _e, std::shared_ptr _session, P2PMessage::Ptr _msg) { + if (_e.errorCode()) + { + return; + } + auto startT = utcTime(); + _msg->setRespPacket(); + _session->session()->asyncSendMessage(_msg); + BCOS_LOG(INFO) << LOG_DESC("sendResponse") << LOG_KV("timeCost", (utcTime() - startT)) + << LOG_KV("msgSize", (_msg->payload()->size())); + }); + while (true) + { + // TEST_SERVER_LOG(INFO, MODULE_NAME) << LOG_BADGE(" [Main] ===>>>> "); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + return EXIT_SUCCESS; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/CMakeLists.txt" "b/BFPL\345\243\271/fisco-bcos-tars-service/CMakeLists.txt" new file mode 100644 index 00000000..37690498 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/CMakeLists.txt" @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) + +macro(compile_service SERVICE_SOURCE_PATH BINARY_NAME) + add_subdirectory(${SERVICE_SOURCE_PATH}) + + add_custom_command(OUTPUT ${BINARY_NAME}.tgz + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/fisco-bcos-tars-service/${SERVICE_SOURCE_PATH}/${BINARY_NAME} ${CMAKE_BINARY_DIR}/fisco-bcos-tars-service/${BINARY_NAME}/${BINARY_NAME} + COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR}/fisco-bcos-tars-service tar czfv ${BINARY_NAME}.tgz ${BINARY_NAME}/${BINARY_NAME} + COMMENT "Compressing ${BINARY_NAME}..." DEPENDS ${CMAKE_BINARY_DIR}/fisco-bcos-tars-service/${SERVICE_SOURCE_PATH}/${BINARY_NAME}) + + add_custom_target(${BINARY_NAME}-tar DEPENDS ${BINARY_NAME}.tgz ${BINARY_NAME}) + list(APPEND SERVICE_TAR_LIST ${BINARY_NAME}-tar) +endmacro() + +# RpcService +set(RPC_BINARY_NAME BcosRpcService) +compile_service("RpcService/main" ${RPC_BINARY_NAME}) + +# GatewayService +set(GATEWAY_BINARY_NAME BcosGatewayService) +compile_service("GatewayService/main" ${GATEWAY_BINARY_NAME}) + +# pro-NodeService +set(PRO_NODE_SERVICE_BINARY_NAME BcosNodeService) +compile_service("NodeService/pro" ${PRO_NODE_SERVICE_BINARY_NAME}) + +# max-NodeService +set(MAX_NODE_SERVICE_BINARY_NAME BcosMaxNodeService) +compile_service("NodeService/max" ${MAX_NODE_SERVICE_BINARY_NAME}) + +if(WITH_TIKV) + # ExecutorService + set(EXECUTOR_BINARY_NAME BcosExecutorService) + compile_service("ExecutorService/main" ${EXECUTOR_BINARY_NAME}) +endif() + +add_custom_target(tar DEPENDS ${SERVICE_TAR_LIST}) \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/Common/TarsUtils.h" "b/BFPL\345\243\271/fisco-bcos-tars-service/Common/TarsUtils.h" new file mode 100644 index 00000000..cc1e590f --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/Common/TarsUtils.h" @@ -0,0 +1,182 @@ +#pragma once +#include "bcos-tars-protocol/bcos-tars-protocol/impl/TarsServantProxyCallback.h" +#include "bcos-utilities/BoostLog.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RPCSERVICE_LOG(LEVEL) BCOS_LOG(LEVEL) << "[RPCSERVICE][INITIALIZER]" +#define GATEWAYSERVICE_LOG(LEVEL) BCOS_LOG(LEVEL) << "[GATEWAYSERVICE][INITIALIZER]" +#define TXPOOLSERVICE_LOG(LEVEL) BCOS_LOG(LEVEL) << "[TXPOOLSERVICE]" +#define PBFTSERVICE_LOG(LEVEL) BCOS_LOG(LEVEL) << "[PBFTSERVICE]" +#define FRONTSERVICE_LOG(LEVEL) BCOS_LOG(LEVEL) << "[FRONTSERVICE]" + +namespace bcostars +{ +inline tars::TC_Endpoint string2TarsEndPoint(const std::string& _strEndPoint) +{ + std::vector temp; + boost::split(temp, _strEndPoint, boost::is_any_of(":"), boost::token_compress_on); + + if (temp.size() != 2) + { + BOOST_THROW_EXCEPTION(bcos::InvalidParameter() << bcos::errinfo_comment( + "incorrect string endpoint, it should be in IP:Port format")); + } + + tars::TC_Endpoint ep(temp[0], boost::lexical_cast(temp[1]), 60000); + return ep; +} + +inline std::string getProxyDesc(std::string const& _servantName) +{ + std::string desc = + tars::ServerConfig::Application + "." + tars::ServerConfig::ServerName + "." + _servantName; + return desc; +} +inline std::string getLogPath() +{ + return tars::ServerConfig::LogPath + "/" + tars::ServerConfig::Application + "/" + + tars::ServerConfig::ServerName; +} + +inline std::string endPointToString( + std::string const& _serviceName, const std::string& _host, uint16_t _port) +{ + return _serviceName + "@tcp -h " + _host + " -p " + boost::lexical_cast(_port); +} + +inline std::string endPointToString( + std::string const& _serviceName, tars::TC_Endpoint const& _endPoint) +{ + return endPointToString(_serviceName, _endPoint.getHost(), _endPoint.getPort()); +} + +inline std::string endPointToString( + std::string const& _serviceName, const std::vector& _eps) +{ + if (_eps.empty()) + { + BOOST_THROW_EXCEPTION( + bcos::InvalidParameter() << bcos::errinfo_comment( + "vector should not be empty in endPointToString")); + } + + bool first = true; + std::string endPointStr = _serviceName; + for (const auto& _ep : _eps) + { + endPointStr += (first ? "@" : ":"); + endPointStr += + ("tcp -h " + _ep.getHost() + " -p " + boost::lexical_cast(_ep.getPort())); + + first = false; + } + return endPointStr; +} + +inline std::pair getEndPointDescByAdapter( + tars::Application* _application, std::string const& _servantName) +{ + auto adapters = _application->getEpollServer()->getBindAdapters(); + if (adapters.size() == 0) + { + return std::make_pair(false, ""); + } + auto prxDesc = getProxyDesc(_servantName); + auto adapterName = prxDesc + "Adapter"; + for (auto const& adapter : adapters) + { + if (adapter->getName() == adapterName) + { + return std::make_pair(true, endPointToString(prxDesc, adapter->getEndpoint())); + } + } + return std::make_pair(false, ""); +} + +template +S createServantProxy(tars::Communicator* communicator, std::string const& _serviceName, + TarsServantProxyOnConnectHandler _connectHandler = TarsServantProxyOnConnectHandler(), + TarsServantProxyOnCloseHandler _closeHandler = TarsServantProxyOnCloseHandler()) +{ + auto prx = communicator->stringToProxy(_serviceName); + BCOS_LOG(INFO) << LOG_DESC("createServantProxy ") << LOG_KV("serviceName", _serviceName) + << LOG_KV("proxy addr", prx.get()); + if (!prx->tars_get_push_callback()) + { + auto proxyCallback = new bcostars::TarsServantProxyCallback(_serviceName); + + if (_connectHandler) + { + proxyCallback->setOnConnectHandler(_connectHandler); + } + + if (_closeHandler) + { + proxyCallback->setOnCloseHandler(_closeHandler); + } + + prx->tars_set_push_callback(proxyCallback); + } + + prx->tars_async_ping(); + prx->tars_reconnect(5); + + return prx; +} + +template +S createServantProxy(std::string const& _serviceName) +{ + return createServantProxy(tars::Application::getCommunicator().get(), _serviceName, + TarsServantProxyOnConnectHandler(), TarsServantProxyOnCloseHandler()); +} + +template +S createServantProxy(std::string const& _serviceName, const std::string& _host, uint16_t _port) +{ + auto endPointStr = endPointToString(_serviceName, _host, _port); + return createServantProxy(endPointStr); +} + +template +S createServantProxy(std::string const& _serviceName, const tars::TC_Endpoint& _ep) +{ + auto endPointStr = endPointToString(_serviceName, _ep); + return createServantProxy(endPointStr); +} + +template +S createServantProxy(std::string const& _serviceName, const std::vector& _eps) +{ + std::string endPointStr = endPointToString(_serviceName, _eps); + + return createServantProxy(endPointStr); +} + +template +S createServantProxy(bool _withEndPoints, std::string const& _serviceName, + const std::vector& _eps = std::vector{}) +{ + std::string serviceParams; + if (_withEndPoints) + { + serviceParams = endPointToString(_serviceName, _eps); + } + else + { + serviceParams = _serviceName; + } + + return createServantProxy(serviceParams); +} + +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/ExecutorServiceServer.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/ExecutorServiceServer.cpp" new file mode 100644 index 00000000..d0ac5d89 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/ExecutorServiceServer.cpp" @@ -0,0 +1,259 @@ +/** + * 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 ExecutorServiceServer.h + * @author: yujiechen + * @date 2022-5-10 + */ +#include "ExecutorServiceServer.h" +#include +#include +#include +#include + +using namespace bcostars; + +bcostars::ExecutionMessage toTarsMessage(const bcos::protocol::ExecutionMessage::UniquePtr& message) +{ + if (message) + { + return ((bcostars::protocol::ExecutionMessageImpl::UniquePtr&)message)->inner(); + } + else + { + return bcostars::ExecutionMessage(); + } +} + +bcostars::Error ExecutorServiceServer::status( + bcostars::ExecutorStatus& _output, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + m_executor->status( + [_current](bcos::Error::Ptr _error, bcos::protocol::ExecutorStatus::UniquePtr _status) { + bcostars::ExecutorStatus status; + status.seq = _status->seq(); + async_response_status(_current, toTarsError(_error), std::move(status)); + }); + return bcostars::Error(); +} + + +bcostars::Error ExecutorServiceServer::nextBlockHeader(tars::Int64 schedulerTermId, + bcostars::BlockHeader const& _blockHeader, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + auto header = std::make_shared( + m_cryptoSuite, [m_header = _blockHeader]() mutable { return &m_header; }); + m_executor->nextBlockHeader(schedulerTermId, header, [_current](bcos::Error::UniquePtr _error) { + async_response_nextBlockHeader(_current, toTarsError(std::move(_error))); + }); + return bcostars::Error(); +} + +bcostars::Error ExecutorServiceServer::executeTransaction(bcostars::ExecutionMessage const& _input, + bcostars::ExecutionMessage&, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + auto executionMessageImpl = std::make_unique( + [m_message = _input]() mutable { return &m_message; }); + m_executor->executeTransaction( + std::move(executionMessageImpl), [_current](bcos::Error::UniquePtr _error, + bcos::protocol::ExecutionMessage::UniquePtr _output) { + async_response_executeTransaction( + _current, toTarsError(std::move(_error)), toTarsMessage(_output)); + }); + return bcostars::Error(); +} + +bcostars::Error ExecutorServiceServer::call(bcostars::ExecutionMessage const& _input, + bcostars::ExecutionMessage& _output, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + auto msg = std::make_unique( + [m_message = _input]() mutable { return &m_message; }); + m_executor->call(std::move(msg), [_current](bcos::Error::UniquePtr _error, + bcos::protocol::ExecutionMessage::UniquePtr _output) { + async_response_call(_current, toTarsError(std::move(_error)), toTarsMessage(_output)); + }); + return bcostars::Error(); +} + +bcostars::Error ExecutorServiceServer::executeTransactions(std::string const& _contractAddress, + std::vector const& _inputs, + std::vector&, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + auto executionMessages = + std::make_shared>(); + for (auto const& input : _inputs) + { + auto msg = std::make_unique( + [m_message = input]() mutable { return &m_message; }); + executionMessages->emplace_back(std::move(msg)); + } + m_executor->executeTransactions(_contractAddress, *executionMessages, + [_current](bcos::Error::UniquePtr _error, + std::vector _outputs) { + std::vector tarsOutputs; + for (auto const& it : _outputs) + { + tarsOutputs.emplace_back(toTarsMessage(it)); + } + async_response_executeTransactions( + _current, toTarsError(std::move(_error)), std::move(tarsOutputs)); + }); + return bcostars::Error(); +} + +bcostars::Error ExecutorServiceServer::dmcExecuteTransactions(std::string const& _contractAddress, + std::vector const& _inputs, + std::vector&, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + auto executionMessages = + std::make_shared>(); + for (auto const& input : _inputs) + { + auto msg = std::make_unique( + [m_message = input]() mutable { return &m_message; }); + executionMessages->emplace_back(std::move(msg)); + } + m_executor->dmcExecuteTransactions(_contractAddress, *executionMessages, + [_current](bcos::Error::UniquePtr _error, + std::vector _outputs) { + std::vector tarsOutputs; + for (auto const& it : _outputs) + { + tarsOutputs.emplace_back(toTarsMessage(it)); + } + async_response_dmcExecuteTransactions( + _current, toTarsError(std::move(_error)), std::move(tarsOutputs)); + }); + return bcostars::Error(); +} + +bcostars::Error ExecutorServiceServer::dagExecuteTransactions( + std::vector const& _inputs, + std::vector&, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + std::vector executionMessages; + for (auto const& input : _inputs) + { + auto msg = std::make_unique( + [m_message = input]() mutable { return &m_message; }); + executionMessages.emplace_back(std::move(msg)); + } + m_executor->dagExecuteTransactions( + executionMessages, [_current](bcos::Error::UniquePtr _error, + std::vector _outputs) { + std::vector tarsOutputs; + for (auto const& it : _outputs) + { + tarsOutputs.emplace_back(toTarsMessage(it)); + } + async_response_dagExecuteTransactions( + _current, toTarsError(std::move(_error)), std::move(tarsOutputs)); + }); + return bcostars::Error(); +} + +bcostars::Error ExecutorServiceServer::dmcCall(bcostars::ExecutionMessage const& _input, + bcostars::ExecutionMessage& _output, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + auto msg = std::make_unique( + [m_message = _input]() mutable { return &m_message; }); + m_executor->dmcCall(std::move(msg), [_current](bcos::Error::UniquePtr _error, + bcos::protocol::ExecutionMessage::UniquePtr _output) { + async_response_dmcCall(_current, toTarsError(std::move(_error)), toTarsMessage(_output)); + }); + return bcostars::Error(); +} + +bcostars::Error ExecutorServiceServer::getHash( + tars::Int64 _blockNumber, std::vector&, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + m_executor->getHash( + _blockNumber, [_current](bcos::Error::UniquePtr _error, bcos::crypto::HashType _hash) { + std::vector hashData(_hash.begin(), _hash.end()); + async_response_getHash(_current, toTarsError(std::move(_error)), std::move(hashData)); + }); + return bcostars::Error(); +} + +bcostars::Error ExecutorServiceServer::prepare( + bcostars::TwoPCParams const& _params, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + auto bcosTwoPCParams = toBcosTwoPCParams(_params); + m_executor->prepare(bcosTwoPCParams, [_current](bcos::Error::Ptr _error) { + async_response_prepare(_current, toTarsError(_error)); + }); + return bcostars::Error(); +} + +bcostars::Error ExecutorServiceServer::commit( + bcostars::TwoPCParams const& _params, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + auto bcosTwoPCParams = toBcosTwoPCParams(_params); + m_executor->commit(bcosTwoPCParams, [_current](bcos::Error::Ptr _error) { + async_response_commit(_current, toTarsError(_error)); + }); + return bcostars::Error(); +} + +bcostars::Error ExecutorServiceServer::rollback( + bcostars::TwoPCParams const& _params, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + auto bcosTwoPCParams = toBcosTwoPCParams(_params); + m_executor->rollback(bcosTwoPCParams, [_current](bcos::Error::Ptr _error) { + async_response_rollback(_current, toTarsError(_error)); + }); + return bcostars::Error(); +} + +bcostars::Error ExecutorServiceServer::getCode( + std::string const& _contractAddress, std::vector&, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + m_executor->getCode( + _contractAddress, [_current](bcos::Error::Ptr _error, bcos::bytes _codeBytes) { + std::vector code(_codeBytes.begin(), _codeBytes.end()); + async_response_getCode(_current, toTarsError(_error), std::move(code)); + }); + return bcostars::Error(); +} +bcostars::Error ExecutorServiceServer::getABI( + std::string const& _contractAddress, std::string& _abi, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + m_executor->getABI(_contractAddress, [_current](bcos::Error::Ptr _error, std::string _abi) { + async_response_getABI(_current, toTarsError(_error), std::move(_abi)); + }); + return bcostars::Error(); +} +bcostars::Error ExecutorServiceServer::reset(tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + m_executor->reset([_current](bcos::Error::Ptr _error) { + async_response_reset(_current, toTarsError(_error)); + }); + return bcostars::Error(); +} diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/ExecutorServiceServer.h" "b/BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/ExecutorServiceServer.h" new file mode 100644 index 00000000..bc79fd7b --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/ExecutorServiceServer.h" @@ -0,0 +1,81 @@ +/** + * 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 ExecutorServiceServer.h + * @author: yujiechen + * @date 2022-5-10 + */ +#pragma once +#include +#include +#include +namespace bcostars +{ +struct ExecutorServiceParam +{ + bcos::executor::ParallelTransactionExecutorInterface::Ptr executor; + bcos::crypto::CryptoSuite::Ptr cryptoSuite; +}; +class ExecutorServiceServer : public ExecutorService +{ +public: + using Ptr = std::shared_ptr; + ExecutorServiceServer(ExecutorServiceParam const& _param) + : m_executor(_param.executor), m_cryptoSuite(_param.cryptoSuite) + {} + ~ExecutorServiceServer() override {} + + void initialize() override {} + void destroy() override {} + bcostars::Error status( + bcostars::ExecutorStatus& _output, tars::TarsCurrentPtr current) override; + bcostars::Error nextBlockHeader(tars::Int64 schedulerTermId, + bcostars::BlockHeader const& _blockHeader, tars::TarsCurrentPtr _current) override; + bcostars::Error executeTransaction(bcostars::ExecutionMessage const& _input, + bcostars::ExecutionMessage& _output, tars::TarsCurrentPtr _current) override; + bcostars::Error call(bcostars::ExecutionMessage const& _input, + bcostars::ExecutionMessage& _output, tars::TarsCurrentPtr _current) override; + bcostars::Error executeTransactions(std::string const& _contractAddress, + std::vector const& _inputs, + std::vector& _ouptputs, tars::TarsCurrentPtr _current) override; + bcostars::Error dmcExecuteTransactions(std::string const& _contractAddress, + std::vector const& _inputs, + std::vector& _ouptputs, tars::TarsCurrentPtr _current) override; + bcostars::Error dagExecuteTransactions(std::vector const& _inputs, + std::vector& _ouptputs, tars::TarsCurrentPtr _current) override; + bcostars::Error dmcCall(bcostars::ExecutionMessage const& _input, + bcostars::ExecutionMessage& _output, tars::TarsCurrentPtr _current) override; + + bcostars::Error getHash(tars::Int64 _blockNumber, std::vector& _hash, + tars::TarsCurrentPtr _current) override; + + bcostars::Error prepare( + bcostars::TwoPCParams const& _params, tars::TarsCurrentPtr _current) override; + bcostars::Error commit( + bcostars::TwoPCParams const& _params, tars::TarsCurrentPtr _current) override; + bcostars::Error rollback( + bcostars::TwoPCParams const& _params, tars::TarsCurrentPtr _current) override; + + bcostars::Error getCode(std::string const& _contractAddress, std::vector& _code, + tars::TarsCurrentPtr _current) override; + bcostars::Error getABI(std::string const& _contractAddress, std::string& _abi, + tars::TarsCurrentPtr _current) override; + bcostars::Error reset(tars::TarsCurrentPtr _current) override; + +private: + bcos::executor::ParallelTransactionExecutorInterface::Ptr m_executor; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; +}; +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/main/CMakeLists.txt" "b/BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/main/CMakeLists.txt" new file mode 100644 index 00000000..58f27d4a --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/main/CMakeLists.txt" @@ -0,0 +1,7 @@ +file(GLOB SRC_LIST "*.cpp") +file(GLOB HEADERS "*.h") + +aux_source_directory(../ SRC_LIST) +add_executable(${EXECUTOR_BINARY_NAME} ${SRC_LIST} ${HEADERS}) + +target_link_libraries(${EXECUTOR_BINARY_NAME} PUBLIC ${PROTOCOL_INIT_LIB} ${COMMAND_HELPER_LIB} ${EXECUTOR_TARGET} ${LEDGER_TARGET} ${STORAGE_TARGET}) \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/main/ExecutorServiceApp.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/main/ExecutorServiceApp.cpp" new file mode 100644 index 00000000..fa4c0bd2 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/main/ExecutorServiceApp.cpp" @@ -0,0 +1,210 @@ +/** + * 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. + * + * @brief Application for the ExecutorService + * @file ExecutorServiceApp.cpp + * @author: yujiechen + * @date 2022-5-10 + */ +#include "ExecutorServiceApp.h" +#include "../../Common/TarsUtils.h" +#include "../ExecutorServiceServer.h" +#include "bcos-executor/src/executor/SwitchExecutorManager.h" +#include "libinitializer/CommandHelper.h" +#include "libinitializer/ExecutorInitializer.h" +#include "libinitializer/StorageInitializer.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace bcostars; +using namespace bcos::initializer; +using namespace bcos::protocol; + +void ExecutorServiceApp::initialize() +{ + try + { + // m_timer = std::make_shared(3000, "registerExecutor"); + createAndInitExecutor(); + // m_timer->registerTimeoutHandler(boost::bind(&ExecutorServiceApp::registerExecutor, + // this)); m_timer->start(); + } + catch (std::exception const& e) + { + std::cout << "init ExecutorService failed, error: " << boost::diagnostic_information(e) + << std::endl; + exit(-1); + } +} + +void ExecutorServiceApp::createAndInitExecutor() +{ + // fetch config + m_iniConfigPath = tars::ServerConfig::BasePath + "/config.ini"; + m_genesisConfigPath = tars::ServerConfig::BasePath + "/config.genesis"; + addConfig("config.ini"); + addConfig("config.genesis"); + EXECUTOR_SERVICE_LOG(INFO) << LOG_DESC("createAndInitExecutor: fetch config success") + << LOG_KV("iniConfigPath", m_iniConfigPath) + << LOG_KV("genesisConfigPath", m_genesisConfigPath); + + m_nodeConfig = + std::make_shared(std::make_shared()); + + // init log + boost::property_tree::ptree pt; + boost::property_tree::read_ini(m_iniConfigPath, pt); + + // init service.without_tars_framework first for determine the log path + m_nodeConfig->loadWithoutTarsFrameworkConfig(pt); + + m_logInitializer = std::make_shared(); + if (!m_nodeConfig->withoutTarsFramework()) + { + m_logInitializer->setLogPath(getLogPath()); + } + m_logInitializer->initLog(pt); + + boost::property_tree::ptree genesisPt; + boost::property_tree::read_ini(m_genesisConfigPath, genesisPt); + + // load protocolInitializer + EXECUTOR_SERVICE_LOG(INFO) << LOG_DESC("loadNodeConfig"); + + m_nodeConfig->loadGenesisConfig(genesisPt); + m_nodeConfig->loadConfig(pt); + m_nodeConfig->loadNodeServiceConfig(m_nodeConfig->nodeName(), pt, true); + // init the protocol + m_protocolInitializer = std::make_shared(); + m_protocolInitializer->init(m_nodeConfig); + EXECUTOR_SERVICE_LOG(INFO) << LOG_DESC("loadNodeConfig success"); + // for stat the nodeVersion + bcos::initializer::showNodeVersionMetric(); + + auto withoutTarsFramework = m_nodeConfig->withoutTarsFramework(); + + // create txpool client + auto txpoolServiceName = m_nodeConfig->txpoolServiceName(); + EXECUTOR_SERVICE_LOG(INFO) << LOG_DESC("create TxPoolServiceClient") + << LOG_KV("txpoolServiceName", txpoolServiceName) + << LOG_KV("withoutTarsFramework", withoutTarsFramework); + + std::vector endPoints; + m_nodeConfig->getTarsClientProxyEndpoints(bcos::protocol::TXPOOL_NAME, endPoints); + + auto txpoolServicePrx = createServantProxy( + withoutTarsFramework, txpoolServiceName, endPoints); + + m_txpool = std::make_shared(txpoolServicePrx, + m_protocolInitializer->cryptoSuite(), m_protocolInitializer->blockFactory()); + + auto schedulerServiceName = m_nodeConfig->schedulerServiceName(); + EXECUTOR_SERVICE_LOG(INFO) << LOG_DESC("create SchedulerServiceClient") + << LOG_KV("schedulerServiceName", schedulerServiceName); + + auto schedulerPrx = createServantProxy(schedulerServiceName); + + m_scheduler = std::make_shared( + schedulerPrx, m_protocolInitializer->cryptoSuite()); + + // create executor + auto storage = StorageInitializer::build(m_nodeConfig->pdAddrs(), getLogPath(), + m_nodeConfig->pdCaPath(), m_nodeConfig->pdCertPath(), m_nodeConfig->pdKeyPath()); + + bcos::storage::CacheStorageFactory::Ptr cacheFactory = nullptr; + if (m_nodeConfig->enableLRUCacheStorage()) + { + cacheFactory = std::make_shared( + storage, m_nodeConfig->cacheSize()); + EXECUTOR_SERVICE_LOG(INFO) + << "createAndInitExecutor: enableLRUCacheStorage, size: " << m_nodeConfig->cacheSize(); + } + else + { + EXECUTOR_SERVICE_LOG(INFO) << LOG_DESC("createAndInitExecutor: disableLRUCacheStorage"); + } + + auto executionMessageFactory = + std::make_shared(); + + auto blockFactory = m_protocolInitializer->blockFactory(); + auto ledger = std::make_shared(blockFactory, storage); + + auto executorFactory = std::make_shared(ledger, + m_txpool, cacheFactory, storage, executionMessageFactory, + m_protocolInitializer->cryptoSuite()->hashImpl(), m_nodeConfig->isWasm(), + m_nodeConfig->isAuthCheck(), m_nodeConfig->keyPageSize(), "executor"); + + m_executor = std::make_shared(executorFactory); + + std::weak_ptr executorWeakPtr = m_executor; + std::weak_ptr storageWeakPtr = dynamic_pointer_cast(storage); + auto switchHandler = [executor = executorWeakPtr, storageWeakPtr]() { + if (executor.lock()) + { + executor.lock()->triggerSwitch(); + } + auto storage = storageWeakPtr.lock(); + if(storage) + { + storage->reset(); + } + }; + dynamic_pointer_cast(storage)->setSwitchHandler(switchHandler); + + + ExecutorServiceParam param; + param.executor = m_executor; + param.cryptoSuite = m_protocolInitializer->cryptoSuite(); + addServantWithParams( + getProxyDesc(EXECUTOR_SERVANT_NAME), param); + auto ret = getEndPointDescByAdapter(this, EXECUTOR_SERVANT_NAME); + if (!ret.first) + { + throw std::runtime_error("load endpoint information failed"); + } + m_executorName = ret.second; + // registerExecutor(); + EXECUTOR_SERVICE_LOG(INFO) << LOG_DESC("createAndInitExecutor success"); +} + +void ExecutorServiceApp::registerExecutor() +{ + if (m_registerExecutorSuccess) + { + m_timer->stop(); + return; + } + m_timer->restart(); + EXECUTOR_SERVICE_LOG(INFO) << LOG_DESC("registerExecutor") + << LOG_KV("executorName", m_executorName); + m_scheduler->registerExecutor(m_executorName, nullptr, [this](bcos::Error::Ptr&& _error) { + if (_error) + { + EXECUTOR_SERVICE_LOG(ERROR) + << LOG_DESC("registerExecutor error") << LOG_KV("name", m_executorName) + << LOG_KV("code", _error->errorCode()) << LOG_KV("msg", _error->errorMessage()); + return; + } + m_registerExecutorSuccess = true; + EXECUTOR_SERVICE_LOG(INFO) + << LOG_DESC("registerExecutor success") << LOG_KV("name", m_executorName); + }); +} diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/main/ExecutorServiceApp.h" "b/BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/main/ExecutorServiceApp.h" new file mode 100644 index 00000000..8c844ac5 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/main/ExecutorServiceApp.h" @@ -0,0 +1,69 @@ +/** + * 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. + * + * @brief Application for the ExecutorService + * @file ExecutorServiceApp.h + * @author: yujiechen + * @date 2022-5-10 + */ +#pragma once +#include "libinitializer/ProtocolInitializer.h" +#include +#include +#include +#include +#include +#include +#include + +#define EXECUTOR_SERVICE_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("ExecutorServiceApp") + +namespace bcostars +{ +class ExecutorServiceApp : public tars::Application +{ +public: + ExecutorServiceApp() = default; + ~ExecutorServiceApp() override {} + void initialize() override; + void destroyApp() override + { + // stop executor + if (m_executor) + { + m_executor->stop(); + } + } + +protected: + virtual void createAndInitExecutor(); + virtual void registerExecutor(); + +private: + std::string m_iniConfigPath; + std::string m_genesisConfigPath; + bcos::BoostLogInitializer::Ptr m_logInitializer; + + bcos::tool::NodeConfig::Ptr m_nodeConfig; + bcos::initializer::ProtocolInitializer::Ptr m_protocolInitializer; + + bcos::scheduler::SchedulerInterface::Ptr m_scheduler; + bcos::executor::SwitchExecutorManager::Ptr m_executor; + bcos::txpool::TxPoolInterface::Ptr m_txpool; + std::string m_executorName; + std::shared_ptr m_timer; + bool m_registerExecutorSuccess = false; +}; +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/main/main.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/main/main.cpp" new file mode 100644 index 00000000..7781a531 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/ExecutorService/main/main.cpp" @@ -0,0 +1,54 @@ +/** + * 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. + * + * @brief main for ExecutorService + * @file main.cpp + * @author: yujiechen + * @date 2022-05-10 + */ +#include "ExecutorServiceApp.h" +#include "libinitializer/CommandHelper.h" +#include +#include +#include + +using namespace bcostars; +using namespace bcos; +using namespace bcos::initializer; +int main(int argc, char* argv[]) +{ + try + { + bcos::initializer::initCommandLine(argc, argv); + ExecutorServiceApp app; + printVersion(); + std::cout << "[" << getCurrentDateTime() << "] "; + std::cout << "The ExecutorService is running..." << std::endl; + app.main(argc, argv); + app.waitForShutdown(); + std::cout << "[" << getCurrentDateTime() << "] "; + std::cout << "The ExecutorService program exit normally." << std::endl; + return 0; + } + catch (std::exception& e) + { + std::cerr << "ExecutorService std::exception:" << e.what() << std::endl; + } + catch (...) + { + std::cerr << "ExecutorService unknown exception." << std::endl; + } + return -1; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/FrontService/FrontServiceServer.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/FrontService/FrontServiceServer.cpp" new file mode 100644 index 00000000..0a626614 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/FrontService/FrontServiceServer.cpp" @@ -0,0 +1,150 @@ +#include "FrontServiceServer.h" +#include "../Common/TarsUtils.h" +#include + +using namespace bcostars; + +bcostars::Error FrontServiceServer::asyncGetGroupNodeInfo( + GroupNodeInfo&, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + + m_frontServiceInitializer->front()->asyncGetGroupNodeInfo( + [current](bcos::Error::Ptr _error, bcos::gateway::GroupNodeInfo::Ptr _groupNodeInfo) { + // Note: the nodeIDs maybe null if no connections + std::vector> tarsNodeIDs; + if (!_groupNodeInfo) + { + async_response_asyncGetGroupNodeInfo( + current, toTarsError(_error), bcostars::GroupNodeInfo()); + return; + } + auto groupInfoImpl = + std::dynamic_pointer_cast(_groupNodeInfo); + async_response_asyncGetGroupNodeInfo( + current, toTarsError(_error), groupInfoImpl->inner()); + }); + + return bcostars::Error(); +} + +void FrontServiceServer::asyncSendBroadcastMessage(tars::Int32 _nodeType, tars::Int32 moduleID, + const vector& data, tars::TarsCurrentPtr) +{ + m_frontServiceInitializer->front()->asyncSendBroadcastMessage( + _nodeType, moduleID, bcos::bytesConstRef((bcos::byte*)data.data(), data.size())); +} + +bcostars::Error FrontServiceServer::asyncSendMessageByNodeID(tars::Int32 moduleID, + const vector& nodeID, const vector& data, tars::UInt32 timeout, + tars::Bool requireRespCallback, vector& responseNodeID, + vector& responseData, std::string& seq, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + + auto bcosNodeID = m_frontServiceInitializer->keyFactory()->createKey( + bcos::bytesConstRef((bcos::byte*)nodeID.data(), nodeID.size())); + if (requireRespCallback) + { + m_frontServiceInitializer->front()->asyncSendMessageByNodeID(moduleID, bcosNodeID, + bcos::bytesConstRef((bcos::byte*)data.data(), data.size()), timeout, + [current](bcos::Error::Ptr _error, bcos::crypto::NodeIDPtr _nodeID, + bcos::bytesConstRef _data, const std::string& _id, + bcos::front::ResponseFunc _respFunc) { + boost::ignore_unused(_respFunc); + auto encodedNodeID = *_nodeID->encode(); + async_response_asyncSendMessageByNodeID(current, toTarsError(_error), + std::vector(encodedNodeID.begin(), encodedNodeID.end()), + std::vector(_data.begin(), _data.end()), _id); + }); + } + else + { + m_frontServiceInitializer->front()->asyncSendMessageByNodeID(moduleID, bcosNodeID, + bcos::bytesConstRef((bcos::byte*)data.data(), data.size()), timeout, nullptr); + + // response directly + bcos::bytesConstRef respData; + async_response_asyncSendMessageByNodeID(current, toTarsError(nullptr), + std::vector(nodeID.begin(), nodeID.end()), + std::vector(respData.begin(), respData.end()), seq); + } + + return bcostars::Error(); +} + +void FrontServiceServer::asyncSendMessageByNodeIDs(tars::Int32 moduleID, + const vector>& nodeIDs, const vector& data, + tars::TarsCurrentPtr current) +{ + std::vector bcosNodeIDs; + bcosNodeIDs.reserve(nodeIDs.size()); + for (auto const& it : nodeIDs) + { + bcosNodeIDs.push_back(m_frontServiceInitializer->keyFactory()->createKey( + bcos::bytesConstRef((bcos::byte*)it.data(), it.size()))); + } + + m_frontServiceInitializer->front()->asyncSendMessageByNodeIDs( + moduleID, bcosNodeIDs, bcos::bytesConstRef((bcos::byte*)data.data(), data.size())); +} + +bcostars::Error FrontServiceServer::asyncSendResponse(const std::string& id, tars::Int32 moduleID, + const vector& nodeID, const vector& data, tars::TarsCurrentPtr current) +{ + FRONTSERVICE_LOG(TRACE) << LOG_DESC("asyncSendResponse server") << LOG_KV("id", id); + current->setResponse(false); + m_frontServiceInitializer->front()->asyncSendResponse(id, moduleID, + m_frontServiceInitializer->keyFactory()->createKey( + bcos::bytesConstRef((bcos::byte*)nodeID.data(), nodeID.size())), + bcos::bytesConstRef((bcos::byte*)data.data(), data.size()), + [current](bcos::Error::Ptr error) { + async_response_asyncSendResponse(current, toTarsError(error)); + }); + return bcostars::Error(); +} + +bcostars::Error FrontServiceServer::onReceiveBroadcastMessage(const std::string& groupID, + const vector& nodeID, const vector& data, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + + m_frontServiceInitializer->front()->onReceiveBroadcastMessage(groupID, + m_frontServiceInitializer->keyFactory()->createKey( + bcos::bytesConstRef((bcos::byte*)nodeID.data(), nodeID.size())), + bcos::bytesConstRef((bcos::byte*)data.data(), data.size()), + [current](bcos::Error::Ptr error) { + async_response_onReceiveBroadcastMessage(current, toTarsError(error)); + }); + + return bcostars::Error(); +} + +bcostars::Error FrontServiceServer::onReceiveMessage(const std::string& groupID, + const vector& nodeID, const vector& data, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + + m_frontServiceInitializer->front()->onReceiveMessage(groupID, + m_frontServiceInitializer->keyFactory()->createKey( + bcos::bytesConstRef((bcos::byte*)nodeID.data(), nodeID.size())), + bcos::bytesConstRef((bcos::byte*)data.data(), data.size()), + [current](bcos::Error::Ptr error) { + async_response_onReceiveMessage(current, toTarsError(error)); + }); + + return bcostars::Error(); +} + +bcostars::Error FrontServiceServer::onReceiveGroupNodeInfo(const std::string& groupID, + const bcostars::GroupNodeInfo& _groupNodeInfo, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + auto bcosGroupNodeInfo = std::make_shared( + [m_groupNodeInfo = _groupNodeInfo]() mutable { return &m_groupNodeInfo; }); + m_frontServiceInitializer->front()->onReceiveGroupNodeInfo( + groupID, bcosGroupNodeInfo, [current](bcos::Error::Ptr error) { + async_response_onReceiveGroupNodeInfo(current, toTarsError(error)); + }); + return bcostars::Error(); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/FrontService/FrontServiceServer.h" "b/BFPL\345\243\271/fisco-bcos-tars-service/FrontService/FrontServiceServer.h" new file mode 100644 index 00000000..1a3b93b3 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/FrontService/FrontServiceServer.h" @@ -0,0 +1,59 @@ +#pragma once + +#include "libinitializer/FrontServiceInitializer.h" +#include "libinitializer/ProtocolInitializer.h" +#include +#include +#include +#include +#include +#include +#include +namespace bcostars +{ +struct FrontServiceParam +{ + bcos::initializer::FrontServiceInitializer::Ptr frontServiceInitializer; +}; +class FrontServiceServer : public FrontService +{ +public: + FrontServiceServer(FrontServiceParam const& _param) + : m_frontServiceInitializer(_param.frontServiceInitializer) + {} + + ~FrontServiceServer() override {} + void initialize() override {} + void destroy() override {} + + bcostars::Error asyncGetGroupNodeInfo( + GroupNodeInfo& groupNodeInfo, tars::TarsCurrentPtr current) override; + + void asyncSendBroadcastMessage(tars::Int32 _nodeType, tars::Int32 moduleID, + const vector& data, tars::TarsCurrentPtr current) override; + + bcostars::Error asyncSendMessageByNodeID(tars::Int32 moduleID, const vector& nodeID, + const vector& data, tars::UInt32 timeout, tars::Bool requireRespCallback, + vector& responseNodeID, vector& responseData, std::string& seq, + tars::TarsCurrentPtr current) override; + + void asyncSendMessageByNodeIDs(tars::Int32 moduleID, const vector>& nodeIDs, + const vector& data, tars::TarsCurrentPtr current) override; + + bcostars::Error asyncSendResponse(const std::string& id, tars::Int32 moduleID, + const vector& nodeID, const vector& data, + tars::TarsCurrentPtr current) override; + bcostars::Error onReceiveBroadcastMessage(const std::string& groupID, + const vector& nodeID, const vector& data, + tars::TarsCurrentPtr current) override; + + bcostars::Error onReceiveMessage(const std::string& groupID, const vector& nodeID, + const vector& data, tars::TarsCurrentPtr current) override; + + bcostars::Error onReceiveGroupNodeInfo(const std::string& groupID, + const bcostars::GroupNodeInfo& groupNodeInfo, tars::TarsCurrentPtr current) override; + +private: + bcos::initializer::FrontServiceInitializer::Ptr m_frontServiceInitializer; +}; +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/GatewayInitializer.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/GatewayInitializer.cpp" new file mode 100644 index 00000000..b79afcc2 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/GatewayInitializer.cpp" @@ -0,0 +1,124 @@ +/** + * Copyright (C) 2021 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. + * + * @brief initializer for the GatewayService + * @file GatewayInitializer.cpp + * @author: yujiechen + * @date 2021-10-15 + */ +#include "GatewayInitializer.h" +#include "../Common/TarsUtils.h" +#include "libinitializer/ProtocolInitializer.h" +#include +#include +#include +#include +#include + +#ifdef WITH_TIKV +#include +#endif + +#include +#include +#include + +using namespace tars; +using namespace bcostars; + +void GatewayInitializer::init(std::string const& _configPath) +{ + GATEWAYSERVICE_LOG(INFO) << LOG_DESC("initGlobalConfig"); + g_BCOSConfig.setCodec(std::make_shared()); + + GATEWAYSERVICE_LOG(INFO) << LOG_DESC("initGateWayConfig") << LOG_KV("configPath", _configPath); + + GATEWAYSERVICE_LOG(INFO) << LOG_DESC("load nodeConfig"); + auto nodeConfig = std::make_shared(); + nodeConfig->loadConfig(_configPath, false, true); + + boost::property_tree::ptree pt; + boost::property_tree::read_ini(_configPath, pt); + nodeConfig->loadServiceConfig(pt); + GATEWAYSERVICE_LOG(INFO) << LOG_DESC("load nodeConfig success"); +#ifdef WITH_TIKV + if (nodeConfig->enableFailOver()) + { + GATEWAYSERVICE_LOG(INFO) << LOG_DESC("enable failover"); + auto memberFactory = std::make_shared(); + auto leaderEntryPointFactory = + std::make_shared(memberFactory); + auto watchDir = "/" + nodeConfig->chainId() + bcos::election::CONSENSUS_LEADER_DIR; + m_leaderEntryPoint = leaderEntryPointFactory->createLeaderEntryPoint( + nodeConfig->failOverClusterUrl(), watchDir, "watchLeaderChange", nodeConfig->pdCaPath(), + nodeConfig->pdCertPath(), nodeConfig->pdKeyPath()); + } +#endif + + auto protocolInitializer = std::make_shared(); + protocolInitializer->init(nodeConfig); + + bcos::gateway::GatewayFactory factory( + nodeConfig->chainId(), nodeConfig->rpcServiceName(), protocolInitializer->dataEncryption()); + auto gatewayServiceName = bcostars::getProxyDesc(bcos::protocol::GATEWAY_SERVANT_NAME); + GATEWAYSERVICE_LOG(INFO) << LOG_DESC("buildGateWay") + << LOG_KV("certPath", m_gatewayConfig->certPath()) + << LOG_KV("nodePath", m_gatewayConfig->nodePath()) + << LOG_KV("gatewayServiceName", gatewayServiceName); + auto gateway = + factory.buildGateway(m_gatewayConfig, false, m_leaderEntryPoint, gatewayServiceName); + + m_gateway = gateway; + GATEWAYSERVICE_LOG(INFO) << LOG_DESC("buildGateway success"); +} + +void GatewayInitializer::start() +{ + if (m_running) + { + GATEWAYSERVICE_LOG(INFO) << LOG_DESC("the gateway has already been started"); + return; + } + m_running = true; + if (m_leaderEntryPoint) + { + GATEWAYSERVICE_LOG(INFO) << LOG_DESC("start leader-entry-point"); + m_leaderEntryPoint->start(); + } + // start the gateway + GATEWAYSERVICE_LOG(INFO) << LOG_DESC("start the gateway"); + m_gateway->start(); + GATEWAYSERVICE_LOG(INFO) << LOG_DESC("start the gateway success"); +} + +void GatewayInitializer::stop() +{ + if (!m_running) + { + GATEWAYSERVICE_LOG(INFO) << LOG_DESC("The GatewayService has already been stopped"); + return; + } + m_running = false; + GATEWAYSERVICE_LOG(INFO) << LOG_DESC("Stop the GatewayService"); + if (m_leaderEntryPoint) + { + m_leaderEntryPoint->stop(); + } + if (m_gateway) + { + m_gateway->stop(); + } + TLOGINFO(LOG_DESC("[GATEWAYSERVICE] Stop the GatewayService success") << std::endl); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/GatewayInitializer.h" "b/BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/GatewayInitializer.h" new file mode 100644 index 00000000..03101c0f --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/GatewayInitializer.h" @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2021 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. + * + * @brief initializer for the GatewayService + * @file GatewayInitializer.h + * @author: yujiechen + * @date 2021-10-15 + */ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace bcostars +{ +class GatewayInitializer +{ +public: + using Ptr = std::shared_ptr; + GatewayInitializer( + std::string const& _configPath, bcos::gateway::GatewayConfig::Ptr _gatewayConfig) + : m_gatewayConfig(_gatewayConfig), + m_keyFactory(std::make_shared()), + m_groupInfoFactory(std::make_shared()), + m_chainNodeInfoFactory(std::make_shared()) + { + init(_configPath); + } + + virtual ~GatewayInitializer() { stop(); } + + + virtual void start(); + virtual void stop(); + + bcos::gateway::GatewayInterface::Ptr gateway() { return m_gateway; } + bcos::group::ChainNodeInfoFactory::Ptr chainNodeInfoFactory() { return m_chainNodeInfoFactory; } + bcos::group::GroupInfoFactory::Ptr groupInfoFactory() { return m_groupInfoFactory; } + + bcos::crypto::KeyFactory::Ptr keyFactory() { return m_keyFactory; } + +protected: + virtual void init(std::string const& _configPath); + +private: + bcos::gateway::GatewayConfig::Ptr m_gatewayConfig; + bcos::crypto::KeyFactory::Ptr m_keyFactory; + bcos::group::GroupInfoFactory::Ptr m_groupInfoFactory; + bcos::group::ChainNodeInfoFactory::Ptr m_chainNodeInfoFactory; + bcos::gateway::GatewayInterface::Ptr m_gateway; + std::atomic_bool m_running = {false}; + + bcos::election::LeaderEntryPointInterface::Ptr m_leaderEntryPoint; +}; +} // namespace bcostars diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/GatewayServiceServer.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/GatewayServiceServer.cpp" new file mode 100644 index 00000000..986e50c7 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/GatewayServiceServer.cpp" @@ -0,0 +1,65 @@ +#include "GatewayServiceServer.h" +#include +using namespace bcostars; +bcostars::Error GatewayServiceServer::asyncNotifyGroupInfo( + const bcostars::GroupInfo& groupInfo, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + auto bcosGroupInfo = toBcosGroupInfo(m_gatewayInitializer->chainNodeInfoFactory(), + m_gatewayInitializer->groupInfoFactory(), groupInfo); + m_gatewayInitializer->gateway()->asyncNotifyGroupInfo( + bcosGroupInfo, [current](bcos::Error::Ptr&& _error) { + async_response_asyncNotifyGroupInfo(current, toTarsError(_error)); + }); + return bcostars::Error(); +} + +bcostars::Error GatewayServiceServer::asyncSendMessageByTopic(const std::string& _topic, + const vector& _data, tars::Int32& _type, vector&, + tars::TarsCurrentPtr current) +{ + current->setResponse(false); + m_gatewayInitializer->gateway()->asyncSendMessageByTopic(_topic, + bcos::bytesConstRef((const bcos::byte*)_data.data(), _data.size()), + [current](bcos::Error::Ptr&& _error, int16_t _type, bcos::bytesPointer _responseData) { + vector response; + if (_responseData) + { + response.assign(_responseData->begin(), _responseData->end()); + } + async_response_asyncSendMessageByTopic(current, toTarsError(_error), _type, response); + }); + return bcostars::Error(); +} + +bcostars::Error GatewayServiceServer::asyncSubscribeTopic( + const std::string& _clientID, const std::string& _topicInfo, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + m_gatewayInitializer->gateway()->asyncSubscribeTopic( + _clientID, _topicInfo, [current](bcos::Error::Ptr&& _error) { + async_response_asyncSubscribeTopic(current, toTarsError(_error)); + }); + return bcostars::Error(); +} +bcostars::Error GatewayServiceServer::asyncSendBroadcastMessageByTopic( + const std::string& _topic, const vector& _data, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + m_gatewayInitializer->gateway()->asyncSendBroadcastMessageByTopic( + _topic, bcos::bytesConstRef((const bcos::byte*)_data.data(), _data.size())); + async_response_asyncSendBroadcastMessageByTopic( + current, toTarsError(nullptr)); + return bcostars::Error(); +} + +bcostars::Error GatewayServiceServer::asyncRemoveTopic(const std::string& _clientID, + const vector& _topicList, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + m_gatewayInitializer->gateway()->asyncRemoveTopic( + _clientID, _topicList, [current](bcos::Error::Ptr&& _error) { + async_response_asyncRemoveTopic(current, toTarsError(_error)); + }); + return bcostars::Error(); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/GatewayServiceServer.h" "b/BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/GatewayServiceServer.h" new file mode 100644 index 00000000..9586c2a4 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/GatewayServiceServer.h" @@ -0,0 +1,150 @@ +#pragma once + +#include "../Common/TarsUtils.h" +#include "GatewayInitializer.h" +#include "libinitializer/ProtocolInitializer.h" +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::group; + +namespace bcostars +{ +struct GatewayServiceParam +{ + GatewayInitializer::Ptr gatewayInitializer; +}; +class GatewayServiceServer : public bcostars::GatewayService +{ +public: + GatewayServiceServer(GatewayServiceParam const& _param) + : m_gatewayInitializer(_param.gatewayInitializer) + {} + void initialize() override {} + void destroy() override {} + + bcostars::Error asyncSendBroadcastMessage(tars::Int32 _type, const std::string& groupID, + tars::Int32 moduleID, const vector& srcNodeID, + const vector& payload, tars::TarsCurrentPtr current) override + { + current->setResponse(false); + auto bcosNodeID = m_gatewayInitializer->keyFactory()->createKey( + bcos::bytesConstRef((const bcos::byte*)srcNodeID.data(), srcNodeID.size())); + m_gatewayInitializer->gateway()->asyncSendBroadcastMessage(_type, groupID, moduleID, + bcosNodeID, bcos::bytesConstRef((const bcos::byte*)payload.data(), payload.size())); + + async_response_asyncSendBroadcastMessage(current, toTarsError(nullptr)); + return bcostars::Error(); + } + + bcostars::Error asyncGetPeers(bcostars::GatewayInfo&, std::vector&, + tars::TarsCurrentPtr current) override + { + GATEWAYSERVICE_LOG(DEBUG) << LOG_DESC("asyncGetPeers: request"); + current->setResponse(false); + m_gatewayInitializer->gateway()->asyncGetPeers( + [current](const bcos::Error::Ptr _error, bcos::gateway::GatewayInfo::Ptr _localP2pInfo, + bcos::gateway::GatewayInfosPtr _peers) { + auto localtarsP2pInfo = toTarsGatewayInfo(_localP2pInfo); + std::vector peersInfo; + if (_peers) + { + for (auto const& peer : *_peers) + { + peersInfo.emplace_back(toTarsGatewayInfo(peer)); + } + } + async_response_asyncGetPeers( + current, toTarsError(_error), localtarsP2pInfo, peersInfo); + }); + return bcostars::Error(); + } + + bcostars::Error asyncSendMessageByNodeID(const std::string& groupID, tars::Int32 moduleID, + const vector& srcNodeID, const vector& dstNodeID, + const vector& payload, tars::TarsCurrentPtr current) override + { + current->setResponse(false); + auto keyFactory = m_gatewayInitializer->keyFactory(); + auto bcosSrcNodeID = keyFactory->createKey( + bcos::bytesConstRef((const bcos::byte*)srcNodeID.data(), srcNodeID.size())); + auto bcosDstNodeID = keyFactory->createKey( + bcos::bytesConstRef((const bcos::byte*)dstNodeID.data(), dstNodeID.size())); + + m_gatewayInitializer->gateway()->asyncSendMessageByNodeID(groupID, moduleID, bcosSrcNodeID, + bcosDstNodeID, bcos::bytesConstRef((const bcos::byte*)payload.data(), payload.size()), + [current](bcos::Error::Ptr error) { + async_response_asyncSendMessageByNodeID(current, toTarsError(error)); + }); + return bcostars::Error(); + } + + bcostars::Error asyncSendMessageByNodeIDs(const std::string& groupID, tars::Int32 moduleID, + const vector& srcNodeID, const vector>& dstNodeID, + const vector& payload, tars::TarsCurrentPtr current) override + { + current->setResponse(false); + auto keyFactory = m_gatewayInitializer->keyFactory(); + auto bcosSrcNodeID = keyFactory->createKey( + bcos::bytesConstRef((const bcos::byte*)srcNodeID.data(), srcNodeID.size())); + std::vector nodeIDs; + nodeIDs.reserve(dstNodeID.size()); + for (auto const& it : dstNodeID) + { + nodeIDs.push_back(keyFactory->createKey( + bcos::bytesConstRef((const bcos::byte*)it.data(), it.size()))); + } + + m_gatewayInitializer->gateway()->asyncSendMessageByNodeIDs(groupID, moduleID, bcosSrcNodeID, + nodeIDs, bcos::bytesConstRef((const bcos::byte*)payload.data(), payload.size())); + + async_response_asyncSendMessageByNodeIDs(current, toTarsError(nullptr)); + return bcostars::Error(); + } + + bcostars::Error asyncGetGroupNodeInfo( + const std::string& groupID, GroupNodeInfo&, tars::TarsCurrentPtr current) override + { + current->setResponse(false); + + m_gatewayInitializer->gateway()->asyncGetGroupNodeInfo( + groupID, [current](bcos::Error::Ptr _error, + bcos::gateway::GroupNodeInfo::Ptr _bcosGroupNodeInfo) { + // Note: the nodeIDs maybe null if no connections + if (!_bcosGroupNodeInfo || _bcosGroupNodeInfo->nodeIDList().empty()) + { + async_response_asyncGetGroupNodeInfo( + current, toTarsError(_error), bcostars::GroupNodeInfo()); + return; + } + auto groupInfoImpl = + std::dynamic_pointer_cast( + _bcosGroupNodeInfo); + async_response_asyncGetGroupNodeInfo( + current, toTarsError(_error), groupInfoImpl->inner()); + }); + + return bcostars::Error(); + } + bcostars::Error asyncNotifyGroupInfo( + const bcostars::GroupInfo& groupInfo, tars::TarsCurrentPtr current) override; + + bcostars::Error asyncSendMessageByTopic(const std::string& _topic, + const vector& _data, tars::Int32& _type, vector& _responseData, + tars::TarsCurrentPtr current) override; + bcostars::Error asyncSubscribeTopic(const std::string& _clientID, const std::string& _topicInfo, + tars::TarsCurrentPtr current) override; + bcostars::Error asyncSendBroadcastMessageByTopic(const std::string& _topic, + const vector& _data, tars::TarsCurrentPtr current) override; + bcostars::Error asyncRemoveTopic(const std::string& _clientID, + const vector& _topicList, tars::TarsCurrentPtr current) override; + +private: + GatewayInitializer::Ptr m_gatewayInitializer; +}; +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/main/Application.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/main/Application.cpp" new file mode 100644 index 00000000..da0f1967 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/main/Application.cpp" @@ -0,0 +1,125 @@ +#include "../../Common/TarsUtils.h" +#include "../GatewayInitializer.h" +#include "../GatewayServiceServer.h" +#include "bcos-tool/NodeConfig.h" +#include "libinitializer/CommandHelper.h" +#include +#include +#include + +using namespace bcostars; + +class GatewayServiceApp : public tars::Application +{ +public: + GatewayServiceApp() {} + ~GatewayServiceApp() override{}; + + void destroyApp() override + { + if (m_gatewayInitializer) + { + m_gatewayInitializer->stop(); + } + } + void initialize() override + { + // Note: since tars Application catch the exception and output the error message with + // e.what() which unable to explicitly display specific error messages, we actively catch + // and output exception information here + try + { + m_iniConfigPath = tars::ServerConfig::BasePath + "/config.ini"; + addConfig("config.ini"); + initService(m_iniConfigPath); + GatewayServiceParam param; + param.gatewayInitializer = m_gatewayInitializer; + addServantWithParams( + getProxyDesc(bcos::protocol::GATEWAY_SERVANT_NAME), param); + } + catch (std::exception const& e) + { + std::cout << "init GatewayService failed, error: " << boost::diagnostic_information(e) + << std::endl; + exit(-1); + } + } + +protected: + virtual void initService(std::string const& _configPath) + { + boost::property_tree::ptree pt; + boost::property_tree::read_ini(_configPath, pt); + + tool::NodeConfig nodeConfig; + + nodeConfig.loadWithoutTarsFrameworkConfig(pt); + + // init the log + m_logInitializer = std::make_shared(); + if (!nodeConfig.withoutTarsFramework()) + { + m_logInitializer->setLogPath(getLogPath()); + } + + m_logInitializer->initLog(pt); + + // init gateway config + auto gatewayConfig = std::make_shared(); + gatewayConfig->initP2PConfig(pt, true); + gatewayConfig->initRateLimitConfig(pt); + gatewayConfig->setCertPath(tars::ServerConfig::BasePath); + gatewayConfig->setNodePath(tars::ServerConfig::BasePath); + if (gatewayConfig->smSSL()) + { + addConfig("sm_ca.crt"); + addConfig("sm_ssl.crt"); + addConfig("sm_enssl.crt"); + addConfig("sm_ssl.key"); + addConfig("sm_enssl.key"); + gatewayConfig->initSMCertConfig(pt); + } + else + { + addConfig("ca.crt"); + addConfig("ssl.key"); + addConfig("ssl.crt"); + gatewayConfig->initCertConfig(pt); + } + + // nodes.json + addConfig("nodes.json"); + gatewayConfig->loadP2pConnectedNodes(); + // init rpc + m_gatewayInitializer = std::make_shared(_configPath, gatewayConfig); + bcos::initializer::showNodeVersionMetric(); + m_gatewayInitializer->start(); + } + +private: + std::string m_iniConfigPath; + bcos::BoostLogInitializer::Ptr m_logInitializer; + GatewayInitializer::Ptr m_gatewayInitializer; +}; + +int main(int argc, char* argv[]) +{ + try + { + bcos::initializer::initCommandLine(argc, argv); + GatewayServiceApp app; + app.main(argc, argv); + app.waitForShutdown(); + + return 0; + } + catch (std::exception& e) + { + cerr << "GatewayService std::exception:" << e.what() << std::endl; + } + catch (...) + { + cerr << "GatewayService unknown exception." << std::endl; + } + return -1; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/main/CMakeLists.txt" "b/BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/main/CMakeLists.txt" new file mode 100644 index 00000000..d559cd68 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/GatewayService/main/CMakeLists.txt" @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.14) + +project(bcostars-Gateway) + +include_directories(${CMAKE_SOURCE_DIR}) + +find_package(Boost REQUIRED program_options) + +file(GLOB SRC_LIST "*.cpp") + +aux_source_directory(../ SRC_LIST) +add_executable(${GATEWAY_BINARY_NAME} ${SRC_LIST}) + +if(WITH_TIKV) + target_link_libraries(${GATEWAY_BINARY_NAME} ${COMMAND_HELPER_LIB} ${PROTOCOL_INIT_LIB} ${TOOL_TARGET} ${GATEWAY_TARGET} ${LEADER_ELECTION_TARGET}) +else() + target_link_libraries(${GATEWAY_BINARY_NAME} bcos-crypto ${COMMAND_HELPER_LIB} ${PROTOCOL_INIT_LIB} ${TOOL_TARGET} ${GATEWAY_TARGET}) +endif() diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/LedgerService/CMakeLists.txt" "b/BFPL\345\243\271/fisco-bcos-tars-service/LedgerService/CMakeLists.txt" new file mode 100644 index 00000000..641da9e2 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/LedgerService/CMakeLists.txt" @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.14) + +project(bcostars-ledger) +file(GLOB SRC_LIST "*.cpp") + +add_library(${LEDGER_SERVICE_LIB} ${SRC_LIST}) +target_link_libraries(${LEDGER_SERVICE_LIB} ${TARS_PROTOCOL_TARGET}) \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/LedgerService/LedgerServiceServer.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/LedgerService/LedgerServiceServer.cpp" new file mode 100644 index 00000000..870dad4a --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/LedgerService/LedgerServiceServer.cpp" @@ -0,0 +1,230 @@ +/** + * Copyright (C) 2021 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. + * + * @brief server for Ledger + * @file LedgerServiceServer.cpp + * @author: yujiechen + * @date 2021-10-18 + */ + +#include "LedgerServiceServer.h" +#include +#include +#include +#include +#include + +using namespace bcostars; + +bcostars::Error LedgerServiceServer::asyncGetBatchTxsByHashList( + const vector>& _txsHashList, tars::Bool _withProof, + vector&, map>&, + tars::TarsCurrentPtr current) +{ + current->setResponse(false); + auto hashList = std::make_shared(); + for (auto const& hash : _txsHashList) + { + if (hash.size() < bcos::crypto::HashType::SIZE) + { + continue; + } + hashList->emplace_back(bcos::crypto::HashType( + reinterpret_cast(hash.data()), bcos::crypto::HashType::SIZE)); + } + m_ledger->asyncGetBatchTxsByHashList(hashList, _withProof, + [current](bcos::Error::Ptr _error, bcos::protocol::TransactionsPtr _txsList, + std::shared_ptr> _proofList) { + // to tars transaction + std::vector tarsTxs; + if (_txsList) + { + for (auto const& tx : *_txsList) + { + tarsTxs.emplace_back( + std::dynamic_pointer_cast(tx) + ->inner()); + } + } + // to tars proof + std::map> tarsMerkleProofs; + if (_proofList) + { + for (auto const& it : *_proofList) + { + std::vector tarsMerkleItem; + auto const& proofs = it.second; + for (auto const& proof : *proofs) + { + MerkleProofItem tarsProof; + tarsProof.left = proof.first; + tarsProof.right = proof.second; + tarsMerkleItem.emplace_back(tarsProof); + } + tarsMerkleProofs[it.first] = tarsMerkleItem; + } + } + async_response_asyncGetBatchTxsByHashList( + current, toTarsError(_error), tarsTxs, tarsMerkleProofs); + }); + return bcostars::Error(); +} + +bcostars::Error LedgerServiceServer::asyncGetBlockDataByNumber(tars::Int64 _blockNumber, + tars::Int64 _blockFlag, bcostars::Block&, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + m_ledger->asyncGetBlockDataByNumber(_blockNumber, _blockFlag, + [current](bcos::Error::Ptr _error, bcos::protocol::Block::Ptr _block) { + bcostars::Block tarsBlock; + if (_block) + { + tarsBlock = + std::dynamic_pointer_cast(_block)->inner(); + } + async_response_asyncGetBlockDataByNumber(current, toTarsError(_error), tarsBlock); + }); + return bcostars::Error(); +} + +bcostars::Error LedgerServiceServer::asyncGetBlockHashByNumber( + tars::Int64 _blockNumber, vector&, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + m_ledger->asyncGetBlockHashByNumber( + _blockNumber, [current](bcos::Error::Ptr _error, bcos::crypto::HashType const& _blockHash) { + if (_error) + { + async_response_asyncGetBlockHashByNumber( + current, toTarsError(_error), vector()); + return; + } + vector blockHash(_blockHash.begin(), _blockHash.end()); + async_response_asyncGetBlockHashByNumber(current, toTarsError(_error), blockHash); + }); + return bcostars::Error(); +} + +bcostars::Error LedgerServiceServer::asyncGetBlockNumber(tars::Int64&, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + m_ledger->asyncGetBlockNumber( + [current](bcos::Error::Ptr _error, bcos::protocol::BlockNumber _blockNumber) { + async_response_asyncGetBlockNumber(current, toTarsError(_error), _blockNumber); + }); + return bcostars::Error(); +} +bcostars::Error LedgerServiceServer::asyncGetBlockNumberByHash( + const vector& _blockHash, tars::Int64&, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + bcos::crypto::HashType blockHash; + if (_blockHash.size() >= bcos::crypto::HashType::SIZE) + { + blockHash = bcos::crypto::HashType( + reinterpret_cast(_blockHash.data()), bcos::crypto::HashType::SIZE); + } + // _blockHash + m_ledger->asyncGetBlockNumberByHash( + blockHash, [current](bcos::Error::Ptr _error, bcos::protocol::BlockNumber _blockNumber) { + async_response_asyncGetBlockNumberByHash(current, toTarsError(_error), _blockNumber); + }); + return bcostars::Error(); +} + +bcostars::Error LedgerServiceServer::asyncGetNodeListByType( + const std::string& _type, vector&, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + m_ledger->asyncGetNodeListByType( + _type, [current](bcos::Error::Ptr _error, bcos::consensus::ConsensusNodeListPtr _nodeList) { + if (_nodeList) + { + async_response_asyncGetNodeListByType( + current, toTarsError(_error), toTarsConsensusNodeList(*_nodeList)); + return; + } + async_response_asyncGetNodeListByType( + current, toTarsError(_error), std::vector()); + }); + return bcostars::Error(); +} + +bcostars::Error LedgerServiceServer::asyncGetSystemConfigByKey( + const std::string& _key, std::string&, tars::Int64&, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + m_ledger->asyncGetSystemConfigByKey(_key, [current](bcos::Error::Ptr _error, std::string _value, + bcos::protocol::BlockNumber _blockNumber) { + async_response_asyncGetSystemConfigByKey( + current, toTarsError(_error), _value, _blockNumber); + }); + return bcostars::Error(); +} +bcostars::Error LedgerServiceServer::asyncGetTotalTransactionCount( + tars::Int64&, tars::Int64&, tars::Int64&, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + m_ledger->asyncGetTotalTransactionCount( + [current](bcos::Error::Ptr _error, int64_t _totalTxCount, int64_t _failedTxCount, + bcos::protocol::BlockNumber _latestBlockNumber) { + async_response_asyncGetTotalTransactionCount( + current, toTarsError(_error), _totalTxCount, _failedTxCount, _latestBlockNumber); + }); + return bcostars::Error(); +} + +bcostars::Error LedgerServiceServer::asyncGetTransactionReceiptByHash( + const vector& _txHash, tars::Bool _withProof, bcostars::TransactionReceipt&, + vector&, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + bcos::crypto::HashType txHash; + if (_txHash.size() >= bcos::crypto::HashType::SIZE) + { + txHash = bcos::crypto::HashType( + reinterpret_cast(_txHash.data()), bcos::crypto::HashType::SIZE); + } + + m_ledger->asyncGetTransactionReceiptByHash(txHash, _withProof, + [current](bcos::Error::Ptr _error, bcos::protocol::TransactionReceipt::ConstPtr _receipt, + bcos::ledger::MerkleProofPtr _merkleProofList) { + // get tars receipt + bcostars::TransactionReceipt tarsReceipt; + if (_receipt) + { + auto mutableReceipt = + std::const_pointer_cast(_receipt); + tarsReceipt = std::dynamic_pointer_cast( + mutableReceipt) + ->inner(); + } + // get tars merkle + vector tarsMerkleItemList; + if (_merkleProofList) + { + for (auto const& merkle : *_merkleProofList) + { + MerkleProofItem item; + item.left = merkle.first; + item.right = merkle.second; + tarsMerkleItemList.emplace_back(item); + } + } + async_response_asyncGetTransactionReceiptByHash( + current, toTarsError(_error), tarsReceipt, tarsMerkleItemList); + }); + return bcostars::Error(); +} diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/LedgerService/LedgerServiceServer.h" "b/BFPL\345\243\271/fisco-bcos-tars-service/LedgerService/LedgerServiceServer.h" new file mode 100644 index 00000000..88b94541 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/LedgerService/LedgerServiceServer.h" @@ -0,0 +1,67 @@ +/** + * Copyright (C) 2021 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. + * + * @brief server for Ledger + * @file LedgerServiceServer.h + * @author: yujiechen + * @date 2021-10-18 + */ +#pragma once +#include +#include + +namespace bcostars +{ +struct LedgerServiceParam +{ + bcos::ledger::LedgerInterface::Ptr ledger; +}; +class LedgerServiceServer : public LedgerService +{ +public: + LedgerServiceServer(LedgerServiceParam const& _param) : m_ledger(_param.ledger) {} + ~LedgerServiceServer() override {} + + void initialize() override {} + void destroy() override {} + + bcostars::Error asyncGetBatchTxsByHashList(const vector >& _txsHashList, + tars::Bool _withProof, vector& _transactions, + map >& _merkleProofList, + tars::TarsCurrentPtr current) override; + bcostars::Error asyncGetBlockDataByNumber(tars::Int64 _blockNumber, tars::Int64 _blockFlag, + bcostars::Block& _block, tars::TarsCurrentPtr current) override; + bcostars::Error asyncGetBlockHashByNumber(tars::Int64 _blockNumber, + vector& _blockHash, tars::TarsCurrentPtr current) override; + bcostars::Error asyncGetBlockNumber( + tars::Int64& _blockNumber, tars::TarsCurrentPtr current) override; + bcostars::Error asyncGetBlockNumberByHash(const vector& _blockHash, + tars::Int64& _blockNumber, tars::TarsCurrentPtr current) override; + bcostars::Error asyncGetNodeListByType(const std::string& _type, + vector& _nodeList, tars::TarsCurrentPtr current) override; + bcostars::Error asyncGetSystemConfigByKey(const std::string& _key, std::string& _value, + tars::Int64& _blockNumber, tars::TarsCurrentPtr current) override; + bcostars::Error asyncGetTotalTransactionCount(tars::Int64& _totalTxCount, + tars::Int64& _failedTxCount, tars::Int64& _latestBlockNumber, + tars::TarsCurrentPtr current) override; + + bcostars::Error asyncGetTransactionReceiptByHash(const vector& _txHash, + tars::Bool _withProof, bcostars::TransactionReceipt& _receipt, + vector& _proof, tars::TarsCurrentPtr current) override; + +private: + bcos::ledger::LedgerInterface::Ptr m_ledger; +}; +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/NodeService/NodeServiceApp.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/NodeService/NodeServiceApp.cpp" new file mode 100644 index 00000000..1a811727 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/NodeService/NodeServiceApp.cpp" @@ -0,0 +1,194 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Application for the NodeService + * @file NodeServiceApp.cpp + * @author: yujiechen + * @date 2021-10-18 + */ +#include "NodeServiceApp.h" +#include "../Common/TarsUtils.h" +#include "../FrontService/FrontServiceServer.h" +#include "../LedgerService/LedgerServiceServer.h" +#include "../PBFTService/PBFTServiceServer.h" +#include "../SchedulerService/SchedulerServiceServer.h" +#include "../TxPoolService/TxPoolServiceServer.h" +#include "bcos-framework/protocol/ServiceDesc.h" +#include "bcos-tool/NodeConfig.h" +#include "libinitializer/Initializer.h" +#include +#include +#include +#include + +using namespace bcostars; +using namespace bcos; +using namespace bcos::initializer; +using namespace bcos::protocol; + +void NodeServiceApp::destroyApp() +{ + if (m_nodeInitializer) + { + // stop the nodeService + m_nodeInitializer->stop(); + } +} + +void NodeServiceApp::initialize() +{ + // Note: since tars Application catch the exception and output the error message with + // e.what() which unable to explicitly display specific error messages, we actively catch + // and output exception information here + try + { + BCOS_LOG(INFO) << LOG_DESC("initGlobalConfig"); + g_BCOSConfig.setCodec(std::make_shared()); + + initConfig(); + initLog(); + initNodeService(); + initTarsNodeService(); + initServiceInfo(this); + m_nodeInitializer->start(); + } + catch (std::exception const& e) + { + std::cout << "init NodeService failed, error: " << boost::diagnostic_information(e) + << std::endl; + exit(-1); + } +} + +void NodeServiceApp::initLog() +{ + boost::property_tree::ptree pt; + boost::property_tree::read_ini(m_iniConfigPath, pt); + + tool::NodeConfig nodeConfig; + nodeConfig.loadWithoutTarsFrameworkConfig(pt); + + m_logInitializer = std::make_shared(); + if (!nodeConfig.withoutTarsFramework()) + { + m_logInitializer->setLogPath(getLogPath()); + } + + m_logInitializer->initLog(pt); +} + +void NodeServiceApp::initNodeService() +{ + m_nodeInitializer = std::make_shared(); + m_nodeInitializer->initMicroServiceNode( + m_nodeArchType, m_iniConfigPath, m_genesisConfigPath, m_privateKeyPath, getLogPath()); + auto rpcServiceName = m_nodeInitializer->nodeConfig()->rpcServiceName(); + + auto withoutTarsFramework = m_nodeInitializer->nodeConfig()->withoutTarsFramework(); + + std::vector endPoints; + m_nodeInitializer->nodeConfig()->getTarsClientProxyEndpoints( + bcos::protocol::RPC_NAME, endPoints); + + auto rpcServicePrx = bcostars::createServantProxy( + withoutTarsFramework, rpcServiceName, endPoints); + + auto rpc = std::make_shared(rpcServicePrx, rpcServiceName); + m_nodeInitializer->initNotificationHandlers(rpc); + + // NOTE: this should be last called + m_nodeInitializer->initSysContract(); +} + +void NodeServiceApp::initTarsNodeService() +{ + // init the txpool servant + TxPoolServiceParam txpoolParam; + txpoolParam.txPoolInitializer = m_nodeInitializer->txPoolInitializer(); + addServantWithParams( + getProxyDesc(TXPOOL_SERVANT_NAME), txpoolParam); + + // init the pbft servant + PBFTServiceParam pbftParam; + pbftParam.pbftInitializer = m_nodeInitializer->pbftInitializer(); + addServantWithParams( + getProxyDesc(CONSENSUS_SERVANT_NAME), pbftParam); + + // init the ledger + LedgerServiceParam ledgerParam; + ledgerParam.ledger = m_nodeInitializer->ledger(); + addServantWithParams( + getProxyDesc(LEDGER_SERVANT_NAME), ledgerParam); + + // init the scheduler + SchedulerServiceParam schedulerParam; + schedulerParam.scheduler = m_nodeInitializer->scheduler(); + schedulerParam.cryptoSuite = m_nodeInitializer->protocolInitializer()->cryptoSuite(); + addServantWithParams( + getProxyDesc(SCHEDULER_SERVANT_NAME), schedulerParam); + + // init the frontService, for the gateway to access the frontService + FrontServiceParam frontServiceParam; + frontServiceParam.frontServiceInitializer = m_nodeInitializer->frontService(); + addServantWithParams( + getProxyDesc(FRONT_SERVANT_NAME), frontServiceParam); +} + +void NodeServiceApp::initServiceInfo(Application* _application) +{ + auto nodeInfo = m_nodeInitializer->pbftInitializer()->nodeInfo(); + // Note: must call this after addServantWithParams of NodeServiceApp + auto schedulerDesc = getEndPointDescByAdapter(_application, SCHEDULER_SERVANT_NAME); + if (!schedulerDesc.first) + { + throw std::runtime_error("init NodeService failed for get scheduler-desc failed"); + } + nodeInfo->appendServiceInfo(SCHEDULER, schedulerDesc.second); + + auto ledgerDesc = getEndPointDescByAdapter(_application, LEDGER_SERVANT_NAME); + if (!ledgerDesc.first) + { + throw std::runtime_error("init NodeService failed for get ledger-desc failed"); + } + nodeInfo->appendServiceInfo(LEDGER, ledgerDesc.second); + + auto frontServiceDesc = getEndPointDescByAdapter(_application, FRONT_SERVANT_NAME); + if (!frontServiceDesc.first) + { + throw std::runtime_error("init NodeService failed for get front-service-desc failed"); + } + nodeInfo->appendServiceInfo(FRONT, frontServiceDesc.second); + + auto txpoolServiceDesc = getEndPointDescByAdapter(_application, TXPOOL_SERVANT_NAME); + if (!txpoolServiceDesc.first) + { + throw std::runtime_error("init NodeService failed for get txpool-service-desc failed"); + } + nodeInfo->appendServiceInfo(TXPOOL, txpoolServiceDesc.second); + + auto consensusServiceDesc = getEndPointDescByAdapter(_application, CONSENSUS_SERVANT_NAME); + if (!consensusServiceDesc.first) + { + throw std::runtime_error("init NodeService failed for get consensus-service-desc failed"); + } + nodeInfo->appendServiceInfo(CONSENSUS, consensusServiceDesc.second); + // sync the latest groupInfo to rpc/gateway + m_nodeInitializer->pbftInitializer()->onGroupInfoChanged(); + BCOS_LOG(INFO) << LOG_DESC("initServiceInfo") << LOG_KV("schedulerDesc", schedulerDesc.second) + << LOG_KV("ledgerDesc", ledgerDesc.second) + << LOG_KV("frontServiceDesc", frontServiceDesc.second) + << LOG_KV("txpoolServiceDesc", txpoolServiceDesc.second) + << LOG_KV("consensusServiceDesc", consensusServiceDesc.second); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/NodeService/NodeServiceApp.h" "b/BFPL\345\243\271/fisco-bcos-tars-service/NodeService/NodeServiceApp.h" new file mode 100644 index 00000000..b72622a3 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/NodeService/NodeServiceApp.h" @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Application for the NodeService + * @file NodeServiceApp.h + * @author: yujiechen + * @date 2021-10-18 + */ +#pragma once +#include "bcos-tars-protocol/tars/RpcService.h" +#include +#include +#include +#include + +namespace bcos::initializer +{ +class Initializer; +} +namespace bcostars +{ +class NodeServiceApp : public tars::Application +{ +public: + NodeServiceApp() {} + ~NodeServiceApp() override {} + + void initialize() override; + void destroyApp() override; + + void setNodeArchType(bcos::protocol::NodeArchitectureType _type) { m_nodeArchType = _type; } + +protected: + virtual void initConfig() + { + m_iniConfigPath = tars::ServerConfig::BasePath + "/config.ini"; + m_genesisConfigPath = tars::ServerConfig::BasePath + "/config.genesis"; + m_privateKeyPath = tars::ServerConfig::BasePath + "/node.pem"; + addConfig("node.pem"); + addConfig("config.genesis"); + addConfig("config.ini"); + } + virtual void initLog(); + virtual void initNodeService(); + virtual void initTarsNodeService(); + void initServiceInfo(Application* _application); + +private: + bcos::BoostLogInitializer::Ptr m_logInitializer; + std::string m_iniConfigPath; + std::string m_genesisConfigPath; + std::string m_privateKeyPath; + std::shared_ptr m_nodeInitializer; + bcos::protocol::NodeArchitectureType m_nodeArchType; +}; +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/NodeService/max/CMakeLists.txt" "b/BFPL\345\243\271/fisco-bcos-tars-service/NodeService/max/CMakeLists.txt" new file mode 100644 index 00000000..ba2546f4 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/NodeService/max/CMakeLists.txt" @@ -0,0 +1,16 @@ +file(GLOB SRC_LIST "*.cpp") +file(GLOB HEADERS "*.h") + +find_package(Boost REQUIRED program_options) + +aux_source_directory(. SRC_LIST) +aux_source_directory(../ SRC_LIST) +aux_source_directory(../../FrontService SRC_LIST) +aux_source_directory(../../LedgerService SRC_LIST) +aux_source_directory(../../SchedulerService SRC_LIST) +aux_source_directory(../../TxPoolService SRC_LIST) +aux_source_directory(../../PBFTService SRC_LIST) + +add_executable(${MAX_NODE_SERVICE_BINARY_NAME} ${SRC_LIST} ${HEADERS}) + +target_link_libraries(${MAX_NODE_SERVICE_BINARY_NAME} PUBLIC ${INIT_LIB} ${PBFT_INIT_LIB} ${TARS_PROTOCOL_TARGET} ${COMMAND_HELPER_LIB}) \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/NodeService/max/main.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/NodeService/max/main.cpp" new file mode 100644 index 00000000..0fed15ab --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/NodeService/max/main.cpp" @@ -0,0 +1,55 @@ +/** + * 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. + * + * @brief main for the BCOSMaxNodeService + * @file main.cpp + * @author: yujiechen + * @date 2022-05-11 + */ +#include "../NodeServiceApp.h" +#include "libinitializer/CommandHelper.h" +#include +#include +#include + +using namespace bcostars; +using namespace bcos; +using namespace bcos::initializer; +int main(int argc, char* argv[]) +{ + try + { + bcos::initializer::initCommandLine(argc, argv); + NodeServiceApp app; + app.setNodeArchType(bcos::protocol::NodeArchitectureType::MAX); + printVersion(); + std::cout << "[" << getCurrentDateTime() << "] "; + std::cout << "The fisco-bcos is running..." << std::endl; + app.main(argc, argv); + app.waitForShutdown(); + std::cout << "[" << getCurrentDateTime() << "] "; + std::cout << "fisco-bcos program exit normally." << std::endl; + return 0; + } + catch (std::exception& e) + { + cerr << "NodeService std::exception:" << e.what() << std::endl; + } + catch (...) + { + cerr << "NodeService unknown exception." << std::endl; + } + return -1; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/NodeService/pro/CMakeLists.txt" "b/BFPL\345\243\271/fisco-bcos-tars-service/NodeService/pro/CMakeLists.txt" new file mode 100644 index 00000000..04d92f73 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/NodeService/pro/CMakeLists.txt" @@ -0,0 +1,16 @@ +file(GLOB SRC_LIST "*.cpp") +file(GLOB HEADERS "*.h") + +find_package(Boost REQUIRED program_options) + +aux_source_directory(. SRC_LIST) +aux_source_directory(../ SRC_LIST) +aux_source_directory(../../FrontService SRC_LIST) +aux_source_directory(../../LedgerService SRC_LIST) +aux_source_directory(../../SchedulerService SRC_LIST) +aux_source_directory(../../TxPoolService SRC_LIST) +aux_source_directory(../../PBFTService SRC_LIST) + +add_executable(${PRO_NODE_SERVICE_BINARY_NAME} ${SRC_LIST} ${HEADERS}) + +target_link_libraries(${PRO_NODE_SERVICE_BINARY_NAME} PUBLIC ${INIT_LIB} ${PBFT_INIT_LIB} ${TARS_PROTOCOL_TARGET} ${COMMAND_HELPER_LIB}) \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/NodeService/pro/main.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/NodeService/pro/main.cpp" new file mode 100644 index 00000000..6d3ecb8d --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/NodeService/pro/main.cpp" @@ -0,0 +1,59 @@ +/** + * Copyright (C) 2021 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. + * + * @brief main for the fisco-bcos + * @file main.cpp + * @author: yujiechen + * @date 2021-07-26 + * @brief main for the fisco-bcos + * @file main.cpp + * @author: ancelmo + * @date 2021-10-14 + */ +#include "../NodeServiceApp.h" +#include "libinitializer/CommandHelper.h" +#include +#include +#include + +using namespace bcostars; +using namespace bcos; +using namespace bcos::initializer; +int main(int argc, char* argv[]) +{ + try + { + bcos::initializer::initCommandLine(argc, argv); + NodeServiceApp app; + app.setNodeArchType(bcos::protocol::NodeArchitectureType::PRO); + printVersion(); + std::cout << "[" << getCurrentDateTime() << "] "; + std::cout << "The fisco-bcos is running..." << std::endl; + app.main(argc, argv); + app.waitForShutdown(); + std::cout << "[" << getCurrentDateTime() << "] "; + std::cout << "fisco-bcos program exit normally." << std::endl; + return 0; + } + catch (std::exception& e) + { + cerr << "NodeService std::exception:" << e.what() << std::endl; + } + catch (...) + { + cerr << "NodeService unknown exception." << std::endl; + } + return -1; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/PBFTService/PBFTServiceClient.h" "b/BFPL\345\243\271/fisco-bcos-tars-service/PBFTService/PBFTServiceClient.h" new file mode 100644 index 00000000..67ede22b --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/PBFTService/PBFTServiceClient.h" @@ -0,0 +1,198 @@ +/** + * Copyright (C) 2021 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. + * + * @brief client for the PBFTService + * @file PBFTServiceClient.h + * @author: yujiechen + * @date 2021-06-29 + */ + +#pragma once + +#include "bcos-framework/sealer/SealerInterface.h" +#include +#include +#include +#include + +namespace bcostars +{ +class PBFTServiceCommonCallback : public bcostars::PBFTServicePrxCallback +{ +public: + PBFTServiceCommonCallback(std::function _callback) + : PBFTServicePrxCallback(), m_callback(_callback) + {} + ~PBFTServiceCommonCallback() override {} + + void callback_asyncNoteUnSealedTxsSize(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + void callback_asyncNoteUnSealedTxsSize_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + void callback_asyncNotifyConsensusMessage(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + void callback_asyncNotifyConsensusMessage_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + void callback_asyncNotifyNewBlock(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + + void callback_asyncNotifyNewBlock_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + void callback_asyncSubmitProposal(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + void callback_asyncSubmitProposal_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + + void callback_asyncNotifyBlockSyncMessage(const bcostars::Error& ret) override + { + m_callback(toBcosError(ret)); + } + + void callback_asyncNotifyBlockSyncMessage_exception(tars::Int32 ret) override + { + m_callback(toBcosError(ret)); + } + +private: + std::function m_callback; +}; + +class PBFTServiceClient : virtual public bcos::consensus::ConsensusInterface, + virtual public bcos::sealer::SealerInterface +{ +public: + using Ptr = std::shared_ptr; + PBFTServiceClient(bcostars::PBFTServicePrx _proxy) : m_proxy(_proxy) {} + ~PBFTServiceClient() override {} + + void start() override {} + void stop() override {} + // called by frontService to dispatch message + void asyncNotifyConsensusMessage(bcos::Error::Ptr _error, std::string const& _uuid, + bcos::crypto::NodeIDPtr _nodeID, bcos::bytesConstRef _data, + std::function _onRecv) override; + void asyncGetPBFTView( + std::function _onGetView) override; + + // the txpool notify the unsealed txsSize to the sealer module + void asyncNoteUnSealedTxsSize( + size_t _unsealedTxsSize, std::function _onRecvResponse) override; + + // the sealer submit proposal to the consensus module + // Note: if the sealer module integrates with the PBFT module, no need to implement this + // interface + void asyncSubmitProposal(bool _containSysTxs, bcos::bytesConstRef _proposalData, + bcos::protocol::BlockNumber _proposalIndex, bcos::crypto::HashType const& _proposalHash, + std::function _onProposalSubmitted) override; + + // the sync module calls this interface to check block + // Note: if the sync module integrates with the PBFT module, no need to implement this interface + void asyncCheckBlock(bcos::protocol::Block::Ptr _block, + std::function _onVerifyFinish) override; + + // the sync module calls this interface to notify new block + // Note: if the sync module integrates with the PBFT module, no need to implement this interface + void asyncNotifyNewBlock(bcos::ledger::LedgerConfig::Ptr _ledgerConfig, + std::function _onRecv) override; + + // for the sync module to notify the syncing number + // Note: since the sync module is integrated with the PBFT module, no need to implement the + // client interface + void notifyHighestSyncingNumber(bcos::protocol::BlockNumber _number) override + { + throw std::runtime_error("notifyHighestSyncingNumber: unimplemented interface!"); + } + void asyncNotifySealProposal( + size_t, size_t, size_t, std::function) override + { + throw std::runtime_error("asyncNotifySealProposal: unimplemented interface!"); + } + // for the consensus module to notify the latest blockNumber to the sealer module + // Note: since the sealer module is integrated with the PBFT module, no need to implement the + // client interface + void asyncNoteLatestBlockNumber(int64_t) override + { + throw std::runtime_error("asyncNoteLatestBlockNumber: unimplemented interface!"); + } + + // the consensus module notify the sealer to reset sealing when viewchange + void asyncResetSealing(std::function _onRecvResponse) override + { + throw std::runtime_error("asyncResetSealing: unimplemented interface!"); + } + +private: + bcostars::PBFTServicePrx m_proxy; +}; + +class BlockSyncServiceClient : virtual public bcos::sync::BlockSyncInterface +{ +public: + using Ptr = std::shared_ptr; + BlockSyncServiceClient(bcostars::PBFTServicePrx _proxy) : m_proxy(_proxy) {} + ~BlockSyncServiceClient() override {} + + // called by the consensus module when commit a new block + void asyncNotifyNewBlock( + bcos::ledger::LedgerConfig::Ptr, std::function) override + {} + + // called by the consensus module to notify the consensusing block number + void asyncNotifyCommittedIndex( + bcos::protocol::BlockNumber, std::function) override + {} + + // called by the RPC to get the sync status + void asyncGetSyncInfo( + std::function _onGetSyncInfo) 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, + std::function _onRecv) override + { + auto nodeIDData = _nodeID->data(); + m_proxy->async_asyncNotifyBlockSyncMessage(new PBFTServiceCommonCallback(_onRecv), _uuid, + std::vector(nodeIDData.begin(), nodeIDData.end()), + std::vector(_data.begin(), _data.end())); + } + + void notifyConnectedNodes(bcos::crypto::NodeIDSet const& _connectedNodes, + std::function _onRecvResponse); + +protected: + void start() override {} + void stop() override {} + +private: + bcostars::PBFTServicePrx m_proxy; +}; +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/PBFTService/PBFTServiceServer.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/PBFTService/PBFTServiceServer.cpp" new file mode 100644 index 00000000..342c2314 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/PBFTService/PBFTServiceServer.cpp" @@ -0,0 +1,147 @@ +/** + * Copyright (C) 2021 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. + * + * @brief server for the PBFTService + * @file PBFTServiceServer.h + * @author: yujiechen + * @date 2021-06-29 + */ + +#include "PBFTServiceServer.h" +#include "../Common/TarsUtils.h" + +using namespace bcostars; +using namespace bcos::consensus; +using namespace bcos::initializer; + +Error PBFTServiceServer::asyncCheckBlock( + const Block& _block, tars::Bool&, tars::TarsCurrentPtr _current) +{ + auto blockFactory = m_pbftInitializer->blockFactory(); + _current->setResponse(false); + auto block = std::make_shared( + blockFactory->transactionFactory(), blockFactory->receiptFactory()); + block->setInner(std::move(*const_cast(&_block))); + m_pbftInitializer->pbft()->asyncCheckBlock( + block, [_current](bcos::Error::Ptr _error, bool _verifyResult) { + async_response_asyncCheckBlock(_current, toTarsError(_error), _verifyResult); + }); + return bcostars::Error(); +} + +Error PBFTServiceServer::asyncGetPBFTView(tars::Int64& _view, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + m_pbftInitializer->pbft()->asyncGetPBFTView( + [_current](bcos::Error::Ptr _error, bcos::consensus::ViewType _view) { + async_response_asyncGetPBFTView(_current, toTarsError(_error), _view); + }); + return bcostars::Error(); +} + + +Error PBFTServiceServer::asyncNoteUnSealedTxsSize( + tars::Int64 _unsealedTxsSize, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + m_pbftInitializer->sealer()->asyncNoteUnSealedTxsSize( + _unsealedTxsSize, [_current](bcos::Error::Ptr _error) { + async_response_asyncNoteUnSealedTxsSize(_current, toTarsError(_error)); + }); + return bcostars::Error(); +} + +Error PBFTServiceServer::asyncNotifyConsensusMessage(std::string const& _uuid, + const vector& _nodeId, const vector& _data, + tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + auto nodeId = m_pbftInitializer->keyFactory()->createKey( + bcos::bytesConstRef((const bcos::byte*)_nodeId.data(), _nodeId.size())); + m_pbftInitializer->pbft()->asyncNotifyConsensusMessage(nullptr, _uuid, nodeId, + bcos::bytesConstRef((const bcos::byte*)_data.data(), _data.size()), + [_current](bcos::Error::Ptr _error) { + async_response_asyncNotifyConsensusMessage(_current, toTarsError(_error)); + }); + return bcostars::Error(); +} + +bcostars::Error PBFTServiceServer::asyncNotifyBlockSyncMessage(std::string const& _uuid, + const vector& _nodeId, const vector& _data, + tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + auto nodeId = m_pbftInitializer->keyFactory()->createKey( + bcos::bytesConstRef((const bcos::byte*)_nodeId.data(), _nodeId.size())); + m_pbftInitializer->blockSync()->asyncNotifyBlockSyncMessage(nullptr, _uuid, nodeId, + bcos::bytesConstRef((const bcos::byte*)_data.data(), _data.size()), + [_current](bcos::Error::Ptr _error) { + async_response_asyncNotifyBlockSyncMessage(_current, toTarsError(_error)); + }); + return bcostars::Error(); +} + + +Error PBFTServiceServer::asyncNotifyNewBlock( + const LedgerConfig& _ledgerConfig, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + auto ledgerConfig = toLedgerConfig(_ledgerConfig, m_pbftInitializer->keyFactory()); + m_pbftInitializer->pbft()->asyncNotifyNewBlock( + ledgerConfig, [_current](bcos::Error::Ptr _error) { + async_response_asyncNotifyNewBlock(_current, toTarsError(_error)); + }); + return bcostars::Error(); +} + +Error PBFTServiceServer::asyncSubmitProposal(bool _containSysTxs, + const vector& _proposalData, tars::Int64 _proposalIndex, + const vector& _proposalHash, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + auto proposalHash = bcos::crypto::HashType(); + if (_proposalHash.size() >= bcos::crypto::HashType::SIZE) + { + proposalHash = bcos::crypto::HashType( + (const bcos::byte*)_proposalHash.data(), bcos::crypto::HashType::SIZE); + } + m_pbftInitializer->pbft()->asyncSubmitProposal(_containSysTxs, + bcos::bytesConstRef((const bcos::byte*)_proposalData.data(), _proposalData.size()), + _proposalIndex, proposalHash, [_current](bcos::Error::Ptr _error) { + async_response_asyncSubmitProposal(_current, toTarsError(_error)); + }); + return bcostars::Error(); +} + +bcostars::Error PBFTServiceServer::asyncGetSyncInfo(std::string&, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + m_pbftInitializer->blockSync()->asyncGetSyncInfo( + [_current](bcos::Error::Ptr _error, std::string const& _syncInfo) { + async_response_asyncGetSyncInfo(_current, toTarsError(_error), _syncInfo); + }); + return bcostars::Error(); +} + +bcostars::Error PBFTServiceServer::asyncGetConsensusStatus( + std::string&, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + m_pbftInitializer->pbft()->asyncGetConsensusStatus( + [_current](bcos::Error::Ptr _error, std::string _consensusStatus) { + async_response_asyncGetConsensusStatus(_current, toTarsError(_error), _consensusStatus); + }); + return bcostars::Error(); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/PBFTService/PBFTServiceServer.h" "b/BFPL\345\243\271/fisco-bcos-tars-service/PBFTService/PBFTServiceServer.h" new file mode 100644 index 00000000..1ccfb8a2 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/PBFTService/PBFTServiceServer.h" @@ -0,0 +1,108 @@ +/** + * Copyright (C) 2021 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. + * + * @brief server for the PBFTService + * @file PBFTServiceServers.h + * @author: yujiechen + * @date 2021-06-29 + */ + +#pragma once +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include "libinitializer/PBFTInitializer.h" +#include +#include +#include +#include +#include +#include + +namespace bcostars +{ +struct PBFTServiceParam +{ + bcos::initializer::PBFTInitializer::Ptr pbftInitializer; +}; +class PBFTServiceServer : public bcostars::PBFTService +{ +public: + using Ptr = std::shared_ptr; + PBFTServiceServer(PBFTServiceParam const& _param) : m_pbftInitializer(_param.pbftInitializer) {} + ~PBFTServiceServer() override {} + + void initialize() override {} + void destroy() override {} + + // for the sync module to check block + // Node: since the sync module is integrated with the PBFT, this interfaces is useless now + bcostars::Error asyncCheckBlock( + const bcostars::Block& _block, tars::Bool&, tars::TarsCurrentPtr _current) override; + + // for the rpc module to get the pbft view + bcostars::Error asyncGetPBFTView(tars::Int64& _view, tars::TarsCurrentPtr _current) override; + bcostars::Error asyncGetSyncInfo( + std::string& _syncInfo, tars::TarsCurrentPtr _current) override; + + // Note: since the sealer is integrated with the PBFT, this interfaces is useless now + bcostars::Error asyncNoteUnSealedTxsSize( + tars::Int64 _unsealedTxsSize, tars::TarsCurrentPtr _current) override; + + bcostars::Error asyncNotifyConsensusMessage(std::string const& _uuid, + const vector& _nodeId, const vector& _data, + tars::TarsCurrentPtr _current) override; + + bcostars::Error asyncNotifyBlockSyncMessage(std::string const& _uuid, + const vector& _nodeId, const vector& _data, + tars::TarsCurrentPtr _current) override; + + // Note: since the blockSync module is integrated with the PBFT, this interfaces is useless now + bcostars::Error asyncNotifyNewBlock( + const bcostars::LedgerConfig& _ledgerConfig, tars::TarsCurrentPtr _current) override; + + // Note: since the sealer module is integrated with the PBFT, the interface is useless now + bcostars::Error asyncSubmitProposal(bool _containSysTxs, + const vector& _proposalData, tars::Int64 _proposalIndex, + const vector& _proposalHash, tars::TarsCurrentPtr _current) override; + + bcostars::Error asyncNotifyConnectedNodes( + const vector>& connectedNodes, tars::TarsCurrentPtr current) override + { + current->setResponse(false); + + bcos::crypto::NodeIDSet bcosNodeIDSet; + for (auto const& it : connectedNodes) + { + bcosNodeIDSet.insert(m_pbftInitializer->keyFactory()->createKey( + bcos::bytesConstRef((const bcos::byte*)it.data(), it.size()))); + } + m_pbftInitializer->blockSync()->notifyConnectedNodes( + bcosNodeIDSet, [current](bcos::Error::Ptr error) { + async_response_asyncNotifyConnectedNodes(current, bcostars::toTarsError(error)); + }); + m_pbftInitializer->pbft()->notifyConnectedNodes( + bcosNodeIDSet, [](bcos::Error::Ptr error) {}); + return bcostars::Error(); + } + + bcostars::Error asyncGetConsensusStatus( + std::string& _consensusStatus, tars::TarsCurrentPtr current) override; + +private: + bcos::initializer::PBFTInitializer::Ptr m_pbftInitializer; +}; + +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/RpcService/RpcInitializer.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/RpcService/RpcInitializer.cpp" new file mode 100644 index 00000000..9a82484c --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/RpcService/RpcInitializer.cpp" @@ -0,0 +1,164 @@ +/** + * Copyright (C) 2021 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. + * + * @brief initializer for the RpcService + * @file RpcInitializer.cpp + * @author: yujiechen + * @date 2021-10-15 + */ +#include "RpcInitializer.h" +#include "../Common/TarsUtils.h" +#include "bcos-framework/protocol/ServiceDesc.h" +#include "bcos-utilities/BoostLog.h" +#include "libinitializer/ProtocolInitializer.h" +#include +#include +#ifdef WITH_TIKV +#include +#endif +#include +#include +#include + +using namespace bcos::group; +using namespace bcostars; + +void RpcInitializer::init(std::string const& _configDir) +{ + // init node config + RPCSERVICE_LOG(INFO) << LOG_DESC("init node config") << LOG_KV("configDir", _configDir); + + if (!m_nodeConfig->rpcSmSsl()) + { + m_nodeConfig->setCaCert(_configDir + "/" + "ca.crt"); + m_nodeConfig->setNodeCert(_configDir + "/" + "ssl.crt"); + m_nodeConfig->setNodeKey(_configDir + "/" + "ssl.key"); + } + else + { + m_nodeConfig->setSmCaCert(_configDir + "/" + "sm_ca.crt"); + m_nodeConfig->setSmNodeCert(_configDir + "/" + "sm_ssl.crt"); + m_nodeConfig->setSmNodeKey(_configDir + "/" + "sm_ssl.key"); + m_nodeConfig->setEnSmNodeCert(_configDir + "/" + "sm_enssl.crt"); + m_nodeConfig->setEnSmNodeKey(_configDir + "/" + "sm_enssl.key"); + } +#ifdef WITH_TIKV + if (m_nodeConfig->enableFailOver()) + { + RPCSERVICE_LOG(INFO) << LOG_DESC("enable failover"); + auto memberFactory = std::make_shared(); + auto leaderEntryPointFactory = + std::make_shared(memberFactory); + auto watchDir = "/" + m_nodeConfig->chainId() + bcos::election::CONSENSUS_LEADER_DIR; + m_leaderEntryPoint = leaderEntryPointFactory->createLeaderEntryPoint( + m_nodeConfig->failOverClusterUrl(), watchDir, "watchLeaderChange", + m_nodeConfig->pdCaPath(), m_nodeConfig->pdCertPath(), m_nodeConfig->pdKeyPath()); + } +#endif + // init rpc config + RPCSERVICE_LOG(INFO) << LOG_DESC("init rpc factory"); + auto factory = initRpcFactory(m_nodeConfig); + + auto rpcServiceName = bcostars::getProxyDesc(bcos::protocol::RPC_SERVANT_NAME); + RPCSERVICE_LOG(INFO) << LOG_DESC("init rpc factory success") + << LOG_KV("rpcServiceName", rpcServiceName); + auto rpc = + factory->buildRpc(m_nodeConfig->gatewayServiceName(), rpcServiceName, m_leaderEntryPoint); + m_rpc = rpc; +} + +void RpcInitializer::setClientID(std::string const& _clientID) +{ + m_rpc->setClientID(_clientID); +} + +bcos::rpc::RPCInterface::Ptr RpcInitializer::rpc() +{ + return m_rpc; +} + +bcos::rpc::RpcFactory::Ptr RpcInitializer::initRpcFactory(bcos::tool::NodeConfig::Ptr _nodeConfig) +{ + // init the protocol + auto protocolInitializer = std::make_shared(); + protocolInitializer->init(_nodeConfig); + m_keyFactory = protocolInitializer->keyFactory(); + + auto withoutTarsFramework = m_nodeConfig->withoutTarsFramework(); + auto gatewayServiceName = _nodeConfig->gatewayServiceName(); + + std::vector endPoints; + m_nodeConfig->getTarsClientProxyEndpoints(bcos::protocol::GATEWAY_NAME, endPoints); + + // get the gateway client + auto gatewayPrx = bcostars::createServantProxy( + withoutTarsFramework, gatewayServiceName, endPoints); + + auto gateway = std::make_shared( + gatewayPrx, gatewayServiceName, protocolInitializer->keyFactory()); + + auto factory = std::make_shared(_nodeConfig->chainId(), gateway, + protocolInitializer->keyFactory(), protocolInitializer->dataEncryption()); + factory->setNodeConfig(_nodeConfig); + RPCSERVICE_LOG(INFO) << LOG_DESC("create rpc factory success") + << LOG_KV("withoutTarsFramework", withoutTarsFramework) + << LOG_KV("gatewayServiceName", gatewayServiceName); + return factory; +} + +void RpcInitializer::start() +{ + if (m_running) + { + RPCSERVICE_LOG(INFO) << LOG_DESC("The RpcService has already been started"); + return; + } + m_running = true; + +#ifdef WITH_TIKV + if (m_leaderEntryPoint) + { + RPCSERVICE_LOG(INFO) << LOG_DESC("start leader-entry-point"); + m_leaderEntryPoint->start(); + } +#endif + RPCSERVICE_LOG(INFO) << LOG_DESC("start rpc"); + m_rpc->start(); + RPCSERVICE_LOG(INFO) << LOG_DESC("start rpc success"); +} + +void RpcInitializer::stop() +{ + if (!m_running) + { + RPCSERVICE_LOG(INFO) << LOG_DESC("The RpcService has already stopped!"); + return; + } + m_running = false; + RPCSERVICE_LOG(INFO) << LOG_DESC("Stop the RpcService"); + +#ifdef WITH_TIKV + if (m_leaderEntryPoint) + { + m_leaderEntryPoint->stop(); + } +#endif + + if (m_rpc) + { + m_rpc->stop(); + } + RPCSERVICE_LOG(INFO) << LOG_DESC("Stop the RpcService success"); +} diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/RpcService/RpcInitializer.h" "b/BFPL\345\243\271/fisco-bcos-tars-service/RpcService/RpcInitializer.h" new file mode 100644 index 00000000..a9aae42c --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/RpcService/RpcInitializer.h" @@ -0,0 +1,73 @@ +/** + * Copyright (C) 2021 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. + * + * @brief initializer for the RpcService + * @file RpcInitializer.h + * @author: yujiechen + * @date 2021-10-15 + */ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::rpc +{ +class RpcFactory; +class Rpc; +} // namespace bcos::rpc +namespace bcostars +{ +class RpcInitializer +{ +public: + using Ptr = std::shared_ptr; + RpcInitializer(std::string const& _configDir, bcos::tool::NodeConfig::Ptr _nodeConfig) + : m_nodeConfig(_nodeConfig), + m_groupInfoFactory(std::make_shared()), + m_chainNodeInfoFactory(std::make_shared()) + { + init(_configDir); + } + virtual ~RpcInitializer() { stop(); } + + + virtual void start(); + virtual void stop(); + + void setClientID(std::string const& _clientID); + bcos::rpc::RPCInterface::Ptr rpc(); + bcos::crypto::KeyFactory::Ptr keyFactory() { return m_keyFactory; } + bcos::group::GroupInfoFactory::Ptr groupInfoFactory() { return m_groupInfoFactory; } + bcos::group::ChainNodeInfoFactory::Ptr chainNodeInfoFactory() { return m_chainNodeInfoFactory; } + +protected: + virtual void init(std::string const& _configPath); + std::shared_ptr initRpcFactory(bcos::tool::NodeConfig::Ptr _nodeConfig); + +private: + std::shared_ptr m_rpc; + bcos::tool::NodeConfig::Ptr m_nodeConfig; + bcos::crypto::KeyFactory::Ptr m_keyFactory; + bcos::group::GroupInfoFactory::Ptr m_groupInfoFactory; + bcos::group::ChainNodeInfoFactory::Ptr m_chainNodeInfoFactory; + std::atomic_bool m_running = {false}; + bcos::election::LeaderEntryPointInterface::Ptr m_leaderEntryPoint; +}; +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/RpcService/RpcServiceServer.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/RpcService/RpcServiceServer.cpp" new file mode 100644 index 00000000..6e34d36b --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/RpcService/RpcServiceServer.cpp" @@ -0,0 +1,67 @@ +#include "RpcServiceServer.h" +#include "../Common/TarsUtils.h" +#include +#include +#include +#include +#include + +using namespace bcostars; +bcostars::Error RpcServiceServer::asyncNotifyBlockNumber(const std::string& _groupID, + const std::string& _nodeName, tars::Int64 blockNumber, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + + m_rpcInitializer->rpc()->asyncNotifyBlockNumber( + _groupID, _nodeName, blockNumber, [current, blockNumber](bcos::Error::Ptr _error) { + RPCSERVICE_LOG(DEBUG) << LOG_BADGE("asyncNotifyBlockNumber") + << LOG_KV("blockNumber", blockNumber) + << LOG_KV("code", _error ? _error->errorCode() : 0) + << LOG_KV("msg", _error ? _error->errorMessage() : ""); + async_response_asyncNotifyBlockNumber(current, toTarsError(_error)); + }); + + return bcostars::Error(); +} + +bcostars::Error RpcServiceServer::asyncNotifyGroupInfo( + const bcostars::GroupInfo& groupInfo, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + auto bcosGroupInfo = toBcosGroupInfo( + m_rpcInitializer->chainNodeInfoFactory(), m_rpcInitializer->groupInfoFactory(), groupInfo); + m_rpcInitializer->rpc()->asyncNotifyGroupInfo( + bcosGroupInfo, [current](bcos::Error::Ptr&& _error) { + async_response_asyncNotifyGroupInfo(current, toTarsError(_error)); + }); + return bcostars::Error(); +} + +bcostars::Error RpcServiceServer::asyncNotifyAMOPMessage(tars::Int32 _type, + const std::string& _topic, const vector& _requestData, vector&, + tars::TarsCurrentPtr current) +{ + current->setResponse(false); + m_rpcInitializer->rpc()->asyncNotifyAMOPMessage(_type, _topic, + bcos::bytesConstRef((const bcos::byte*)_requestData.data(), _requestData.size()), + [current](bcos::Error::Ptr&& _error, bcos::bytesPointer _responseData) { + vector response; + if (_responseData) + { + response.assign(_responseData->begin(), _responseData->end()); + } + async_response_asyncNotifyAMOPMessage(current, toTarsError(_error), response); + }); + return bcostars::Error(); +} + +bcostars::Error RpcServiceServer::asyncNotifySubscribeTopic( + std::string&, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + m_rpcInitializer->rpc()->asyncNotifySubscribeTopic( + [current](bcos::Error::Ptr&& _error, std::string _topicInfo) { + async_response_asyncNotifySubscribeTopic(current, toTarsError(_error), _topicInfo); + }); + return bcostars::Error(); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/RpcService/RpcServiceServer.h" "b/BFPL\345\243\271/fisco-bcos-tars-service/RpcService/RpcServiceServer.h" new file mode 100644 index 00000000..2dd60249 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/RpcService/RpcServiceServer.h" @@ -0,0 +1,37 @@ +#pragma once +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include "RpcInitializer.h" +#include +namespace bcostars +{ +struct RpcServiceParam +{ + RpcInitializer::Ptr rpcInitializer; +}; +class RpcServiceServer : public bcostars::RpcService +{ +public: + RpcServiceServer(RpcServiceParam const& _param) : m_rpcInitializer(_param.rpcInitializer) {} + virtual ~RpcServiceServer() {} + + void initialize() override {} + void destroy() override {} + + bcostars::Error asyncNotifyBlockNumber(const std::string& _groupID, + const std::string& _nodeName, tars::Int64 blockNumber, + tars::TarsCurrentPtr current) override; + bcostars::Error asyncNotifyGroupInfo( + const bcostars::GroupInfo& groupInfo, tars::TarsCurrentPtr current) override; + + bcostars::Error asyncNotifyAMOPMessage(tars::Int32 _type, const std::string& _topic, + const vector& _requestData, vector& _responseData, + tars::TarsCurrentPtr current) override; + bcostars::Error asyncNotifySubscribeTopic( + std::string& _topicInfo, tars::TarsCurrentPtr current) override; + +private: + RpcInitializer::Ptr m_rpcInitializer; +}; +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/RpcService/main/Application.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/RpcService/main/Application.cpp" new file mode 100644 index 00000000..5840ea9e --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/RpcService/main/Application.cpp" @@ -0,0 +1,127 @@ +#include "../../Common/TarsUtils.h" +#include "../RpcInitializer.h" +#include "../RpcServiceServer.h" +#include "libinitializer/CommandHelper.h" +#include +#include +#include + +using namespace bcostars; +class RpcServiceApp : public tars::Application +{ +public: + RpcServiceApp() {} + + ~RpcServiceApp() override {} + + void initialize() override + { + // Note: since tars Application catch the exception and output the error message with + // e.what() which unable to explicitly display specific error messages, we actively catch + // and output exception information here + try + { + auto configDir = tars::ServerConfig::BasePath; + m_iniConfigPath = configDir + "/config.ini"; + addConfig("config.ini"); + + BCOS_LOG(INFO) << LOG_BADGE("[Rpc][Application]") << LOG_DESC("add config.ini") + << LOG_KV("configDir", configDir); + + initService(configDir); + RpcServiceParam param; + param.rpcInitializer = m_rpcInitializer; + addServantWithParams( + getProxyDesc(bcos::protocol::RPC_SERVANT_NAME), param); + // init rpc + auto ret = getEndPointDescByAdapter(this, bcos::protocol::RPC_SERVANT_NAME); + if (!ret.first) + { + throw std::runtime_error("load endpoint information failed"); + } + std::string clientID = ret.second; + BCOS_LOG(INFO) << LOG_DESC("begin init rpc") << LOG_KV("rpcID", ret.second); + param.rpcInitializer->setClientID(ret.second); + m_rpcInitializer->start(); + } + catch (std::exception const& e) + { + std::cout << "init RpcService failed, error: " << boost::diagnostic_information(e) + << std::endl; + exit(-1); + } + } + + void destroyApp() override + { + if (m_rpcInitializer) + { + m_rpcInitializer->stop(); + } + } + +protected: + virtual void initService(std::string const& _configDir) + { + // !!! Notice: + auto nodeConfig = std::make_shared( + std::make_shared()); + nodeConfig->loadConfig(m_iniConfigPath, false, true); + if (nodeConfig->rpcSmSsl()) + { + addConfig("sm_ca.crt"); + addConfig("sm_ssl.crt"); + addConfig("sm_enssl.crt"); + addConfig("sm_ssl.key"); + addConfig("sm_enssl.key"); + } + else + { + addConfig("ca.crt"); + addConfig("ssl.key"); + addConfig("ssl.crt"); + } + + // init the log + boost::property_tree::ptree pt; + boost::property_tree::read_ini(m_iniConfigPath, pt); + // init service.without_tars_framework first for determine the log path + nodeConfig->loadWithoutTarsFrameworkConfig(pt); + + m_logInitializer = std::make_shared(); + if (!nodeConfig->withoutTarsFramework()) + { + m_logInitializer->setLogPath(getLogPath()); + } + + m_logInitializer->initLog(pt); + nodeConfig->loadServiceConfig(pt); + // for stat the nodeVersion + bcos::initializer::showNodeVersionMetric(); + + m_rpcInitializer = std::make_shared(_configDir, nodeConfig); + } + +private: + std::string m_iniConfigPath; + bcos::BoostLogInitializer::Ptr m_logInitializer; + RpcInitializer::Ptr m_rpcInitializer; +}; + +int main(int argc, char* argv[]) +{ + try + { + bcos::initializer::initCommandLine(argc, argv); + RpcServiceApp app; + app.main(argc, argv); + app.waitForShutdown(); + + return 0; + } + catch (std::exception& e) + { + cerr << "RpcService std::exception:" << e.what() << std::endl; + } + return -1; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/RpcService/main/CMakeLists.txt" "b/BFPL\345\243\271/fisco-bcos-tars-service/RpcService/main/CMakeLists.txt" new file mode 100644 index 00000000..b81b6cbb --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/RpcService/main/CMakeLists.txt" @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.14) + +project(bcostars-rpc) +file(GLOB SRC_LIST "*.cpp") + +find_package(Boost REQUIRED program_options) + +aux_source_directory(../ SRC_LIST) +add_executable(${RPC_BINARY_NAME} ${SRC_LIST}) +if(WITH_TIKV) + target_link_libraries(${RPC_BINARY_NAME} ${PROTOCOL_INIT_LIB} ${RPC_TARGET} ${TOOL_TARGET} ${PROTOCOL_INIT_LIB} ${COMMAND_HELPER_LIB} ${LEADER_ELECTION_TARGET}) +else() + target_link_libraries(${RPC_BINARY_NAME} ${PROTOCOL_INIT_LIB} ${RPC_TARGET} ${TOOL_TARGET} ${PROTOCOL_INIT_LIB} ${COMMAND_HELPER_LIB}) +endif() \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/SchedulerServiceServer.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/SchedulerServiceServer.cpp" new file mode 100644 index 00000000..85535309 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/SchedulerServiceServer.cpp" @@ -0,0 +1,132 @@ +/** + * Copyright (C) 2021 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. + * + * @brief server for Scheduler + * @file SchedulerServiceServer.cpp + * @author: yujiechen + * @date 2021-10-18 + */ +#include "SchedulerServiceServer.h" +#include "fisco-bcos-tars-service/Common/TarsUtils.h" +#include +#include +#include +#include +#include + +using namespace tars; +using namespace bcostars; + +bcostars::Error SchedulerServiceServer::call( + const bcostars::Transaction& _tx, bcostars::TransactionReceipt&, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + auto bcosTransaction = std::make_shared( + m_cryptoSuite, [m_tx = _tx]() mutable { return &m_tx; }); + m_scheduler->call(bcosTransaction, + [current](bcos::Error::Ptr&& _error, bcos::protocol::TransactionReceipt::Ptr&& _receipt) { + bcostars::TransactionReceipt tarsReceipt; + if (_receipt) + { + tarsReceipt = + std::dynamic_pointer_cast(_receipt) + ->inner(); + } + async_response_call(current, toTarsError(_error), tarsReceipt); + }); + return bcostars::Error(); +} + +bcostars::Error SchedulerServiceServer::getCode( + const std::string& contract, vector& code, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + m_scheduler->getCode(contract, [current](bcos::Error::Ptr error, bcos::bytes code) { + vector outCode(code.begin(), code.end()); + + async_response_getCode(current, toTarsError(error), outCode); + }); + + return bcostars::Error(); +} + +bcostars::Error SchedulerServiceServer::getABI( + const std::string& contract, std::string& abi, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + m_scheduler->getABI(contract, [current](bcos::Error::Ptr error, std::string abi) { + async_response_getABI(current, toTarsError(error), abi); + }); + + return bcostars::Error(); +} + +bcostars::Error SchedulerServiceServer::executeBlock(bcostars::Block const& _block, + tars::Bool _verify, bcostars::BlockHeader&, tars::Bool&, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + auto bcosBlock = std::make_shared( + m_blockFactory->transactionFactory(), m_blockFactory->receiptFactory(), _block); + m_scheduler->executeBlock(bcosBlock, _verify, + [_current]( + bcos::Error::Ptr&& _error, bcos::protocol::BlockHeader::Ptr&& _header, bool _sysBlock) { + auto headerImpl = + std::dynamic_pointer_cast(_header); + async_response_executeBlock( + _current, toTarsError(_error), headerImpl->inner(), _sysBlock); + }); + return bcostars::Error(); +} + + +bcostars::Error SchedulerServiceServer::commitBlock( + bcostars::BlockHeader const& _header, bcostars::LedgerConfig&, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + auto bcosHeader = std::make_shared( + m_cryptoSuite, [m_header = _header]() mutable { return &m_header; }); + m_scheduler->commitBlock(bcosHeader, + [_current](bcos::Error::Ptr&& _error, bcos::ledger::LedgerConfig::Ptr&& _bcosLedgerConfig) { + async_response_commitBlock( + _current, toTarsError(_error), toTarsLedgerConfig(_bcosLedgerConfig)); + }); + return bcostars::Error(); +} + +bcostars::Error SchedulerServiceServer::registerExecutor( + std::string const& _name, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + + auto executorServicePrx = bcostars::createServantProxy(_name); + + auto executor = std::make_shared(executorServicePrx); + m_scheduler->registerExecutor(_name, executor, [_current](bcos::Error::Ptr&& _error) { + async_response_registerExecutor(_current, toTarsError(_error)); + }); + return bcostars::Error(); +} + +bcostars::Error SchedulerServiceServer::preExecuteBlock( + const bcostars::Block& _block, tars::Bool _verify, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + auto bcosBlock = std::make_shared( + m_blockFactory->transactionFactory(), m_blockFactory->receiptFactory(), _block); + m_scheduler->preExecuteBlock(bcosBlock, _verify, [_current](bcos::Error::Ptr&& _error) { + async_response_preExecuteBlock(_current, toTarsError(_error)); + }); + return bcostars::Error(); +} diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/SchedulerServiceServer.h" "b/BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/SchedulerServiceServer.h" new file mode 100644 index 00000000..e46b29ce --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/SchedulerServiceServer.h" @@ -0,0 +1,76 @@ +/** + * Copyright (C) 2021 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. + * + * @brief server for Scheduler + * @file SchedulerServiceServer.h + * @author: yujiechen + * @date 2021-10-18 + */ +#pragma once +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include +#include +#include +#include +namespace bcostars +{ +struct SchedulerServiceParam +{ + bcos::scheduler::SchedulerInterface::Ptr scheduler; + bcos::crypto::CryptoSuite::Ptr cryptoSuite; + bcos::protocol::BlockFactory::Ptr blockFactory; +}; +class SchedulerServiceServer : public SchedulerService +{ +public: + SchedulerServiceServer(SchedulerServiceParam const& _param) + : m_scheduler(_param.scheduler), + m_cryptoSuite(_param.cryptoSuite), + m_blockFactory(_param.blockFactory) + {} + ~SchedulerServiceServer() override {} + + void initialize() override {} + void destroy() override {} + + bcostars::Error call(const bcostars::Transaction& _tx, bcostars::TransactionReceipt& _receipt, + tars::TarsCurrentPtr current) override; + + bcostars::Error getCode(const std::string& contract, vector& code, + tars::TarsCurrentPtr current) override; + + bcostars::Error getABI( + const std::string& contract, std::string& code, tars::TarsCurrentPtr current) override; + + bcostars::Error executeBlock(bcostars::Block const& _block, tars::Bool _verify, + bcostars::BlockHeader& _executedHeader, tars::Bool& _sysBlock, + tars::TarsCurrentPtr _current) override; + bcostars::Error commitBlock(bcostars::BlockHeader const& _header, + bcostars::LedgerConfig& _config, tars::TarsCurrentPtr _current) override; + + bcostars::Error registerExecutor( + std::string const& _name, tars::TarsCurrentPtr _current) override; + + bcostars::Error preExecuteBlock( + const bcostars::Block& _block, tars::Bool _verify, tars::TarsCurrentPtr current) override; + +private: + bcos::scheduler::SchedulerInterface::Ptr m_scheduler; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + bcos::protocol::BlockFactory::Ptr m_blockFactory; +}; +} // namespace bcostars diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/main/CMakeLists.txt" "b/BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/main/CMakeLists.txt" new file mode 100644 index 00000000..c74acd9b --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/main/CMakeLists.txt" @@ -0,0 +1,9 @@ +file(GLOB SRC_LIST "*.cpp") +file(GLOB HEADERS "*.h") + +set(BINARY_NAME BcosSchedulerService) + +aux_source_directory(../ SRC_LIST) +add_executable(${BINARY_NAME} ${SRC_LIST} ${HEADERS}) + +target_link_libraries(${BINARY_NAME} PUBLIC ${PROTOCOL_INIT_LIB} ${COMMAND_HELPER_LIB} ${SCHEDULER_TARGET} ${LEDGER_TARGET} ${STORAGE_TARGET}) \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/main/SchedulerServiceApp.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/main/SchedulerServiceApp.cpp" new file mode 100644 index 00000000..77e8b239 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/main/SchedulerServiceApp.cpp" @@ -0,0 +1,181 @@ +/** + * 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. + * + * @brief Application for the SchedulerService + * @file SchedulerServiceApp.cpp + * @author: yujiechen + * @date 2022-5-10 + */ +#include "SchedulerServiceApp.h" +#include "Common/TarsUtils.h" +#include "SchedulerService/SchedulerServiceServer.h" +#include "bcos-utilities/BoostLog.h" +#include "fisco-bcos-tars-service/Common/TarsUtils.h" +#include "generated/bcos-tars-protocol/tars/TxPoolService.h" +#include "libinitializer/CommandHelper.h" +#include "libinitializer/SchedulerInitializer.h" +#include "libinitializer/StorageInitializer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcostars; +using namespace bcos::initializer; +using namespace bcos::ledger; +using namespace bcos::protocol; + +void SchedulerServiceApp::initialize() +{ + try + { + createAndInitSchedulerService(); + } + catch (std::exception const& e) + { + std::cout << "init SchedulerService failed, error: " << boost::diagnostic_information(e) + << std::endl; + exit(-1); + } +} + +void SchedulerServiceApp::createAndInitSchedulerService() +{ + fetchConfig(); + initConfig(); + + // for stat the nodeVersion + bcos::initializer::showNodeVersionMetric(); + + auto rpcServiceName = m_nodeConfig->rpcServiceName(); + auto withoutTarsFramework = m_nodeConfig->withoutTarsFramework(); + + SCHEDULER_SERVICE_LOG(INFO) << LOG_DESC("create RpcServiceClient") + << LOG_KV("rpcServiceName", rpcServiceName) + << LOG_KV("withoutTarsFramework", withoutTarsFramework); + + std::vector endPoints; + m_nodeConfig->getTarsClientProxyEndpoints(bcos::protocol::RPC_NAME, endPoints); + + auto rpcServicePrx = bcostars::createServantProxy( + withoutTarsFramework, rpcServiceName, endPoints); + + m_rpc = std::make_shared(rpcServicePrx, rpcServiceName); + + auto txpoolServiceName = m_nodeConfig->txpoolServiceName(); + + SCHEDULER_SERVICE_LOG(INFO) << LOG_DESC("create TxPoolServiceClient") + << LOG_KV("txpoolServiceName", txpoolServiceName); + + m_nodeConfig->getTarsClientProxyEndpoints(bcos::protocol::TXPOOL_NAME, endPoints); + auto txpoolServicePrx = bcostars::createServantProxy( + withoutTarsFramework, txpoolServiceName, endPoints); + + m_txpool = std::make_shared(txpoolServicePrx, + m_protocolInitializer->cryptoSuite(), m_protocolInitializer->blockFactory()); + + SCHEDULER_SERVICE_LOG(INFO) << LOG_DESC("createScheduler"); + createScheduler(); + SCHEDULER_SERVICE_LOG(INFO) << LOG_DESC("createScheduler success"); + + SCHEDULER_SERVICE_LOG(INFO) << LOG_DESC("addServant for scheduler"); + + SchedulerServiceParam param; + param.scheduler = m_scheduler; + param.cryptoSuite = m_protocolInitializer->cryptoSuite(); + param.blockFactory = m_protocolInitializer->blockFactory(); + addServantWithParams( + getProxyDesc(SCHEDULER_SERVANT_NAME), param); + SCHEDULER_SERVICE_LOG(INFO) << LOG_DESC("addServant for scheduler success"); +} +void SchedulerServiceApp::fetchConfig() +{ + m_iniConfigPath = ServerConfig::BasePath + "/config.ini"; + m_genesisConfigPath = ServerConfig::BasePath + "/config.genesis"; + addConfig("config.ini"); + addConfig("config.genesis"); + SCHEDULER_SERVICE_LOG(INFO) << LOG_DESC("fetchConfig success") + << LOG_KV("iniConfigPath", m_iniConfigPath) + << LOG_KV("genesisConfigPath", m_genesisConfigPath); +} + +void SchedulerServiceApp::initConfig() +{ + boost::property_tree::ptree pt; + boost::property_tree::read_ini(m_iniConfigPath, pt); + + boost::property_tree::ptree genesisPt; + boost::property_tree::read_ini(m_genesisConfigPath, pt); + // init service.without_tars_framework first for determine the log path + m_nodeConfig->loadWithoutTarsFrameworkConfig(pt); + + m_logInitializer = std::make_shared(); + if (!m_nodeConfig->withoutTarsFramework()) + { + m_logInitializer->setLogPath(getLogPath()); + } + m_logInitializer->initLog(pt); + + m_nodeConfig = + std::make_shared(std::make_shared()); + m_nodeConfig->loadGenesisConfig(genesisPt); + m_nodeConfig->loadConfig(pt); + m_nodeConfig->loadServiceConfig(pt); + m_nodeConfig->loadNodeServiceConfig(m_nodeConfig->nodeName(), pt, true); + // init the protocol + m_protocolInitializer = std::make_shared(); + m_protocolInitializer->init(m_nodeConfig); + SCHEDULER_SERVICE_LOG(INFO) << LOG_DESC("initConfig success"); +} + +void SchedulerServiceApp::createScheduler() +{ + auto blockFactory = m_protocolInitializer->blockFactory(); + auto ledger = std::make_shared(blockFactory, + StorageInitializer::build(m_nodeConfig->pdAddrs(), getLogPath(), m_nodeConfig->pdCaPath(), + m_nodeConfig->pdCertPath(), m_nodeConfig->pdKeyPath())); + auto executionMessageFactory = + std::make_shared(); + auto executorManager = std::make_shared( + m_nodeConfig->executorServiceName()); + + m_scheduler = SchedulerInitializer::build(executorManager, ledger, + StorageInitializer::build(m_nodeConfig->pdAddrs(), getLogPath(), m_nodeConfig->pdCaPath(), + m_nodeConfig->pdCertPath(), m_nodeConfig->pdKeyPath()), + executionMessageFactory, blockFactory, m_protocolInitializer->txResultFactory(), + m_protocolInitializer->cryptoSuite()->hashImpl(), m_nodeConfig->isAuthCheck(), + m_nodeConfig->isWasm()); + auto scheduler = std::dynamic_pointer_cast(m_scheduler); + // handler for notify block number + scheduler->registerBlockNumberReceiver([this](bcos::protocol::BlockNumber number) { + BCOS_LOG(INFO) << "Notify blocknumber: " << number; + // Note: the interface will notify blockNumber to all rpc nodes in pro/max mode + m_rpc->asyncNotifyBlockNumber( + m_nodeConfig->groupId(), m_nodeConfig->nodeName(), number, [](bcos::Error::Ptr) {}); + }); + // handler for notify transactions + scheduler->registerTransactionNotifier([this](bcos::protocol::BlockNumber _blockNumber, + bcos::protocol::TransactionSubmitResultsPtr _result, + std::function _callback) { + // only response to the requester + m_txpool->asyncNotifyBlockResult(_blockNumber, _result, _callback); + }); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/main/SchedulerServiceApp.h" "b/BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/main/SchedulerServiceApp.h" new file mode 100644 index 00000000..0899b3ac --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/main/SchedulerServiceApp.h" @@ -0,0 +1,68 @@ +/** + * 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. + * + * @brief Application for the SchedulerService + * @file SchedulerServiceApp.h + * @author: yujiechen + * @date 2022-5-10 + */ +#pragma once + +#include "bcos-framework/rpc/RPCInterface.h" +#include "libinitializer/ProtocolInitializer.h" +#include +#include +#include +#include +#include +#include + +#define SCHEDULER_SERVICE_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_DESC("SchedulerServiceApp") + +namespace bcostars +{ +class SchedulerServiceApp : public tars::Application +{ +public: + SchedulerServiceApp() = default; + ~SchedulerServiceApp() override {} + void initialize() override; + void destroyApp() override + { + if (m_scheduler) + { + m_scheduler->stop(); + } + } + +protected: + virtual void createAndInitSchedulerService(); + virtual void createScheduler(); + virtual void fetchConfig(); + virtual void initConfig(); + +private: + std::string m_iniConfigPath; + std::string m_genesisConfigPath; + bcos::BoostLogInitializer::Ptr m_logInitializer; + bcos::tool::NodeConfig::Ptr m_nodeConfig; + bcos::initializer::ProtocolInitializer::Ptr m_protocolInitializer; + + bcos::rpc::RPCInterface::Ptr m_rpc; + bcos::txpool::TxPoolInterface::Ptr m_txpool; + + bcos::scheduler::SchedulerInterface::Ptr m_scheduler; +}; +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/main/main.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/main/main.cpp" new file mode 100644 index 00000000..5e6d6e63 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/SchedulerService/main/main.cpp" @@ -0,0 +1,54 @@ +/** + * 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. + * + * @brief main for SchedulerService + * @file main.cpp + * @author: yujiechen + * @date 2022-05-10 + */ +#include "SchedulerServiceApp.h" +#include "libinitializer/CommandHelper.h" +#include +#include +#include + +using namespace bcostars; +using namespace bcos; +using namespace bcos::initializer; +int main(int argc, char* argv[]) +{ + try + { + bcos::initializer::initCommandLine(argc, argv); + SchedulerServiceApp app; + printVersion(); + std::cout << "[" << getCurrentDateTime() << "] "; + std::cout << "The SchedulerService is running..." << std::endl; + app.main(argc, argv); + app.waitForShutdown(); + std::cout << "[" << getCurrentDateTime() << "] "; + std::cout << "The SchedulerService program exit normally." << std::endl; + return 0; + } + catch (std::exception& e) + { + cerr << "SchedulerService std::exception:" << e.what() << std::endl; + } + catch (...) + { + cerr << "unknown exception." << std::endl; + } + return -1; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/TxPoolService/TxPoolServiceServer.cpp" "b/BFPL\345\243\271/fisco-bcos-tars-service/TxPoolService/TxPoolServiceServer.cpp" new file mode 100644 index 00000000..b323a5a1 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/TxPoolService/TxPoolServiceServer.cpp" @@ -0,0 +1,263 @@ +#include "TxPoolServiceServer.h" +#include "../Common/TarsUtils.h" +#include +using namespace bcostars; + +bcostars::Error TxPoolServiceServer::asyncFillBlock(const vector>& txHashs, + vector& filled, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + auto hashList = std::make_shared>(); + for (auto const& hashData : txHashs) + { + hashList->push_back(bcos::crypto::HashType( + reinterpret_cast(hashData.data()), hashData.size())); + } + + m_txpoolInitializer->txpool()->asyncFillBlock( + hashList, [current](bcos::Error::Ptr error, bcos::protocol::TransactionsPtr txs) { + std::vector txList; + if (error) + { + async_response_asyncFillBlock(current, toTarsError(error), txList); + TXPOOLSERVICE_LOG(WARNING) + << LOG_DESC("asyncFillBlock failed") << LOG_KV("code", error->errorCode()) + << LOG_KV("msg", error->errorMessage()); + return; + } + for (auto tx : *txs) + { + txList.push_back( + std::dynamic_pointer_cast(tx)->inner()); + } + + async_response_asyncFillBlock(current, toTarsError(error), txList); + }); + + return bcostars::Error(); +} + + +bcostars::Error TxPoolServiceServer::asyncMarkTxs(const vector>& txHashs, + tars::Bool sealedFlag, tars::Int64 _batchId, const vector& _batchHash, + tars::TarsCurrentPtr current) +{ + current->setResponse(false); + auto hashList = std::make_shared>(); + for (auto hashData : txHashs) + { + hashList->push_back(bcos::crypto::HashType( + reinterpret_cast(hashData.data()), hashData.size())); + } + auto batchHash = bcos::crypto::HashType( + reinterpret_cast(_batchHash.data()), _batchHash.size()); + m_txpoolInitializer->txpool()->asyncMarkTxs( + hashList, sealedFlag, _batchId, batchHash, [current](bcos::Error::Ptr error) { + async_response_asyncMarkTxs(current, toTarsError(error)); + }); + return bcostars::Error(); +} + +bcostars::Error TxPoolServiceServer::asyncNotifyBlockResult(tars::Int64 blockNumber, + const vector& result, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + + auto bcosResultList = std::make_shared(); + for (auto tarsResult : result) + { + auto bcosResult = std::make_shared( + m_txpoolInitializer->cryptoSuite(), + [inner = std::move(const_cast( + tarsResult))]() mutable { return &inner; }); + bcosResultList->push_back(bcosResult); + } + + m_txpoolInitializer->txpool()->asyncNotifyBlockResult( + blockNumber, bcosResultList, [current](bcos::Error::Ptr error) { + async_response_asyncNotifyBlockResult(current, toTarsError(error)); + }); + return bcostars::Error(); +} + +bcostars::Error TxPoolServiceServer::asyncNotifyTxsSyncMessage(const bcostars::Error& error, + const std::string& id, const vector& nodeID, const vector& data, + tars::TarsCurrentPtr current) +{ + current->setResponse(false); + + auto bcosNodeID = m_txpoolInitializer->cryptoSuite()->keyFactory()->createKey( + bcos::bytes(nodeID.begin(), nodeID.end())); + + m_txpoolInitializer->txpool()->asyncNotifyTxsSyncMessage(toBcosError(error), id, bcosNodeID, + bcos::bytesConstRef(reinterpret_cast(data.data()), data.size()), + [current](bcos::Error::Ptr error) { + async_response_asyncNotifyTxsSyncMessage(current, toTarsError(error)); + }); + + return bcostars::Error(); +} + +bcostars::Error TxPoolServiceServer::asyncSealTxs(tars::Int64 txsLimit, + const vector>& avoidTxs, bcostars::Block& txsList, + bcostars::Block& sysTxsList, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + + auto bcosAvoidTxs = std::make_shared(); + for (auto tx : avoidTxs) + { + bcosAvoidTxs->insert(bcos::crypto::HashType(bcos::bytes(tx.begin(), tx.end()))); + } + + m_txpoolInitializer->txpool()->asyncSealTxs(txsLimit, bcosAvoidTxs, + [current](bcos::Error::Ptr error, bcos::protocol::Block::Ptr _txsList, + bcos::protocol::Block::Ptr _sysTxsList) { + if (error) + { + TXPOOLSERVICE_LOG(WARNING) + << LOG_DESC("asyncSealTxs failed") << LOG_KV("code", error->errorCode()) + << LOG_KV("msg", error->errorMessage()); + async_response_asyncSealTxs( + current, toTarsError(error), bcostars::Block(), bcostars::Block()); + return; + } + async_response_asyncSealTxs(current, toTarsError(error), + std::dynamic_pointer_cast(_txsList)->inner(), + std::dynamic_pointer_cast(_sysTxsList)->inner()); + }); + + return bcostars::Error(); +} + +bcostars::Error TxPoolServiceServer::submit(const bcostars::Transaction& tx, + bcostars::TransactionSubmitResult& result, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + + auto transaction = + std::make_shared(m_txpoolInitializer->cryptoSuite(), + [m_transaction = std::move(const_cast(tx))]() mutable { + return &m_transaction; + }); + bcos::task::wait([](std::shared_ptr txpool, + protocol::TransactionImpl::Ptr transaction, + tars::TarsCurrentPtr current) -> bcos::task::Task { + try + { + auto submitResult = co_await txpool->submitTransaction(std::move(transaction)); + async_response_submit(current, {}, + std::dynamic_pointer_cast( + submitResult) + ->inner()); + } + catch (bcos::Error& e) + { + async_response_submit(current, toTarsError(e), {}); + } + }(m_txpoolInitializer->txpool(), std::move(transaction), std::move(current))); + + return {}; +} + +bcostars::Error TxPoolServiceServer::asyncVerifyBlock(const vector& generatedNodeID, + const vector& block, tars::Bool& result, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + + bcos::crypto::PublicPtr pk = m_txpoolInitializer->cryptoSuite()->keyFactory()->createKey( + bcos::bytesConstRef((const bcos::byte*)generatedNodeID.data(), generatedNodeID.size())); + m_txpoolInitializer->txpool()->asyncVerifyBlock(pk, + bcos::bytesConstRef((const bcos::byte*)block.data(), block.size()), + [current](bcos::Error::Ptr error, bool result) { + async_response_asyncVerifyBlock(current, toTarsError(error), result); + }); + + return bcostars::Error(); +} + +bcostars::Error TxPoolServiceServer::notifyConnectedNodes( + const vector>& connectedNodes, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + + bcos::crypto::NodeIDSet bcosNodeIDSet; + for (auto const& it : connectedNodes) + { + bcosNodeIDSet.insert(m_txpoolInitializer->cryptoSuite()->keyFactory()->createKey( + bcos::bytesConstRef((const bcos::byte*)it.data(), it.size()))); + } + + m_txpoolInitializer->txpool()->notifyConnectedNodes( + bcosNodeIDSet, [current](bcos::Error::Ptr error) { + async_response_notifyConnectedNodes(current, toTarsError(error)); + }); + + return bcostars::Error(); +} + +bcostars::Error TxPoolServiceServer::notifyConsensusNodeList( + const vector& consensusNodeList, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + + bcos::consensus::ConsensusNodeList bcosNodeList; + for (auto const& it : consensusNodeList) + { + auto node = std::make_shared( + m_txpoolInitializer->cryptoSuite()->keyFactory()->createKey( + bcos::bytesConstRef((const bcos::byte*)it.nodeID.data(), it.nodeID.size())), + it.weight); + bcosNodeList.emplace_back(node); + } + + m_txpoolInitializer->txpool()->notifyConsensusNodeList( + bcosNodeList, [current](bcos::Error::Ptr error) { + async_response_notifyConsensusNodeList(current, toTarsError(error)); + }); + + return bcostars::Error(); +} + +bcostars::Error TxPoolServiceServer::notifyObserverNodeList( + const vector& observerNodeList, tars::TarsCurrentPtr current) +{ + current->setResponse(false); + + bcos::consensus::ConsensusNodeList bcosObserverNodeList; + for (auto const& it : observerNodeList) + { + auto node = std::make_shared( + m_txpoolInitializer->cryptoSuite()->keyFactory()->createKey( + bcos::bytesConstRef((const bcos::byte*)it.nodeID.data(), it.nodeID.size())), + it.weight); + bcosObserverNodeList.emplace_back(node); + } + + m_txpoolInitializer->txpool()->notifyObserverNodeList( + bcosObserverNodeList, [current](bcos::Error::Ptr error) { + async_response_notifyObserverNodeList(current, toTarsError(error)); + }); + + return bcostars::Error(); +} + +bcostars::Error TxPoolServiceServer::asyncGetPendingTransactionSize( + tars::Int64& _pendingTxsSize, tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + m_txpoolInitializer->txpool()->asyncGetPendingTransactionSize( + [_current](bcos::Error::Ptr _error, uint64_t _txsSize) { + async_response_asyncGetPendingTransactionSize(_current, toTarsError(_error), _txsSize); + }); + return bcostars::Error(); +} + +bcostars::Error TxPoolServiceServer::asyncResetTxPool(tars::TarsCurrentPtr _current) +{ + _current->setResponse(false); + m_txpoolInitializer->txpool()->asyncResetTxPool([_current](bcos::Error::Ptr _error) { + async_response_asyncResetTxPool(_current, toTarsError(_error)); + }); + return bcostars::Error(); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/fisco-bcos-tars-service/TxPoolService/TxPoolServiceServer.h" "b/BFPL\345\243\271/fisco-bcos-tars-service/TxPoolService/TxPoolServiceServer.h" new file mode 100644 index 00000000..20eaa072 --- /dev/null +++ "b/BFPL\345\243\271/fisco-bcos-tars-service/TxPoolService/TxPoolServiceServer.h" @@ -0,0 +1,77 @@ +#pragma once +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#include "libinitializer/ProtocolInitializer.h" +#include "libinitializer/TxPoolInitializer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcostars +{ +struct TxPoolServiceParam +{ + bcos::initializer::TxPoolInitializer::Ptr txPoolInitializer; +}; +class TxPoolServiceServer : public bcostars::TxPoolService +{ +public: + using Ptr = std::shared_ptr; + TxPoolServiceServer(TxPoolServiceParam const& _param) + : m_txpoolInitializer(_param.txPoolInitializer) + {} + ~TxPoolServiceServer() override {} + + void initialize() override {} + void destroy() override {} + + bcostars::Error asyncFillBlock(const vector>& txHashs, + vector& filled, tars::TarsCurrentPtr current) override; + + bcostars::Error asyncMarkTxs(const vector>& txHashs, tars::Bool sealedFlag, + tars::Int64 _batchId, const vector& _batchHash, + tars::TarsCurrentPtr current) override; + + bcostars::Error asyncNotifyBlockResult(tars::Int64 blockNumber, + const vector& result, + tars::TarsCurrentPtr current) override; + + bcostars::Error asyncNotifyTxsSyncMessage(const bcostars::Error& error, const std::string& id, + const vector& nodeID, const vector& data, + tars::TarsCurrentPtr current) override; + + bcostars::Error asyncSealTxs(tars::Int64 txsLimit, const vector>& avoidTxs, + bcostars::Block& txsList, bcostars::Block& sysTxsList, + tars::TarsCurrentPtr current) override; + + bcostars::Error submit(const bcostars::Transaction& tx, + bcostars::TransactionSubmitResult& result, tars::TarsCurrentPtr current) override; + + bcostars::Error asyncVerifyBlock(const vector& generatedNodeID, + const vector& block, tars::Bool& result, tars::TarsCurrentPtr current) override; + + bcostars::Error notifyConnectedNodes( + const vector>& connectedNodes, tars::TarsCurrentPtr current) override; + + bcostars::Error notifyConsensusNodeList( + const vector& consensusNodeList, + tars::TarsCurrentPtr current) override; + + bcostars::Error notifyObserverNodeList(const vector& observerNodeList, + tars::TarsCurrentPtr current) override; + bcostars::Error asyncGetPendingTransactionSize( + tars::Int64& _pendingTxsSize, tars::TarsCurrentPtr _current) override; + bcostars::Error asyncResetTxPool(tars::TarsCurrentPtr _current) override; + +private: + bcos::initializer::TxPoolInitializer::Ptr m_txpoolInitializer; +}; +} // namespace bcostars \ No newline at end of file diff --git "a/BFPL\345\243\271/libinitializer/AuthInitializer.h" "b/BFPL\345\243\271/libinitializer/AuthInitializer.h" new file mode 100644 index 00000000..5b128fb4 --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/AuthInitializer.h" @@ -0,0 +1,76 @@ +/** + * Copyright (C) 2021 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 AuthInitializer.h + * @author: kyonRay + * @date 2021-11-24 + */ + +#pragma once +#include "Common.h" +#include "libinitializer/ProtocolInitializer.h" +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::tool; +using namespace bcos::initializer; +namespace bcos::initializer +{ +// clang-format off +constexpr static const char* const committeeBin = "60806040523480156200001157600080fd5b5060405162005e6138038062005e6183398101604081905262000034916200022c565b83838383604051620000469062000103565b6200005594939291906200032f565b604051809103906000f08015801562000072573d6000803e3d6000fd5b50600080546001600160a01b0319166001600160a01b03929092169182179055604051309190620000a39062000111565b6001600160a01b03928316815291166020820152604001604051809103906000f080158015620000d7573d6000803e3d6000fd5b50600180546001600160a01b0319166001600160a01b039290921691909117905550620003d892505050565b611584806200286a83390190565b6120738062003dee83390190565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156200016057620001606200011f565b604052919050565b60006001600160401b038211156200018457620001846200011f565b5060051b60200190565b600082601f830112620001a057600080fd5b81516020620001b9620001b38362000168565b62000135565b82815260059290921b84018101918181019086841115620001d957600080fd5b8286015b848110156200020a57805163ffffffff81168114620001fc5760008081fd5b8352918301918301620001dd565b509695505050505050565b805160ff811681146200022757600080fd5b919050565b600080600080608085870312156200024357600080fd5b84516001600160401b03808211156200025b57600080fd5b818701915087601f8301126200027057600080fd5b8151602062000283620001b38362000168565b82815260059290921b8401810191818101908b841115620002a357600080fd5b948201945b83861015620002da5785516001600160a01b0381168114620002ca5760008081fd5b82529482019490820190620002a8565b918a0151919850909350505080821115620002f457600080fd5b5062000303878288016200018e565b935050620003146040860162000215565b9150620003246060860162000215565b905092959194509250565b6080808252855190820181905260009060209060a0840190828901845b82811015620003735781516001600160a01b0316845292840192908401906001016200034c565b5050508381038285015286518082528783019183019060005b81811015620003b057835163ffffffff16835292840192918401916001016200038c565b505060ff871660408601529250620003c6915050565b60ff8316606083015295945050505050565b61248280620003e86000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c80637475f00f11610097578063bcfb9b6111610066578063bcfb9b61146101f8578063d978ffba1461020b578063e43581b814610240578063f675fdaa1461026357600080fd5b80637475f00f146101aa5780637a25132d146101bd57806385a6a091146101d05780639e3f4f4e146101e557600080fd5b8063614235f3116100d3578063614235f31461015e57806365012582146101715780636ba4790c146101845780637222b4a81461019757600080fd5b806303f19159146100fa578063185c1587146101205780633234f0e61461014b575b600080fd5b61010d610108366004611d36565b610276565b6040519081526020015b60405180910390f35b600054610133906001600160a01b031681565b6040516001600160a01b039091168152602001610117565b61010d610159366004611d9e565b610419565b61010d61016c366004611dce565b6104db565b61010d61017f366004611eb1565b610679565b61010d610192366004611f04565b61077a565b61010d6101a5366004611f34565b610851565b61010d6101b8366004611f52565b610b36565b61010d6101cb366004611f82565b610d31565b6101e36101de366004611fef565b610e56565b005b61010d6101f3366004612008565b610ee2565b6101e3610206366004612072565b611001565b61022e610219366004611fef565b60009081526002602052604090205460ff1690565b60405160ff9091168152602001610117565b61025361024e3660046120a2565b611909565b6040519015158152602001610117565b600154610133906001600160a01b031681565b600061028133611909565b6102a65760405162461bcd60e51b815260040161029d906120bf565b60405180910390fd5b60648460ff1611156103045760405162461bcd60e51b815260206004820152602160248201527f696e76616c69642072616e6765206f66207061727469636970617465735261746044820152606560f81b606482015260840161029d565b60648360ff1611156103585760405162461bcd60e51b815260206004820152601860248201527f696e76616c69642072616e6765206f662077696e526174650000000000000000604482015260640161029d565b6040805160028082526060808301845292600092919060208301908036833701905050905060608682600081518110610393576103936120ed565b602002602001019060ff16908160ff168152505085826001815181106103bb576103bb6120ed565b60ff9092166020928302919091018201526040805160e081018252600c8152309281019290925281018390526060810182905260006080820181905260a0820185905260c082015261040d818761197e565b98975050505050505050565b600061042433611909565b6104405760405162461bcd60e51b815260040161029d906120bf565b604080516001808252818301909252600091602080830190803683370190505090506060808683600081518110610479576104796120ed565b6001600160a01b039283166020918202929092018101919091526040805160e081018252600b8152928a169183019190915281018390526060810182905263ffffffff8716608082015260a08101849052600160c082015261040d818761197e565b60006104e633611909565b6105025760405162461bcd60e51b815260040161029d906120bf565b8260ff166110056001600160a01b0316631749bea96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610546573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061056a9190612103565b14156105de5760405162461bcd60e51b815260206004820152603b60248201527f7468652063757272656e74206465706c6f79206175746820747970652069732060448201527f7468652073616d6520617320796f752077616e7420746f207365740000000000606482015260840161029d565b60408051600180825281830190925260609160009190602080830190803683370190505090506060858260008151811061061a5761061a6120ed565b60ff9092166020928302919091018201526040805160e081018252601581526110059281019290925281018390526060810182905260006080820181905260a0820185905260c082015261066e818761197e565b979650505050505050565b600061068433611909565b6106a05760405162461bcd60e51b815260040161029d906120bf565b60018351116106e15760405162461bcd60e51b815260206004820152600d60248201526c34b73b30b634b2103737b2329760991b604482015260640161029d565b6040805160018082528183019092526060918291600091816020015b60608152602001906001900390816106fd5790505090508581600081518110610728576107286120ed565b6020908102919091018101919091526040805160e08101825260348152611003928101929092528101839052606081018290526000608082015260a08101849052600160c082015261066e818761197e565b600061078533611909565b6107a15760405162461bcd60e51b815260040161029d906120bf565b6040805160018082528183019092526000916020808301908036833701905050905084816000815181106107d7576107d76120ed565b60200260200101906001600160a01b031690816001600160a01b03168152505060608060006040518060e00160405280601660ff168152602001896001600160a01b03168152602001848152602001838152602001600063ffffffff168152602001858152602001881515815250905061040d818761197e565b600061085c33611909565b6108785760405162461bcd60e51b815260040161029d906120bf565b60008054604080516302f3bff160e51b8152905160609384936001600160a01b031692635e77fe2092600480830193928290030181865afa1580156108c1573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526108e991908101906121b4565b506000546040805163185c158760e01b815290519296508994506001600160a01b0391821693509084169163185c1587916004808201926020929091908290030181865afa15801561093f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061096391906122a1565b6001600160a01b0316146109ce5760405162461bcd60e51b815260206004820152602c60248201527f6e657720766f746520636f6d707574657220636f6d6d6974746565206164647260448201526b0cae6e640dad2e6dac2e8c6d60a31b606482015260840161029d565b6040516353bfcf2f60e01b81526001600160a01b038216906353bfcf2f906109fc9086908690600401612302565b602060405180830381865afa158015610a19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a3d9190612330565b60ff16600214610a8f5760405162461bcd60e51b815260206004820152601e60248201527f6e657720766f746520636f6d707574657220696d70657266656374696f6e0000604482015260640161029d565b604080516001808252818301909252600091602080830190803683370190505090506060808883600081518110610ac857610ac86120ed565b6001600160a01b039283166020918202929092018101919091526040805160e081018252600d81526001549093169183019190915281018390526060810182905260006080820181905260a0820185905260c0820152610b28818a61197e565b9a9950505050505050505050565b6000610b4133611909565b610b5d5760405162461bcd60e51b815260040161029d906120bf565b6001600160a01b038316610bb35760405162461bcd60e51b815260206004820152601c60248201527f636f6e74726163742061646472657373206e6f74206578697374732e00000000604482015260640161029d565b6040516364efb22b60e01b81526001600160a01b0384166004820152611005906364efb22b90602401602060405180830381865afa158015610bf9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c1d91906122a1565b6001600160a01b0316846001600160a01b03161415610c9b5760405162461bcd60e51b815260206004820152603460248201527f746865206163636f756e7420686173206265656e207468652061646d696e206f604482015273331031b7b731bab9393a1031b7b73a3930b1ba1760611b606482015260840161029d565b604080516001808252818301909252600091602080830190803683370190505090506060808683600081518110610cd457610cd46120ed565b6001600160a01b039283166020918202929092018101919091526040805160e081018252601f81529289169183019190915281018390526060810182905260006080820181905260a0820185905260c082015261040d818761197e565b6000610d3c33611909565b610d585760405162461bcd60e51b815260040161029d906120bf565b6001845111610d9f5760405162461bcd60e51b815260206004820152601360248201527234b73b30b634b21035b2bc903632b733ba341760691b604482015260640161029d565b60408051600280825260608281019093528291600091816020015b6060815260200190600190039081610dba5790505090508681600081518110610de557610de56120ed565b60200260200101819052508581600181518110610e0457610e046120ed565b6020908102919091018101919091526040805160e081018252602981526110009281019290925281018390526060810182905260006080820181905260a0820185905260c082015261040d818761197e565b610e5f33611909565b610e7b5760405162461bcd60e51b815260040161029d906120bf565b600154604051631068aa6d60e11b8152600481018390523360248201526001600160a01b03909116906320d154da90604401600060405180830381600087803b158015610ec757600080fd5b505af1158015610edb573d6000803e3d6000fd5b5050505050565b6000610eed33611909565b610f095760405162461bcd60e51b815260040161029d906120bf565b6001855111610f4a5760405162461bcd60e51b815260206004820152600d60248201526c34b73b30b634b2103737b2329760991b604482015260640161029d565b6040805160018082528183019092526060918291600091816020015b6060815260200190600190039081610f665790505090508781600081518110610f9157610f916120ed565b602002602001018190525060006040518060e00160405280603360ff1681526020016110036001600160a01b031681526020018481526020018381526020018963ffffffff1681526020018581526020018815158152509050610ff4818761197e565b9998505050505050505050565b61100a33611909565b6110265760405162461bcd60e51b815260040161029d906120bf565b6001546040516318ae72f160e11b81526004810184905282151560248201523360448201526000916001600160a01b03169063315ce5e2906064016020604051808303816000875af1158015611080573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110a49190612330565b90506110fa6040518060e00160405280600060ff16815260200160006001600160a01b031681526020016060815260200160608152602001600063ffffffff168152602001606081526020016000151581525090565b8160ff16600214156119035760008481526002602090815260408083208054825160e08101845260ff82168082526101009092046001600160a01b031681860152600183018054855181880281018801875281815293969295860193928301828280156111a457602002820191906000526020600020906000905b825461010083900a900460ff168152602060019283018181049485019490930390920291018084116111755790505b5050505050815260200160028201805480602002602001604051908101604052809291908181526020016000905b8282101561127e5783829060005260206000200180546111f19061234d565b80601f016020809104026020016040519081016040528092919081815260200182805461121d9061234d565b801561126a5780601f1061123f5761010080835404028352916020019161126a565b820191906000526020600020905b81548152906001019060200180831161124d57829003601f168201915b5050505050815260200190600101906111d2565b50505090825250600382015463ffffffff1660208083019190915260048301805460408051828502810185018252828152940193928301828280156112ec57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116112ce575b50505091835250506005919091015460ff90811615156020909201919091529093508116600b141561144b57608083015163ffffffff166113a757336001600160a01b03168360a00151600081518110611348576113486120ed565b60200260200101516001600160a01b031614156113a75760405162461bcd60e51b815260206004820152601c60248201527f596f752063616e206e6f742072656d6f766520796f757273656c662100000000604482015260640161029d565b6000805460a085015180516001600160a01b039092169263f437695a926113d0576113d06120ed565b602002602001015185608001516040518363ffffffff1660e01b81526004016114149291906001600160a01b0392909216825263ffffffff16602082015260400190565b600060405180830381600087803b15801561142e57600080fd5b505af1158015611442573d6000803e3d6000fd5b505050506118cd565b8060ff16600c14156114d35760008054604085015180516001600160a01b03909216926399bc9c1b92611480576114806120ed565b6020026020010151856040015160018151811061149f5761149f6120ed565b60200260200101516040518363ffffffff1660e01b815260040161141492919060ff92831681529116602082015260400190565b8060ff16600d141561153e5760015460a084015180516001600160a01b039092169163290bc797919060009061150b5761150b6120ed565b60200260200101516040518263ffffffff1660e01b815260040161141491906001600160a01b0391909116815260200190565b8060ff16601514156115e9576110056001600160a01b031663bb0aa40c8460400151600081518110611572576115726120ed565b60200260200101516040518263ffffffff1660e01b815260040161159f919060ff91909116815260200190565b6020604051808303816000875af11580156115be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115e29190612103565b91506118cd565b8060ff1660161415611682578260c001511561165a576110056001600160a01b031663615480998460a00151600081518110611627576116276120ed565b60200260200101516040518263ffffffff1660e01b815260040161159f91906001600160a01b0391909116815260200190565b6110056001600160a01b03166356bd70848460a00151600081518110611627576116276120ed565b8060ff16601f14156116f5576110056001600160a01b031663c53057b484602001518560a001516000815181106116bb576116bb6120ed565b60200260200101516040518363ffffffff1660e01b815260040161159f9291906001600160a01b0392831681529116602082015260400190565b8060ff166029141561176d576110006001600160a01b031663bd291aef8460600151600081518110611729576117296120ed565b60200260200101518560600151600181518110611748576117486120ed565b60200260200101516040518363ffffffff1660e01b815260040161159f9291906123d5565b8060ff166033141561185e578260c001511561183657608083015163ffffffff166117de576110036001600160a01b0316632800efc084606001516000815181106117ba576117ba6120ed565b60200260200101516040518263ffffffff1660e01b815260040161159f91906123fa565b6110036001600160a01b031663359168568460600151600081518110611806576118066120ed565b6020026020010151856080015163ffffffff166040518363ffffffff1660e01b815260040161159f92919061240d565b6110036001600160a01b031663ce6fa5c58460600151600081518110611806576118066120ed565b8060ff1660341415611892576110036001600160a01b03166380599e4b84606001516000815181106117ba576117ba6120ed565b60405162461bcd60e51b815260206004820152601060248201526f3b37ba32903a3cb8329032b93937b91760811b604482015260640161029d565b6040518281527f7251e13f6f51fdfe60094817f80310366d2e1148fe8a46cb475b582d35bfdea89060200160405180910390a150505b50505050565b60008054604051631c86b03760e31b81526001600160a01b0384811660048301529091169063e43581b890602401602060405180830381865afa158015611954573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611978919061242f565b92915050565b6001548251602084015160405163161cab7f60e11b815233600482015260ff90921660248301526001600160a01b039081166044830152606482018490526000928392911690632c3956fe906084016020604051808303816000875af11580156119ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a109190612103565b60008181526002602090815260409182902087518154838a01516001600160a01b0316610100026001600160a81b031990911660ff909216919091171781559187015180519394508793611a6a9260018501920190611af0565b5060608201518051611a86916002840191602090910190611b96565b50608082015160038201805463ffffffff191663ffffffff90921691909117905560a08201518051611ac2916004840191602090910190611bef565b5060c091909101516005909101805460ff1916911515919091179055611ae9816001611001565b9392505050565b82805482825590600052602060002090601f01602090048101928215611b865791602002820160005b83821115611b5757835183826101000a81548160ff021916908360ff1602179055509260200192600101602081600001049283019260010302611b19565b8015611b845782816101000a81549060ff0219169055600101602081600001049283019260010302611b57565b505b50611b92929150611c44565b5090565b828054828255906000526020600020908101928215611be3579160200282015b82811115611be35782518051611bd3918491602090910190611c59565b5091602001919060010190611bb6565b50611b92929150611ccd565b828054828255906000526020600020908101928215611b86579160200282015b82811115611b8657825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190611c0f565b5b80821115611b925760008155600101611c45565b828054611c659061234d565b90600052602060002090601f016020900481019282611c875760008555611b86565b82601f10611ca057805160ff1916838001178555611b86565b82800160010185558215611b86579182015b82811115611b86578251825591602001919060010190611cb2565b80821115611b92576000611ce18282611cea565b50600101611ccd565b508054611cf69061234d565b6000825580601f10611d06575050565b601f016020900490600052602060002090810190611d249190611c44565b50565b60ff81168114611d2457600080fd5b600080600060608486031215611d4b57600080fd5b8335611d5681611d27565b92506020840135611d6681611d27565b929592945050506040919091013590565b6001600160a01b0381168114611d2457600080fd5b63ffffffff81168114611d2457600080fd5b600080600060608486031215611db357600080fd5b8335611dbe81611d77565b92506020840135611d6681611d8c565b60008060408385031215611de157600080fd5b8235611dec81611d27565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715611e3957611e39611dfa565b604052919050565b600082601f830112611e5257600080fd5b813567ffffffffffffffff811115611e6c57611e6c611dfa565b611e7f601f8201601f1916602001611e10565b818152846020838601011115611e9457600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215611ec457600080fd5b823567ffffffffffffffff811115611edb57600080fd5b611ee785828601611e41565b95602094909401359450505050565b8015158114611d2457600080fd5b600080600060608486031215611f1957600080fd5b8335611f2481611d77565b92506020840135611d6681611ef6565b60008060408385031215611f4757600080fd5b8235611dec81611d77565b600080600060608486031215611f6757600080fd5b8335611f7281611d77565b92506020840135611d6681611d77565b600080600060608486031215611f9757600080fd5b833567ffffffffffffffff80821115611faf57600080fd5b611fbb87838801611e41565b94506020860135915080821115611fd157600080fd5b50611fde86828701611e41565b925050604084013590509250925092565b60006020828403121561200157600080fd5b5035919050565b6000806000806080858703121561201e57600080fd5b843567ffffffffffffffff81111561203557600080fd5b61204187828801611e41565b945050602085013561205281611d8c565b9250604085013561206281611ef6565b9396929550929360600135925050565b6000806040838503121561208557600080fd5b82359150602083013561209781611ef6565b809150509250929050565b6000602082840312156120b457600080fd5b8135611ae981611d77565b6020808252601490820152733cb7ba9036bab9ba1031329033b7bb32b93737b960611b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561211557600080fd5b5051919050565b600067ffffffffffffffff82111561213657612136611dfa565b5060051b60200190565b600082601f83011261215157600080fd5b815160206121666121618361211c565b611e10565b82815260059290921b8401810191818101908684111561218557600080fd5b8286015b848110156121a957805161219c81611d8c565b8352918301918301612189565b509695505050505050565b600080600080608085870312156121ca57600080fd5b84516121d581611d27565b809450506020808601516121e881611d27565b604087015190945067ffffffffffffffff8082111561220657600080fd5b818801915088601f83011261221a57600080fd5b81516122286121618261211c565b81815260059190911b8301840190848101908b83111561224757600080fd5b938501935b8285101561226e57845161225f81611d77565b8252938501939085019061224c565b60608b0151909750945050508083111561228757600080fd5b505061229587828801612140565b91505092959194509250565b6000602082840312156122b357600080fd5b8151611ae981611d77565b600081518084526020808501945080840160005b838110156122f75781516001600160a01b0316875295820195908201906001016122d2565b509495945050505050565b60408152600061231560408301856122be565b828103602084015261232781856122be565b95945050505050565b60006020828403121561234257600080fd5b8151611ae981611d27565b600181811c9082168061236157607f821691505b6020821081141561238257634e487b7160e01b600052602260045260246000fd5b50919050565b6000815180845260005b818110156123ae57602081850181015186830182015201612392565b818111156123c0576000602083870101525b50601f01601f19169290920160200192915050565b6040815260006123e86040830185612388565b82810360208401526123278185612388565b602081526000611ae96020830184612388565b6040815260006124206040830185612388565b90508260208301529392505050565b60006020828403121561244157600080fd5b8151611ae981611ef656fea2646970667358221220e85439387687fb40a454c2907e0cc9f5672a2730662480ebb006640171ea0d1464736f6c634300080b003360806040523480156200001157600080fd5b506040516200158438038062001584833981016040819052620000349162000724565b600080546001600160a01b031916331781555b84518163ffffffff161015620000c257620000ad858263ffffffff168151811062000076576200007662000827565b6020026020010151858363ffffffff168151811062000099576200009962000827565b6020026020010151620000fb60201b60201c565b80620000b98162000853565b91505062000047565b506004805461ffff191661010060ff8481169190910260ff191691909117908416179055620000f13362000287565b50505050620008c5565b6200010633620002f0565b620001465760405162461bcd60e51b815260206004820152600b60248201526a4f6e6c79206f776e65722160a81b60448201526064015b60405180910390fd5b63ffffffff8116620001f0576001600160a01b038216321415620001ad5760405162461bcd60e51b815260206004820152601c60248201527f596f752063616e206e6f742072656d6f766520796f757273656c66210000000060448201526064016200013d565b6001600160a01b0382166000908152600360209081526040909120805463ffffffff19169055620001ec906001908490620005c462000338821b17901c565b5050565b6200020b826001620004d660201b620007421790919060201c565b1562000240576001600160a01b0382166000908152600360205260409020805463ffffffff191663ffffffff83161790555050565b6001600160a01b0382166000908152600360209081526040909120805463ffffffff191663ffffffff8416179055620001ec906001908490620004f4811b6200076017901c565b6200029233620002f0565b620002ce5760405162461bcd60e51b815260206004820152600b60248201526a4f6e6c79206f776e65722160a81b60448201526064016200013d565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b60006001600160a01b0382163014156200030c57506001919050565b6000546001600160a01b03838116911614156200032b57506001919050565b506000919050565b919050565b6001600160a01b038116600090815260208390526040902054620003ab5760405162461bcd60e51b815260206004820152602360248201527f4c6962416464726573735365743a2076616c756520646f65736e27742065786960448201526239ba1760e91b60648201526084016200013d565b6001600160a01b038116600090815260208390526040812054620003d2906001906200087a565b600184810154919250600091620003ea91906200087a565b9050600084600101828154811062000406576200040662000827565b6000918252602090912001546001860180546001600160a01b0390921692508291859081106200043a576200043a62000827565b600091825260209091200180546001600160a01b0319166001600160a01b03929092169190911790556200047083600162000894565b6001600160a01b038083166000908152602088905260408082209390935590861681529081205560018501805480620004ad57620004ad620008af565b600082815260209020810160001990810180546001600160a01b03191690550190555050505050565b6001600160a01b031660009081526020919091526040902054151590565b6001600160a01b038116620005565760405162461bcd60e51b815260206004820152602160248201527f4c6962416464726573735365743a2076616c75652063616e27742062652030786044820152600360fc1b60648201526084016200013d565b6001600160a01b03811660009081526020839052604090205415620005d65760405162461bcd60e51b815260206004820152602f60248201527f4c6962416464726573735365743a2076616c756520616c72656164792065786960448201526e39ba399034b7103a34329039b2ba1760891b60648201526084016200013d565b6001808301805491820181556000818152602080822090930180546001600160a01b039095166001600160a01b0319909516851790559054928152929052604090912055565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156200065d576200065d6200061c565b604052919050565b60006001600160401b038211156200068157620006816200061c565b5060051b60200190565b600082601f8301126200069d57600080fd5b81516020620006b6620006b08362000665565b62000632565b82815260059290921b84018101918181019086841115620006d657600080fd5b8286015b848110156200070757805163ffffffff81168114620006f95760008081fd5b8352918301918301620006da565b509695505050505050565b805160ff811681146200033357600080fd5b600080600080608085870312156200073b57600080fd5b84516001600160401b03808211156200075357600080fd5b818701915087601f8301126200076857600080fd5b815160206200077b620006b08362000665565b82815260059290921b8401810191818101908b8411156200079b57600080fd5b948201945b83861015620007d25785516001600160a01b0381168114620007c25760008081fd5b82529482019490820190620007a0565b918a0151919850909350505080821115620007ec57600080fd5b50620007fb878288016200068b565b9350506200080c6040860162000712565b91506200081c6060860162000712565b905092959194509250565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600063ffffffff808316818114156200087057620008706200083d565b6001019392505050565b6000828210156200088f576200088f6200083d565b500390565b60008219821115620008aa57620008aa6200083d565b500190565b634e487b7160e01b600052603160045260246000fd5b610caf80620008d56000396000f3fe608060405234801561001057600080fd5b50600436106100b45760003560e01c8063ac6c525111610071578063ac6c52511461014d578063b2bdfa7b1461017c578063b6fd9067146101a7578063cd5d2118146101b9578063e43581b8146101dc578063f437695a146101ef57600080fd5b806313af4035146100b957806322acb867146100ce5780635615696f146100f05780635e77fe201461010f578063965b9ff11461012757806399bc9c1b1461013a575b600080fd5b6100cc6100c7366004610959565b610202565b005b6100d6610252565b60405163ffffffff90911681526020015b60405180910390f35b6004546100fd9060ff1681565b60405160ff90911681526020016100e7565b610117610266565b6040516100e7949392919061097b565b6100d6610135366004610a33565b61036b565b6100cc610148366004610b09565b6103ea565b6100d661015b366004610959565b6001600160a01b031660009081526003602052604090205463ffffffff1690565b60005461018f906001600160a01b031681565b6040516001600160a01b0390911681526020016100e7565b6004546100fd90610100900460ff1681565b6101cc6101c7366004610959565b610432565b60405190151581526020016100e7565b6101cc6101ea366004610959565b610478565b6100cc6101fd366004610b3c565b61048b565b61020b33610432565b6102305760405162461bcd60e51b815260040161022790610b7c565b60405180910390fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b60006102616101356001610875565b905090565b6000806060806102766001610875565b9150815167ffffffffffffffff81111561029257610292610a1d565b6040519080825280602002602001820160405280156102bb578160200160208202803683370190505b50905060005b825181101561035257600360008483815181106102e0576102e0610ba1565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a900463ffffffff1682828151811061032b5761032b610ba1565b63ffffffff909216602092830291909101909101528061034a81610bcd565b9150506102c1565b5060045460ff8082169661010090920416945091925090565b600080805b83518163ffffffff1610156103e35760036000858363ffffffff168151811061039b5761039b610ba1565b6020908102919091018101516001600160a01b03168252810191909152604001600020546103cf9063ffffffff1683610be8565b9150806103db81610c10565b915050610370565b5092915050565b6103f333610432565b61040f5760405162461bcd60e51b815260040161022790610b7c565b6004805461ffff191661010060ff9384160260ff19161792909116919091179055565b60006001600160a01b03821630141561044d57506001919050565b6000546001600160a01b038381169116141561046b57506001919050565b506000919050565b919050565b6000610485600183610742565b92915050565b61049433610432565b6104b05760405162461bcd60e51b815260040161022790610b7c565b63ffffffff8116610546576001600160a01b0382163214156105145760405162461bcd60e51b815260206004820152601c60248201527f596f752063616e206e6f742072656d6f766520796f757273656c6621000000006044820152606401610227565b6001600160a01b0382166000908152600360205260409020805463ffffffff191690556105426001836105c4565b5050565b610551600183610742565b15610585576001600160a01b0382166000908152600360205260409020805463ffffffff191663ffffffff83161790555050565b6001600160a01b0382166000908152600360205260409020805463ffffffff191663ffffffff8381169190911790915561054290600190849061076016565b6105ce8282610742565b6106265760405162461bcd60e51b815260206004820152602360248201527f4c6962416464726573735365743a2076616c756520646f65736e27742065786960448201526239ba1760e91b6064820152608401610227565b6001600160a01b03811660009081526020839052604081205461064b90600190610c34565b6001848101549192506000916106619190610c34565b9050600084600101828154811061067a5761067a610ba1565b6000918252602090912001546001860180546001600160a01b0390921692508291859081106106ab576106ab610ba1565b600091825260209091200180546001600160a01b0319166001600160a01b03929092169190911790556106df836001610c4b565b6001600160a01b03808316600090815260208890526040808220939093559086168152908120556001850180548061071957610719610c63565b600082815260209020810160001990810180546001600160a01b03191690550190555050505050565b6001600160a01b031660009081526020919091526040902054151590565b6001600160a01b0381166107c05760405162461bcd60e51b815260206004820152602160248201527f4c6962416464726573735365743a2076616c75652063616e27742062652030786044820152600360fc1b6064820152608401610227565b6107ca8282610742565b1561082f5760405162461bcd60e51b815260206004820152602f60248201527f4c6962416464726573735365743a2076616c756520616c72656164792065786960448201526e39ba399034b7103a34329039b2ba1760891b6064820152608401610227565b6001808301805491820181556000818152602080822090930180546001600160a01b039095166001600160a01b0319909516851790559054928152929052604090912055565b600181015460609060009067ffffffffffffffff81111561089857610898610a1d565b6040519080825280602002602001820160405280156108c1578160200160208202803683370190505b50905060005b60018401548110156103e3578360010181815481106108e8576108e8610ba1565b9060005260206000200160009054906101000a90046001600160a01b031682828151811061091857610918610ba1565b6001600160a01b03909216602092830291909101909101528061093a81610bcd565b9150506108c7565b80356001600160a01b038116811461047357600080fd5b60006020828403121561096b57600080fd5b61097482610942565b9392505050565b60006080820160ff87168352602060ff8716818501526080604085015281865180845260a086019150828801935060005b818110156109d15784516001600160a01b0316835293830193918301916001016109ac565b50508481036060860152855180825290820192508186019060005b81811015610a0e57825163ffffffff16855293830193918301916001016109ec565b50929998505050505050505050565b634e487b7160e01b600052604160045260246000fd5b60006020808385031215610a4657600080fd5b823567ffffffffffffffff80821115610a5e57600080fd5b818501915085601f830112610a7257600080fd5b813581811115610a8457610a84610a1d565b8060051b604051601f19603f83011681018181108582111715610aa957610aa9610a1d565b604052918252848201925083810185019188831115610ac757600080fd5b938501935b82851015610aec57610add85610942565b84529385019392850192610acc565b98975050505050505050565b803560ff8116811461047357600080fd5b60008060408385031215610b1c57600080fd5b610b2583610af8565b9150610b3360208401610af8565b90509250929050565b60008060408385031215610b4f57600080fd5b610b5883610942565b9150602083013563ffffffff81168114610b7157600080fd5b809150509250929050565b6020808252600b908201526a4f6e6c79206f776e65722160a81b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600019821415610be157610be1610bb7565b5060010190565b600063ffffffff808316818516808303821115610c0757610c07610bb7565b01949350505050565b600063ffffffff80831681811415610c2a57610c2a610bb7565b6001019392505050565b600082821015610c4657610c46610bb7565b500390565b60008219821115610c5e57610c5e610bb7565b500190565b634e487b7160e01b600052603160045260246000fdfea264697066735822122085fafc65a9ded246b2c31779d206cec20aefb591774f8d42cc273420eb860a2664736f6c634300080b003360806040523480156200001157600080fd5b5060405162002073380380620020738339810160408190526200003491620000e1565b600080546001600160a01b03191633179055604051829082906200005890620000b6565b6001600160a01b03928316815291166020820152604001604051809103906000f0801580156200008c573d6000803e3d6000fd5b50600180546001600160a01b0319166001600160a01b039290921691909117905550620001199050565b610a2b806200164883390190565b80516001600160a01b0381168114620000dc57600080fd5b919050565b60008060408385031215620000f557600080fd5b6200010083620000c4565b91506200011060208401620000c4565b90509250929050565b61151f80620001296000396000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c8063401853b711610097578063bc903cb811610066578063bc903cb8146102c3578063cd5d2118146102e9578063dde248e31461030c578063fcf81c141461032c57600080fd5b8063401853b7146102555780636d23cd581461027c5780636f2904cc1461028f578063b2bdfa7b1461029857600080fd5b806320d154da116100d357806320d154da14610209578063290bc7971461021c5780632c3956fe1461022f578063315ce5e21461024257600080fd5b80630a4948401461010557806313af40351461019657806319dcd07e146101ab5780631cc05cbc146101d0575b600080fd5b6101586101133660046110c1565b600360208190526000918252604090912080546001820154600283015492909301546001600160a01b03918216939182169260ff600160a01b90930483169290911685565b604080516001600160a01b03968716815295909416602086015260ff92831693850193909352606084015216608082015260a0015b60405180910390f35b6101a96101a43660046110f1565b61033f565b005b6101be6101b93660046110c1565b61038f565b60405160ff909116815260200161018d565b6101fb6101de366004611125565b600460209081526000928352604080842090915290825290205481565b60405190815260200161018d565b6101a961021736600461115a565b61043b565b6101a961022a3660046110f1565b61054d565b6101fb61023d36600461117d565b610594565b6101be6102503660046111ca565b6107d6565b6101be6102633660046110c1565b6000908152600360208190526040909120015460ff1690565b6101fb61028a366004611125565b610b26565b6101fb60025481565b6000546102ab906001600160a01b031681565b6040516001600160a01b03909116815260200161018d565b6102d66102d13660046110c1565b610b54565b60405161018d9796959493929190611251565b6102fc6102f73660046110f1565b610c62565b604051901515815260200161018d565b61031f61031a3660046112b5565b610ca8565b60405161018d91906112d7565b6001546102ab906001600160a01b031681565b61034833610c62565b61036d5760405162461bcd60e51b8152600401610364906113af565b60405180910390fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b600081815260036020819052604082200154829060ff166103e75760405162461bcd60e51b8152602060048201526012602482015271141c9bdc1bdcd85b081b9bdd08195e1a5cdd60721b6044820152606401610364565b60008381526003602081905260409091209081015460ff166001141561042b57806002015443111561042b57600301805460ff191660059081179091559150610435565b6003015460ff1691505b50919050565b61044433610c62565b6104605760405162461bcd60e51b8152600401610364906113af565b60008281526003602052604090206104778361038f565b60ff166001146104dc5760405162461bcd60e51b815260206004820152602a60248201527f4f6e6c79206e65776c7920637265617465642070726f706f73616c2063616e206044820152691899481c995d9bdad95960b21b6064820152608401610364565b60018101546001600160a01b0383811691161461053b5760405162461bcd60e51b815260206004820152601860248201527f4f6e6c792070726f706f7365722063616e207265766f6b6500000000000000006044820152606401610364565b600301805460ff191660041790555050565b61055633610c62565b6105725760405162461bcd60e51b8152600401610364906113af565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b600061059f33610c62565b6105bb5760405162461bcd60e51b8152600401610364906113af565b60ff80851660009081526004602090815260408083206001600160a01b03881684528252808320548084526003928390529220015490911660011415610606576106048161038f565b505b6000818152600360208190526040909120015460ff166001141561066c5760405162461bcd60e51b815260206004820152601860248201527f43757272656e742070726f706f73616c206e6f7420656e6400000000000000006044820152606401610364565b6002805490600061067c836113ea565b91905055506000600254905060608060006040518060e00160405280896001600160a01b031681526020018b6001600160a01b031681526020018a60ff16815260200188436106cb9190611405565b815260016020808301829052604080840188905260609384018790526000898152600380845290829020865181546001600160a01b0319166001600160a01b0391821617825587850151958201805494890151969091166001600160a81b031990941693909317600160a01b60ff96871602179092559385015160028201556080850151938101805460ff1916949093169390931790915560a08301518051939450849361077f9260048501920190611047565b5060c0820151805161079b916005840191602090910190611047565b50505060ff891660009081526004602090815260408083206001600160a01b038c168452909152902084905550919350505050949350505050565b60006107e133610c62565b6107fd5760405162461bcd60e51b8152600401610364906113af565b60008481526003602081905260409091200154849060ff166108565760405162461bcd60e51b8152602060048201526012602482015271141c9bdc1bdcd85b081b9bdd08195e1a5cdd60721b6044820152606401610364565b60008581526003602081905260409091200154859060ff166001146108bd5760405162461bcd60e51b815260206004820152601760248201527f50726f706f73616c206973206e6f7420766f7461626c650000000000000000006044820152606401610364565b600086815260036020818152604092839020835160e08101855281546001600160a01b03908116825260018301549081168285015260ff600160a01b9091048116828701526002830154606083015293820154909316608084015260048101805485518185028101850190965280865291946109e69493869360a08601939183018282801561097557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610957575b50505050508152602001600582018054806020026020016040519081016040528092919081815260200182805480156109d757602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116109b9575b50505050508152505086610fa7565b15610a235760405162461bcd60e51b815260206004820152600d60248201526c105b1c9958591e481d9bdd1959609a1b6044820152606401610364565b8515610a5e57600481018054600181018255600091825260209091200180546001600160a01b0319166001600160a01b038716179055610a8f565b600581018054600181018255600091825260209091200180546001600160a01b0319166001600160a01b0387161790555b6001546040516353bfcf2f60e01b81526000916001600160a01b0316906353bfcf2f90610ac79060048087019160058801910161145b565b602060405180830381865afa158015610ae4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b089190611489565b600392909201805460ff191660ff8416179055509695505050505050565b60ff821660009081526004602090815260408083206001600160a01b03851684529091529020545b92915050565b600081815260036020818152604092839020805460018201546002830154948301546004840180548851818802810188019099528089526001600160a01b039485169894841697600160a01b90940460ff908116979496931694606094859493919290830182828015610bf057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610bd2575b5050505050925080600501805480602002602001604051908101604052809291908181526020018280548015610c4f57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c31575b5050505050915050919395979092949650565b60006001600160a01b038216301415610c7d57506001919050565b6000546001600160a01b0383811691161415610c9b57506001919050565b506000919050565b919050565b6060600254831115610d0b5760405162461bcd60e51b815260206004820152602660248201527f2766726f6d272069732067726561746572207468616e202770726f706f73616c604482015265436f756e742760d01b6064820152608401610364565b81831115610d5b5760405162461bcd60e51b815260206004820152601b60248201527f2766726f6d272069732067726561746572207468616e2027746f2700000000006044820152606401610364565b600254821115610d6b5760025491505b6001831015610d7957600192505b6000610d8584846114a6565b610d90906001611405565b67ffffffffffffffff811115610da857610da86114bd565b604051908082528060200260200182016040528015610e3157816020015b610e1e6040518060e0016040528060006001600160a01b0316815260200160006001600160a01b03168152602001600060ff16815260200160008152602001600060ff16815260200160608152602001606081525090565b815260200190600190039081610dc65790505b5090506000845b848111610f9d57600081815260036020818152604092839020835160e08101855281546001600160a01b03908116825260018301549081168285015260ff600160a01b909104811682870152600283015460608301529382015490931660808401526004810180548551818502810185019096528086529194859360a086019391929190830182828015610ef557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610ed7575b5050505050815260200160058201805480602002602001604051908101604052809291908181526020018280548015610f5757602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610f39575b505050505081525050848480610f6c906113ea565b955081518110610f7e57610f7e6114d3565b6020026020010181905250508080610f95906113ea565b915050610e38565b5090949350505050565b6000610fb78360a0015183610fe1565b80610fcb5750610fcb8360c0015183610fe1565b15610fd857506001610b4e565b50600092915050565b6000805b835181101561103d57838181518110611000576110006114d3565b60200260200101516001600160a01b0316836001600160a01b0316141561102b576001915050610b4e565b80611035816113ea565b915050610fe5565b5060009392505050565b82805482825590600052602060002090810192821561109c579160200282015b8281111561109c57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190611067565b506110a89291506110ac565b5090565b5b808211156110a857600081556001016110ad565b6000602082840312156110d357600080fd5b5035919050565b80356001600160a01b0381168114610ca357600080fd5b60006020828403121561110357600080fd5b61110c826110da565b9392505050565b60ff8116811461112257600080fd5b50565b6000806040838503121561113857600080fd5b823561114381611113565b9150611151602084016110da565b90509250929050565b6000806040838503121561116d57600080fd5b82359150611151602084016110da565b6000806000806080858703121561119357600080fd5b61119c856110da565b935060208501356111ac81611113565b92506111ba604086016110da565b9396929550929360600135925050565b6000806000606084860312156111df57600080fd5b83359250602084013580151581146111f657600080fd5b9150611204604085016110da565b90509250925092565b600081518084526020808501945080840160005b838110156112465781516001600160a01b031687529582019590820190600101611221565b509495945050505050565b6001600160a01b0388811682528716602082015260ff8681166040830152606082018690528416608082015260e060a082018190526000906112959083018561120d565b82810360c08401526112a7818561120d565b9a9950505050505050505050565b600080604083850312156112c857600080fd5b50508035926020909101359150565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b838110156113a157888303603f19018552815180516001600160a01b0390811685528882015116888501528681015160ff16878501526060808201519085015260808082015160e091906113568288018260ff169052565b505060a080830151828288015261136f8388018261120d565b9250505060c0808301519250858203818701525061138d818361120d565b9689019694505050908601906001016112fe565b509098975050505050505050565b6020808252600b908201526a4f6e6c79206f776e65722160a81b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b60006000198214156113fe576113fe6113d4565b5060010190565b60008219821115611418576114186113d4565b500190565b6000815480845260208085019450836000528060002060005b838110156112465781546001600160a01b031687529582019560019182019101611436565b60408152600061146e604083018561141d565b8281036020840152611480818561141d565b95945050505050565b60006020828403121561149b57600080fd5b815161110c81611113565b6000828210156114b8576114b86113d4565b500390565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fdfea26469706673582212202722242e3b6e57d58d24efcb69cf52b88dbcc2c1e33fb95d0406bae9462e0a2764736f6c634300080b0033608060405234801561001057600080fd5b50604051610a2b380380610a2b83398101604081905261002f916101fe565b600080546001600160a01b03191633179055818161004c8261013c565b600180546001600160a01b0319166001600160a01b0383169081178255604080516322acb86760e01b815290516322acb867916004808201926020929091908290030181865afa1580156100a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100c89190610231565b63ffffffff1610156101335760405162461bcd60e51b815260206004820152602960248201527f636f6d6d6974746565206973206572726f722c20706c6561736520636865636b60448201526820616464726573732160b81b60648201526084015b60405180910390fd5b5050505061025e565b610145336101a1565b61017f5760405162461bcd60e51b815260206004820152600b60248201526a4f6e6c79206f776e65722160a81b604482015260640161012a565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b60006001600160a01b0382163014156101bc57506001919050565b6000546001600160a01b03838116911614156101da57506001919050565b506000919050565b919050565b80516001600160a01b03811681146101e257600080fd5b6000806040838503121561021157600080fd5b61021a836101e7565b9150610228602084016101e7565b90509250929050565b60006020828403121561024357600080fd5b815163ffffffff8116811461025757600080fd5b9392505050565b6107be8061026d6000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c806301410de21461006757806313af403514610091578063185c1587146100a657806353bfcf2f146100d1578063b2bdfa7b146100e4578063cd5d2118146100f7575b600080fd5b61007a6100753660046104d1565b61011a565b60405160ff90911681526020015b60405180910390f35b6100a461009f366004610559565b61018c565b005b6001546100b9906001600160a01b031681565b6040516001600160a01b039091168152602001610088565b61007a6100df366004610633565b6101f5565b6000546100b9906001600160a01b031681565b61010a610105366004610559565b610467565b6040519015158152602001610088565b600061012960ff8416856106ad565b63ffffffff1661013a8660646106ad565b63ffffffff16101561014e57506001610183565b61015b8560ff84166106ad565b63ffffffff1661016c8760646106ad565b63ffffffff161061017f57506002610183565b5060035b95945050505050565b61019533610467565b6101d35760405162461bcd60e51b815260206004820152600b60248201526a4f6e6c79206f776e65722160a81b604482015260640160405180910390fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b60015460405163965b9ff160e01b815260009182916001600160a01b039091169063965b9ff19061022a9087906004016106d9565b602060405180830381865afa158015610247573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061026b9190610726565b60015460405163965b9ff160e01b81529192506000916001600160a01b039091169063965b9ff1906102a19087906004016106d9565b602060405180830381865afa1580156102be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102e29190610726565b6102ec9083610743565b90506000600160009054906101000a90046001600160a01b03166001600160a01b03166322acb8676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610343573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103679190610726565b905061045d838383600160009054906101000a90046001600160a01b03166001600160a01b0316635615696f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103e6919061076b565b600160009054906101000a90046001600160a01b03166001600160a01b031663b6fd90676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610439573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610075919061076b565b9695505050505050565b60006001600160a01b03821630141561048257506001919050565b6000546001600160a01b03838116911614156104a057506001919050565b506000919050565b919050565b63ffffffff811681146104bf57600080fd5b50565b60ff811681146104bf57600080fd5b600080600080600060a086880312156104e957600080fd5b85356104f4816104ad565b94506020860135610504816104ad565b93506040860135610514816104ad565b92506060860135610524816104c2565b91506080860135610534816104c2565b809150509295509295909350565b80356001600160a01b03811681146104a857600080fd5b60006020828403121561056b57600080fd5b61057482610542565b9392505050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126105a257600080fd5b8135602067ffffffffffffffff808311156105bf576105bf61057b565b8260051b604051601f19603f830116810181811084821117156105e4576105e461057b565b60405293845285810183019383810192508785111561060257600080fd5b83870191505b848210156106285761061982610542565b83529183019190830190610608565b979650505050505050565b6000806040838503121561064657600080fd5b823567ffffffffffffffff8082111561065e57600080fd5b61066a86838701610591565b9350602085013591508082111561068057600080fd5b5061068d85828601610591565b9150509250929050565b634e487b7160e01b600052601160045260246000fd5b600063ffffffff808316818516818304811182151516156106d0576106d0610697565b02949350505050565b6020808252825182820181905260009190848201906040850190845b8181101561071a5783516001600160a01b0316835292840192918401916001016106f5565b50909695505050505050565b60006020828403121561073857600080fd5b8151610574816104ad565b600063ffffffff80831681851680830382111561076257610762610697565b01949350505050565b60006020828403121561077d57600080fd5b8151610574816104c256fea26469706673582212207e324ba2bb41822f929f1314b540322ac6d0e6f01442a9d4e444f42878dcb0b364736f6c634300080b0033"; +constexpr static const char* const committeeSmBin = "60806040523480156200001157600080fd5b5060405162005e9038038062005e9083398101604081905262000034916200022c565b83838383604051620000469062000103565b6200005594939291906200032f565b604051809103906000f08015801562000072573d6000803e3d6000fd5b50600080546001600160a01b0319166001600160a01b03929092169182179055604051309190620000a39062000111565b6001600160a01b03928316815291166020820152604001604051809103906000f080158015620000d7573d6000803e3d6000fd5b50600180546001600160a01b0319166001600160a01b039290921691909117905550620003d892505050565b611591806200287b83390190565b6120848062003e0c83390190565b63b95aa35560e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156200016057620001606200011f565b604052919050565b60006001600160401b038211156200018457620001846200011f565b5060051b60200190565b600082601f830112620001a057600080fd5b81516020620001b9620001b38362000168565b62000135565b82815260059290921b84018101918181019086841115620001d957600080fd5b8286015b848110156200020a57805163ffffffff81168114620001fc5760008081fd5b8352918301918301620001dd565b509695505050505050565b805160ff811681146200022757600080fd5b919050565b600080600080608085870312156200024357600080fd5b84516001600160401b03808211156200025b57600080fd5b818701915087601f8301126200027057600080fd5b8151602062000283620001b38362000168565b82815260059290921b8401810191818101908b841115620002a357600080fd5b948201945b83861015620002da5785516001600160a01b0381168114620002ca5760008081fd5b82529482019490820190620002a8565b918a0151919850909350505080821115620002f457600080fd5b5062000303878288016200018e565b935050620003146040860162000215565b9150620003246060860162000215565b905092959194509250565b6080808252855190820181905260009060209060a0840190828901845b82811015620003735781516001600160a01b0316845292840192908401906001016200034c565b5050508381038285015286518082528783019183019060005b81811015620003b057835163ffffffff16835292840192918401916001016200038c565b505060ff871660408601529250620003c6915050565b60ff8316606083015295945050505050565b61249380620003e86000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c8063869328ad11610097578063cbca676d11610066578063cbca676d1461022a578063cfe0a26c1461023d578063d294443914610250578063de0033561461026357600080fd5b8063869328ad146101ac578063922b7906146101e1578063b5f23235146101f4578063ba2de76c1461021757600080fd5b80635b5cc736116100d35780635b5cc736146101465780635d826f401461015b57806368baf8bd1461016e5780637bc855161461019957600080fd5b80630cb0a8d9146100fa5780631f975035146101205780634489465614610133575b600080fd5b61010d610108366004611d5b565b610276565b6040519081526020015b60405180910390f35b61010d61012e366004611e53565b610363565b61010d610141366004611e98565b610471565b610159610154366004611ec4565b610759565b005b61010d610169366004611f06565b611064565b600154610181906001600160a01b031681565b6040516001600160a01b039091168152602001610117565b600054610181906001600160a01b031681565b6101cf6101ba366004611f70565b60009081526002602052604090205460ff1690565b60405160ff9091168152602001610117565b61010d6101ef366004611f89565b611185565b610207610202366004611fb9565b611381565b6040519015158152602001610117565b610159610225366004611f70565b6113f6565b61010d610238366004611fe5565b611483565b61010d61024b366004612015565b611614565b61010d61025e366004612082565b61173b565b61010d6102713660046120b2565b6117fe565b600061028133611381565b6102a757604051636381e58960e11b815260040161029e906120d0565b60405180910390fd5b6040805160018082528183019092526000916020808301908036833701905050905084816000815181106102dd576102dd6120fe565b60200260200101906001600160a01b031690816001600160a01b03168152505060608060006040518060e00160405280601660ff168152602001896001600160a01b03168152602001848152602001838152602001600063ffffffff1681526020018581526020018815158152509050610357818761198f565b98975050505050505050565b600061036e33611381565b61038b57604051636381e58960e11b815260040161029e906120d0565b60018351116103cd57604051636381e58960e11b815260206004820152600d60248201526c34b73b30b634b2103737b2329760991b604482015260640161029e565b6040805160018082528183019092526060918291600091816020015b60608152602001906001900390816103e95790505090508581600081518110610414576104146120fe565b6020908102919091018101919091526040805160e08101825260348152611003928101929092528101839052606081018290526000608082015260a08101849052600160c0820152610466818761198f565b979650505050505050565b600061047c33611381565b61049957604051636381e58960e11b815260040161029e906120d0565b600080546040805163749280c760e01b8152905160609384936001600160a01b03169263749280c792600480830193928290030181865afa1580156104e2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261050a91908101906121ac565b5060005460408051633de42a8b60e11b815290519296508994506001600160a01b03918216935090841691637bc85516916004808201926020929091908290030181865afa158015610560573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105849190612299565b6001600160a01b0316146105f057604051636381e58960e11b815260206004820152602c60248201527f6e657720766f746520636f6d707574657220636f6d6d6974746565206164647260448201526b0cae6e640dad2e6dac2e8c6d60a31b606482015260840161029e565b6040516308e2b09560e41b81526001600160a01b03821690638e2b09509061061e90869086906004016122fa565b602060405180830381865afa15801561063b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061065f9190612328565b60ff166002146106b257604051636381e58960e11b815260206004820152601e60248201527f6e657720766f746520636f6d707574657220696d70657266656374696f6e0000604482015260640161029e565b6040805160018082528183019092526000916020808301908036833701905050905060608088836000815181106106eb576106eb6120fe565b6001600160a01b039283166020918202929092018101919091526040805160e081018252600d81526001549093169183019190915281018390526060810182905260006080820181905260a0820185905260c082015261074b818a61198f565b9a9950505050505050505050565b61076233611381565b61077f57604051636381e58960e11b815260040161029e906120d0565b6001546040516368b739c960e01b81526004810184905282151560248201523360448201526000916001600160a01b0316906368b739c9906064016020604051808303816000875af11580156107d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107fd9190612328565b90506108536040518060e00160405280600060ff16815260200160006001600160a01b031681526020016060815260200160608152602001600063ffffffff168152602001606081526020016000151581525090565b8160ff166002141561105e5760008481526002602090815260408083208054825160e08101845260ff82168082526101009092046001600160a01b031681860152600183018054855181880281018801875281815293969295860193928301828280156108fd57602002820191906000526020600020906000905b825461010083900a900460ff168152602060019283018181049485019490930390920291018084116108ce5790505b5050505050815260200160028201805480602002602001604051908101604052809291908181526020016000905b828210156109d757838290600052602060002001805461094a90612345565b80601f016020809104026020016040519081016040528092919081815260200182805461097690612345565b80156109c35780601f10610998576101008083540402835291602001916109c3565b820191906000526020600020905b8154815290600101906020018083116109a657829003601f168201915b50505050508152602001906001019061092b565b50505090825250600382015463ffffffff166020808301919091526004830180546040805182850281018501825282815294019392830182828015610a4557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610a27575b50505091835250506005919091015460ff90811615156020909201919091529093508116600b1415610ba557608083015163ffffffff16610b0157336001600160a01b03168360a00151600081518110610aa157610aa16120fe565b60200260200101516001600160a01b03161415610b0157604051636381e58960e11b815260206004820152601c60248201527f596f752063616e206e6f742072656d6f766520796f757273656c662100000000604482015260640161029e565b6000805460a085015180516001600160a01b0390921692639067531d92610b2a57610b2a6120fe565b602002602001015185608001516040518363ffffffff1660e01b8152600401610b6e9291906001600160a01b0392909216825263ffffffff16602082015260400190565b600060405180830381600087803b158015610b8857600080fd5b505af1158015610b9c573d6000803e3d6000fd5b50505050611028565b8060ff16600c1415610c2d5760008054604085015180516001600160a01b03909216926358dd53d992610bda57610bda6120fe565b60200260200101518560400151600181518110610bf957610bf96120fe565b60200260200101516040518363ffffffff1660e01b8152600401610b6e92919060ff92831681529116602082015260400190565b8060ff16600d1415610c985760015460a084015180516001600160a01b039092169163ecbea6bc9190600090610c6557610c656120fe565b60200260200101516040518263ffffffff1660e01b8152600401610b6e91906001600160a01b0391909116815260200190565b8060ff1660151415610d43576110056001600160a01b031663b0ca889b8460400151600081518110610ccc57610ccc6120fe565b60200260200101516040518263ffffffff1660e01b8152600401610cf9919060ff91909116815260200190565b6020604051808303816000875af1158015610d18573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d3c9190612380565b9150611028565b8060ff1660161415610ddc578260c0015115610db4576110056001600160a01b031663026a22fd8460a00151600081518110610d8157610d816120fe565b60200260200101516040518263ffffffff1660e01b8152600401610cf991906001600160a01b0391909116815260200190565b6110056001600160a01b0316631a2052518460a00151600081518110610d8157610d816120fe565b8060ff16601f1415610e4f576110056001600160a01b03166302b3703b84602001518560a00151600081518110610e1557610e156120fe565b60200260200101516040518363ffffffff1660e01b8152600401610cf99291906001600160a01b0392831681529116602082015260400190565b8060ff1660291415610ec7576110006001600160a01b0316630749b5188460600151600081518110610e8357610e836120fe565b60200260200101518560600151600181518110610ea257610ea26120fe565b60200260200101516040518363ffffffff1660e01b8152600401610cf99291906123e6565b8060ff1660331415610fb8578260c0015115610f9057608083015163ffffffff16610f38576110036001600160a01b03166325e85d168460600151600081518110610f1457610f146120fe565b60200260200101516040518263ffffffff1660e01b8152600401610cf9919061240b565b6110036001600160a01b03166350f4c5098460600151600081518110610f6057610f606120fe565b6020026020010151856080015163ffffffff166040518363ffffffff1660e01b8152600401610cf992919061241e565b6110036001600160a01b0316639fdc9df88460600151600081518110610f6057610f606120fe565b8060ff1660341415610fec576110036001600160a01b03166386b733f98460600151600081518110610f1457610f146120fe565b604051636381e58960e11b815260206004820152601060248201526f3b37ba32903a3cb8329032b93937b91760811b604482015260640161029e565b6040518281527f406818d541a0d6840b3ed377d6be620edc0ffdb3912d08690fca6715d14512229060200160405180910390a150505b50505050565b600061106f33611381565b61108c57604051636381e58960e11b815260040161029e906120d0565b60018551116110ce57604051636381e58960e11b815260206004820152600d60248201526c34b73b30b634b2103737b2329760991b604482015260640161029e565b6040805160018082528183019092526060918291600091816020015b60608152602001906001900390816110ea5790505090508781600081518110611115576111156120fe565b602002602001018190525060006040518060e00160405280603360ff1681526020016110036001600160a01b031681526020018481526020018381526020018963ffffffff1681526020018581526020018815158152509050611178818761198f565b9998505050505050505050565b600061119033611381565b6111ad57604051636381e58960e11b815260040161029e906120d0565b6001600160a01b03831661120457604051636381e58960e11b815260206004820152601c60248201527f636f6e74726163742061646472657373206e6f74206578697374732e00000000604482015260640161029e565b6040516205d1af60e31b81526001600160a01b038416600482015261100590622e8d7890602401602060405180830381865afa158015611248573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061126c9190612299565b6001600160a01b0316846001600160a01b031614156112eb57604051636381e58960e11b815260206004820152603460248201527f746865206163636f756e7420686173206265656e207468652061646d696e206f604482015273331031b7b731bab9393a1031b7b73a3930b1ba1760611b606482015260840161029e565b604080516001808252818301909252600091602080830190803683370190505090506060808683600081518110611324576113246120fe565b6001600160a01b039283166020918202929092018101919091526040805160e081018252601f81529289169183019190915281018390526060810182905260006080820181905260a0820185905260c0820152610357818761198f565b6000805460405163b5f2323560e01b81526001600160a01b0384811660048301529091169063b5f2323590602401602060405180830381865afa1580156113cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f09190612440565b92915050565b6113ff33611381565b61141c57604051636381e58960e11b815260040161029e906120d0565b6001546040516394d8f86f60e01b8152600481018390523360248201526001600160a01b03909116906394d8f86f90604401600060405180830381600087803b15801561146857600080fd5b505af115801561147c573d6000803e3d6000fd5b5050505050565b600061148e33611381565b6114ab57604051636381e58960e11b815260040161029e906120d0565b60648460ff16111561150a57604051636381e58960e11b815260206004820152602160248201527f696e76616c69642072616e6765206f66207061727469636970617465735261746044820152606560f81b606482015260840161029e565b60648360ff16111561155f57604051636381e58960e11b815260206004820152601860248201527f696e76616c69642072616e6765206f662077696e526174650000000000000000604482015260640161029e565b604080516002808252606080830184529260009291906020830190803683370190505090506060868260008151811061159a5761159a6120fe565b602002602001019060ff16908160ff168152505085826001815181106115c2576115c26120fe565b60ff9092166020928302919091018201526040805160e081018252600c8152309281019290925281018390526060810182905260006080820181905260a0820185905260c0820152610357818761198f565b600061161f33611381565b61163c57604051636381e58960e11b815260040161029e906120d0565b600184511161168457604051636381e58960e11b815260206004820152601360248201527234b73b30b634b21035b2bc903632b733ba341760691b604482015260640161029e565b60408051600280825260608281019093528291600091816020015b606081526020019060019003908161169f57905050905086816000815181106116ca576116ca6120fe565b602002602001018190525085816001815181106116e9576116e96120fe565b6020908102919091018101919091526040805160e081018252602981526110009281019290925281018390526060810182905260006080820181905260a0820185905260c0820152610357818761198f565b600061174633611381565b61176357604051636381e58960e11b815260040161029e906120d0565b60408051600180825281830190925260009160208083019080368337019050509050606080868360008151811061179c5761179c6120fe565b6001600160a01b039283166020918202929092018101919091526040805160e081018252600b8152928a169183019190915281018390526060810182905263ffffffff8716608082015260a08101849052600160c0820152610357818761198f565b600061180933611381565b61182657604051636381e58960e11b815260040161029e906120d0565b8260ff166110056001600160a01b031663598ab5966040518163ffffffff1660e01b8152600401602060405180830381865afa15801561186a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061188e9190612380565b141561190357604051636381e58960e11b815260206004820152603b60248201527f7468652063757272656e74206465706c6f79206175746820747970652069732060448201527f7468652073616d6520617320796f752077616e7420746f207365740000000000606482015260840161029e565b60408051600180825281830190925260609160009190602080830190803683370190505090506060858260008151811061193f5761193f6120fe565b60ff9092166020928302919091018201526040805160e081018252601581526110059281019290925281018390526060810182905260006080820181905260a0820185905260c082015261046681875b60015482516020840151604051631ca37ba360e11b815233600482015260ff90921660248301526001600160a01b039081166044830152606482018490526000928392911690633946f746906084016020604051808303816000875af11580156119fd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a219190612380565b60008181526002602090815260409182902087518154838a01516001600160a01b0316610100026001600160a81b031990911660ff909216919091171781559187015180519394508793611a7b9260018501920190611b01565b5060608201518051611a97916002840191602090910190611ba7565b50608082015160038201805463ffffffff191663ffffffff90921691909117905560a08201518051611ad3916004840191602090910190611c00565b5060c091909101516005909101805460ff1916911515919091179055611afa816001610759565b9392505050565b82805482825590600052602060002090601f01602090048101928215611b975791602002820160005b83821115611b6857835183826101000a81548160ff021916908360ff1602179055509260200192600101602081600001049283019260010302611b2a565b8015611b955782816101000a81549060ff0219169055600101602081600001049283019260010302611b68565b505b50611ba3929150611c55565b5090565b828054828255906000526020600020908101928215611bf4579160200282015b82811115611bf45782518051611be4918491602090910190611c6a565b5091602001919060010190611bc7565b50611ba3929150611cde565b828054828255906000526020600020908101928215611b97579160200282015b82811115611b9757825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190611c20565b5b80821115611ba35760008155600101611c56565b828054611c7690612345565b90600052602060002090601f016020900481019282611c985760008555611b97565b82601f10611cb157805160ff1916838001178555611b97565b82800160010185558215611b97579182015b82811115611b97578251825591602001919060010190611cc3565b80821115611ba3576000611cf28282611cfb565b50600101611cde565b508054611d0790612345565b6000825580601f10611d17575050565b601f016020900490600052602060002090810190611d359190611c55565b50565b6001600160a01b0381168114611d3557600080fd5b8015158114611d3557600080fd5b600080600060608486031215611d7057600080fd5b8335611d7b81611d38565b92506020840135611d8b81611d4d565b929592945050506040919091013590565b63b95aa35560e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715611ddb57611ddb611d9c565b604052919050565b600082601f830112611df457600080fd5b813567ffffffffffffffff811115611e0e57611e0e611d9c565b611e21601f8201601f1916602001611db2565b818152846020838601011115611e3657600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215611e6657600080fd5b823567ffffffffffffffff811115611e7d57600080fd5b611e8985828601611de3565b95602094909401359450505050565b60008060408385031215611eab57600080fd5b8235611eb681611d38565b946020939093013593505050565b60008060408385031215611ed757600080fd5b823591506020830135611ee981611d4d565b809150509250929050565b63ffffffff81168114611d3557600080fd5b60008060008060808587031215611f1c57600080fd5b843567ffffffffffffffff811115611f3357600080fd5b611f3f87828801611de3565b9450506020850135611f5081611ef4565b92506040850135611f6081611d4d565b9396929550929360600135925050565b600060208284031215611f8257600080fd5b5035919050565b600080600060608486031215611f9e57600080fd5b8335611fa981611d38565b92506020840135611d8b81611d38565b600060208284031215611fcb57600080fd5b8135611afa81611d38565b60ff81168114611d3557600080fd5b600080600060608486031215611ffa57600080fd5b833561200581611fd6565b92506020840135611d8b81611fd6565b60008060006060848603121561202a57600080fd5b833567ffffffffffffffff8082111561204257600080fd5b61204e87838801611de3565b9450602086013591508082111561206457600080fd5b5061207186828701611de3565b925050604084013590509250925092565b60008060006060848603121561209757600080fd5b83356120a281611d38565b92506020840135611d8b81611ef4565b600080604083850312156120c557600080fd5b8235611eb681611fd6565b6020808252601490820152733cb7ba9036bab9ba1031329033b7bb32b93737b960611b604082015260600190565b63b95aa35560e01b600052603260045260246000fd5b600067ffffffffffffffff82111561212e5761212e611d9c565b5060051b60200190565b600082601f83011261214957600080fd5b8151602061215e61215983612114565b611db2565b82815260059290921b8401810191818101908684111561217d57600080fd5b8286015b848110156121a157805161219481611ef4565b8352918301918301612181565b509695505050505050565b600080600080608085870312156121c257600080fd5b84516121cd81611fd6565b809450506020808601516121e081611fd6565b604087015190945067ffffffffffffffff808211156121fe57600080fd5b818801915088601f83011261221257600080fd5b815161222061215982612114565b81815260059190911b8301840190848101908b83111561223f57600080fd5b938501935b8285101561226657845161225781611d38565b82529385019390850190612244565b60608b0151909750945050508083111561227f57600080fd5b505061228d87828801612138565b91505092959194509250565b6000602082840312156122ab57600080fd5b8151611afa81611d38565b600081518084526020808501945080840160005b838110156122ef5781516001600160a01b0316875295820195908201906001016122ca565b509495945050505050565b60408152600061230d60408301856122b6565b828103602084015261231f81856122b6565b95945050505050565b60006020828403121561233a57600080fd5b8151611afa81611fd6565b600181811c9082168061235957607f821691505b6020821081141561237a5763b95aa35560e01b600052602260045260246000fd5b50919050565b60006020828403121561239257600080fd5b5051919050565b6000815180845260005b818110156123bf576020818501810151868301820152016123a3565b818111156123d1576000602083870101525b50601f01601f19169290920160200192915050565b6040815260006123f96040830185612399565b828103602084015261231f8185612399565b602081526000611afa6020830184612399565b6040815260006124316040830185612399565b90508260208301529392505050565b60006020828403121561245257600080fd5b8151611afa81611d4d56fea264697066735822122025c2ff1d74d5fbc92a29d8fcf59aa314bd7c9bf9dc3bd91e399198263194f85e64736f6c634300080b003360806040523480156200001157600080fd5b50604051620015913803806200159183398101604081905262000034916200072a565b600080546001600160a01b031916331781555b84518163ffffffff161015620000c257620000ad858263ffffffff16815181106200007657620000766200082d565b6020026020010151858363ffffffff16815181106200009957620000996200082d565b6020026020010151620000fb60201b60201c565b80620000b98162000859565b91505062000047565b506004805461ffff191661010060ff8481169190910260ff191691909117908416179055620000f13362000289565b50505050620008cb565b6200010633620002f3565b6200014757604051636381e58960e11b815260206004820152600b60248201526a4f6e6c79206f776e65722160a81b60448201526064015b60405180910390fd5b63ffffffff8116620001f2576001600160a01b038216321415620001af57604051636381e58960e11b815260206004820152601c60248201527f596f752063616e206e6f742072656d6f766520796f757273656c66210000000060448201526064016200013e565b6001600160a01b0382166000908152600360209081526040909120805463ffffffff19169055620001ee906001908490620005c86200033b821b17901c565b5050565b6200020d826001620004da60201b620007471790919060201c565b1562000242576001600160a01b0382166000908152600360205260409020805463ffffffff191663ffffffff83161790555050565b6001600160a01b0382166000908152600360209081526040909120805463ffffffff191663ffffffff8416179055620001ee906001908490620004f8811b6200076517901c565b6200029433620002f3565b620002d157604051636381e58960e11b815260206004820152600b60248201526a4f6e6c79206f776e65722160a81b60448201526064016200013e565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b60006001600160a01b0382163014156200030f57506001919050565b6000546001600160a01b03838116911614156200032e57506001919050565b506000919050565b919050565b6001600160a01b038116600090815260208390526040902054620003af57604051636381e58960e11b815260206004820152602360248201527f4c6962416464726573735365743a2076616c756520646f65736e27742065786960448201526239ba1760e91b60648201526084016200013e565b6001600160a01b038116600090815260208390526040812054620003d69060019062000880565b600184810154919250600091620003ee919062000880565b905060008460010182815481106200040a576200040a6200082d565b6000918252602090912001546001860180546001600160a01b0390921692508291859081106200043e576200043e6200082d565b600091825260209091200180546001600160a01b0319166001600160a01b0392909216919091179055620004748360016200089a565b6001600160a01b038083166000908152602088905260408082209390935590861681529081205560018501805480620004b157620004b1620008b5565b600082815260209020810160001990810180546001600160a01b03191690550190555050505050565b6001600160a01b031660009081526020919091526040902054151590565b6001600160a01b0381166200055b57604051636381e58960e11b815260206004820152602160248201527f4c6962416464726573735365743a2076616c75652063616e27742062652030786044820152600360fc1b60648201526084016200013e565b6001600160a01b03811660009081526020839052604090205415620005dc57604051636381e58960e11b815260206004820152602f60248201527f4c6962416464726573735365743a2076616c756520616c72656164792065786960448201526e39ba399034b7103a34329039b2ba1760891b60648201526084016200013e565b6001808301805491820181556000818152602080822090930180546001600160a01b039095166001600160a01b0319909516851790559054928152929052604090912055565b63b95aa35560e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171562000663576200066362000622565b604052919050565b60006001600160401b0382111562000687576200068762000622565b5060051b60200190565b600082601f830112620006a357600080fd5b81516020620006bc620006b6836200066b565b62000638565b82815260059290921b84018101918181019086841115620006dc57600080fd5b8286015b848110156200070d57805163ffffffff81168114620006ff5760008081fd5b8352918301918301620006e0565b509695505050505050565b805160ff811681146200033657600080fd5b600080600080608085870312156200074157600080fd5b84516001600160401b03808211156200075957600080fd5b818701915087601f8301126200076e57600080fd5b8151602062000781620006b6836200066b565b82815260059290921b8401810191818101908b841115620007a157600080fd5b948201945b83861015620007d85785516001600160a01b0381168114620007c85760008081fd5b82529482019490820190620007a6565b918a0151919850909350505080821115620007f257600080fd5b50620008018782880162000691565b935050620008126040860162000718565b9150620008226060860162000718565b905092959194509250565b63b95aa35560e01b600052603260045260246000fd5b63b95aa35560e01b600052601160045260246000fd5b600063ffffffff8083168181141562000876576200087662000843565b6001019392505050565b60008282101562000895576200089562000843565b500390565b60008219821115620008b057620008b062000843565b500190565b63b95aa35560e01b600052603160045260246000fd5b610cb680620008db6000396000f3fe608060405234801561001057600080fd5b50600436106100b45760003560e01c806358dd53d91161007157806358dd53d91461015f5780636e0376d414610172578063749280c7146101955780639067531d146101ad578063b5f23235146101c0578063c77695e4146101d357600080fd5b806305282c70146100b957806307f44999146100ce5780630e878ed0146100f757806323bdace11461010457806328e914891461012c57806337df996214610157575b600080fd5b6100cc6100c7366004610960565b610202565b005b6004546100e090610100900460ff1681565b60405160ff90911681526020015b60405180910390f35b6004546100e09060ff1681565b610117610112366004610998565b610253565b60405163ffffffff90911681526020016100ee565b60005461013f906001600160a01b031681565b6040516001600160a01b0390911681526020016100ee565b6101176102d2565b6100cc61016d366004610a6e565b6102e6565b610185610180366004610960565b61032f565b60405190151581526020016100ee565b61019d610375565b6040516100ee9493929190610aa1565b6100cc6101bb366004610b43565b61047a565b6101856101ce366004610960565b6105b5565b6101176101e1366004610960565b6001600160a01b031660009081526003602052604090205463ffffffff1690565b61020b3361032f565b61023157604051636381e58960e11b815260040161022890610b83565b60405180910390fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b600080805b83518163ffffffff1610156102cb5760036000858363ffffffff168151811061028357610283610ba8565b6020908102919091018101516001600160a01b03168252810191909152604001600020546102b79063ffffffff1683610bd4565b9150806102c381610bfc565b915050610258565b5092915050565b60006102e1610112600161087c565b905090565b6102ef3361032f565b61030c57604051636381e58960e11b815260040161022890610b83565b6004805461ffff191661010060ff9384160260ff19161792909116919091179055565b60006001600160a01b03821630141561034a57506001919050565b6000546001600160a01b038381169116141561036857506001919050565b506000919050565b919050565b600080606080610385600161087c565b9150815167ffffffffffffffff8111156103a1576103a1610982565b6040519080825280602002602001820160405280156103ca578160200160208202803683370190505b50905060005b825181101561046157600360008483815181106103ef576103ef610ba8565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a900463ffffffff1682828151811061043a5761043a610ba8565b63ffffffff909216602092830291909101909101528061045981610c20565b9150506103d0565b5060045460ff8082169661010090920416945091925090565b6104833361032f565b6104a057604051636381e58960e11b815260040161022890610b83565b63ffffffff8116610537576001600160a01b03821632141561050557604051636381e58960e11b815260206004820152601c60248201527f596f752063616e206e6f742072656d6f766520796f757273656c6621000000006044820152606401610228565b6001600160a01b0382166000908152600360205260409020805463ffffffff191690556105336001836105c8565b5050565b610542600183610747565b15610576576001600160a01b0382166000908152600360205260409020805463ffffffff191663ffffffff83161790555050565b6001600160a01b0382166000908152600360205260409020805463ffffffff191663ffffffff8381169190911790915561053390600190849061076516565b60006105c2600183610747565b92915050565b6105d28282610747565b61062b57604051636381e58960e11b815260206004820152602360248201527f4c6962416464726573735365743a2076616c756520646f65736e27742065786960448201526239ba1760e91b6064820152608401610228565b6001600160a01b03811660009081526020839052604081205461065090600190610c3b565b6001848101549192506000916106669190610c3b565b9050600084600101828154811061067f5761067f610ba8565b6000918252602090912001546001860180546001600160a01b0390921692508291859081106106b0576106b0610ba8565b600091825260209091200180546001600160a01b0319166001600160a01b03929092169190911790556106e4836001610c52565b6001600160a01b03808316600090815260208890526040808220939093559086168152908120556001850180548061071e5761071e610c6a565b600082815260209020810160001990810180546001600160a01b03191690550190555050505050565b6001600160a01b031660009081526020919091526040902054151590565b6001600160a01b0381166107c657604051636381e58960e11b815260206004820152602160248201527f4c6962416464726573735365743a2076616c75652063616e27742062652030786044820152600360fc1b6064820152608401610228565b6107d08282610747565b1561083657604051636381e58960e11b815260206004820152602f60248201527f4c6962416464726573735365743a2076616c756520616c72656164792065786960448201526e39ba399034b7103a34329039b2ba1760891b6064820152608401610228565b6001808301805491820181556000818152602080822090930180546001600160a01b039095166001600160a01b0319909516851790559054928152929052604090912055565b600181015460609060009067ffffffffffffffff81111561089f5761089f610982565b6040519080825280602002602001820160405280156108c8578160200160208202803683370190505b50905060005b60018401548110156102cb578360010181815481106108ef576108ef610ba8565b9060005260206000200160009054906101000a90046001600160a01b031682828151811061091f5761091f610ba8565b6001600160a01b03909216602092830291909101909101528061094181610c20565b9150506108ce565b80356001600160a01b038116811461037057600080fd5b60006020828403121561097257600080fd5b61097b82610949565b9392505050565b63b95aa35560e01b600052604160045260246000fd5b600060208083850312156109ab57600080fd5b823567ffffffffffffffff808211156109c357600080fd5b818501915085601f8301126109d757600080fd5b8135818111156109e9576109e9610982565b8060051b604051601f19603f83011681018181108582111715610a0e57610a0e610982565b604052918252848201925083810185019188831115610a2c57600080fd5b938501935b82851015610a5157610a4285610949565b84529385019392850192610a31565b98975050505050505050565b803560ff8116811461037057600080fd5b60008060408385031215610a8157600080fd5b610a8a83610a5d565b9150610a9860208401610a5d565b90509250929050565b60006080820160ff87168352602060ff8716818501526080604085015281865180845260a086019150828801935060005b81811015610af75784516001600160a01b031683529383019391830191600101610ad2565b50508481036060860152855180825290820192508186019060005b81811015610b3457825163ffffffff1685529383019391830191600101610b12565b50929998505050505050505050565b60008060408385031215610b5657600080fd5b610b5f83610949565b9150602083013563ffffffff81168114610b7857600080fd5b809150509250929050565b6020808252600b908201526a4f6e6c79206f776e65722160a81b604082015260600190565b63b95aa35560e01b600052603260045260246000fd5b63b95aa35560e01b600052601160045260246000fd5b600063ffffffff808316818516808303821115610bf357610bf3610bbe565b01949350505050565b600063ffffffff80831681811415610c1657610c16610bbe565b6001019392505050565b6000600019821415610c3457610c34610bbe565b5060010190565b600082821015610c4d57610c4d610bbe565b500390565b60008219821115610c6557610c65610bbe565b500190565b63b95aa35560e01b600052603160045260246000fdfea26469706673582212209d87ae3059ef7c4e89a9812d140136cc3f2e1d099fad7b316cba2f4d2bc5b06a64736f6c634300080b003360806040523480156200001157600080fd5b5060405162002084380380620020848339810160408190526200003491620000e1565b600080546001600160a01b03191633179055604051829082906200005890620000b6565b6001600160a01b03928316815291166020820152604001604051809103906000f0801580156200008c573d6000803e3d6000fd5b50600180546001600160a01b0319166001600160a01b039290921691909117905550620001199050565b610a2e806200165683390190565b80516001600160a01b0381168114620000dc57600080fd5b919050565b60008060408385031215620000f557600080fd5b6200010083620000c4565b91506200011060208401620000c4565b90509250929050565b61152d80620001296000396000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c806389ebc3b711610097578063d47e52c211610066578063d47e52c2146102c8578063e59e7b34146102f3578063ec84604414610306578063ecbea6bc1461032c57600080fd5b806389ebc3b71461025b57806394d8f86f1461028257806398159bb514610295578063c0b560c2146102b557600080fd5b806362e7ec50116100d357806362e7ec50146101f757806368b739c9146102005780636e0376d41461022557806386840d091461024857600080fd5b806302ad26cc1461010557806305282c701461019657806328e91489146101ab5780633946f746146101d6575b600080fd5b6101586101133660046110cf565b600360208190526000918252604090912080546001820154600283015492909301546001600160a01b03918216939182169260ff600160a01b90930483169290911685565b604080516001600160a01b03968716815295909416602086015260ff92831693850193909352606084015216608082015260a0015b60405180910390f35b6101a96101a43660046110ff565b61033f565b005b6000546101be906001600160a01b031681565b6040516001600160a01b03909116815260200161018d565b6101e96101e4366004611133565b610390565b60405190815260200161018d565b6101e960025481565b61021361020e366004611180565b6105d4565b60405160ff909116815260200161018d565b6102386102333660046110ff565b610928565b604051901515815260200161018d565b6101e96102563660046111c3565b61096e565b6102136102693660046110cf565b6000908152600360208190526040909120015460ff1690565b6101a96102903660046111f8565b61099c565b6102a86102a336600461121b565b610ab1565b60405161018d9190611281565b6001546101be906001600160a01b031681565b6101e96102d63660046111c3565b600460209081526000928352604080842090915290825290205481565b6102136103013660046110cf565b610db2565b6103196103143660046110cf565b610e5f565b60405161018d9796959493929190611359565b6101a961033a3660046110ff565b610f6d565b61034833610928565b61036e57604051636381e58960e11b8152600401610365906113bd565b60405180910390fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b600061039b33610928565b6103b857604051636381e58960e11b8152600401610365906113bd565b60ff80851660009081526004602090815260408083206001600160a01b038816845282528083205480845260039283905292200154909116600114156104035761040181610db2565b505b6000818152600360208190526040909120015460ff166001141561046a57604051636381e58960e11b815260206004820152601860248201527f43757272656e742070726f706f73616c206e6f7420656e6400000000000000006044820152606401610365565b6002805490600061047a836113f8565b91905055506000600254905060608060006040518060e00160405280896001600160a01b031681526020018b6001600160a01b031681526020018a60ff16815260200188436104c99190611413565b815260016020808301829052604080840188905260609384018790526000898152600380845290829020865181546001600160a01b0319166001600160a01b0391821617825587850151958201805494890151969091166001600160a81b031990941693909317600160a01b60ff96871602179092559385015160028201556080850151938101805460ff1916949093169390931790915560a08301518051939450849361057d9260048501920190611055565b5060c08201518051610599916005840191602090910190611055565b50505060ff891660009081526004602090815260408083206001600160a01b038c168452909152902084905550919350505050949350505050565b60006105df33610928565b6105fc57604051636381e58960e11b8152600401610365906113bd565b60008481526003602081905260409091200154849060ff1661065657604051636381e58960e11b8152602060048201526012602482015271141c9bdc1bdcd85b081b9bdd08195e1a5cdd60721b6044820152606401610365565b60008581526003602081905260409091200154859060ff166001146106be57604051636381e58960e11b815260206004820152601760248201527f50726f706f73616c206973206e6f7420766f7461626c650000000000000000006044820152606401610365565b600086815260036020818152604092839020835160e08101855281546001600160a01b03908116825260018301549081168285015260ff600160a01b9091048116828701526002830154606083015293820154909316608084015260048101805485518185028101850190965280865291946107e79493869360a08601939183018282801561077657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610758575b50505050508152602001600582018054806020026020016040519081016040528092919081815260200182805480156107d857602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116107ba575b50505050508152505086610fb5565b1561082557604051636381e58960e11b815260206004820152600d60248201526c105b1c9958591e481d9bdd1959609a1b6044820152606401610365565b851561086057600481018054600181018255600091825260209091200180546001600160a01b0319166001600160a01b038716179055610891565b600581018054600181018255600091825260209091200180546001600160a01b0319166001600160a01b0387161790555b6001546040516308e2b09560e41b81526000916001600160a01b031690638e2b0950906108c990600480870191600588019101611469565b602060405180830381865afa1580156108e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061090a9190611497565b600392909201805460ff191660ff8416179055509695505050505050565b60006001600160a01b03821630141561094357506001919050565b6000546001600160a01b038381169116141561096157506001919050565b506000919050565b919050565b60ff821660009081526004602090815260408083206001600160a01b03851684529091529020545b92915050565b6109a533610928565b6109c257604051636381e58960e11b8152600401610365906113bd565b60008281526003602052604090206109d983610db2565b60ff16600114610a3f57604051636381e58960e11b815260206004820152602a60248201527f4f6e6c79206e65776c7920637265617465642070726f706f73616c2063616e206044820152691899481c995d9bdad95960b21b6064820152608401610365565b60018101546001600160a01b03838116911614610a9f57604051636381e58960e11b815260206004820152601860248201527f4f6e6c792070726f706f7365722063616e207265766f6b6500000000000000006044820152606401610365565b600301805460ff191660041790555050565b6060600254831115610b1557604051636381e58960e11b815260206004820152602660248201527f2766726f6d272069732067726561746572207468616e202770726f706f73616c604482015265436f756e742760d01b6064820152608401610365565b81831115610b6657604051636381e58960e11b815260206004820152601b60248201527f2766726f6d272069732067726561746572207468616e2027746f2700000000006044820152606401610365565b600254821115610b765760025491505b6001831015610b8457600192505b6000610b9084846114b4565b610b9b906001611413565b67ffffffffffffffff811115610bb357610bb36114cb565b604051908082528060200260200182016040528015610c3c57816020015b610c296040518060e0016040528060006001600160a01b0316815260200160006001600160a01b03168152602001600060ff16815260200160008152602001600060ff16815260200160608152602001606081525090565b815260200190600190039081610bd15790505b5090506000845b848111610da857600081815260036020818152604092839020835160e08101855281546001600160a01b03908116825260018301549081168285015260ff600160a01b909104811682870152600283015460608301529382015490931660808401526004810180548551818502810185019096528086529194859360a086019391929190830182828015610d0057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610ce2575b5050505050815260200160058201805480602002602001604051908101604052809291908181526020018280548015610d6257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610d44575b505050505081525050848480610d77906113f8565b955081518110610d8957610d896114e1565b6020026020010181905250508080610da0906113f8565b915050610c43565b5090949350505050565b600081815260036020819052604082200154829060ff16610e0b57604051636381e58960e11b8152602060048201526012602482015271141c9bdc1bdcd85b081b9bdd08195e1a5cdd60721b6044820152606401610365565b60008381526003602081905260409091209081015460ff1660011415610e4f578060020154431115610e4f57600301805460ff191660059081179091559150610e59565b6003015460ff1691505b50919050565b600081815260036020818152604092839020805460018201546002830154948301546004840180548851818802810188019099528089526001600160a01b039485169894841697600160a01b90940460ff908116979496931694606094859493919290830182828015610efb57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610edd575b5050505050925080600501805480602002602001604051908101604052809291908181526020018280548015610f5a57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610f3c575b5050505050915050919395979092949650565b610f7633610928565b610f9357604051636381e58960e11b8152600401610365906113bd565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000610fc58360a0015183610fef565b80610fd95750610fd98360c0015183610fef565b15610fe657506001610996565b50600092915050565b6000805b835181101561104b5783818151811061100e5761100e6114e1565b60200260200101516001600160a01b0316836001600160a01b03161415611039576001915050610996565b80611043816113f8565b915050610ff3565b5060009392505050565b8280548282559060005260206000209081019282156110aa579160200282015b828111156110aa57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190611075565b506110b69291506110ba565b5090565b5b808211156110b657600081556001016110bb565b6000602082840312156110e157600080fd5b5035919050565b80356001600160a01b038116811461096957600080fd5b60006020828403121561111157600080fd5b61111a826110e8565b9392505050565b60ff8116811461113057600080fd5b50565b6000806000806080858703121561114957600080fd5b611152856110e8565b9350602085013561116281611121565b9250611170604086016110e8565b9396929550929360600135925050565b60008060006060848603121561119557600080fd5b83359250602084013580151581146111ac57600080fd5b91506111ba604085016110e8565b90509250925092565b600080604083850312156111d657600080fd5b82356111e181611121565b91506111ef602084016110e8565b90509250929050565b6000806040838503121561120b57600080fd5b823591506111ef602084016110e8565b6000806040838503121561122e57600080fd5b50508035926020909101359150565b600081518084526020808501945080840160005b838110156112765781516001600160a01b031687529582019590820190600101611251565b509495945050505050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b8381101561134b57888303603f19018552815180516001600160a01b0390811685528882015116888501528681015160ff16878501526060808201519085015260808082015160e091906113008288018260ff169052565b505060a08083015182828801526113198388018261123d565b9250505060c08083015192508582038187015250611337818361123d565b9689019694505050908601906001016112a8565b509098975050505050505050565b6001600160a01b0388811682528716602082015260ff8681166040830152606082018690528416608082015260e060a0820181905260009061139d9083018561123d565b82810360c08401526113af818561123d565b9a9950505050505050505050565b6020808252600b908201526a4f6e6c79206f776e65722160a81b604082015260600190565b63b95aa35560e01b600052601160045260246000fd5b600060001982141561140c5761140c6113e2565b5060010190565b60008219821115611426576114266113e2565b500190565b6000815480845260208085019450836000528060002060005b838110156112765781546001600160a01b031687529582019560019182019101611444565b60408152600061147c604083018561142b565b828103602084015261148e818561142b565b95945050505050565b6000602082840312156114a957600080fd5b815161111a81611121565b6000828210156114c6576114c66113e2565b500390565b63b95aa35560e01b600052604160045260246000fd5b63b95aa35560e01b600052603260045260246000fdfea26469706673582212205bbf6564dd81edaf252007f48f4aa3987af9ad8d4f92c6308579d2c64edc7f1664736f6c634300080b0033608060405234801561001057600080fd5b50604051610a2e380380610a2e83398101604081905261002f91610200565b600080546001600160a01b03191633179055818161004c8261013d565b600180546001600160a01b0319166001600160a01b038316908117825560408051631befccb160e11b815290516337df9962916004808201926020929091908290030181865afa1580156100a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100c89190610233565b63ffffffff16101561013457604051636381e58960e11b815260206004820152602960248201527f636f6d6d6974746565206973206572726f722c20706c6561736520636865636b60448201526820616464726573732160b81b60648201526084015b60405180910390fd5b50505050610260565b610146336101a3565b61018157604051636381e58960e11b815260206004820152600b60248201526a4f6e6c79206f776e65722160a81b604482015260640161012b565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b60006001600160a01b0382163014156101be57506001919050565b6000546001600160a01b03838116911614156101dc57506001919050565b506000919050565b919050565b80516001600160a01b03811681146101e457600080fd5b6000806040838503121561021357600080fd5b61021c836101e9565b915061022a602084016101e9565b90509250929050565b60006020828403121561024557600080fd5b815163ffffffff8116811461025957600080fd5b9392505050565b6107bf8061026f6000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c806305282c701461006757806328e914891461007c5780636e0376d4146100ac5780637bc85516146100cf5780638e2b0950146100e25780639a93008c14610107575b600080fd5b61007a6100753660046104c5565b61011a565b005b60005461008f906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100bf6100ba3660046104c5565b610184565b60405190151581526020016100a3565b60015461008f906001600160a01b031681565b6100f56100f036600461059f565b6101ca565b60405160ff90911681526020016100a3565b6100f5610115366004610627565b61043c565b61012333610184565b61016257604051636381e58960e11b815260206004820152600b60248201526a4f6e6c79206f776e65722160a81b604482015260640160405180910390fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b60006001600160a01b03821630141561019f57506001919050565b6000546001600160a01b03838116911614156101bd57506001919050565b506000919050565b919050565b6001546040516323bdace160e01b815260009182916001600160a01b03909116906323bdace1906101ff908790600401610698565b602060405180830381865afa15801561021c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061024091906106e5565b6001546040516323bdace160e01b81529192506000916001600160a01b03909116906323bdace190610276908790600401610698565b602060405180830381865afa158015610293573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102b791906106e5565b6102c19083610718565b90506000600160009054906101000a90046001600160a01b03166001600160a01b03166337df99626040518163ffffffff1660e01b8152600401602060405180830381865afa158015610318573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061033c91906106e5565b9050610432838383600160009054906101000a90046001600160a01b03166001600160a01b0316630e878ed06040518163ffffffff1660e01b8152600401602060405180830381865afa158015610397573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103bb9190610740565b600160009054906101000a90046001600160a01b03166001600160a01b03166307f449996040518163ffffffff1660e01b8152600401602060405180830381865afa15801561040e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101159190610740565b9695505050505050565b600061044b60ff84168561075d565b63ffffffff1661045c86606461075d565b63ffffffff161015610470575060016104a5565b61047d8560ff841661075d565b63ffffffff1661048e87606461075d565b63ffffffff16106104a1575060026104a5565b5060035b95945050505050565b80356001600160a01b03811681146101c557600080fd5b6000602082840312156104d757600080fd5b6104e0826104ae565b9392505050565b63b95aa35560e01b600052604160045260246000fd5b600082601f83011261050e57600080fd5b8135602067ffffffffffffffff8083111561052b5761052b6104e7565b8260051b604051601f19603f83011681018181108482111715610550576105506104e7565b60405293845285810183019383810192508785111561056e57600080fd5b83870191505b8482101561059457610585826104ae565b83529183019190830190610574565b979650505050505050565b600080604083850312156105b257600080fd5b823567ffffffffffffffff808211156105ca57600080fd5b6105d6868387016104fd565b935060208501359150808211156105ec57600080fd5b506105f9858286016104fd565b9150509250929050565b63ffffffff8116811461061557600080fd5b50565b60ff8116811461061557600080fd5b600080600080600060a0868803121561063f57600080fd5b853561064a81610603565b9450602086013561065a81610603565b9350604086013561066a81610603565b9250606086013561067a81610618565b9150608086013561068a81610618565b809150509295509295909350565b6020808252825182820181905260009190848201906040850190845b818110156106d95783516001600160a01b0316835292840192918401916001016106b4565b50909695505050505050565b6000602082840312156106f757600080fd5b81516104e081610603565b63b95aa35560e01b600052601160045260246000fd5b600063ffffffff80831681851680830382111561073757610737610702565b01949350505050565b60006020828403121561075257600080fd5b81516104e081610618565b600063ffffffff8083168185168183048111821515161561078057610780610702565b0294935050505056fea2646970667358221220fabea83a81929920d3c210ab7da2ccedb38f40dcd6c8c80fc0e4c020d8ff182164736f6c634300080b0033"; +// clang-format on + +class AuthInitializer +{ +public: + static void init(protocol::BlockNumber _number, + const std::shared_ptr& _protocol, + const std::shared_ptr& _nodeConfig, const protocol::Block::Ptr& block) + { + // hex bin code to bytes + bytes code; + boost::algorithm::unhex( + _nodeConfig->smCryptoType() ? committeeSmBin : committeeBin, std::back_inserter(code)); + + + // constructor (address[] initGovernors, = [authAdminAddress] + // uint32[] memory weights, = [1] + // uint8 participatesRate, = 0 + // uint8 winRate) = 0 + auto authAdmin = Address(_nodeConfig->authAdminAddress()); + std::vector
initGovernors({authAdmin}); + std::vector weights({bcos::codec::toString32(h256(1))}); + INITIALIZER_LOG(INFO) << LOG_BADGE("AuthInitializer") + << LOG_KV("authAdminAddress", _nodeConfig->authAdminAddress()); + + // bytes code + abi encode constructor params + codec::abi::ContractABICodec abi(_protocol->cryptoSuite()->hashImpl()); + bytes input = code + abi.abiIn("", initGovernors, weights, codec::toString32(h256(0)), + codec::toString32(h256(0))); + + auto tx = _protocol->blockFactory()->transactionFactory()->createTransaction(3, + precompiled::AUTH_COMMITTEE_ADDRESS, input, u256(_number), 500, _nodeConfig->chainId(), + _nodeConfig->groupId(), utcTime()); + tx->forceSender(authAdmin.asBytes()); + block->appendTransaction(tx); + } +}; +} // namespace bcos::initializer \ No newline at end of file diff --git "a/BFPL\345\243\271/libinitializer/BfsInitializer.h" "b/BFPL\345\243\271/libinitializer/BfsInitializer.h" new file mode 100644 index 00000000..79cab89c --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/BfsInitializer.h" @@ -0,0 +1,50 @@ +/** + * 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 BfsInitializer.h + * @author: kyonGuo + * @date 2022/10/20 + */ + +#pragma once +#include "Common.h" +#include +#include +#include + +using namespace bcos; +using namespace bcos::tool; +using namespace bcos::initializer; +namespace bcos::initializer +{ +class BfsInitializer +{ +public: + BfsInitializer() = delete; + static void init(protocol::BlockNumber _number, + const std::shared_ptr& _protocol, + const std::shared_ptr& _nodeConfig, const protocol::Block::Ptr& _block) + { + bcos::CodecWrapper codecWrapper( + _protocol->cryptoSuite()->hashImpl(), _nodeConfig->isWasm()); + bytes input = codecWrapper.encodeWithSig("initBfs()"); + + auto transaction = _protocol->blockFactory()->transactionFactory()->createTransaction(3, + _nodeConfig->isWasm() ? precompiled::BFS_NAME : precompiled::BFS_ADDRESS, input, + u256(_number), 500, _nodeConfig->chainId(), _nodeConfig->groupId(), utcTime()); + _block->appendTransaction(std::move(transaction)); + } +}; +} // namespace bcos::initializer diff --git "a/BFPL\345\243\271/libinitializer/CMakeLists.txt" "b/BFPL\345\243\271/libinitializer/CMakeLists.txt" new file mode 100644 index 00000000..22a37149 --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/CMakeLists.txt" @@ -0,0 +1,36 @@ +file(GLOB SRC_LIST "*.cpp") + +find_package(Boost REQUIRED program_options) +find_package(jsoncpp CONFIG REQUIRED) + +add_library(${COMMAND_HELPER_LIB} CommandHelper.cpp) +target_link_libraries(${COMMAND_HELPER_LIB} PUBLIC bcos-framework Boost::program_options) + +add_library(${PROTOCOL_INIT_LIB} ProtocolInitializer.cpp) +target_link_libraries(${PROTOCOL_INIT_LIB} PUBLIC + ${CRYPTO_TARGET} ${TOOL_TARGET} ${TARS_PROTOCOL_TARGET} ${SECURITY_TARGET}) + +add_library(${FRONTSERVICE_INIT_LIB} FrontServiceInitializer.cpp) +target_link_libraries(${FRONTSERVICE_INIT_LIB} PUBLIC ${PROTOCOL_INIT_LIB} ${TOOL_TARGET} ${FRONT_TARGET}) + +add_library(${PBFT_INIT_LIB} PBFTInitializer.cpp ProPBFTInitializer.cpp) +target_link_libraries(${PBFT_INIT_LIB} PUBLIC ${PROTOCOL_INIT_LIB} ${LEDGER_TARGET} ${TOOL_TARGET} ${SEALER_TARGET} ${PBFT_TARGET} ${SYNC_TARGET}) +if(WITH_TIKV) + target_link_libraries(${PBFT_INIT_LIB} PUBLIC ${LEADER_ELECTION_TARGET}) +endif() + +add_library(${TXPOOL_INIT_LIB} TxPoolInitializer.cpp) +target_link_libraries(${TXPOOL_INIT_LIB} PUBLIC ${PROTOCOL_INIT_LIB} ${TOOL_TARGET} ${TXPOOL_TARGET}) + +add_library(${INIT_LIB} Initializer.cpp LightNodeInitializer.h) +list(APPEND INIT_LIB_DEPENDS ${PROTOCOL_INIT_LIB} ${FRONTSERVICE_INIT_LIB} ${TXPOOL_INIT_LIB} ${SCHEDULER_TARGET} ${STORAGE_TARGET} ${EXECUTOR_TARGET}) +if(WITH_TIKV) + list(APPEND INIT_LIB_DEPENDS ${LEADER_ELECTION_TARGET}) +endif() +if(WITH_LIGHTNODE) + add_library(lightnodeinit LightNodeInitializer.cpp LightNodeInitializer.h) + target_link_libraries(lightnodeinit PUBLIC bcos-lightnode bcos-concepts ${TARS_PROTOCOL_TARGET}) + list(APPEND INIT_LIB_DEPENDS lightnodeinit bcos-concepts) +endif() +target_link_libraries(${INIT_LIB} PUBLIC ${INIT_LIB_DEPENDS}) +add_dependencies(${INIT_LIB} BuildInfo.h) diff --git "a/BFPL\345\243\271/libinitializer/CommandHelper.cpp" "b/BFPL\345\243\271/libinitializer/CommandHelper.cpp" new file mode 100644 index 00000000..cdddad6b --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/CommandHelper.cpp" @@ -0,0 +1,152 @@ +/** + * Copyright (C) 2021 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 CommandHelper.cpp + * @author: yujiechen + * @date 2021-06-10 + */ +#include "CommandHelper.h" +#include "Common.h" +#include +#include +#include + +void bcos::initializer::printVersion() +{ + std::cout << "FISCO BCOS Version : " << FISCO_BCOS_PROJECT_VERSION << std::endl; + std::cout << "Build Time : " << FISCO_BCOS_BUILD_TIME << std::endl; + std::cout << "Build Type : " << FISCO_BCOS_BUILD_PLATFORM << "/" + << FISCO_BCOS_BUILD_TYPE << std::endl; + std::cout << "Git Branch : " << FISCO_BCOS_BUILD_BRANCH << std::endl; + std::cout << "Git Commit : " << FISCO_BCOS_COMMIT_HASH << std::endl; +} + +void bcos::initializer::showNodeVersionMetric() +{ + INITIALIZER_LOG(INFO) << METRIC << LOG_KV("binaryVersion", FISCO_BCOS_PROJECT_VERSION) + << LOG_KV("buildTime", FISCO_BCOS_BUILD_TIME) + << LOG_KV("buildType", FISCO_BCOS_BUILD_TYPE) + << LOG_KV("platform", FISCO_BCOS_BUILD_PLATFORM) + << LOG_KV("gitBranch", FISCO_BCOS_BUILD_BRANCH) + << LOG_KV("commitHash", FISCO_BCOS_COMMIT_HASH); +} + +void bcos::initializer::initCommandLine(int argc, char* argv[]) +{ + boost::program_options::options_description main_options("Usage of FISCO BCOS"); + main_options.add_options()("help,h", "print help information")( + "version,v", "version of FISCO BCOS"); + boost::program_options::variables_map vm; + try + { + boost::program_options::store( + boost::program_options::parse_command_line(argc, argv, main_options), vm); + } + catch (...) + { + printVersion(); + } + /// help information + if (vm.count("help") || vm.count("h")) + { + std::cout << main_options << std::endl; + exit(0); + } + /// version information + if (vm.count("version") || vm.count("v")) + { + printVersion(); + exit(0); + } +} + +bcos::initializer::Params bcos::initializer::initAirNodeCommandLine( + int argc, const char* argv[], bool _autoSendTx) +{ + boost::program_options::options_description main_options("Usage of FISCO-BCOS"); + main_options.add_options()("help,h", "print help information")( + "version,v", "version of FISCO-BCOS")("config,c", + boost::program_options::value()->default_value("./config.ini"), + "config file path, eg. config.ini")("genesis,g", + boost::program_options::value()->default_value("./config.genesis"), + "genesis config file path, eg. genesis.ini"); + + if (_autoSendTx) + { + main_options.add_options()( + "txSpeed,t", boost::program_options::value(), "transaction generate speed"); + } + boost::program_options::variables_map vm; + try + { + boost::program_options::store( + boost::program_options::parse_command_line(argc, argv, main_options), vm); + } + catch (...) + { + std::cout << "invalid parameters" << std::endl; + std::cout << main_options << std::endl; + exit(0); + } + /// help information + if (vm.count("help") || vm.count("h")) + { + std::cout << main_options << std::endl; + exit(0); + } + /// version information + if (vm.count("version") || vm.count("v")) + { + bcos::initializer::printVersion(); + exit(0); + } + std::string configPath("./config.ini"); + if (vm.count("config")) + { + configPath = vm["config"].as(); + } + if (vm.count("c")) + { + configPath = vm["c"].as(); + } + std::string genesisFilePath("./config.genesis"); + if (vm.count("genesis")) + { + genesisFilePath = vm["genesis"].as(); + } + if (vm.count("g")) + { + genesisFilePath = vm["g"].as(); + } + if (!boost::filesystem::exists(configPath)) + { + std::cout << "config \'" << configPath << "\' not found!"; + exit(0); + } + if (!boost::filesystem::exists(genesisFilePath)) + { + std::cout << "genesis config \'" << genesisFilePath << "\' not found!"; + exit(0); + } + float txSpeed = 10; + if (_autoSendTx) + { + if (vm.count("txSpeed") || vm.count("t")) + { + txSpeed = vm["txSpeed"].as(); + } + } + return bcos::initializer::Params{configPath, genesisFilePath, txSpeed}; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/libinitializer/CommandHelper.h" "b/BFPL\345\243\271/libinitializer/CommandHelper.h" new file mode 100644 index 00000000..68690e9d --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/CommandHelper.h" @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2021 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 CommandHelper.h + * @author: yujiechen + * @date 2021-06-10 + */ +#pragma once +#include +#include +namespace bcos +{ +namespace initializer +{ +void printVersion(); +void showNodeVersionMetric(); +void initCommandLine(int argc, char* argv[]); + +struct Params +{ + std::string configFilePath; + std::string genesisFilePath; + float txSpeed; +}; +Params initAirNodeCommandLine(int argc, const char* argv[], bool _autoSendTx); +} // namespace initializer +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/libinitializer/Common.h" "b/BFPL\345\243\271/libinitializer/Common.h" new file mode 100644 index 00000000..dbd3f605 --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/Common.h" @@ -0,0 +1,91 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Common for libinitializer + * @file Common.h + * @author: yujiechen + * @date 2021-06-10 + */ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define INITIALIZER_LOG(LEVEL) BCOS_LOG(LEVEL) << "[INITIALIZER]" +namespace bcos +{ +namespace initializer +{ +inline std::shared_ptr loadPrivateKey(std::string const& _keyPath, + unsigned _hexedPrivateKeySize, bcos::security::DataEncryptInterface::Ptr _certEncryptionHandler) +{ + auto content = readContents(boost::filesystem::path(_keyPath)); + auto keyContent = content; + if (_certEncryptionHandler) + { + keyContent = _certEncryptionHandler->decryptContents(content); + } + if (keyContent->empty()) + { + return nullptr; + } + std::shared_ptr ecKey; + try + { + INITIALIZER_LOG(INFO) << LOG_BADGE("SecureInitializer") << LOG_DESC("loading privateKey"); + std::shared_ptr bioMem(BIO_new(BIO_s_mem()), [&](BIO* p) { BIO_free(p); }); + BIO_write(bioMem.get(), keyContent->data(), keyContent->size()); + + std::shared_ptr evpPKey(PEM_read_bio_PrivateKey(bioMem.get(), NULL, NULL, NULL), + [](EVP_PKEY* p) { EVP_PKEY_free(p); }); + if (!evpPKey) + { + return nullptr; + } + ecKey.reset(EVP_PKEY_get1_EC_KEY(evpPKey.get()), [](EC_KEY* p) { EC_KEY_free(p); }); + } + catch (bcos::Exception& e) + { + INITIALIZER_LOG(ERROR) << LOG_BADGE("SecureInitializer") + << LOG_DESC("parse privateKey failed") + << LOG_KV("EINFO", boost::diagnostic_information(e)); + BOOST_THROW_EXCEPTION(bcos::tool::InvalidConfig() + << errinfo_comment("SecureInitializer: parse privateKey failed")); + } + std::shared_ptr ecPrivateKey( + EC_KEY_get0_private_key(ecKey.get()), [](const BIGNUM*) {}); + + std::shared_ptr privateKeyData( + BN_bn2hex(ecPrivateKey.get()), [](char* p) { OPENSSL_free(p); }); + std::string keyHex(privateKeyData.get()); + if (keyHex.size() >= _hexedPrivateKeySize) + { + return fromHexString(keyHex); + } + for (size_t i = keyHex.size(); i < _hexedPrivateKeySize; i++) + { + keyHex = '0' + keyHex; + } + return fromHexString(keyHex); +} +} // namespace initializer +} // namespace bcos diff --git "a/BFPL\345\243\271/libinitializer/ExecutorInitializer.h" "b/BFPL\345\243\271/libinitializer/ExecutorInitializer.h" new file mode 100644 index 00000000..68e50b90 --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/ExecutorInitializer.h" @@ -0,0 +1,24 @@ +#pragma once + +#include "bcos-framework/storage/StorageInterface.h" +#include + +namespace bcos::initializer +{ +class ExecutorInitializer +{ +public: + // static bcos::executor::TransactionExecutorFactory::Ptr buildFactory( + // bcos::ledger::LedgerInterface::Ptr ledger, txpool::TxPoolInterface::Ptr txpool, + // storage::MergeableStorageInterface::Ptr cache, + // storage::TransactionalStorageInterface::Ptr storage, + // protocol::ExecutionMessageFactory::Ptr executionMessageFactory, + // bcos::crypto::Hash::Ptr hashImpl, bool isWasm, bool isAuthCheck, size_t keyPageSize = 0, + // std::string name = "executor") + // { + // return std::make_shared(ledger, txpool, + // cache, + // storage, executionMessageFactory, hashImpl, isWasm, isAuthCheck, keyPageSize, name); + // } +}; +} // namespace bcos::initializer \ No newline at end of file diff --git "a/BFPL\345\243\271/libinitializer/FrontServiceInitializer.cpp" "b/BFPL\345\243\271/libinitializer/FrontServiceInitializer.cpp" new file mode 100644 index 00000000..0c758886 --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/FrontServiceInitializer.cpp" @@ -0,0 +1,190 @@ +/** + * Copyright (C) 2021 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. + * + * @brief initializer for the the front module + * @file FrontServiceInitializer.cpp + * @author: yujiechen + * @date 2021-06-10 + */ +#include "FrontServiceInitializer.h" +#include "bcos-framework/protocol/Protocol.h" +#include "libinitializer/ProtocolInitializer.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::initializer; +using namespace bcos::front; + +FrontServiceInitializer::FrontServiceInitializer(bcos::tool::NodeConfig::Ptr _nodeConfig, + bcos::initializer::ProtocolInitializer::Ptr _protocolInitializer, + bcos::gateway::GatewayInterface::Ptr _gateWay) + : m_nodeConfig(_nodeConfig), m_protocolInitializer(_protocolInitializer), m_gateWay(_gateWay) +{ + auto frontServiceFactory = std::make_shared(); + frontServiceFactory->setGatewayInterface(m_gateWay); + + // make the threadpool configurable + auto threadPool = + std::make_shared("frontService", std::thread::hardware_concurrency()); + frontServiceFactory->setThreadPool(threadPool); + m_front = frontServiceFactory->buildFrontService( + m_nodeConfig->groupId(), m_protocolInitializer->keyPair()->publicKey()); +} + +void FrontServiceInitializer::start() +{ + if (m_running) + { + FRONTSERVICE_LOG(INFO) << LOG_DESC("The front service has already been started"); + return; + } + FRONTSERVICE_LOG(INFO) << LOG_DESC("Start the front service"); + m_running = true; + m_front->start(); +} +void FrontServiceInitializer::stop() +{ + if (!m_running) + { + FRONTSERVICE_LOG(INFO) << LOG_DESC("The front service has already been stopped"); + return; + } + FRONTSERVICE_LOG(INFO) << LOG_DESC("Stop the front service"); + m_running = false; + m_front->stop(); +} + +void FrontServiceInitializer::init(bcos::consensus::ConsensusInterface::Ptr _pbft, + bcos::sync::BlockSyncInterface::Ptr _blockSync, bcos::txpool::TxPoolInterface::Ptr _txpool) +{ + initMsgHandlers(_pbft, _blockSync, _txpool); +} + + +void FrontServiceInitializer::initMsgHandlers(bcos::consensus::ConsensusInterface::Ptr _pbft, + bcos::sync::BlockSyncInterface::Ptr _blockSync, bcos::txpool::TxPoolInterface::Ptr _txpool) +{ + // register the message dispatcher handler to the frontService + // register the message dispatcher for PBFT module + m_front->registerModuleMessageDispatcher( + bcos::protocol::ModuleID::PBFT, [_pbft](bcos::crypto::NodeIDPtr _nodeID, + const std::string& _id, bcos::bytesConstRef _data) { + _pbft->asyncNotifyConsensusMessage( + nullptr, _id, _nodeID, _data, [](bcos::Error::Ptr _error) { + if (_error) + { + FRONTSERVICE_LOG(WARNING) + << LOG_DESC("registerModuleMessageDispatcher failed") + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + } + }); + }); + FRONTSERVICE_LOG(INFO) << LOG_DESC( + "registerModuleMessageDispatcher for the consensus module success"); + + // register the message dispatcher for the txsSync module + m_front->registerModuleMessageDispatcher( + bcos::protocol::ModuleID::TxsSync, [_txpool](bcos::crypto::NodeIDPtr _nodeID, + std::string const& _id, bcos::bytesConstRef _data) { + _txpool->asyncNotifyTxsSyncMessage( + nullptr, _id, _nodeID, _data, [_id](bcos::Error::Ptr _error) { + if (_error) + { + FRONTSERVICE_LOG(WARNING) << LOG_DESC("asyncNotifyTxsSyncMessage failed") + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + } + }); + }); + + // register the message dispatcher for the consensus txs sync module + m_front->registerModuleMessageDispatcher(bcos::protocol::ModuleID::ConsTxsSync, + [_txpool]( + bcos::crypto::NodeIDPtr _nodeID, std::string const& _id, bcos::bytesConstRef _data) { + _txpool->asyncNotifyTxsSyncMessage( + nullptr, _id, _nodeID, _data, [_id](bcos::Error::Ptr _error) { + if (_error) + { + FRONTSERVICE_LOG(WARNING) << LOG_DESC("asyncNotifyTxsSyncMessage failed") + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + } + }); + }); + FRONTSERVICE_LOG(INFO) << LOG_DESC( + "registerModuleMessageDispatcher for the txsSync module success"); + + // register the message dispatcher for the block sync module + m_front->registerModuleMessageDispatcher(bcos::protocol::ModuleID::BlockSync, + [_blockSync]( + bcos::crypto::NodeIDPtr _nodeID, std::string const& _id, bcos::bytesConstRef _data) { + _blockSync->asyncNotifyBlockSyncMessage( + nullptr, _id, _nodeID, _data, [_id, _nodeID](bcos::Error::Ptr _error) { + if (_error) + { + FRONTSERVICE_LOG(WARNING) + << LOG_DESC("asyncNotifyBlockSyncMessage failed") + << LOG_KV("peer", _nodeID->shortHex()) << LOG_KV("id", _id) + << LOG_KV("code", _error->errorCode()) + << LOG_KV("msg", _error->errorMessage()); + } + }); + }); + FRONTSERVICE_LOG(INFO) << LOG_DESC( + "registerModuleMessageDispatcher for the BlockSync module success"); + + // register the groupNodeInfo notification to the frontService + // Note: since txpool/blocksync/pbft are in the same module, they can share the same + // GroupNodeInfoNotification + m_front->registerGroupNodeInfoNotification(bcos::protocol::ModuleID::TxsSync, + [this, _txpool, _blockSync, _pbft](bcos::gateway::GroupNodeInfo::Ptr _groupNodeInfo, + bcos::front::ReceiveMsgFunc _receiveMsgCallback) { + auto const& nodeIDList = _groupNodeInfo->nodeIDList(); + bcos::crypto::NodeIDSet nodeIdSet; + for (auto const& nodeID : nodeIDList) + { + auto nodeIDPtr = keyFactory()->createKey(fromHex(nodeID)); + if (!nodeIDPtr) + { + continue; + } + nodeIdSet.insert(nodeIDPtr); + } + _txpool->notifyConnectedNodes(nodeIdSet, _receiveMsgCallback); + _blockSync->notifyConnectedNodes(nodeIdSet, _receiveMsgCallback); + _pbft->notifyConnectedNodes(nodeIdSet, _receiveMsgCallback); + FRONTSERVICE_LOG(DEBUG) + << LOG_DESC("notifyGroupNodeInfo") << LOG_KV("connectedNodeSize", nodeIdSet.size()); + }); + FRONTSERVICE_LOG(INFO) << LOG_DESC("registerGroupNodeInfoNotification success"); +} + + +bcos::crypto::KeyFactory::Ptr FrontServiceInitializer::keyFactory() +{ + return m_protocolInitializer->keyFactory(); +} +bcos::front::FrontServiceInterface::Ptr FrontServiceInitializer::front() +{ + return m_front; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/libinitializer/FrontServiceInitializer.h" "b/BFPL\345\243\271/libinitializer/FrontServiceInitializer.h" new file mode 100644 index 00000000..d863ccee --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/FrontServiceInitializer.h" @@ -0,0 +1,85 @@ +/** + * Copyright (C) 2021 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. + * + * @brief initializer for the the front module + * @file FrontServiceInitializer.h + * @author: yujiechen + * @date 2021-06-10 + */ +#pragma once +#include +#include +#include + +namespace bcos +{ +namespace consensus +{ +class ConsensusInterface; +} +namespace sync +{ +class BlockSyncInterface; +} +namespace txpool +{ +class TxPoolInterface; +} +namespace gateway +{ +class GatewayInterface; +} +namespace front +{ +class FrontService; +} + +namespace initializer +{ +class ProtocolInitializer; + +class FrontServiceInitializer +{ +public: + using Ptr = std::shared_ptr; + FrontServiceInitializer(bcos::tool::NodeConfig::Ptr _nodeConfig, + std::shared_ptr _protocolInitializer, + std::shared_ptr _gateWay); + virtual ~FrontServiceInitializer() { stop(); } + + virtual void init(std::shared_ptr _pbft, + std::shared_ptr _blockSync, + std::shared_ptr _txpool); + virtual void start(); + virtual void stop(); + + bcos::front::FrontServiceInterface::Ptr front(); + bcos::crypto::KeyFactory::Ptr keyFactory(); + +protected: + virtual void initMsgHandlers(std::shared_ptr _pbft, + std::shared_ptr _blockSync, + std::shared_ptr _txpool); + +private: + bcos::tool::NodeConfig::Ptr m_nodeConfig; + std::shared_ptr m_protocolInitializer; + std::shared_ptr m_gateWay; + + std::shared_ptr m_front; + std::atomic_bool m_running = {false}; +}; +} // namespace initializer +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/libinitializer/Initializer.cpp" "b/BFPL\345\243\271/libinitializer/Initializer.cpp" new file mode 100644 index 00000000..f0f5c0c3 --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/Initializer.cpp" @@ -0,0 +1,511 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Initializer for all the modules + * @file Initializer.cpp + * @author: yujiechen + * @date 2021-06-11 + + * @brief Initializer for all the modules + * @file Initializer.cpp + * @author: ancelmo + * @date 2021-10-23 + */ + +#include "Initializer.h" +#include "AuthInitializer.h" +#include "BfsInitializer.h" +#include "ExecutorInitializer.h" +#include "LedgerInitializer.h" +#include "SchedulerInitializer.h" +#include "StorageInitializer.h" +#include "bcos-crypto/hasher/OpenSSLHasher.h" +#include "bcos-executor/src/executor/SwitchExecutorManager.h" +#include "bcos-framework/storage/StorageInterface.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::tool; +using namespace bcos::protocol; +using namespace bcos::initializer; + +void Initializer::initAirNode(std::string const& _configFilePath, std::string const& _genesisFile, + bcos::gateway::GatewayInterface::Ptr _gateway, const std::string& _logPath) +{ + initConfig(_configFilePath, _genesisFile, "", true); + init(bcos::protocol::NodeArchitectureType::AIR, _configFilePath, _genesisFile, _gateway, true, + _logPath); +} +void Initializer::initMicroServiceNode(bcos::protocol::NodeArchitectureType _nodeArchType, + std::string const& _configFilePath, std::string const& _genesisFile, + std::string const& _privateKeyPath, const std::string& _logPath) +{ + initConfig(_configFilePath, _genesisFile, _privateKeyPath, false); + // get gateway client + auto keyFactory = std::make_shared(); + + auto gatewayServiceName = m_nodeConfig->gatewayServiceName(); + auto withoutTarsFramework = m_nodeConfig->withoutTarsFramework(); + + std::vector endPoints; + m_nodeConfig->getTarsClientProxyEndpoints(bcos::protocol::GATEWAY_NAME, endPoints); + + auto gatewayPrx = bcostars::createServantProxy( + withoutTarsFramework, gatewayServiceName, endPoints); + + auto gateWay = std::make_shared( + gatewayPrx, m_nodeConfig->gatewayServiceName(), keyFactory); + init(_nodeArchType, _configFilePath, _genesisFile, gateWay, false, _logPath); +} + +void Initializer::initConfig(std::string const& _configFilePath, std::string const& _genesisFile, + std::string const& _privateKeyPath, bool _airVersion) +{ + m_nodeConfig = std::make_shared(std::make_shared()); + m_nodeConfig->loadGenesisConfig(_genesisFile); + m_nodeConfig->loadConfig(_configFilePath); + + // init the protocol + m_protocolInitializer = std::make_shared(); + m_protocolInitializer->init(m_nodeConfig); + auto privateKeyPath = m_nodeConfig->privateKeyPath(); + if (!_airVersion) + { + privateKeyPath = _privateKeyPath; + } + m_protocolInitializer->loadKeyPair(privateKeyPath); + boost::property_tree::ptree pt; + boost::property_tree::read_ini(_configFilePath, pt); + m_nodeConfig->loadNodeServiceConfig( + m_protocolInitializer->keyPair()->publicKey()->hex(), pt, false); + if (!_airVersion) + { + // load the service config + m_nodeConfig->loadServiceConfig(pt); + } +} + +void Initializer::init(bcos::protocol::NodeArchitectureType _nodeArchType, + std::string const& _configFilePath, std::string const& _genesisFile, + bcos::gateway::GatewayInterface::Ptr _gateway, bool _airVersion, const std::string& _logPath) +{ + // build the front service + m_frontServiceInitializer = + std::make_shared(m_nodeConfig, m_protocolInitializer, _gateway); + + // build the storage + auto storagePath = m_nodeConfig->storagePath(); + // build and init the pbft related modules + auto consensusStoragePath = + m_nodeConfig->storagePath() + c_fileSeparator + c_consensusStorageDBName; + if (!_airVersion) + { + storagePath = tars::ServerConfig::BasePath + ".." + c_fileSeparator + + m_nodeConfig->groupId() + c_fileSeparator + m_nodeConfig->storagePath(); + consensusStoragePath = tars::ServerConfig::BasePath + ".." + c_fileSeparator + + m_nodeConfig->groupId() + c_fileSeparator + c_consensusStorageDBName; + } + INITIALIZER_LOG(INFO) << LOG_DESC("initNode") << LOG_KV("storagePath", storagePath) + << LOG_KV("storageType", m_nodeConfig->storageType()) + << LOG_KV("consensusStoragePath", consensusStoragePath); + bcos::storage::TransactionalStorageInterface::Ptr storage = nullptr; + bcos::storage::TransactionalStorageInterface::Ptr schedulerStorage = nullptr; + bcos::storage::TransactionalStorageInterface::Ptr consensusStorage = nullptr; + + + if (boost::iequals(m_nodeConfig->storageType(), "RocksDB")) + { + // m_protocolInitializer->dataEncryption() will return nullptr when storage_security = false + storage = StorageInitializer::build( + storagePath, m_protocolInitializer->dataEncryption(), m_nodeConfig->keyPageSize()); + schedulerStorage = storage; + consensusStorage = StorageInitializer::build( + consensusStoragePath, m_protocolInitializer->dataEncryption()); + } + else if (boost::iequals(m_nodeConfig->storageType(), "TiKV")) + { +#ifdef WITH_TIKV + storage = StorageInitializer::build(m_nodeConfig->pdAddrs(), _logPath, + m_nodeConfig->pdCaPath(), m_nodeConfig->pdCertPath(), m_nodeConfig->pdKeyPath()); + if (_nodeArchType == bcos::protocol::NodeArchitectureType::MAX) + { + schedulerStorage = storage; + consensusStorage = storage; + } + else + { // in AIR node, scheduler and executor in one process so need different storage + schedulerStorage = StorageInitializer::build(m_nodeConfig->pdAddrs(), _logPath, + m_nodeConfig->pdCaPath(), m_nodeConfig->pdCertPath(), m_nodeConfig->pdKeyPath()); + consensusStorage = StorageInitializer::build(m_nodeConfig->pdAddrs(), _logPath, + m_nodeConfig->pdCaPath(), m_nodeConfig->pdCertPath(), m_nodeConfig->pdKeyPath()); + } +#endif + } + else + { + throw std::runtime_error("storage type not support"); + } + + // build ledger + auto ledger = + LedgerInitializer::build(m_protocolInitializer->blockFactory(), storage, m_nodeConfig); + m_ledger = ledger; + + bcos::protocol::ExecutionMessageFactory::Ptr executionMessageFactory = nullptr; + bool preStoreTxs = true; + // Note: since tikv-storage store txs with transaction, batch writing is more efficient than + // writing one by one, disable preStoreTxs in max-node mode + if (_nodeArchType == bcos::protocol::NodeArchitectureType::MAX) + { + executionMessageFactory = + std::make_shared(); + preStoreTxs = false; + } + else + { + executionMessageFactory = std::make_shared(); + } + auto executorManager = std::make_shared( + m_nodeConfig->executorServiceName()); + + auto transactionSubmitResultFactory = + std::make_shared(); + + // init the txpool + m_txpoolInitializer = std::make_shared(m_nodeConfig, m_protocolInitializer, + m_frontServiceInitializer->front(), ledger, preStoreTxs); + + auto factory = SchedulerInitializer::buildFactory(executorManager, ledger, schedulerStorage, + executionMessageFactory, m_protocolInitializer->blockFactory(), + m_txpoolInitializer->txpool(), m_protocolInitializer->txResultFactory(), + m_protocolInitializer->cryptoSuite()->hashImpl(), m_nodeConfig->isAuthCheck(), + m_nodeConfig->isWasm(), m_nodeConfig->isSerialExecute()); + + int64_t schedulerSeq = 0; // In Max node, this seq will be update after consensus module switch + // to a leader during startup + m_scheduler = + std::make_shared(schedulerSeq, factory, executorManager); + + if (boost::iequals(m_nodeConfig->storageType(), "TiKV")) + { +#ifdef WITH_TIKV + std::weak_ptr schedulerWeakPtr = m_scheduler; + auto switchHandler = [scheduler = schedulerWeakPtr]() { + if (scheduler.lock()) + { + scheduler.lock()->triggerSwitch(); + } + }; + dynamic_pointer_cast(storage)->setSwitchHandler(switchHandler); + dynamic_pointer_cast(schedulerStorage) + ->setSwitchHandler(switchHandler); +#endif + } + + bcos::storage::CacheStorageFactory::Ptr cacheFactory = nullptr; + if (m_nodeConfig->enableLRUCacheStorage()) + { + cacheFactory = std::make_shared( + storage, m_nodeConfig->cacheSize()); + INITIALIZER_LOG(INFO) << "initNode: enableLRUCacheStorage, size: " + << m_nodeConfig->cacheSize(); + } + else + { + INITIALIZER_LOG(INFO) << LOG_DESC("initNode: disableLRUCacheStorage"); + } + + if (_nodeArchType == bcos::protocol::NodeArchitectureType::MAX) + { + INITIALIZER_LOG(INFO) << LOG_DESC("waiting for connect executor") + << LOG_KV("nodeArchType", _nodeArchType); + executorManager->start(); // will waiting for connecting some executors + + // init scheduler + dynamic_pointer_cast(m_scheduler)->initSchedulerIfNotExist(); + } + else + { + INITIALIZER_LOG(INFO) << LOG_DESC("create Executor") + << LOG_KV("nodeArchType", _nodeArchType); + + // Note: ensure that there has at least one executor before pbft/sync execute block + + std::string executorName = "executor-local"; + auto executorFactory = std::make_shared( + m_ledger, m_txpoolInitializer->txpool(), cacheFactory, storage, executionMessageFactory, + m_protocolInitializer->cryptoSuite()->hashImpl(), m_nodeConfig->isWasm(), + m_nodeConfig->isAuthCheck(), m_nodeConfig->keyPageSize(), executorName); + auto switchExecutorManager = + std::make_shared(executorFactory); + executorManager->addExecutor(executorName, switchExecutorManager); + m_switchExecutorManager = switchExecutorManager; + } + + // build node time synchronization tool + auto nodeTimeMaintenance = std::make_shared(); + + // build and init the pbft related modules + if (_nodeArchType == protocol::NodeArchitectureType::AIR) + { + m_pbftInitializer = std::make_shared(_nodeArchType, m_nodeConfig, + m_protocolInitializer, m_txpoolInitializer->txpool(), ledger, m_scheduler, + consensusStorage, m_frontServiceInitializer->front(), nodeTimeMaintenance); + auto nodeID = m_protocolInitializer->keyPair()->publicKey(); + auto frontService = m_frontServiceInitializer->front(); + auto groupID = m_nodeConfig->groupId(); + auto blockSync = + std::dynamic_pointer_cast(m_pbftInitializer->blockSync()); + + auto nodeProtocolInfo = g_BCOSConfig.protocolInfo(protocol::ProtocolModuleID::NodeService); + // registerNode when air node first start-up + _gateway->registerNode( + groupID, nodeID, blockSync->config()->nodeType(), frontService, nodeProtocolInfo); + INITIALIZER_LOG(INFO) << LOG_DESC("registerNode") << LOG_KV("group", groupID) + << LOG_KV("node", nodeID->hex()) + << LOG_KV("type", blockSync->config()->nodeType()); + // update the frontServiceInfo when nodeType changed + blockSync->config()->registerOnNodeTypeChanged( + [_gateway, groupID, nodeID, frontService, nodeProtocolInfo](protocol::NodeType _type) { + _gateway->registerNode(groupID, nodeID, _type, frontService, nodeProtocolInfo); + INITIALIZER_LOG(INFO) << LOG_DESC("registerNode") << LOG_KV("group", groupID) + << LOG_KV("node", nodeID->hex()) << LOG_KV("type", _type); + }); + } + else + { + m_pbftInitializer = std::make_shared(_nodeArchType, m_nodeConfig, + m_protocolInitializer, m_txpoolInitializer->txpool(), ledger, m_scheduler, + consensusStorage, m_frontServiceInitializer->front(), nodeTimeMaintenance); + } + if (_nodeArchType == bcos::protocol::NodeArchitectureType::MAX) + { + INITIALIZER_LOG(INFO) << LOG_DESC("Register switch handler in scheduler manager"); + // PBFT and scheduler are in the same process here, we just cast m_scheduler to + // SchedulerService + auto schedulerServer = + std::dynamic_pointer_cast(m_scheduler); + auto consensus = m_pbftInitializer->pbft(); + schedulerServer->registerOnSwitchTermHandler([consensus]( + bcos::protocol::BlockNumber blockNumber) { + INITIALIZER_LOG(DEBUG) + << LOG_BADGE("Switch") + << "Receive scheduler switch term notify of number " + std::to_string(blockNumber); + consensus->clearExceptionProposalState(blockNumber); + }); + } + // init the txpool + m_txpoolInitializer->init(m_pbftInitializer->sealer()); + + // Note: must init PBFT after txpool, in case of pbft calls txpool to verifyBlock before + // txpool init finished + m_pbftInitializer->init(); + + // init the frontService + m_frontServiceInitializer->init( + m_pbftInitializer->pbft(), m_pbftInitializer->blockSync(), m_txpoolInitializer->txpool()); + +#ifdef WITH_LIGHTNODE + bcos::storage::StorageImpl storageWrapper(storage); + + auto anyHasher = m_protocolInitializer->cryptoSuite()->hashImpl()->hasher(); + std::visit( + [&](auto& hasher) { + using Hasher = std::remove_cvref_t; + auto ledger = + std::make_shared>( + std::move(storageWrapper)); + + auto txpool = m_txpoolInitializer->txpool(); + auto transactionPool = + std::make_shared>( + m_protocolInitializer->cryptoSuite(), txpool); + auto scheduler = std::make_shared>>( + m_scheduler, m_protocolInitializer->cryptoSuite()); + + m_lightNodeInitializer = std::make_shared(); + m_lightNodeInitializer->initLedgerServer( + std::dynamic_pointer_cast( + m_frontServiceInitializer->front()), + ledger, transactionPool, scheduler); + }, + anyHasher); +#endif +} + +void Initializer::initNotificationHandlers(bcos::rpc::RPCInterface::Ptr _rpc) +{ + // init handlers + auto nodeName = m_nodeConfig->nodeName(); + auto groupID = m_nodeConfig->groupId(); + auto schedulerFactory = + dynamic_pointer_cast(m_scheduler)->getFactory(); + // notify blockNumber + schedulerFactory->setBlockNumberReceiver( + [_rpc, groupID, nodeName](bcos::protocol::BlockNumber number) { + INITIALIZER_LOG(INFO) << "Notify blocknumber: " << number; + // Note: the interface will notify blockNumber to all rpc nodes in pro/max mode + _rpc->asyncNotifyBlockNumber(groupID, nodeName, number, [](bcos::Error::Ptr) {}); + }); + // notify transactions + auto txpool = m_txpoolInitializer->txpool(); + schedulerFactory->setTransactionNotifier( + [txpool](bcos::protocol::BlockNumber _blockNumber, + bcos::protocol::TransactionSubmitResultsPtr _result, + std::function _callback) { + // only response to the requester + txpool->asyncNotifyBlockResult(_blockNumber, _result, _callback); + }); + m_pbftInitializer->initNotificationHandlers(_rpc); +} + +void Initializer::initSysContract() +{ + // check is it deploy first time + std::promise> getNumberPromise; + m_ledger->asyncGetBlockNumber([&](Error::Ptr _error, protocol::BlockNumber _number) { + getNumberPromise.set_value(std::make_tuple(std::move(_error), _number)); + }); + auto getNumberTuple = getNumberPromise.get_future().get(); + if (std::get<0>(getNumberTuple) != nullptr || + std::get<1>(getNumberTuple) > SYS_CONTRACT_DEPLOY_NUMBER) + { + return; + } + auto block = m_protocolInitializer->blockFactory()->createBlock(); + block->blockHeader()->setNumber(SYS_CONTRACT_DEPLOY_NUMBER); + block->blockHeader()->setVersion(m_nodeConfig->compatibilityVersion()); + + if (m_nodeConfig->compatibilityVersion() >= static_cast(BlockVersion::V3_1_VERSION)) + { + BfsInitializer::init( + SYS_CONTRACT_DEPLOY_NUMBER, m_protocolInitializer, m_nodeConfig, block); + } + + if (!m_nodeConfig->isWasm() && m_nodeConfig->isAuthCheck()) + { + // add auth deploy func here + AuthInitializer::init( + SYS_CONTRACT_DEPLOY_NUMBER, m_protocolInitializer, m_nodeConfig, block); + } + + + if (block->transactionsSize() > 0) + { + std::promise executedHeader; + m_scheduler->executeBlock(block, false, + [&](bcos::Error::Ptr&& _error, bcos::protocol::BlockHeader::Ptr&& _header, bool) { + if (_error) + { + BOOST_THROW_EXCEPTION( + BCOS_ERROR(-1, "SysInitializer: scheduler executeBlock error")); + } + INITIALIZER_LOG(INFO) + << LOG_BADGE("SysInitializer") << LOG_DESC("scheduler execute block success!") + << LOG_KV("blockHash", block->blockHeader()->hash().hex()); + executedHeader.set_value(std::move(_header)); + }); + auto header = executedHeader.get_future().get(); + + std::promise> committedConfig; + m_scheduler->commitBlock( + header, [&](Error::Ptr&& _error, bcos::ledger::LedgerConfig::Ptr&& _config) { + if (_error) + { + INITIALIZER_LOG(ERROR) << LOG_BADGE("SysInitializer") + << LOG_KV("errorMsg", _error->errorMessage()); + committedConfig.set_value(std::make_tuple(std::move(_error), nullptr)); + return; + } + committedConfig.set_value(std::make_tuple(nullptr, std::move(_config))); + }); + auto [error, newConfig] = committedConfig.get_future().get(); + if (error != nullptr && newConfig->blockNumber() != SYS_CONTRACT_DEPLOY_NUMBER) + { + INITIALIZER_LOG(ERROR) + << LOG_BADGE("SysInitializer") << LOG_DESC("Error in commitBlock") + << (error ? "errorMsg" + error->errorMessage() : "") + << LOG_KV("configNumber", newConfig->blockNumber()); + BOOST_THROW_EXCEPTION(BCOS_ERROR(-1, "SysInitializer commitBlock error")); + } + } +} + +void Initializer::start() +{ + if (m_txpoolInitializer) + { + m_txpoolInitializer->start(); + } + if (m_pbftInitializer) + { + m_pbftInitializer->start(); + } + + if (m_frontServiceInitializer) + { + m_frontServiceInitializer->start(); + } +} + +void Initializer::stop() +{ + try + { + if (m_frontServiceInitializer) + { + m_frontServiceInitializer->stop(); + } + if (m_pbftInitializer) + { + m_pbftInitializer->stop(); + } + if (m_txpoolInitializer) + { + m_txpoolInitializer->stop(); + } + if (m_scheduler) + { + m_scheduler->stop(); + } + } + catch (std::exception const& e) + { + std::cout << "stop bcos-node failed for " << boost::diagnostic_information(e); + exit(-1); + } +} diff --git "a/BFPL\345\243\271/libinitializer/Initializer.h" "b/BFPL\345\243\271/libinitializer/Initializer.h" new file mode 100644 index 00000000..8949e42b --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/Initializer.h" @@ -0,0 +1,105 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Initializer for all the modules + * @file Initializer.h + * @author: yujiechen + * @date 2021-06-11 + */ +#pragma once +#include "FrontServiceInitializer.h" +#include "PBFTInitializer.h" +#include "ProPBFTInitializer.h" +#include "ProtocolInitializer.h" +#include "TxPoolInitializer.h" +#include +#include +#include +#include +#ifdef WITH_LIGHTNODE +#include "LightNodeInitializer.h" +#endif + +namespace bcos +{ +namespace gateway +{ +class GatewayInterface; +} +namespace scheduler +{ +class SchedulerInterface; +} +namespace initializer +{ +class Initializer +{ +public: + using Ptr = std::shared_ptr; + Initializer() = default; + virtual ~Initializer() { stop(); } + + virtual void start(); + virtual void stop(); + + bcos::tool::NodeConfig::Ptr nodeConfig() { return m_nodeConfig; } + ProtocolInitializer::Ptr protocolInitializer() { return m_protocolInitializer; } + PBFTInitializer::Ptr pbftInitializer() { return m_pbftInitializer; } + TxPoolInitializer::Ptr txPoolInitializer() { return m_txpoolInitializer; } + + bcos::ledger::LedgerInterface::Ptr ledger() { return m_ledger; } + std::shared_ptr scheduler() { return m_scheduler; } + + FrontServiceInitializer::Ptr frontService() { return m_frontServiceInitializer; } + + void initAirNode(std::string const& _configFilePath, std::string const& _genesisFile, + std::shared_ptr _gateway, const std::string& _logPath); + void initMicroServiceNode(bcos::protocol::NodeArchitectureType _nodeArchType, + std::string const& _configFilePath, std::string const& _genesisFile, + std::string const& _privateKeyPath, const std::string& _logPath); + + virtual void initNotificationHandlers(bcos::rpc::RPCInterface::Ptr _rpc); + +public: + virtual void init(bcos::protocol::NodeArchitectureType _nodeArchType, + std::string const& _configFilePath, std::string const& _genesisFile, + std::shared_ptr _gateway, bool _airVersion, + const std::string& _logPath); + + virtual void initConfig(std::string const& _configFilePath, std::string const& _genesisFile, + std::string const& _privateKeyPath, bool _airVersion); + + /// NOTE: this should be last called + void initSysContract(); + +private: + bcos::tool::NodeConfig::Ptr m_nodeConfig; + ProtocolInitializer::Ptr m_protocolInitializer; + FrontServiceInitializer::Ptr m_frontServiceInitializer; + TxPoolInitializer::Ptr m_txpoolInitializer; + PBFTInitializer::Ptr m_pbftInitializer; +#ifdef WITH_LIGHTNODE + // Note: since LightNodeInitializer use weak_ptr of shared_from_this, this object must be exists + // for the whole life time + std::shared_ptr m_lightNodeInitializer; +#endif + bcos::ledger::LedgerInterface::Ptr m_ledger; + std::shared_ptr m_scheduler; + std::weak_ptr m_switchExecutorManager; + std::string const c_consensusStorageDBName = "consensus_log"; + std::string const c_fileSeparator = "/"; +}; +} // namespace initializer +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/libinitializer/LedgerInitializer.h" "b/BFPL\345\243\271/libinitializer/LedgerInitializer.h" new file mode 100644 index 00000000..627e8994 --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/LedgerInitializer.h" @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2021 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. + * + * @brief Initializer for the ledger + * @file LedgerInitializer.h + * @author: yujiechen + * @date 2021-06-10 + */ +#pragma once +#include +#include +#include +#include +#include + +namespace bcos::initializer +{ +class LedgerInitializer +{ +public: + static std::shared_ptr build( + bcos::protocol::BlockFactory::Ptr _blockFactory, + bcos::storage::StorageInterface::Ptr _storage, bcos::tool::NodeConfig::Ptr _nodeConfig) + { + auto ledger = std::make_shared(_blockFactory, _storage); + // build genesis block + ledger->buildGenesisBlock(_nodeConfig->ledgerConfig(), _nodeConfig->txGasLimit(), + _nodeConfig->genesisData(), _nodeConfig->compatibilityVersionStr()); + return ledger; + } +}; +} // namespace bcos::initializer diff --git "a/BFPL\345\243\271/libinitializer/LightNodeInitializer.cpp" "b/BFPL\345\243\271/libinitializer/LightNodeInitializer.cpp" new file mode 100644 index 00000000..e659d9d8 --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/LightNodeInitializer.cpp" @@ -0,0 +1 @@ +#include "LightNodeInitializer.h" \ No newline at end of file diff --git "a/BFPL\345\243\271/libinitializer/LightNodeInitializer.h" "b/BFPL\345\243\271/libinitializer/LightNodeInitializer.h" new file mode 100644 index 00000000..be84f7e0 --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/LightNodeInitializer.h" @@ -0,0 +1,306 @@ +#pragma once + +#include + +#include "bcos-concepts/Exception.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::initializer +{ +class LightNodeInitializer : public std::enable_shared_from_this +{ +public: + // Note: FrontService is owned by Initializier for the entire lifetime + void initLedgerServer(std::shared_ptr front, + bcos::concepts::ledger::Ledger auto ledger, + std::shared_ptr>> + transactionPool, + std::shared_ptr>> + scheduler) + { + auto weakFront = std::weak_ptr(front); + auto self = std::weak_ptr(shared_from_this()); + front->registerModuleMessageDispatcher(bcos::protocol::LIGHTNODE_GET_BLOCK, + [self, ledger, weakFront]( + bcos::crypto::NodeIDPtr nodeID, const std::string& messageID, bytesConstRef data) { + auto init = self.lock(); + auto front = weakFront.lock(); + if (!front || !init) + { + return; + } + + bcostars::RequestBlock request; + init->decodeRequest( + request, front, protocol::LIGHTNODE_GET_BLOCK, nodeID, messageID, data); + bcos::task::wait(init->getBlock(std::move(front), ledger, std::move(nodeID), + std::string(messageID), std::move(request))); + }); + + front->registerModuleMessageDispatcher(bcos::protocol::LIGHTNODE_GET_TRANSACTIONS, + [ledger, front]( + bcos::crypto::NodeIDPtr nodeID, const std::string& id, bytesConstRef data) { + task::wait([](auto ledger, auto front, auto nodeID, std::string id, + bytesConstRef data) -> task::Task { + bcostars::ResponseTransactions response; + + try + { + bcostars::RequestTransactions request; + bcos::concepts::serialize::decode(data, request); + + LIGHTNODE_LOG(INFO) << "Get transactions:" << request.hashes.size() << " | " + << request.withProof; + + co_await concepts::getRef(ledger).getTransactions( + request.hashes, response.transactions); + } + catch (std::exception& e) + { + response.error.errorCode = -1; + response.error.errorMessage = boost::diagnostic_information(e); + } + + bcos::bytes responseBuffer; + bcos::concepts::serialize::encode(response, responseBuffer); + front->asyncSendResponse(id, bcos::protocol::LIGHTNODE_GET_TRANSACTIONS, nodeID, + bcos::ref(responseBuffer), []([[maybe_unused]] Error::Ptr _error) {}); + }(ledger, front, std::move(nodeID), id, data)); + }); + front->registerModuleMessageDispatcher(bcos::protocol::LIGHTNODE_GET_RECEIPTS, + [ledger, weakFront]( + bcos::crypto::NodeIDPtr nodeID, const std::string& id, bytesConstRef data) { + task::wait([](auto ledger, auto weakFront, std::string id, auto nodeID, + bytesConstRef data) -> task::Task { + auto front = weakFront.lock(); + if (!front) + { + co_return; + } + + bcostars::ResponseReceipts response; + try + { + bcostars::RequestReceipts request; + bcos::concepts::serialize::decode(data, request); + + co_await concepts::getRef(ledger).getTransactions( + request.hashes, response.receipts); + } + catch (std::exception& e) + { + LIGHTNODE_LOG(ERROR) + << "Get receipt error!" << boost::diagnostic_information(e); + + response.error.errorCode = -1; + response.error.errorMessage = boost::diagnostic_information(e); + } + + bcos::bytes responseBuffer; + bcos::concepts::serialize::encode(response, responseBuffer); + front->asyncSendResponse(id, bcos::protocol::LIGHTNODE_GET_RECEIPTS, nodeID, + bcos::ref(responseBuffer), {}); + }(ledger, weakFront, id, std::move(nodeID), data)); + }); + front->registerModuleMessageDispatcher(bcos::protocol::LIGHTNODE_GET_STATUS, + [ledger, weakFront]( + bcos::crypto::NodeIDPtr nodeID, const std::string& id, bytesConstRef data) { + // task::wait([](auto weakFront, auto ledger, auto nodeID, std::string id, + // bytesConstRef data) -> task::Task { + auto front = weakFront.lock(); + if (!front) + { + // co_return; + return; + } + bcostars::ResponseGetStatus response; + + try + { + bcostars::RequestGetStatus request; + bcos::concepts::serialize::decode(data, request); + + auto status = ~concepts::getRef(ledger).getStatus(); + response.total = status.total; + response.failed = status.failed; + response.blockNumber = status.blockNumber; + } + catch (std::exception& e) + { + response.error.errorCode = -1; + response.error.errorMessage = boost::diagnostic_information(e); + } + bcos::bytes responseBuffer; + bcos::concepts::serialize::encode(response, responseBuffer); + front->asyncSendResponse(id, bcos::protocol::LIGHTNODE_GET_STATUS, nodeID, + bcos::ref(responseBuffer), []([[maybe_unused]] Error::Ptr error) {}); + // }(weakFront, ledger, std::move(nodeID), id, data)); + }); + front->registerModuleMessageDispatcher(bcos::protocol::LIGHTNODE_SEND_TRANSACTION, + [transactionPool, self, weakFront]( + bcos::crypto::NodeIDPtr nodeID, const std::string& id, bytesConstRef data) { + auto front = weakFront.lock(); + auto init = self.lock(); + if (!front || !init) + { + return; + } + bcostars::RequestSendTransaction request; + init->decodeRequest( + request, front, protocol::LIGHTNODE_SEND_TRANSACTION, nodeID, id, data); + bcos::task::wait(init->submitTransaction( + front, transactionPool, nodeID, id, std::move(request))); + }); + + front->registerModuleMessageDispatcher(bcos::protocol::LIGHTNODE_CALL, + [self, scheduler, weakFront]( + bcos::crypto::NodeIDPtr nodeID, const std::string& id, bytesConstRef data) mutable { + auto front = weakFront.lock(); + auto init = self.lock(); + if (!front || !init) + { + return; + } + + bcostars::RequestSendTransaction request; + init->decodeRequest( + request, front, protocol::LIGHTNODE_CALL, nodeID, id, data); + bcos::task::wait(init->call(front, scheduler, nodeID, id, std::move(request))); + }); + } + +private: + template + bool decodeRequest(auto& request, std::shared_ptr front, + bcos::protocol::ModuleID moduleID, bcos::crypto::NodeIDPtr nodeID, const std::string& id, + bytesConstRef data) + { + bool success = true; + try + { + bcos::concepts::serialize::decode(data, request); + return success; + } + catch (std::exception const& e) + { + Response response; + response.error.errorCode = -1; + response.error.errorMessage = boost::diagnostic_information(e); + success = false; + + bcos::bytes responseBuffer; + bcos::concepts::serialize::encode(response, responseBuffer); + front->asyncSendResponse( + std::string(id), moduleID, nodeID, bcos::ref(responseBuffer), [](Error::Ptr) {}); + } + + return success; + } + + task::Task getBlock(std::shared_ptr front, + bcos::concepts::ledger::Ledger auto ledger, bcos::crypto::NodeIDPtr nodeID, + std::string messageID, bcostars::RequestBlock request) + { + bcostars::ResponseBlock response; + try + { + LIGHTNODE_LOG(INFO) << "Get block:" << request.blockNumber << " | " + << request.onlyHeader; + + if (request.onlyHeader) + { + co_await concepts::getRef(ledger).template getBlock( + request.blockNumber, response.block); + } + else + { + co_await concepts::getRef(ledger).template getBlock( + request.blockNumber, response.block); + } + } + catch (std::exception& e) + { + response.error.errorCode = -1; + response.error.errorMessage = boost::diagnostic_information(e); + } + + bcos::bytes responseBuffer; + bcos::concepts::serialize::encode(response, responseBuffer); + front->asyncSendResponse(messageID, bcos::protocol::LIGHTNODE_GET_BLOCK, nodeID, + bcos::ref(responseBuffer), [](Error::Ptr _error) { + if (_error) + {} + }); + } + + task::Task submitTransaction(std::shared_ptr front, + std::shared_ptr>> + transactionPool, + bcos::crypto::NodeIDPtr nodeID, std::string id, bcostars::RequestSendTransaction request) + { + bcostars::ResponseSendTransaction response; + try + { + LIGHTNODE_LOG(INFO) << "Request submit transaction: " << id; + co_await transactionPool->submitTransaction( + std::move(request.transaction), response.receipt); + } + catch (std::exception& e) + { + response.error.errorCode = -1; + response.error.errorMessage = boost::diagnostic_information(e); + } + + bcos::bytes responseBuffer; + bcos::concepts::serialize::encode(response, responseBuffer); + LIGHTNODE_LOG(INFO) << "Response submit transaction: " << id << " | " + << responseBuffer.size(); + + front->asyncSendResponse(id, bcos::protocol::LIGHTNODE_SEND_TRANSACTION, nodeID, + bcos::ref(responseBuffer), [](Error::Ptr) {}); + } + + task::Task call(std::shared_ptr front, + std::shared_ptr>> + scheduler, + bcos::crypto::NodeIDPtr nodeID, std::string id, bcostars::RequestSendTransaction request) + { + bcostars::ResponseSendTransaction response; + try + { + co_await scheduler->call(request.transaction, response.receipt); + } + catch (std::exception& e) + { + response.error.errorCode = -1; + response.error.errorMessage = boost::diagnostic_information(e); + } + + bcos::bytes responseBuffer; + bcos::concepts::serialize::encode(response, responseBuffer); + front->asyncSendResponse(id, bcos::protocol::LIGHTNODE_CALL, nodeID, + bcos::ref(responseBuffer), [](Error::Ptr) {}); + } +}; +} // namespace bcos::initializer \ No newline at end of file diff --git "a/BFPL\345\243\271/libinitializer/PBFTInitializer.cpp" "b/BFPL\345\243\271/libinitializer/PBFTInitializer.cpp" new file mode 100644 index 00000000..cd460671 --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/PBFTInitializer.cpp" @@ -0,0 +1,541 @@ +/** + * Copyright (C) 2021 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. + * + * @brief initializer for the PBFT module + * @file PBFTInitializer.cpp + * @author: yujiechen + * @date 2021-06-10 + */ +#include "PBFTInitializer.h" +#include +#include +#include + +#ifdef WITH_TIKV +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::tool; +using namespace bcos::protocol; +using namespace bcos::crypto; +using namespace bcos::consensus; +using namespace bcos::sealer; +using namespace bcos::txpool; +using namespace bcos::sync; +using namespace bcos::ledger; +using namespace bcos::storage; +using namespace bcos::scheduler; +using namespace bcos::initializer; +using namespace bcos::group; +using namespace bcos::protocol; +using namespace bcos::election; + +PBFTInitializer::PBFTInitializer(bcos::protocol::NodeArchitectureType _nodeArchType, + bcos::tool::NodeConfig::Ptr _nodeConfig, ProtocolInitializer::Ptr _protocolInitializer, + bcos::txpool::TxPoolInterface::Ptr _txpool, std::shared_ptr _ledger, + bcos::scheduler::SchedulerInterface::Ptr _scheduler, + bcos::storage::StorageInterface::Ptr _storage, + std::shared_ptr _frontService, + bcos::tool::NodeTimeMaintenance::Ptr _nodeTimeMaintenance) + : m_nodeArchType(_nodeArchType), + m_nodeConfig(std::move(_nodeConfig)), + m_protocolInitializer(std::move(_protocolInitializer)), + m_txpool(std::move(_txpool)), + m_ledger(std::move(_ledger)), + m_scheduler(std::move(_scheduler)), + m_storage(std::move(_storage)), + m_frontService(std::move(_frontService)), + m_nodeTimeMaintenance(std::move(_nodeTimeMaintenance)) +{ + m_groupInfoCodec = std::make_shared(); + createSealer(); + createPBFT(); + createSync(); + registerHandlers(); + initChainNodeInfo(m_nodeArchType, m_nodeConfig); +} + +std::string PBFTInitializer::generateGenesisConfig(bcos::tool::NodeConfig::Ptr _nodeConfig) +{ + Json::Value genesisConfig; + genesisConfig["consensusType"] = _nodeConfig->consensusType(); + genesisConfig["blockTxCountLimit"] = _nodeConfig->ledgerConfig()->blockTxCountLimit(); + genesisConfig["txGasLimit"] = (int64_t)(_nodeConfig->txGasLimit()); + genesisConfig["consensusLeaderPeriod"] = _nodeConfig->ledgerConfig()->leaderSwitchPeriod(); + Json::Value sealerList(Json::arrayValue); + auto consensusNodeList = _nodeConfig->ledgerConfig()->consensusNodeList(); + for (auto const& node : consensusNodeList) + { + Json::Value sealer; + sealer["nodeID"] = node->nodeID()->hex(); + sealer["weight"] = node->weight(); + sealerList.append(sealer); + } + genesisConfig["sealerList"] = sealerList; + Json::FastWriter fastWriter; + std::string genesisConfigStr = fastWriter.write(genesisConfig); + return genesisConfigStr; +} +std::string PBFTInitializer::generateIniConfig(bcos::tool::NodeConfig::Ptr _nodeConfig) +{ + Json::Value iniConfig; + Json::Value binaryInfo; + + // get the binaryInfo + binaryInfo["version"] = FISCO_BCOS_PROJECT_VERSION; + binaryInfo["gitCommitHash"] = FISCO_BCOS_COMMIT_HASH; + binaryInfo["platform"] = FISCO_BCOS_BUILD_PLATFORM; + binaryInfo["buildTime"] = FISCO_BCOS_BUILD_TIME; + iniConfig["binaryInfo"] = binaryInfo; + + iniConfig["chainID"] = _nodeConfig->chainId(); + iniConfig["groupID"] = _nodeConfig->groupId(); + iniConfig["smCryptoType"] = _nodeConfig->smCryptoType(); + iniConfig["isWasm"] = _nodeConfig->isWasm(); + iniConfig["isAuthCheck"] = _nodeConfig->isAuthCheck(); + iniConfig["isSerialExecute"] = _nodeConfig->isSerialExecute(); + iniConfig["nodeName"] = _nodeConfig->nodeName(); + iniConfig["nodeID"] = m_protocolInitializer->keyPair()->publicKey()->hex(); + iniConfig["rpcServiceName"] = _nodeConfig->rpcServiceName(); + iniConfig["gatewayServiceName"] = _nodeConfig->gatewayServiceName(); + Json::FastWriter fastWriter; + std::string iniConfigStr = fastWriter.write(iniConfig); + return iniConfigStr; +} + +void PBFTInitializer::initChainNodeInfo( + bcos::protocol::NodeArchitectureType _nodeArchType, bcos::tool::NodeConfig::Ptr _nodeConfig) +{ + m_groupInfo = std::make_shared(_nodeConfig->chainId(), _nodeConfig->groupId()); + m_groupInfo->setGenesisConfig(generateGenesisConfig(_nodeConfig)); + m_groupInfo->setWasm(_nodeConfig->isWasm()); + int32_t nodeType = bcos::group::NodeCryptoType::NON_SM_NODE; + if (_nodeConfig->smCryptoType()) + { + nodeType = bcos::group::NodeCryptoType::SM_NODE; + } + bool microServiceMode = true; + if (_nodeArchType == bcos::protocol::NodeArchitectureType::AIR) + { + microServiceMode = false; + } + + m_nodeInfo = std::make_shared(_nodeConfig->nodeName(), nodeType); + m_nodeInfo->setNodeID(m_protocolInitializer->keyPair()->publicKey()->hex()); + + m_nodeInfo->setIniConfig(generateIniConfig(_nodeConfig)); + m_nodeInfo->setMicroService(microServiceMode); + m_nodeInfo->setNodeType(m_blockSync->config()->nodeType()); + m_nodeInfo->setNodeCryptoType( + (_nodeConfig->smCryptoType() ? NodeCryptoType::SM_NODE : NON_SM_NODE)); + if (_nodeArchType == bcos::protocol::NodeArchitectureType::AIR) + { + m_nodeInfo->appendServiceInfo(SCHEDULER, SCHEDULER_SERVANT_NAME); + m_nodeInfo->appendServiceInfo(LEDGER, LEDGER_SERVANT_NAME); + m_nodeInfo->appendServiceInfo(FRONT, FRONT_SERVANT_NAME); + m_nodeInfo->appendServiceInfo(TXPOOL, TXPOOL_SERVANT_NAME); + } + // Note: must set the serviceInfo for rpc/gateway to pass the groupInfo check when sync latest + // groupInfo to rpc/gateway service + m_nodeInfo->appendServiceInfo(GATEWAY, m_nodeConfig->gatewayServiceName()); + m_nodeInfo->appendServiceInfo(RPC, m_nodeConfig->rpcServiceName()); + // set protocolInfo + auto nodeProtocolInfo = g_BCOSConfig.protocolInfo(ProtocolModuleID::NodeService); + m_nodeInfo->setNodeProtocol(*nodeProtocolInfo); + m_nodeInfo->setCompatibilityVersion(m_pbft->compatibilityVersion()); + m_groupInfo->appendNodeInfo(m_nodeInfo); + INITIALIZER_LOG(INFO) << LOG_DESC("PBFTInitializer::initChainNodeInfo") + << LOG_KV("nodeType", m_nodeInfo->nodeType()) + << LOG_KV("nodeCryptoType", m_nodeInfo->nodeCryptoType()) + << LOG_KV("nodeName", _nodeConfig->nodeName()) + << LOG_KV("compatibilityVersion", m_nodeInfo->compatibilityVersion()); +} + +void PBFTInitializer::start() +{ + if (!m_nodeConfig->enableFailOver()) + { + m_blockSync->enableAsMaster(true); + // Note: since enableAsMasterNode will recover pbftState and execute the recovered proposal, + // should call this after every module and handlers has been inited completed + m_pbft->enableAsMasterNode(true); + } + m_sealer->start(); + m_blockSync->start(); + m_pbft->start(); + if (m_leaderElection) + { + m_leaderElection->start(); + } +} + +void PBFTInitializer::stop() +{ + if (m_leaderElection) + { + m_leaderElection->stop(); + } + m_sealer->stop(); + m_blockSync->stop(); + m_pbft->stop(); +} + +void PBFTInitializer::init() +{ + m_sealer->init(m_pbft); + m_blockSync->init(); + m_pbft->init(); + if (m_nodeConfig->enableFailOver()) + { + initConsensusFailOver(m_protocolInitializer->keyPair()->publicKey()); + } + syncGroupNodeInfo(); +} + +void PBFTInitializer::registerHandlers() +{ + // handler to notify the sealer reset the sealing proposals + std::weak_ptr weakedSealer = m_sealer; + m_pbft->registerSealerResetNotifier([weakedSealer]( + std::function _onRecv) { + try + { + auto sealer = weakedSealer.lock(); + if (!sealer) + { + return; + } + sealer->asyncResetSealing(_onRecv); + } + catch (std::exception const& e) + { + INITIALIZER_LOG(WARNING) << LOG_DESC("call asyncResetSealing to the sealer exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); + + // register handlers for the consensus to interact with the sealer + m_pbft->registerSealProposalNotifier( + [weakedSealer](size_t _proposalIndex, size_t _proposalEndIndex, size_t _maxTxsToSeal, + std::function _onRecvResponse) { + try + { + auto sealer = weakedSealer.lock(); + if (!sealer) + { + return; + } + sealer->asyncNotifySealProposal( + _proposalIndex, _proposalEndIndex, _maxTxsToSeal, _onRecvResponse); + } + catch (std::exception const& e) + { + INITIALIZER_LOG(WARNING) << LOG_DESC("call notify proposal sealing exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); + + // the consensus module notify the latest blockNumber to the sealer + m_pbft->registerStateNotifier([weakedSealer](bcos::protocol::BlockNumber _blockNumber) { + try + { + auto sealer = weakedSealer.lock(); + if (!sealer) + { + return; + } + sealer->asyncNoteLatestBlockNumber(_blockNumber); + } + catch (std::exception const& e) + { + INITIALIZER_LOG(WARNING) + << LOG_DESC("call notify the latest block number to the sealer exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); + + // the consensus moudle notify new block to the sync module + std::weak_ptr weakedSync = m_blockSync; + m_pbft->registerNewBlockNotifier([weakedSync](bcos::ledger::LedgerConfig::Ptr _ledgerConfig, + std::function _onRecv) { + try + { + auto sync = weakedSync.lock(); + if (!sync) + { + return; + } + sync->asyncNotifyNewBlock(_ledgerConfig, _onRecv); + } + catch (std::exception const& e) + { + INITIALIZER_LOG(WARNING) + << LOG_DESC("call notify the latest block to the sync module exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); + + m_pbft->registerFaultyDiscriminator([weakedSync](bcos::crypto::NodeIDPtr _nodeID) -> bool { + try + { + auto sync = weakedSync.lock(); + if (!sync) + { + return false; + } + return sync->faultyNode(_nodeID); + } + catch (std::exception const& e) + { + INITIALIZER_LOG(WARNING) + << LOG_DESC("determine the node is faulty or not through the sync module exception") + << LOG_KV("node", _nodeID->shortHex()) + << LOG_KV("error", boost::diagnostic_information(e)); + } + return false; + }); + + m_pbft->registerCommittedProposalNotifier( + [weakedSync](bcos::protocol::BlockNumber _committedProposal, + std::function _onRecv) { + try + { + auto sync = weakedSync.lock(); + if (!sync) + { + return; + } + sync->asyncNotifyCommittedIndex(_committedProposal, _onRecv); + } + catch (std::exception const& e) + { + INITIALIZER_LOG(WARNING) << LOG_DESC( + "call notify the latest committed proposal index " + "to the sync module exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); + m_txpool->registerTxsCleanUpSwitch([this]() -> bool { + auto config = m_pbft->pbftEngine()->pbftConfig(); + // should clean up expired txs periodically for non-consensus node + if (!config->isConsensusNode()) + { + return true; + } + // clean up the expired txs for the consensus-timeout node + return config->timeout(); + }); +} + +void PBFTInitializer::initNotificationHandlers(bcos::rpc::RPCInterface::Ptr _rpc) +{ + // version notification + m_pbft->registerVersionInfoNotification([_rpc, this](uint32_t _version) { + // Note: the nodeInfo and the groupInfo are mutable + auto nodeInfo = m_groupInfo->nodeInfo(m_nodeConfig->nodeName()); + // Note: notify groupInfo to all rpc nodes in pro/max mode + nodeInfo->setCompatibilityVersion(_version); + _rpc->asyncNotifyGroupInfo(m_groupInfo, [_version](bcos::Error::Ptr&& _error) { + if (!_error) + { + INITIALIZER_LOG(INFO) << LOG_DESC("Election versionInfoNotification success") + << LOG_KV("version", _version); + return; + } + INITIALIZER_LOG(WARNING) + << LOG_DESC("Election versionInfoNotification error") << LOG_KV("version", _version) + << LOG_KV("code", _error->errorCode()) << LOG_KV("msg", _error->errorMessage()); + }); + onGroupInfoChanged(); + }); +} + +void PBFTInitializer::createSealer() +{ + // create sealer + auto sealerFactory = std::make_shared(m_protocolInitializer->blockFactory(), + m_txpool, m_nodeConfig->minSealTime(), m_nodeTimeMaintenance); + m_sealer = sealerFactory->createSealer(); +} + +void PBFTInitializer::createPBFT() +{ + auto keyPair = m_protocolInitializer->keyPair(); + auto kvStorage = std::make_shared(m_storage); + // create pbft + auto pbftFactory = std::make_shared(m_protocolInitializer->cryptoSuite(), + m_protocolInitializer->keyPair(), m_frontService, kvStorage, m_ledger, m_scheduler, + m_txpool, m_protocolInitializer->blockFactory(), m_protocolInitializer->txResultFactory()); + + m_pbft = pbftFactory->createPBFT(); + auto pbftConfig = m_pbft->pbftEngine()->pbftConfig(); + pbftConfig->setCheckPointTimeoutInterval(m_nodeConfig->checkPointTimeoutInterval()); +} + +void PBFTInitializer::createSync() +{ + // create sync + auto keyPair = m_protocolInitializer->keyPair(); + auto blockSyncFactory = std::make_shared(keyPair->publicKey(), + m_protocolInitializer->blockFactory(), m_protocolInitializer->txResultFactory(), m_ledger, + m_txpool, m_frontService, m_scheduler, m_pbft, m_nodeTimeMaintenance); + m_blockSync = blockSyncFactory->createBlockSync(); +} + +std::shared_ptr PBFTInitializer::txpool() +{ + return m_txpool; +} +std::shared_ptr PBFTInitializer::blockSync() +{ + return m_blockSync; +} +std::shared_ptr PBFTInitializer::pbft() +{ + return m_pbft; +} +std::shared_ptr PBFTInitializer::sealer() +{ + return m_sealer; +} + +// sync groupNodeInfo from the gateway +void PBFTInitializer::syncGroupNodeInfo() +{ + // Note: In air mode, the groupNodeInfo must be successful + auto self = std::weak_ptr(shared_from_this()); + m_frontService->asyncGetGroupNodeInfo( + [self](Error::Ptr _error, bcos::gateway::GroupNodeInfo::Ptr _groupNodeInfo) { + auto pbftInit = self.lock(); + if (!pbftInit) + { + return; + } + if (_error != nullptr) + { + INITIALIZER_LOG(WARNING) + << LOG_DESC("asyncGetGroupNodeInfo failed") + << LOG_KV("code", _error->errorCode()) << LOG_KV("msg", _error->errorMessage()); + return; + } + try + { + if (!_groupNodeInfo || _groupNodeInfo->nodeIDList().size() == 0) + { + return; + } + NodeIDSet nodeIdSet; + auto const& nodeIDList = _groupNodeInfo->nodeIDList(); + if (nodeIDList.size() == 0) + { + return; + } + for (auto const& nodeIDStr : nodeIDList) + { + auto nodeID = + pbftInit->m_protocolInitializer->cryptoSuite()->keyFactory()->createKey( + fromHex(nodeIDStr)); + nodeIdSet.insert(nodeID); + } + // the blockSync module set the connected node list + pbftInit->m_blockSync->config()->setConnectedNodeList(std::move(nodeIdSet)); + // the txpool module set the connected node list + auto txpool = std::dynamic_pointer_cast(pbftInit->m_txpool); + txpool->transactionSync()->config()->setConnectedNodeList(std::move(nodeIdSet)); + INITIALIZER_LOG(INFO) << LOG_DESC("syncGroupNodeInfo for block sync and txpool") + << LOG_KV("connectedSize", nodeIdSet.size()); + } + catch (std::exception const& e) + { + INITIALIZER_LOG(WARNING) << LOG_DESC("asyncGetGroupNodeInfo exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); +} + +void PBFTInitializer::onGroupInfoChanged() +{ + if (!m_leaderElection) + { + return; + } + // failover enabled, should sync the latest information to the etcd if the node is + // leader + INITIALIZER_LOG(INFO) << LOG_DESC("Election onGroupInfoChanged, update the memberConfig"); + std::string modifiedConfig; + m_groupInfoCodec->serialize(modifiedConfig, m_groupInfo); + auto memberInfo = m_memberFactory->createMember(); + memberInfo->setMemberID(m_nodeConfig->memberID()); + memberInfo->setMemberConfig(modifiedConfig); + m_leaderElection->updateSelfConfig(memberInfo); +} + +void PBFTInitializer::initConsensusFailOver(KeyInterface::Ptr _nodeID) +{ + m_memberFactory = std::make_shared(); + +#ifdef WITH_TIKV + auto leaderElectionFactory = std::make_shared(m_memberFactory); +#endif + // leader key: /${chainID}/consensus/${nodeID} + std::string leaderKey = + "/" + m_nodeConfig->chainId() + bcos::election::CONSENSUS_LEADER_DIR + _nodeID->hex(); + + std::string nodeConfig; + m_groupInfoCodec->serialize(nodeConfig, m_groupInfo); + +#ifdef WITH_TIKV + m_leaderElection = leaderElectionFactory->createLeaderElection(m_nodeConfig->memberID(), + nodeConfig, m_nodeConfig->failOverClusterUrl(), leaderKey, "consensus_fault_tolerance", + m_nodeConfig->leaseTTL(), m_nodeConfig->pdCaPath(), m_nodeConfig->pdCertPath(), + m_nodeConfig->pdKeyPath()); + + // register the handler + m_leaderElection->registerOnCampaignHandler( + [this](bool _success, bcos::protocol::MemberInterface::Ptr _leader) { + m_pbft->enableAsMasterNode(_success); + m_blockSync->enableAsMaster(_success); + INITIALIZER_LOG(INFO) << LOG_DESC("onCampaignHandler") << LOG_KV("success", _success) + << LOG_KV("leader", _leader ? _leader->memberID() : "None"); + if (!_success) + { + return; + } + auto schedulerManager = + std::dynamic_pointer_cast(m_scheduler); + schedulerManager->asyncSwitchTerm(_leader->seq(), [_leader](Error::Ptr&& error) { + INITIALIZER_LOG(INFO) + << "Notify scheduler switch " << (error ? "failed" : "success") << " with" + << LOG_KV("seq", _leader->seq()); + }); + }); + INITIALIZER_LOG(INFO) << LOG_DESC("initConsensusFailOver") << LOG_KV("leaderKey", leaderKey) + << LOG_KV("nodeConfig", nodeConfig); +#endif +} diff --git "a/BFPL\345\243\271/libinitializer/PBFTInitializer.h" "b/BFPL\345\243\271/libinitializer/PBFTInitializer.h" new file mode 100644 index 00000000..a0bd6522 --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/PBFTInitializer.h" @@ -0,0 +1,130 @@ +/** + * Copyright (C) 2021 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. + * + * @brief initializer for the PBFT module + * @file PBFTInitializer.h + * @author: yujiechen + * @date 2021-06-10 + */ +#pragma once +#include "Common.h" +#include "bcos-framework/rpc/RPCInterface.h" +#include "libinitializer/ProtocolInitializer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace sealer +{ +class Sealer; +} +namespace sync +{ +class BlockSync; +} +namespace consensus +{ +class PBFTImpl; +} + +namespace initializer +{ +class PBFTInitializer : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + PBFTInitializer(bcos::protocol::NodeArchitectureType _nodeArchType, + bcos::tool::NodeConfig::Ptr _nodeConfig, ProtocolInitializer::Ptr _protocolInitializer, + bcos::txpool::TxPoolInterface::Ptr _txpool, std::shared_ptr _ledger, + bcos::scheduler::SchedulerInterface::Ptr _scheduler, + bcos::storage::StorageInterface::Ptr _storage, + bcos::front::FrontServiceInterface::Ptr _frontService, + bcos::tool::NodeTimeMaintenance::Ptr _nodeTimeMaintenance); + + virtual ~PBFTInitializer() { stop(); } + + virtual void init(); + + virtual void start(); + virtual void stop(); + + bcos::txpool::TxPoolInterface::Ptr txpool(); + bcos::sync::BlockSyncInterface::Ptr blockSync(); + bcos::consensus::ConsensusInterface::Ptr pbft(); + bcos::sealer::SealerInterface::Ptr sealer(); + + bcos::protocol::BlockFactory::Ptr blockFactory() + { + return m_protocolInitializer->blockFactory(); + } + bcos::crypto::KeyFactory::Ptr keyFactory() { return m_protocolInitializer->keyFactory(); } + + bcos::group::GroupInfo::Ptr groupInfo() { return m_groupInfo; } + bcos::group::ChainNodeInfo::Ptr nodeInfo() { return m_nodeInfo; } + virtual void onGroupInfoChanged(); + virtual void initNotificationHandlers(bcos::rpc::RPCInterface::Ptr _rpc); + +protected: + virtual void initChainNodeInfo(bcos::protocol::NodeArchitectureType _nodeArchType, + bcos::tool::NodeConfig::Ptr _nodeConfig); + virtual void createSealer(); + virtual void createPBFT(); + virtual void createSync(); + virtual void registerHandlers(); + std::string generateGenesisConfig(bcos::tool::NodeConfig::Ptr _nodeConfig); + std::string generateIniConfig(bcos::tool::NodeConfig::Ptr _nodeConfig); + + void syncGroupNodeInfo(); + virtual void initConsensusFailOver(bcos::crypto::KeyInterface::Ptr _nodeID); + +protected: + bcos::protocol::NodeArchitectureType m_nodeArchType; + bcos::tool::NodeConfig::Ptr m_nodeConfig; + ProtocolInitializer::Ptr m_protocolInitializer; + + bcos::txpool::TxPoolInterface::Ptr m_txpool; + // Note: PBFT and other modules (except rpc and gateway) access ledger with bcos-ledger SDK + std::shared_ptr m_ledger; + bcos::scheduler::SchedulerInterface::Ptr m_scheduler; + bcos::storage::StorageInterface::Ptr m_storage; + bcos::front::FrontServiceInterface::Ptr m_frontService; + + std::shared_ptr m_sealer; + std::shared_ptr m_blockSync; + std::shared_ptr m_pbft; + + bcos::group::GroupInfo::Ptr m_groupInfo; + bcos::group::ChainNodeInfo::Ptr m_nodeInfo; + + bcos::group::GroupInfoCodec::Ptr m_groupInfoCodec; + bcos::protocol::MemberFactoryInterface::Ptr m_memberFactory; + bcos::election::LeaderElectionInterface::Ptr m_leaderElection; + bcos::tool::NodeTimeMaintenance::Ptr m_nodeTimeMaintenance; +}; +} // namespace initializer +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/libinitializer/ProPBFTInitializer.cpp" "b/BFPL\345\243\271/libinitializer/ProPBFTInitializer.cpp" new file mode 100644 index 00000000..de7a305f --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/ProPBFTInitializer.cpp" @@ -0,0 +1,167 @@ +/** + * Copyright (C) 2021 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. + * + * @brief initializer for the PBFT module + * @file ProPBFTInitializer.cpp + * @author: yujiechen + * @date 2021-06-10 + */ +#include "ProPBFTInitializer.h" +#include "bcos-framework/protocol/ServiceDesc.h" +#include "bcos-utilities/Exceptions.h" +#include "fisco-bcos-tars-service/Common/TarsUtils.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcos::tool; +using namespace bcos::protocol; +using namespace bcos::crypto; +using namespace bcos::initializer; + +ProPBFTInitializer::ProPBFTInitializer(bcos::protocol::NodeArchitectureType _nodeArchType, + bcos::tool::NodeConfig::Ptr _nodeConfig, ProtocolInitializer::Ptr _protocolInitializer, + bcos::txpool::TxPoolInterface::Ptr _txpool, std::shared_ptr _ledger, + bcos::scheduler::SchedulerInterface::Ptr _scheduler, + bcos::storage::StorageInterface::Ptr _storage, + std::shared_ptr _frontService, + bcos::tool::NodeTimeMaintenance::Ptr _nodeTimeMaintenance) + : PBFTInitializer(_nodeArchType, _nodeConfig, _protocolInitializer, _txpool, _ledger, _scheduler, + _storage, _frontService, _nodeTimeMaintenance) +{ + m_timer = std::make_shared(m_timerSchedulerInterval, "node info report"); + + std::vector endPoints; + auto withoutTarsFramework = m_nodeConfig->withoutTarsFramework(); + + // init rpc client + auto rpcServiceName = m_nodeConfig->rpcServiceName(); + m_nodeConfig->getTarsClientProxyEndpoints(bcos::protocol::RPC_NAME, endPoints); + auto rpcServicePrx = bcostars::createServantProxy( + withoutTarsFramework, rpcServiceName, endPoints); + m_rpc = std::make_shared(rpcServicePrx, rpcServiceName); + + auto gatewayServiceName = m_nodeConfig->gatewayServiceName(); + m_nodeConfig->getTarsClientProxyEndpoints(bcos::protocol::GATEWAY_NAME, endPoints); + auto gatewayServicePrx = bcostars::createServantProxy( + withoutTarsFramework, gatewayServiceName, endPoints); + m_gateway = + std::make_shared(gatewayServicePrx, gatewayServiceName); +} + +void ProPBFTInitializer::scheduledTask() +{ + if (m_leaderElection && m_leaderElection->electionClusterOk()) + { + m_timer->stop(); + return; + } + // not enable failover, report nodeInfo to rpc/gw periodly + reportNodeInfo(); + m_timer->restart(); + return; +} + +void ProPBFTInitializer::reportNodeInfo() +{ + // notify groupInfo to rpc + m_rpc->asyncNotifyGroupInfo(m_groupInfo, [](bcos::Error::Ptr&& _error) { + if (_error) + { + INITIALIZER_LOG(WARNING) + << LOG_DESC("asyncNotifyGroupInfo to rpc error") + << LOG_KV("code", _error->errorCode()) << LOG_KV("msg", _error->errorMessage()); + } + }); + + // notify groupInfo to gateway + m_gateway->asyncNotifyGroupInfo(m_groupInfo, [](bcos::Error::Ptr&& _error) { + if (_error) + { + INITIALIZER_LOG(WARNING) + << LOG_DESC("asyncNotifyGroupInfo to gateway error") + << LOG_KV("code", _error->errorCode()) << LOG_KV("msg", _error->errorMessage()); + } + }); +} + +void ProPBFTInitializer::start() +{ + PBFTInitializer::start(); + if (m_timer && !m_nodeConfig->enableFailOver()) + { + m_timer->start(); + } +} + +void ProPBFTInitializer::stop() +{ + if (m_timer) + { + m_timer->stop(); + } + PBFTInitializer::stop(); +} + +void ProPBFTInitializer::onGroupInfoChanged() +{ + if (!m_leaderElection || !m_leaderElection->electionClusterOk()) + { + reportNodeInfo(); + return; + } + PBFTInitializer::onGroupInfoChanged(); +} + + +void ProPBFTInitializer::init() +{ + m_timer->registerTimeoutHandler(boost::bind(&ProPBFTInitializer::scheduledTask, this)); + m_blockSync->config()->registerOnNodeTypeChanged([this](bcos::protocol::NodeType _type) { + INITIALIZER_LOG(INFO) << LOG_DESC("OnNodeTypeChange") << LOG_KV("type", _type) + << LOG_KV("nodeName", m_nodeConfig->nodeName()); + auto nodeInfo = m_groupInfo->nodeInfo(m_nodeConfig->nodeName()); + if (!nodeInfo) + { + INITIALIZER_LOG(WARNING) << LOG_DESC("failed to find the given node information") + << LOG_KV("node", m_nodeConfig->nodeName()); + return; + } + nodeInfo->setNodeType(_type); + onGroupInfoChanged(); + }); + PBFTInitializer::init(); + // Note: m_leaderElection is created after PBFTInitializer::init + if (m_leaderElection) + { + m_leaderElection->registerOnElectionClusterException([this]() { + INITIALIZER_LOG(INFO) << LOG_DESC("OnElectionClusterException") + << LOG_KV("nodeName", m_nodeConfig->nodeName()); + }); + m_leaderElection->registerOnElectionClusterRecover([]() { + INITIALIZER_LOG(INFO) << LOG_DESC( + "OnElectionClusterRecover: stop reportNodeInfo to rpc/gateway"); + }); + } + else + { + reportNodeInfo(); + } +} \ No newline at end of file diff --git "a/BFPL\345\243\271/libinitializer/ProPBFTInitializer.h" "b/BFPL\345\243\271/libinitializer/ProPBFTInitializer.h" new file mode 100644 index 00000000..00429dae --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/ProPBFTInitializer.h" @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2021 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. + * + * @brief initializer for the PBFT module + * @file ProPBFTInitializer.h + * @author: yujiechen + * @date 2021-06-10 + */ +#pragma once +#include "libinitializer/PBFTInitializer.h" +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace initializer +{ +class ProPBFTInitializer : public PBFTInitializer +{ +public: + using Ptr = std::shared_ptr; + ProPBFTInitializer(bcos::protocol::NodeArchitectureType _nodeArchType, + bcos::tool::NodeConfig::Ptr _nodeConfig, ProtocolInitializer::Ptr _protocolInitializer, + bcos::txpool::TxPoolInterface::Ptr _txpool, std::shared_ptr _ledger, + bcos::scheduler::SchedulerInterface::Ptr _scheduler, + bcos::storage::StorageInterface::Ptr _storage, + std::shared_ptr _frontService, + bcos::tool::NodeTimeMaintenance::Ptr _nodeTimeMaintenance); + + virtual ~ProPBFTInitializer() { stop(); } + + void init() override; + + void start() override; + void stop() override; + +protected: + // the task triggered by the timer periodically + virtual void scheduledTask(); + virtual void reportNodeInfo(); + + void onGroupInfoChanged() override; + +private: + std::shared_ptr m_timer; + uint64_t m_timerSchedulerInterval = 3000; + + bcos::gateway::GatewayInterface::Ptr m_gateway; + bcos::rpc::RPCInterface::Ptr m_rpc; +}; +} // namespace initializer +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/libinitializer/ProtocolInitializer.cpp" "b/BFPL\345\243\271/libinitializer/ProtocolInitializer.cpp" new file mode 100644 index 00000000..a29cd5fa --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/ProtocolInitializer.cpp" @@ -0,0 +1,120 @@ +/** + * Copyright (C) 2021 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. + * + * @brief initializer for the protocol module + * @file ProtocolInitializer.cpp + * @author: yujiechen + * @date 2021-06-10 + */ +#include "libinitializer/ProtocolInitializer.h" +#include "bcos-crypto/hasher/OpenSSLHasher.h" +#include "libinitializer/Common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos; +using namespace bcostars::protocol; +using namespace bcos::initializer; +using namespace bcos::crypto; +using namespace bcos::tool; +using namespace bcos::security; + +ProtocolInitializer::ProtocolInitializer() + : m_keyFactory(std::make_shared()) +{} +void ProtocolInitializer::init(NodeConfig::Ptr _nodeConfig) +{ + // TODO: hsm/ed25519 + if (_nodeConfig->smCryptoType()) + { + createSMCryptoSuite(); + } + else + { + createCryptoSuite(); + } + INITIALIZER_LOG(INFO) << LOG_DESC("init crypto suite success"); + + if (true == _nodeConfig->storageSecurityEnable()) + { + m_dataEncryption = std::make_shared(_nodeConfig); + m_dataEncryption->init(); + + INITIALIZER_LOG(INFO) << LOG_DESC( + "storage_security.enable = true, init data encryption success"); + } + + // create the block factory + // TODO: pb/tars option + auto blockHeaderFactory = std::make_shared(m_cryptoSuite); + auto transactionFactory = std::make_shared(m_cryptoSuite); + auto receiptFactory = std::make_shared(m_cryptoSuite); + m_blockFactory = std::make_shared( + m_cryptoSuite, blockHeaderFactory, transactionFactory, receiptFactory); + + m_cryptoSuite->setKeyFactory(m_keyFactory); + auto txResultFactory = std::make_shared(); + m_txResultFactory = txResultFactory; + txResultFactory->setCryptoSuite(m_cryptoSuite); + + INITIALIZER_LOG(INFO) << LOG_DESC("init blockFactory success"); +} + +void ProtocolInitializer::createCryptoSuite() +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto encryptImpl = std::make_shared(); + m_cryptoSuite = std::make_shared(hashImpl, signatureImpl, encryptImpl); +} + +void ProtocolInitializer::createSMCryptoSuite() +{ + auto hashImpl = std::make_shared(); + auto signatureImpl = std::make_shared(); + auto encryptImpl = std::make_shared(); + m_cryptoSuite = std::make_shared(hashImpl, signatureImpl, encryptImpl); +} + +void ProtocolInitializer::loadKeyPair(std::string const& _privateKeyPath) +{ + auto privateKeyData = loadPrivateKey(_privateKeyPath, c_hexedPrivateKeySize, m_dataEncryption); + if (!privateKeyData) + { + INITIALIZER_LOG(INFO) << LOG_DESC("loadKeyPair failed") + << LOG_KV("privateKeyPath", _privateKeyPath); + throw std::runtime_error("loadKeyPair failed, keyPair path: " + _privateKeyPath); + } + INITIALIZER_LOG(INFO) << LOG_DESC("loadKeyPair from privateKey") + << LOG_KV("privateKeySize", privateKeyData->size()) + << LOG_KV("enableStorageSecurity", m_dataEncryption ? true : false); + auto privateKey = m_keyFactory->createKey(*privateKeyData); + m_keyPair = m_cryptoSuite->signatureImpl()->createKeyPair(privateKey); + + INITIALIZER_LOG(INFO) << METRIC << LOG_DESC("loadKeyPair success") + << LOG_KV("privateKeyPath", _privateKeyPath) + << LOG_KV("publicKey", m_keyPair->publicKey()->hex()); +} \ No newline at end of file diff --git "a/BFPL\345\243\271/libinitializer/ProtocolInitializer.h" "b/BFPL\345\243\271/libinitializer/ProtocolInitializer.h" new file mode 100644 index 00000000..13fa1931 --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/ProtocolInitializer.h" @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2021 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. + * + * @brief initializer for the protocol module + * @file ProtocolInitializer.h + * @author: yujiechen + * @date 2021-06-10 + */ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace initializer +{ +class ProtocolInitializer +{ +public: + using Ptr = std::shared_ptr; + ProtocolInitializer(); + virtual ~ProtocolInitializer() {} + + virtual void init(bcos::tool::NodeConfig::Ptr _nodeConfig); + void loadKeyPair(std::string const& _privateKeyPath); + + bcos::crypto::CryptoSuite::Ptr cryptoSuite() { return m_cryptoSuite; } + bcos::protocol::BlockFactory::Ptr blockFactory() { return m_blockFactory; } + bcos::protocol::TransactionSubmitResultFactory::Ptr txResultFactory() + { + return m_txResultFactory; + } + + bcos::crypto::KeyPairInterface::Ptr keyPair() const { return m_keyPair; } + bcos::crypto::KeyFactory::Ptr keyFactory() const { return m_keyFactory; } + bcos::security::DataEncryptInterface::Ptr dataEncryption() const { return m_dataEncryption; } + +private: + void createCryptoSuite(); + void createSMCryptoSuite(); + +private: + bcos::crypto::KeyFactory::Ptr m_keyFactory; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + bcos::protocol::BlockFactory::Ptr m_blockFactory; + bcos::protocol::TransactionSubmitResultFactory::Ptr m_txResultFactory; + bcos::crypto::KeyPairInterface::Ptr m_keyPair; + size_t c_hexedPrivateKeySize = 64; + bcos::security::DataEncryptInterface::Ptr m_dataEncryption{nullptr}; +}; +} // namespace initializer +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/libinitializer/SchedulerInitializer.h" "b/BFPL\345\243\271/libinitializer/SchedulerInitializer.h" new file mode 100644 index 00000000..b92a19e6 --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/SchedulerInitializer.h" @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2021 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. + * + * @brief initializer for the dispatcher and executor + * @file DispatcherInitializer.h + * @author: yujiechen + * @date 2021-06-21 + */ +#pragma once +#include "ProtocolInitializer.h" +#include "bcos-framework/protocol/BlockFactory.h" +#include "bcos-protocol/TransactionSubmitResultFactoryImpl.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::initializer +{ +class SchedulerInitializer +{ +public: + static bcos::scheduler::SchedulerInterface::Ptr build( + bcos::scheduler::ExecutorManager::Ptr executorManager, + bcos::ledger::LedgerInterface::Ptr _ledger, + bcos::storage::TransactionalStorageInterface::Ptr storage, + bcos::protocol::ExecutionMessageFactory::Ptr executionMessageFactory, + bcos::protocol::BlockFactory::Ptr blockFactory, bcos::txpool::TxPoolInterface::Ptr txPool, + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory, + crypto::Hash::Ptr hashImpl, bool isAuthCheck, bool isWasm, bool isSerialExecute, + int64_t schedulerSeq) + { + bcos::scheduler::SchedulerFactory factory(std::move(executorManager), std::move(_ledger), + std::move(storage), executionMessageFactory, std::move(blockFactory), std::move(txPool), + std::move(transactionSubmitResultFactory), std::move(hashImpl), isAuthCheck, isWasm, + isSerialExecute); + + return factory.build(schedulerSeq); + } + + static bcos::scheduler::SchedulerFactory::Ptr buildFactory( + bcos::scheduler::ExecutorManager::Ptr executorManager, + bcos::ledger::LedgerInterface::Ptr _ledger, + bcos::storage::TransactionalStorageInterface::Ptr storage, + bcos::protocol::ExecutionMessageFactory::Ptr executionMessageFactory, + bcos::protocol::BlockFactory::Ptr blockFactory, bcos::txpool::TxPoolInterface::Ptr txPool, + bcos::protocol::TransactionSubmitResultFactory::Ptr transactionSubmitResultFactory, + crypto::Hash::Ptr hashImpl, bool isAuthCheck, bool isWasm, bool isSerialExecute) + { + return std::make_shared(std::move(executorManager), + std::move(_ledger), std::move(storage), executionMessageFactory, + std::move(blockFactory), txPool, std::move(transactionSubmitResultFactory), + std::move(hashImpl), isAuthCheck, isWasm, isSerialExecute); + } +}; +} // namespace bcos::initializer \ No newline at end of file diff --git "a/BFPL\345\243\271/libinitializer/StorageInitializer.h" "b/BFPL\345\243\271/libinitializer/StorageInitializer.h" new file mode 100644 index 00000000..9b3dec61 --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/StorageInitializer.h" @@ -0,0 +1,92 @@ +/** + * Copyright (C) 2021 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. + * + * @brief initializer for the storage + * @file StorageInitializer.h + * @author: yujiechen + * @date 2021-06-11 + * @brief initializer for the storage + * @file StorageInitializer.h + * @author: ancelmo + * @date 2021-10-14 + */ +#pragma once +#include "boost/filesystem.hpp" +#include "rocksdb/convenience.h" +#include "rocksdb/write_batch.h" +#include +#include +#include +#include + +namespace bcos::initializer +{ +class StorageInitializer +{ +public: + static bcos::storage::TransactionalStorageInterface::Ptr build(const std::string& _storagePath, + const bcos::security::DataEncryptInterface::Ptr _dataEncrypt, + [[maybe_unused]] size_t keyPageSize = 0) + { + boost::filesystem::create_directories(_storagePath); + rocksdb::DB* db; + rocksdb::Options options; + // Note: This option will increase much memory + // options.IncreaseParallelism(); + // Note: This option will increase much memory + // options.OptimizeLevelStyleCompaction(); + // create the DB if it's not already present + options.create_if_missing = true; + // FIXME: enable blob support when space amplification is acceptable + // options.enable_blob_files = keyPageSize > 1 ? true : false; + options.compression = rocksdb::kZSTD; + options.max_open_files = 512; + // options.min_blob_size = 1024; + + if (boost::filesystem::space(_storagePath).available < 1024 * 1024 * 100) + { + BCOS_LOG(INFO) << "available disk space is less than 100MB"; + throw std::runtime_error("available disk space is less than 100MB"); + } + + // open DB + rocksdb::Status s = rocksdb::DB::Open(options, _storagePath, &db); + if (!s.ok()) + { + BCOS_LOG(INFO) << LOG_DESC("open rocksDB failed") << LOG_KV("error", s.ToString()); + throw std::runtime_error("open rocksDB failed, err:" + s.ToString()); + } + auto unique_db = std::unique_ptr>( + db, [](rocksdb::DB* db) { + CancelAllBackgroundWork(db, true); + delete db; + }); + return std::make_shared(std::move(unique_db), _dataEncrypt); + } + +#ifdef WITH_TIKV + static bcos::storage::TransactionalStorageInterface::Ptr build( + const std::vector& _pdAddrs, const std::string& _logPath, + const std::string& caPath = std::string(""), const std::string& certPath = std::string(""), + const std::string& keyPath = std::string("")) + { + boost::filesystem::create_directories(_logPath); + std::shared_ptr cluster = + storage::newTiKVClient(_pdAddrs, _logPath, caPath, certPath, keyPath); + return std::make_shared(cluster); + } +#endif +}; +} // namespace bcos::initializer \ No newline at end of file diff --git "a/BFPL\345\243\271/libinitializer/TxPoolInitializer.cpp" "b/BFPL\345\243\271/libinitializer/TxPoolInitializer.cpp" new file mode 100644 index 00000000..bf35ec7e --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/TxPoolInitializer.cpp" @@ -0,0 +1,97 @@ +/** + * Copyright (C) 2021 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. + * + * @brief initializer for the TxPool module + * @file TxPoolInitializer.cpp + * @author: yujiechen + * @date 2021-06-10 + */ +#include "TxPoolInitializer.h" +#include "Common.h" +#include +#include + +using namespace bcos; +using namespace bcos::txpool; +using namespace bcos::initializer; + +TxPoolInitializer::TxPoolInitializer(bcos::tool::NodeConfig::Ptr _nodeConfig, + ProtocolInitializer::Ptr _protocolInitializer, + bcos::front::FrontServiceInterface::Ptr _frontService, + bcos::ledger::LedgerInterface::Ptr _ledger, bool _preStoreTxs) + : m_nodeConfig(_nodeConfig), + m_protocolInitializer(_protocolInitializer), + m_frontService(_frontService), + m_ledger(_ledger) +{ + auto keyPair = m_protocolInitializer->keyPair(); + auto cryptoSuite = m_protocolInitializer->cryptoSuite(); + auto txpoolFactory = std::make_shared(keyPair->publicKey(), cryptoSuite, + m_protocolInitializer->txResultFactory(), m_protocolInitializer->blockFactory(), + m_frontService, m_ledger, m_nodeConfig->groupId(), m_nodeConfig->chainId(), + m_nodeConfig->blockLimit()); + // init the txpool + m_txpool = txpoolFactory->createTxPool(m_nodeConfig->notifyWorkerNum(), + m_nodeConfig->verifierWorkerNum(), m_nodeConfig->txsExpirationTime(), _preStoreTxs); + auto txpoolConfig = m_txpool->txpoolConfig(); + txpoolConfig->setPoolLimit(m_nodeConfig->txpoolLimit()); +} + +void TxPoolInitializer::init(bcos::sealer::SealerInterface::Ptr _sealer) +{ + m_txpool->registerUnsealedTxsNotifier( + [_sealer](size_t _unsealedTxsSize, std::function _onRecv) { + try + { + _sealer->asyncNoteUnSealedTxsSize(_unsealedTxsSize, _onRecv); + } + catch (std::exception const& e) + { + INITIALIZER_LOG(WARNING) + << LOG_DESC("call UnsealedTxsNotifier to the sealer exception") + << LOG_KV("error", boost::diagnostic_information(e)); + } + }); + m_txpool->init(); +} + +void TxPoolInitializer::start() +{ + if (m_running) + { + INITIALIZER_LOG(INFO) << LOG_DESC("The txpool has already been started"); + return; + } + INITIALIZER_LOG(INFO) << LOG_DESC("Start the txpool"); + m_running = true; + m_txpool->start(); +} + +void TxPoolInitializer::stop() +{ + if (!m_running) + { + INITIALIZER_LOG(INFO) << LOG_DESC("The txpool has already been stopped"); + return; + } + INITIALIZER_LOG(INFO) << LOG_DESC("Stop the txpool"); + m_running = false; + m_txpool->stop(); +} + +bcos::txpool::TxPoolInterface::Ptr TxPoolInitializer::txpool() +{ + return m_txpool; +} diff --git "a/BFPL\345\243\271/libinitializer/TxPoolInitializer.h" "b/BFPL\345\243\271/libinitializer/TxPoolInitializer.h" new file mode 100644 index 00000000..9b7fbd7c --- /dev/null +++ "b/BFPL\345\243\271/libinitializer/TxPoolInitializer.h" @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2021 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. + * + * @brief initializer for the TxPool module + * @file TxPoolInitializer.h + * @author: yujiechen + * @date 2021-06-10 + */ +#pragma once +#include "libinitializer/ProtocolInitializer.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos +{ +namespace txpool +{ +class TxPool; +} + +namespace initializer +{ +class TxPoolInitializer +{ +public: + using Ptr = std::shared_ptr; + TxPoolInitializer(bcos::tool::NodeConfig::Ptr _nodeConfig, + ProtocolInitializer::Ptr _protocolInitializer, + bcos::front::FrontServiceInterface::Ptr _frontService, + bcos::ledger::LedgerInterface::Ptr _ledger, bool _preStoreTxs); + virtual ~TxPoolInitializer() { stop(); } + + virtual void init(bcos::sealer::SealerInterface::Ptr _sealer); + virtual void start(); + virtual void stop(); + + std::shared_ptr txpool(); + bcos::crypto::CryptoSuite::Ptr cryptoSuite() { return m_protocolInitializer->cryptoSuite(); } + +private: + bcos::tool::NodeConfig::Ptr m_nodeConfig; + ProtocolInitializer::Ptr m_protocolInitializer; + bcos::front::FrontServiceInterface::Ptr m_frontService; + bcos::ledger::LedgerInterface::Ptr m_ledger; + + std::shared_ptr m_txpool; + std::atomic_bool m_running = {false}; +}; +} // namespace initializer +} // namespace bcos \ No newline at end of file diff --git "a/BFPL\345\243\271/libtask/CMakeLists.txt" "b/BFPL\345\243\271/libtask/CMakeLists.txt" new file mode 100644 index 00000000..b2a73ab5 --- /dev/null +++ "b/BFPL\345\243\271/libtask/CMakeLists.txt" @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.17) + +add_library(bcos-task INTERFACE) +target_include_directories(bcos-task INTERFACE + $ + $) +target_link_libraries(bcos-task INTERFACE bcos-concepts) + +include(GNUInstallDirs) +install(TARGETS bcos-task EXPORT fiscobcosTargets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") +install(DIRECTORY "bcos-task" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" FILES_MATCHING PATTERN "*.h") + +if(TESTS) + enable_testing() + add_subdirectory(tests) +endif() \ No newline at end of file diff --git "a/BFPL\345\243\271/libtask/bcos-task/Coroutine.h" "b/BFPL\345\243\271/libtask/bcos-task/Coroutine.h" new file mode 100644 index 00000000..1727b0ff --- /dev/null +++ "b/BFPL\345\243\271/libtask/bcos-task/Coroutine.h" @@ -0,0 +1,9 @@ +#pragma once + +#if __APPLE__ && __clang__ +#include +namespace CO_STD = std::experimental; +#else +#include +namespace CO_STD = std; +#endif \ No newline at end of file diff --git "a/BFPL\345\243\271/libtask/bcos-task/Task.h" "b/BFPL\345\243\271/libtask/bcos-task/Task.h" new file mode 100644 index 00000000..bcfd13f9 --- /dev/null +++ "b/BFPL\345\243\271/libtask/bcos-task/Task.h" @@ -0,0 +1,165 @@ +#pragma once +#include "Coroutine.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace bcos::task +{ + +// clang-format off +struct NoReturnValue : public bcos::error::Exception {}; +// clang-format on + +template +class TaskBase +{ +public: + friend TaskImpl; + + using ReturnType = Value; + + struct PromiseVoid; + struct PromiseValue; + using promise_type = std::conditional_t, PromiseVoid, PromiseValue>; + + template + struct PromiseBase + { + constexpr CO_STD::suspend_always initial_suspend() const noexcept { return {}; } + constexpr auto final_suspend() const noexcept + { + struct FinalAwaitable + { + constexpr bool await_ready() const noexcept { return false; } + void await_suspend(CO_STD::coroutine_handle handle) noexcept + { + if (handle.promise().m_continuationHandle) + { + handle.promise().m_continuationHandle.resume(); + } + handle.destroy(); + } + constexpr void await_resume() noexcept {} + }; + return FinalAwaitable{}; + } + constexpr TaskImpl get_return_object() + { + auto handle = CO_STD::coroutine_handle::from_promise( + *static_cast(this)); + return TaskImpl(handle); + } + void unhandled_exception() + { + m_value.template emplace(std::current_exception()); + } + + CO_STD::coroutine_handle<> m_continuationHandle; + std::conditional_t, std::variant, + std::variant> + m_value; + }; + struct PromiseVoid : public PromiseBase + { + void return_void() {} + }; + struct PromiseValue : public PromiseBase + { + template + void return_value(ReturnValue&& value) + { + PromiseBase::m_value.template emplace( + std::forward(value)); + } + }; + + explicit TaskBase(CO_STD::coroutine_handle handle) : m_handle(handle) {} + TaskBase(const TaskBase&) = delete; + TaskBase(TaskBase&& task) noexcept : m_handle(task.m_handle) { task.m_handle = nullptr; } + TaskBase& operator=(const TaskBase&) = delete; + TaskBase& operator=(TaskBase&& task) noexcept + { + m_handle = task.m_handle; + task.m_handle = nullptr; + } + ~TaskBase() = default; + + constexpr void run() { m_handle.resume(); } + +private: + CO_STD::coroutine_handle m_handle; +}; + +enum class Type +{ + LAZY, + EAGER +}; + +template +class Task : public TaskBase, Value> +{ +public: + using TaskBase, Value>::TaskBase; + using typename TaskBase, Value>::ReturnType; + using typename TaskBase, Value>::promise_type; + + struct Awaitable + { + Awaitable(Task const& task) : m_handle(task.m_handle){}; + Awaitable(const Awaitable&) = delete; + Awaitable(Awaitable&&) noexcept = default; + Awaitable& operator=(const Awaitable&) = delete; + Awaitable& operator=(Awaitable&&) noexcept = default; + ~Awaitable() = default; + + constexpr bool await_ready() const noexcept + { + return type == Type::EAGER || !m_handle || m_handle.done(); + } + + template + auto await_suspend(CO_STD::coroutine_handle handle) + { + m_handle.promise().m_continuationHandle = handle; + return m_handle; + } + constexpr Value await_resume() + { + auto& value = m_handle.promise().m_value; + if (std::holds_alternative(value)) + { + std::rethrow_exception(std::get(value)); + } + + if constexpr (!std::is_void_v) + { + if (!std::holds_alternative(value)) + { + BOOST_THROW_EXCEPTION(NoReturnValue{}); + } + + auto result = std::move(std::get(value)); + return result; + } + } + + CO_STD::coroutine_handle m_handle; + }; + Awaitable operator co_await() { return Awaitable(*static_cast(this)); } + + constexpr bool lazy() const { return type == Type::LAZY; } + + friend Awaitable; +}; + +} // namespace bcos::task \ No newline at end of file diff --git "a/BFPL\345\243\271/libtask/bcos-task/Wait.h" "b/BFPL\345\243\271/libtask/bcos-task/Wait.h" new file mode 100644 index 00000000..97093522 --- /dev/null +++ "b/BFPL\345\243\271/libtask/bcos-task/Wait.h" @@ -0,0 +1,89 @@ +#pragma once +#include "Task.h" +#include +#include +#include +#include +#include +#include + +namespace bcos::task +{ + +template +void wait(Task task, Callback callback) +{ + auto waitTask = [](Task task, Callback callback) -> task::Task { + using TaskType = std::remove_cvref_t; + try + { + if constexpr (std::is_void_v) + { + co_await task; + callback(); + } + else + { + callback(co_await task); + } + } + catch (...) + { + callback(std::current_exception()); + } + + co_return; + }; + waitTask(std::move(task), std::move(callback)).run(); +} + +template +void wait(Task task) +{ + task.run(); +} + +template +auto syncWait(Task task) +{ + std::promise promise; + auto future = promise.get_future(); + + if constexpr (std::is_void_v) + { + wait(std::move(task), [&promise](std::exception_ptr error = nullptr) { + if (error) + { + promise.set_exception(error); + } + else + { + promise.set_value(); + } + }); + future.get(); + } + else + { + wait(std::move(task), [&promise](auto&& value) mutable -> void { + using ValueType = std::remove_cvref_t; + if constexpr (std::is_same_v) + { + promise.set_exception(value); + } + else + { + promise.set_value(std::forward(value)); + } + }); + return future.get(); + } +} + +template +auto operator~(Task task) +{ + return syncWait(std::move(task)); +} + +} // namespace bcos::task \ No newline at end of file diff --git "a/BFPL\345\243\271/libtask/tests/CMakeLists.txt" "b/BFPL\345\243\271/libtask/tests/CMakeLists.txt" new file mode 100644 index 00000000..37826f08 --- /dev/null +++ "b/BFPL\345\243\271/libtask/tests/CMakeLists.txt" @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.17) + +find_package(Boost REQUIRED unit_test_framework) +find_package(TBB REQUIRED) + +add_executable(test-task TaskTest.cpp main.cpp) +target_link_libraries(test-task PUBLIC bcos-task Boost::unit_test_framework TBB::tbb) + +add_test(NAME test-task COMMAND test-task) \ No newline at end of file diff --git "a/BFPL\345\243\271/libtask/tests/TaskTest.cpp" "b/BFPL\345\243\271/libtask/tests/TaskTest.cpp" new file mode 100644 index 00000000..3b28bf12 --- /dev/null +++ "b/BFPL\345\243\271/libtask/tests/TaskTest.cpp" @@ -0,0 +1,142 @@ +#include +#include +#include +#include +#include +#include + +using namespace bcos::task; + +struct TaskFixture +{ + tbb::task_group taskGroup; +}; + +BOOST_FIXTURE_TEST_SUITE(TaskTest, TaskFixture) + +Task nothingTask() +{ + BOOST_FAIL("No expect to run!"); + co_return; +} + +Task level3() +{ + std::cout << "Level3 execute finished" << std::endl; + co_return 100; +} + +Task level2() +{ + auto numResult = co_await level3(); + BOOST_CHECK_EQUAL(numResult, 100); + + constexpr static auto mut = 100L; + + std::cout << "Level2 execute finished" << std::endl; + co_return static_cast(numResult) * mut; +} + +Task level1() +{ + auto num1 = co_await level3(); + auto num2 = co_await level2(); + + BOOST_CHECK_EQUAL(num1, 100); + BOOST_CHECK_EQUAL(num2, 10000); + + std::cout << "Level1 execute finished" << std::endl; +} + +BOOST_AUTO_TEST_CASE(normalTask) +{ + // auto task = nothingTask(); + + bool finished = false; + + bcos::task::wait( + level1(), [&finished]([[maybe_unused]] std::exception_ptr exception = nullptr) { + std::cout << "Callback called!" << std::endl; + finished = true; + }); + BOOST_CHECK_EQUAL(finished, true); + + auto num = bcos::task::syncWait(level2()); + BOOST_CHECK_EQUAL(num, 10000); +} + +Task asyncLevel2(tbb::task_group& taskGroup) +{ + struct Awaitable + { + constexpr bool await_ready() const { return false; } + + void await_suspend(CO_STD::coroutine_handle<> handle) + { + std::cout << "Start run async thread: " << handle.address() << std::endl; + taskGroup.run([this, m_handle = std::move(handle)]() { + std::this_thread::sleep_for(std::chrono::seconds(2)); + num = 100; + + std::cout << "Call m_handle.resume(): " << m_handle.address() << std::endl; + auto handle = const_cast(m_handle); + handle.resume(); + }); + } + + int await_resume() const + { + std::cout << "Call await_resume()" << std::endl; + return num; + } + + tbb::task_group& taskGroup; + int num = 0; + }; + + std::cout << "co_await Awaitable started" << std::endl; + auto num = co_await Awaitable{taskGroup, 0}; + std::cout << "co_await Awaitable ended" << std::endl; + + BOOST_CHECK_EQUAL(num, 100); + + std::cout << "asyncLevel2 co_return" << std::endl; + co_return num; +} + +Task asyncLevel1(tbb::task_group& taskGroup) +{ + std::cout << "co_await asyncLevel2 started" << std::endl; + auto num1 = co_await asyncLevel2(taskGroup); + std::cout << "co_await asyncLevel2 ended" << std::endl; + + BOOST_CHECK_EQUAL(num1, 100); + + std::cout << "AsyncLevel1 execute finished" << std::endl; + co_return num1 * 2; +} + +BOOST_AUTO_TEST_CASE(asyncTask) +{ + auto num = bcos::task::syncWait(asyncLevel1(taskGroup)); + BOOST_CHECK_EQUAL(num, 200); + + bcos::task::wait(asyncLevel1(taskGroup), [](auto&& result) { + using ResultType = std::remove_cvref_t; + if constexpr (std::is_same_v) + { + // nothing to do + } + else + { + BOOST_CHECK_EQUAL(result, 200); + std::cout << "Got async result" << std::endl; + } + }); + std::cout << "Top task destroyed" << std::endl; + + taskGroup.wait(); + std::cout << "asyncTask test over" << std::endl; +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/libtask/tests/main.cpp" "b/BFPL\345\243\271/libtask/tests/main.cpp" new file mode 100644 index 00000000..0cdfc68c --- /dev/null +++ "b/BFPL\345\243\271/libtask/tests/main.cpp" @@ -0,0 +1,4 @@ +#define BOOST_TEST_MODULE FISCO_BCOS_Tests +#define BOOST_TEST_MAIN + +#include \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/CMakeLists.txt" "b/BFPL\345\243\271/lightnode/CMakeLists.txt" new file mode 100644 index 00000000..a4e105c9 --- /dev/null +++ "b/BFPL\345\243\271/lightnode/CMakeLists.txt" @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.17) +project(lightnode VERSION ${VERSION}) + +add_library(bcos-lightnode INTERFACE) +target_include_directories(bcos-lightnode INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(bcos-lightnode INTERFACE bcos-framework bcos-crypto bcos-task bcos-concepts) + +add_subdirectory(fisco-bcos-lightnode) + +if(TESTS) + enable_testing() + add_subdirectory(tests) +endif() \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/bcos-lightnode/Log.h" "b/BFPL\345\243\271/lightnode/bcos-lightnode/Log.h" new file mode 100644 index 00000000..7ca0548c --- /dev/null +++ "b/BFPL\345\243\271/lightnode/bcos-lightnode/Log.h" @@ -0,0 +1,5 @@ +#pragma once + +#define LIGHTNODE_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("LIGHTNODE") +#define TRANSACTIONPOOL_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("TRANSACTIONPOOL") +#define LEDGER_LOG(LEVEL) BCOS_LOG(LEVEL) << LOG_BADGE("LEDGER") \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/bcos-lightnode/ledger/LedgerImpl.h" "b/BFPL\345\243\271/lightnode/bcos-lightnode/ledger/LedgerImpl.h" new file mode 100644 index 00000000..8c7dcc9f --- /dev/null +++ "b/BFPL\345\243\271/lightnode/bcos-lightnode/ledger/LedgerImpl.h" @@ -0,0 +1,586 @@ +#pragma once + +#include "../Log.h" +#include "bcos-task/Task.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::ledger +{ + +// clang-format off +struct NotFoundTransaction : public bcos::error::Exception {}; +struct UnexpectedRowIndex : public bcos::error::Exception {}; +struct MismatchTransactionCount : public bcos::error::Exception {}; +struct MismatchParentHash: public bcos::error::Exception {}; +struct NotFoundBlockHeader: public bcos::error::Exception {}; +// clang-format on + +template +class LedgerImpl : public bcos::concepts::ledger::LedgerBase> +{ + friend bcos::concepts::ledger::LedgerBase>; + +public: + LedgerImpl(Storage storage) : m_storage{std::move(storage)} {} + +private: + template + task::Task impl_getBlock(bcos::concepts::block::BlockNumber auto blockNumber, + bcos::concepts::block::Block auto& block) + { + LEDGER_LOG(INFO) << "getBlock: " << blockNumber; + + auto blockNumberStr = boost::lexical_cast(blockNumber); + (co_await getBlockData(blockNumberStr, block), ...); + } + + template + task::Task impl_setBlock(bcos::concepts::block::Block auto block) + { + LEDGER_LOG(INFO) << "setBlock: " << block.blockHeader.data.blockNumber; + + auto blockNumberStr = boost::lexical_cast(block.blockHeader.data.blockNumber); + (co_await setBlockData(blockNumberStr, block), ...); + co_return; + } + + task::Task impl_getBlockNumberByHash( + bcos::concepts::bytebuffer::ByteBuffer auto const& hash, std::integral auto& number) + { + LEDGER_LOG(INFO) << "getBlockNumberByHash request"; + + auto entry = storage().getRow(SYS_HASH_2_NUMBER, bcos::concepts::bytebuffer::toView(hash)); + + if (!entry) + { + number = -1; + co_return; + } + + try + { + number = boost::lexical_cast(entry->getField(0)); + } + catch (boost::bad_lexical_cast& e) + { + // Ignore the exception + LEDGER_LOG(INFO) << "Cast blockNumber failed, may be empty, set to default value -1" + << LOG_KV("blockNumber str", entry->getField(0)); + number = -1; + } + } + + task::Task impl_getBlockHashByNumber( + std::integral auto number, bcos::concepts::bytebuffer::ByteBuffer auto& hash) + { + LEDGER_LOG(INFO) << "getBlockHashByNumber request" << LOG_KV("blockNumber", number); + + auto key = boost::lexical_cast(number); + auto entry = storage().getRow(SYS_NUMBER_2_HASH, key); + if (!entry) + { + LEDGER_LOG(WARNING) << "Not found block number: " << number; + co_return; + } + + auto hashStr = entry->getField(0); + bcos::concepts::bytebuffer::assignTo(hashStr, hash); + } + + task::Task impl_getTransactions(RANGES::range auto const& hashes, RANGES::range auto& out) + { + bcos::concepts::resizeTo(out, RANGES::size(hashes)); + using DataType = RANGES::range_value_t>; + + constexpr auto tableName = + bcos::concepts::transaction::Transaction ? SYS_HASH_2_TX : SYS_HASH_2_RECEIPT; + + LEDGER_LOG(INFO) << "getTransactions: " << tableName << " " << RANGES::size(hashes); + auto entries = storage().getRows(std::string_view{tableName}, hashes); + + bcos::concepts::resizeTo(out, RANGES::size(hashes)); + tbb::parallel_for(tbb::blocked_range(0u, RANGES::size(entries)), + [&entries, &out](const tbb::blocked_range& range) { + for (auto index = range.begin(); index != range.end(); ++index) + { + if (!entries[index]) [[unlikely]] + BOOST_THROW_EXCEPTION(NotFoundTransaction{} << bcos::error::ErrorMessage{ + "Get transaction not found"}); + + auto field = entries[index]->getField(0); + bcos::concepts::serialize::decode(field, out[index]); + } + }); + + co_return; + } + + task::Task impl_getStatus() + { + LEDGER_LOG(TRACE) << "getStatus"; + constexpr static auto keys = std::to_array({SYS_KEY_TOTAL_TRANSACTION_COUNT, + SYS_KEY_TOTAL_FAILED_TRANSACTION, SYS_KEY_CURRENT_NUMBER}); + + bcos::concepts::ledger::Status status; + auto entries = storage().getRows(SYS_CURRENT_STATE, keys); + for (auto i = 0u; i < RANGES::size(entries); ++i) + { + auto& entry = entries[i]; + + int64_t value = 0; + if (entry) [[likely]] + value = boost::lexical_cast(entry->getField(0)); + + switch (i) + { + case 0: + status.total = value; + break; + case 1: + status.failed = value; + break; + case 2: + status.blockNumber = value; + break; + default: + BOOST_THROW_EXCEPTION( + UnexpectedRowIndex{} << bcos::error::ErrorMessage{ + "Unexpected getRows index: " + boost::lexical_cast(i)}); + break; + } + } + LEDGER_LOG(TRACE) << "getStatus result: " << status.total << " | " << status.failed << " | " + << status.blockNumber; + + co_return status; + } + + template + task::Task impl_setTransactions(RANGES::range auto hashes, RANGES::range auto buffers) + { + auto count = RANGES::size(buffers); + if (count != RANGES::size(hashes)) + { + BOOST_THROW_EXCEPTION( + MismatchTransactionCount{} << bcos::error::ErrorMessage{"No match count"}); + } + constexpr auto tableName = isTransaction ? SYS_HASH_2_TX : SYS_HASH_2_RECEIPT; + + LEDGER_LOG(INFO) << "setTransactionBuffers: " << tableName << " " << RANGES::size(hashes); + + for (auto i = 0U; i < count; ++i) + { + bcos::storage::Entry entry; + entry.importFields({std::move(buffers[i])}); + + auto const& hash = hashes[i]; + storage().setRow( + tableName, std::string_view(std::data(hash), RANGES::size(hash)), std::move(entry)); + } + + co_return; + } + + template + task::Task impl_sync(LedgerType& source, bool onlyHeader) + { + auto& sourceLedger = bcos::concepts::getRef(source); + + auto status = co_await impl_getStatus(); + auto sourceStatus = co_await sourceLedger.getStatus(); + + std::optional parentBlock; + for (auto blockNumber = status.blockNumber + 1; blockNumber <= sourceStatus.blockNumber; + ++blockNumber) + { + LEDGER_LOG(INFO) << "Syncing block from remote: " << blockNumber << " | " + << sourceStatus.blockNumber << " | " << onlyHeader; + BlockType block; + if (onlyHeader) + { + co_await sourceLedger.template getBlock( + blockNumber, block); + } + else + { + co_await sourceLedger.template getBlock( + blockNumber, block); + } + + if (blockNumber > 0) // Ignore verify genesis block + { + if (!parentBlock) + { + parentBlock = BlockType(); + co_await impl_getBlock( + blockNumber - 1, *parentBlock); + } + + std::array parentHash; + bcos::concepts::hash::calculate(*parentBlock, parentHash); + + if (RANGES::empty(block.blockHeader.data.parentInfo) || + (block.blockHeader.data.parentInfo[0].blockNumber != + parentBlock->blockHeader.data.blockNumber) || + !bcos::concepts::bytebuffer::equalTo( + block.blockHeader.data.parentInfo[0].blockHash, parentHash)) + { + LEDGER_LOG(ERROR) << "ParentHash mismatch!"; + BOOST_THROW_EXCEPTION( + MismatchParentHash{} << bcos::error::ErrorMessage{"No match parentHash!"}); + } + } + + if (onlyHeader) + { + co_await impl_setBlock(block); + } + else + { + co_await impl_setBlock(block); + } + + parentBlock = std::move(block); + } + } + + template > + task::Task getBlockData( + std::string_view blockNumberKey, bcos::concepts::block::Block auto& block) + { + LEDGER_LOG(DEBUG) << "getBlockData header: " << blockNumberKey; + + auto entry = storage().getRow(SYS_NUMBER_2_BLOCK_HEADER, blockNumberKey); + if (!entry) [[unlikely]] + { + BOOST_THROW_EXCEPTION( + NotFoundBlockHeader{} << bcos::error::ErrorMessage{"Not found block header!"}); + } + + auto field = entry->getField(0); + bcos::concepts::serialize::decode(field, block.blockHeader); + + co_return; + } + + template > + task::Task getBlockData( + std::string_view blockNumberKey, bcos::concepts::block::Block auto& block) + { + LEDGER_LOG(DEBUG) << "getBlockData transaction metadata: " << blockNumberKey; + + auto entry = storage().getRow(SYS_NUMBER_2_TXS, blockNumberKey); + if (!entry) [[unlikely]] + { + LEDGER_LOG(INFO) << "GetBlock not found transaction meta data!"; + co_return; + } + + auto field = entry->getField(0); + std::remove_reference_t metadataBlock; + bcos::concepts::serialize::decode(field, metadataBlock); + block.transactionsMetaData = std::move(metadataBlock.transactionsMetaData); + block.transactionsMerkle = std::move(metadataBlock.transactionsMerkle); + block.receiptsMerkle = std::move(metadataBlock.receiptsMerkle); + } + + template + requires std::same_as || + std::same_as + task::Task getBlockData( + std::string_view blockNumberKey, bcos::concepts::block::Block auto& block) + { + LEDGER_LOG(DEBUG) << "getBlockData transactions or receipts: " << blockNumberKey; + + if (RANGES::empty(block.transactionsMetaData)) + { + LEDGER_LOG(INFO) << "GetBlock not found transaction meta data!"; + co_return; + } + + auto hashesRange = block.transactionsMetaData | RANGES::views::transform([ + ](typename decltype(block.transactionsMetaData)::value_type const& metaData) -> auto& { + return metaData.hash; + }); + auto outputSize = RANGES::size(block.transactionsMetaData); + + if constexpr (std::is_same_v) + { + bcos::concepts::resizeTo(block.transactions, outputSize); + co_await impl_getTransactions(std::move(hashesRange), block.transactions); + } + else + { + bcos::concepts::resizeTo(block.receipts, outputSize); + co_await impl_getTransactions(std::move(hashesRange), block.receipts); + } + } + + template > + task::Task getBlockData( + std::string_view blockNumberKey, bcos::concepts::block::Block auto& block) + { + LEDGER_LOG(DEBUG) << "getBlockData nonce: " << blockNumberKey; + + auto entry = storage().getRow(SYS_BLOCK_NUMBER_2_NONCES, blockNumberKey); + if (!entry) + { + LEDGER_LOG(INFO) << "GetBlock not found nonce data!"; + co_return; + } + + std::remove_reference_t nonceBlock; + auto field = entry->getField(0); + bcos::concepts::serialize::decode(field, nonceBlock); + block.nonceList = std::move(nonceBlock.nonceList); + } + + template > + task::Task getBlockData( + std::string_view blockNumberKey, bcos::concepts::block::Block auto& block) + { + LEDGER_LOG(DEBUG) << "getBlockData all: " << blockNumberKey; + + co_await getBlockData(blockNumberKey, block); + co_await getBlockData(blockNumberKey, block); + co_await getBlockData(blockNumberKey, block); + co_await getBlockData(blockNumberKey, block); + co_await getBlockData(blockNumberKey, block); + } + + template > + task::Task setBlockData( + std::string_view blockNumberKey, bcos::concepts::block::Block auto& block) + { + LEDGER_LOG(DEBUG) << "setBlockData header: " << blockNumberKey; + + // current number + bcos::storage::Entry numberEntry; + numberEntry.importFields({std::string(blockNumberKey)}); + storage().setRow(SYS_CURRENT_STATE, SYS_KEY_CURRENT_NUMBER, std::move(numberEntry)); + + // number 2 block hash + bcos::storage::Entry hashEntry; + hashEntry.importFields({block.blockHeader.dataHash}); + storage().setRow(SYS_NUMBER_2_HASH, blockNumberKey, std::move(hashEntry)); + + // block hash 2 number + bcos::storage::Entry hash2NumberEntry; + hash2NumberEntry.importFields({std::string(blockNumberKey)}); + storage().setRow(SYS_HASH_2_NUMBER, + std::string_view{block.blockHeader.dataHash.data(), block.blockHeader.dataHash.size()}, + std::move(hash2NumberEntry)); + + // number 2 header + bcos::storage::Entry number2HeaderEntry; + std::vector number2HeaderBuffer; + bcos::concepts::serialize::encode(block.blockHeader, number2HeaderBuffer); + number2HeaderEntry.importFields({std::move(number2HeaderBuffer)}); + storage().setRow(SYS_NUMBER_2_BLOCK_HEADER, blockNumberKey, std::move(number2HeaderEntry)); + + co_return; + } + + template > + task::Task setBlockData( + std::string_view blockNumberKey, bcos::concepts::block::Block auto& block) + { + LEDGER_LOG(DEBUG) << "setBlockData nonce " << blockNumberKey; + + std::remove_cvref_t blockNonceList; + blockNonceList.nonceList = std::move(block.nonceList); + bcos::storage::Entry number2NonceEntry; + std::vector number2NonceBuffer; + bcos::concepts::serialize::encode(blockNonceList, number2NonceBuffer); + number2NonceEntry.importFields({std::move(number2NonceBuffer)}); + storage().setRow(SYS_BLOCK_NUMBER_2_NONCES, blockNumberKey, std::move(number2NonceEntry)); + + co_return; + } + + template > + task::Task setBlockData( + std::string_view blockNumberKey, bcos::concepts::block::Block auto& block) + { + LEDGER_LOG(DEBUG) << "setBlockData transaction metadata: " << blockNumberKey; + + if (RANGES::empty(block.transactionsMetaData) && !RANGES::empty(block.transactions)) + { + block.transactionsMetaData.resize(block.transactions.size()); + tbb::parallel_for(tbb::blocked_range(0, block.transactions.size()), + [&block](const tbb::blocked_range& range) { + for (auto i = range.begin(); i < range.end(); ++i) + { + bcos::concepts::hash::calculate( + block.transactions[i], block.transactionsMetaData[i].hash); + } + }); + } + + if (std::empty(block.transactionsMetaData)) + { + LEDGER_LOG(INFO) << "setBlockData not found transaction meta data!"; + co_return; + } + + std::remove_cvref_t transactionsBlock; + std::swap(block.transactionsMetaData, transactionsBlock.transactionsMetaData); + + bcos::storage::Entry number2TransactionHashesEntry; + std::vector number2TransactionHashesBuffer; + bcos::concepts::serialize::encode(transactionsBlock, number2TransactionHashesBuffer); + number2TransactionHashesEntry.importFields({std::move(number2TransactionHashesBuffer)}); + storage().setRow( + SYS_NUMBER_2_TXS, blockNumberKey, std::move(number2TransactionHashesEntry)); + std::swap(transactionsBlock.transactionsMetaData, block.transactionsMetaData); + + co_return; + } + + template > + task::Task setBlockData( + std::string_view blockNumberKey, bcos::concepts::block::Block auto& block) + { + LEDGER_LOG(DEBUG) << "setBlockData transactions: " << blockNumberKey; + + if (std::empty(block.transactionsMetaData)) + { + LEDGER_LOG(INFO) << "setBlockData not found transaction meta data!"; + co_return; + } + + std::vector> transactionBuffers(block.transactions.size()); + tbb::parallel_for(tbb::blocked_range(0, block.transactions.size()), + [&block, &transactionBuffers](const tbb::blocked_range& range) { + for (auto i = range.begin(); i < range.end(); ++i) + { + auto& transaction = block.transactions[i]; + bcos::concepts::serialize::encode(transaction, transactionBuffers[i]); + } + }); + + auto hashView = block.transactionsMetaData | + RANGES::views::transform([](auto& metaData) { return metaData.hash; }); + co_await impl_setTransactions(hashView, std::move(transactionBuffers)); + } + + template > + task::Task setBlockData( + std::string_view blockNumberKey, bcos::concepts::block::Block auto& block) + { + LEDGER_LOG(DEBUG) << "setBlockData receipts: " << blockNumberKey; + + if (std::empty(block.transactionsMetaData)) + { + LEDGER_LOG(INFO) << "setBlockData not found transaction meta data!"; + co_return; + } + + std::atomic_size_t totalTransactionCount = 0; + std::atomic_size_t failedTransactionCount = 0; + std::vector> receiptBuffers(block.receipts.size()); + tbb::parallel_for(tbb::blocked_range(0, block.receipts.size()), + [&block, &totalTransactionCount, &failedTransactionCount, &receiptBuffers]( + const tbb::blocked_range& range) { + for (auto i = range.begin(); i < range.end(); ++i) + { + auto& receipt = block.receipts[i]; + if (receipt.data.status != 0) + { + ++failedTransactionCount; + } + ++totalTransactionCount; + + bcos::concepts::serialize::encode(receipt, receiptBuffers[i]); + } + }); + + auto hashView = block.transactionsMetaData | + RANGES::views::transform([](auto& metaData) { return metaData.hash; }); + co_await impl_setTransactions(hashView, std::move(receiptBuffers)); + + LEDGER_LOG(DEBUG) << LOG_DESC("Calculate tx counts in block") + << LOG_KV("number", blockNumberKey) + << LOG_KV("totalCount", totalTransactionCount) + << LOG_KV("failedCount", failedTransactionCount); + + auto transactionCount = co_await impl_getStatus(); + transactionCount.total += totalTransactionCount; + transactionCount.failed += failedTransactionCount; + + bcos::storage::Entry totalEntry; + totalEntry.importFields({boost::lexical_cast(transactionCount.total)}); + storage().setRow(SYS_CURRENT_STATE, SYS_KEY_TOTAL_TRANSACTION_COUNT, std::move(totalEntry)); + + if (transactionCount.failed > 0) + { + bcos::storage::Entry failedEntry; + failedEntry.importFields({boost::lexical_cast(transactionCount.failed)}); + storage().setRow( + SYS_CURRENT_STATE, SYS_KEY_TOTAL_FAILED_TRANSACTION, std::move(failedEntry)); + } + + LEDGER_LOG(INFO) << LOG_DESC("setBlock") + << LOG_KV("number", block.blockHeader.data.blockNumber) + << LOG_KV("totalTxs", transactionCount.total) + << LOG_KV("failedTxs", transactionCount.failed) + << LOG_KV("incTxs", totalTransactionCount) + << LOG_KV("incFailedTxs", failedTransactionCount); + } + + template > + task::Task setBlockData( + std::string_view blockNumberKey, bcos::concepts::block::Block auto& block) + { + LEDGER_LOG(DEBUG) << "setBlockData all: " << blockNumberKey; + + co_await setBlockData(blockNumberKey, block); + co_await setBlockData(blockNumberKey, block); + co_await setBlockData(blockNumberKey, block); + co_await setBlockData(blockNumberKey, block); + co_await setBlockData(blockNumberKey, block); + } + + task::Task impl_setupGenesisBlock(bcos::concepts::block::Block auto block) + { + try + { + decltype(block) currentBlock; + + co_await impl_getBlock(0, currentBlock); + co_return; + } + catch (NotFoundBlockHeader& e) + { + LEDGER_LOG(INFO) << "Not found genesis block, may be not initialized"; + } + + co_await impl_setBlock(std::move(block)); + } + + auto& storage() { return bcos::concepts::getRef(m_storage); } + + Storage m_storage; + crypto::merkle::Merkle m_merkle; // Use the default width 2 +}; + +} // namespace bcos::ledger \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/bcos-lightnode/rpc/Converter.h" "b/BFPL\345\243\271/lightnode/bcos-lightnode/rpc/Converter.h" new file mode 100644 index 00000000..0d6ade93 --- /dev/null +++ "b/BFPL\345\243\271/lightnode/bcos-lightnode/rpc/Converter.h" @@ -0,0 +1,161 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::rpc +{ + +void hex2Bin(bcos::concepts::bytebuffer::ByteBuffer auto const& hex, + bcos::concepts::bytebuffer::ByteBuffer auto& out) +{ + auto view = + RANGES::subrange(RANGES::begin(hex), RANGES::end(hex)); + + if (RANGES::size(view) >= 2 && (view[0] == '0' && view[1] == 'x')) + { + view = RANGES::subrange( + RANGES::begin(hex) + 2, RANGES::end(hex)); + } + + if ((RANGES::size(view) % 2 != 0)) [[unlikely]] + BOOST_THROW_EXCEPTION(std::invalid_argument{"Invalid input hex string!"}); + + bcos::concepts::resizeTo(out, RANGES::size(view) / 2); + boost::algorithm::unhex(RANGES::begin(view), RANGES::end(view), + (RANGES::range_value_t*)RANGES::data(out)); +} + +template +void toJsonResp(bcos::concepts::transaction::Transaction auto const& transaction, Json::Value& resp) +{ + resp["version"] = transaction.data.version; + std::string hash; + bcos::concepts::hash::calculate(transaction, hash); + resp["hash"] = toHexStringWithPrefix(hash); + resp["nonce"] = transaction.data.nonce; + resp["blockLimit"] = Json::Value((Json::Int64)transaction.data.blockLimit); + resp["to"] = transaction.data.to; + resp["from"] = toHexStringWithPrefix(transaction.sender); + resp["input"] = toHexStringWithPrefix(transaction.data.input); + resp["importTime"] = (Json::Int64)transaction.importTime; + resp["chainID"] = std::string(transaction.data.chainID); + resp["groupID"] = std::string(transaction.data.groupID); + resp["abi"] = std::string(transaction.data.abi); + resp["signature"] = toHexStringWithPrefix(transaction.signature); +} + +template +void toJsonResp(bcos::concepts::receipt::TransactionReceipt auto const& receipt, + std::string_view txHash, Json::Value& resp) +{ + resp["version"] = Json::Value((Json::Int64)receipt.data.version); + resp["contractAddress"] = receipt.data.contractAddress; + resp["gasUsed"] = receipt.data.gasUsed; + resp["status"] = Json::Value((Json::Int64)receipt.data.status); + resp["blockNumber"] = Json::Value((Json::Int64)receipt.data.blockNumber); + resp["output"] = toHexStringWithPrefix(receipt.data.output); + resp["message"] = receipt.message; + resp["transactionHash"] = "0x" + std::string(txHash); + + std::string hash; + bcos::concepts::hash::calculate(receipt, hash); + resp["hash"] = toHexStringWithPrefix(hash); + resp["logEntries"] = Json::Value(Json::arrayValue); + for (const auto& logEntry : receipt.data.logEntries) + { + Json::Value jLog; + jLog["address"] = logEntry.address; + + auto topisc = Json::Value(Json::arrayValue); + for (const auto& topic : logEntry.topic) + { + topisc.append(toHexStringWithPrefix(topic)); + } + jLog["topics"] = std::move(topisc); + jLog["data"] = toHexStringWithPrefix(logEntry.data); + resp["logEntries"].append(std::move(jLog)); + } +} + +template +void toJsonResp(bcos::concepts::receipt::TransactionReceipt auto const& receipt, + bcos::concepts::transaction::Transaction auto const& transaction, std::string_view txHash, + Json::Value& resp) +{ + toJsonResp(receipt, txHash, resp); + + resp["input"] = toHexStringWithPrefix(transaction.data.input); + resp["from"] = toHexStringWithPrefix(transaction.sender); + resp["to"] = transaction.data.to; +} + +template +void toJsonResp(bcos::concepts::block::Block auto const& block, Json::Value& jResp, bool onlyHeader) +{ + auto const& blockHeader = block.blockHeader; + std::string hash; + bcos::concepts::hash::calculate(block, hash); + jResp["hash"] = toHexStringWithPrefix(hash); + jResp["version"] = Json::Value((Json::Int64)block.version); + jResp["txsRoot"] = toHexStringWithPrefix(blockHeader.data.txsRoot); + jResp["receiptsRoot"] = toHexStringWithPrefix(blockHeader.data.receiptRoot); + jResp["stateRoot"] = toHexStringWithPrefix(blockHeader.data.stateRoot); + jResp["number"] = Json::Value((Json::Int64)blockHeader.data.blockNumber); + jResp["gasUsed"] = blockHeader.data.gasUsed; + jResp["timestamp"] = Json::Value((Json::Int64)blockHeader.data.timestamp); + jResp["sealer"] = Json::Value((Json::Int64)blockHeader.data.sealer); + jResp["extraData"] = toHexStringWithPrefix(blockHeader.data.extraData); + + jResp["consensusWeights"] = Json::Value(Json::arrayValue); + for (const auto& weight : blockHeader.data.consensusWeights) + { + jResp["consensusWeights"].append(Json::Value((Json::Int64)weight)); + } + + jResp["sealerList"] = Json::Value(Json::arrayValue); + for (const auto& sealer : blockHeader.data.sealerList) + { + jResp["sealerList"].append(toHexStringWithPrefix(sealer)); + } + + Json::Value jParentInfo(Json::arrayValue); + for (const auto& parent : blockHeader.data.parentInfo) + { + Json::Value jp; + jp["blockNumber"] = Json::Value((Json::Int64)parent.blockNumber); + jp["blockHash"] = toHexStringWithPrefix(parent.blockHash); + jParentInfo.append(std::move(jp)); + } + jResp["parentInfo"] = std::move(jParentInfo); + + Json::Value jSignList(Json::arrayValue); + for (const auto& sign : blockHeader.signatureList) + { + Json::Value jSign; + jSign["sealerIndex"] = Json::Value((Json::Int64)sign.sealerIndex); + jSign["signature"] = toHexStringWithPrefix(sign.signature); + jSignList.append(std::move(jSign)); + } + jResp["signatureList"] = std::move(jSignList); + + if (!onlyHeader) + { + Json::Value transactions(Json::arrayValue); + for (auto const& transaction : block.transactions) + { + Json::Value transactionObject; + toJsonResp(transaction, transactionObject); + + transactions.append(std::move(transactionObject)); + } + jResp["transactions"] = std::move(transactions); + } +} + +} // namespace bcos::rpc \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/bcos-lightnode/rpc/LightNodeRPC.h" "b/BFPL\345\243\271/lightnode/bcos-lightnode/rpc/LightNodeRPC.h" new file mode 100644 index 00000000..1048cd9b --- /dev/null +++ "b/BFPL\345\243\271/lightnode/bcos-lightnode/rpc/LightNodeRPC.h" @@ -0,0 +1,545 @@ +#pragma once + +#include + +#include "../Log.h" +#include "Converter.h" +#include "bcos-concepts/Basic.h" +#include "bcos-concepts/ByteBuffer.h" +#include "bcos-concepts/Exception.h" +#include "bcos-concepts/Hash.h" +#include "bcos-tars-protocol/tars/TransactionMetaData.h" +#include "bcos-tars-protocol/tars/TransactionReceipt.h" +#include "bcos-utilities/DataConvertUtility.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::rpc +{ +// clang-format off +struct NotFoundTransactionHash: public bcos::error::Exception {}; +// clang-format on + +template +class LightNodeRPC : public bcos::rpc::JsonRpcInterface +{ +public: + LightNodeRPC(LocalLedgerType localLedger, RemoteLedgerType remoteLedger, + TransactionPoolType remoteTransactionPool, SchedulerType scheduler, std::string chainID, + std::string groupID) + : m_localLedger(std::move(localLedger)), + m_remoteLedger(std::move(remoteLedger)), + m_remoteTransactionPool(std::move(remoteTransactionPool)), + m_scheduler(std::move(scheduler)), + m_chainID(std::move(chainID)), + m_groupID(std::move(groupID)) + {} + + void toErrorResp(std::exception_ptr error, RespFunc respFunc) + { + try + { + std::rethrow_exception(error); + } + catch (std::exception& e) + { + toErrorResp(e, std::move(respFunc)); + } + } + + void toErrorResp(std::exception& error, RespFunc respFunc) + { + try + { + auto& bcosError = dynamic_cast(error); + toErrorResp(std::make_shared(std::move(bcosError)), std::move(respFunc)); + } + catch ([[maybe_unused]] std::bad_cast&) + { + // no bcos error + auto bcosError = bcos::Error(-1, boost::diagnostic_information(error)); + toErrorResp(bcosError, std::move(respFunc)); + } + } + + void toErrorResp(bcos::Error::Ptr error, RespFunc respFunc) + { + Json::Value resp; + respFunc(std::move(error), resp); + } + + void call([[maybe_unused]] std::string_view groupID, [[maybe_unused]] std::string_view nodeName, + [[maybe_unused]] std::string_view to, [[maybe_unused]] std::string_view hexTransaction, + RespFunc respFunc) override + { + // call data is json + auto transaction = std::make_unique(); + decodeData(hexTransaction, transaction->data.input); + transaction->data.to = to; + transaction->data.nonce = "0"; + transaction->data.blockLimit = 0; + transaction->data.chainID = ""; + transaction->data.groupID = ""; + transaction->importTime = 0; + + LIGHTNODE_LOG(INFO) << "RPC call request, to: " << to; + + auto receipt = std::make_unique(); + + auto& transactionRef = *transaction; + auto& receiptRef = *receipt; + bcos::task::wait(scheduler().call(transactionRef, receiptRef), + [this, m_transaction = std::move(transaction), m_receipt = std::move(receipt), + m_respFunc = std::move(respFunc)](std::exception_ptr error = {}) mutable { + if (error) + { + toErrorResp(error, m_respFunc); + return; + } + + Json::Value resp; + toJsonResp(*m_receipt, {}, resp); + + LIGHTNODE_LOG(INFO) << "RPC call transaction finished"; + m_respFunc(nullptr, resp); + }); + } + + void sendTransaction([[maybe_unused]] std::string_view _groupID, + [[maybe_unused]] std::string_view _nodeName, std::string_view hexTransaction, + [[maybe_unused]] bool requireProof, RespFunc respFunc) override + { + bcos::task::wait([this](std::string_view hexTransaction, + RespFunc respFunc) -> task::Task { + try + { + bcos::bytes binData; + decodeData(hexTransaction, binData); + bcostars::Transaction transaction; + bcos::concepts::serialize::decode(binData, transaction); + + bcos::bytes txHash; + bcos::concepts::hash::calculate(transaction, txHash); + std::string txHashStr; + txHashStr.reserve(txHash.size() * 2); + boost::algorithm::hex_lower( + txHash.begin(), txHash.end(), std::back_inserter(txHashStr)); + + LIGHTNODE_LOG(INFO) << "RPC send transaction request: " + << "0x" << txHashStr; + + bcostars::TransactionReceipt receipt; + co_await remoteTransactionPool().submitTransaction(std::move(transaction), receipt); + + Json::Value resp; + toJsonResp(receipt, transaction, txHashStr, resp); + + LIGHTNODE_LOG(INFO) << "RPC send transaction finished"; + respFunc(nullptr, resp); + } + catch (std::exception& error) + { + toErrorResp(error, std::move(respFunc)); + } + }(hexTransaction, std::move(respFunc))); + } + + void getTransaction([[maybe_unused]] std::string_view _groupID, + [[maybe_unused]] std::string_view _nodeName, [[maybe_unused]] std::string_view txHash, + [[maybe_unused]] bool _requireProof, RespFunc _respFunc) override + { + bcos::task::wait([this](std::string txHash, RespFunc respFunc) -> task::Task { + try + { + LIGHTNODE_LOG(INFO) << "RPC get transaction request: " << txHash; + + std::array hashes{bcos::h256{txHash, bcos::h256::FromHex}}; + std::vector transactions; + + co_await remoteLedger().getTransactions(hashes, transactions); + + Json::Value resp; + toJsonResp(transactions[0], resp); + + respFunc(nullptr, resp); + } + catch (std::exception& error) + { + toErrorResp(error, std::move(respFunc)); + } + }(std::string(txHash), std::move(_respFunc))); + } + + void getTransactionReceipt([[maybe_unused]] std::string_view groupID, + [[maybe_unused]] std::string_view nodeName, [[maybe_unused]] std::string_view txHash, + [[maybe_unused]] bool requireProof, RespFunc respFunc) override + { + bcos::task::wait( + [this](auto remoteLedger, std::string txHash, RespFunc respFunc) -> task::Task { + try + { + LIGHTNODE_LOG(INFO) << "RPC get receipt request: " << txHash; + + std::array hashes{bcos::h256{txHash, bcos::h256::FromHex}}; + std::vector receipts(1); + std::vector transactions(1); + + co_await remoteLedger.getTransactions(hashes, receipts); + co_await remoteLedger.getTransactions(hashes, transactions); + + + Json::Value resp; + toJsonResp(receipts[0], transactions[0], txHash, resp); + + respFunc(nullptr, resp); + } + catch (std::exception& error) + { + toErrorResp(error, std::move(respFunc)); + } + }(remoteLedger(), std::string(txHash), std::move(respFunc))); + } + + void getBlockByHash([[maybe_unused]] std::string_view _groupID, + [[maybe_unused]] std::string_view _nodeName, [[maybe_unused]] std::string_view blockHash, + bool _onlyHeader, bool _onlyTxHash, RespFunc _respFunc) override + { + auto blockNumber = std::make_unique(-1); + auto hash = std::make_unique>(); + hex2Bin(blockHash, *hash); + + auto& hashRef = *hash; + auto& blockNumberRef = *blockNumber; + bcos::task::wait(localLedger().getBlockNumberByHash(hashRef, blockNumberRef), + [this, m_hash = std::move(hash), m_blockNumber = std::move(blockNumber), + m_onlyHeader = _onlyHeader, m_respFunc = std::move(_respFunc), + m_groupID = std::string(_groupID), m_nodeName = std::string(_nodeName), + m_onlyTxHash = _onlyTxHash](std::exception_ptr error = {}) mutable { + if (error) + { + toErrorResp(error, m_respFunc); + return; + } + + LIGHTNODE_LOG(INFO) + << "RPC get block by hash request: 0x" + // << bcos::toHex(*m_hash) << " " + << *m_blockNumber << " " << m_onlyHeader; + if (*m_blockNumber < 0) + BOOST_THROW_EXCEPTION(std::runtime_error{"Unable to find block hash!"}); + + getBlockByNumber(m_groupID, m_nodeName, *m_blockNumber, m_onlyHeader, m_onlyTxHash, + std::move(m_respFunc)); + }); + ; + } + + void getBlockByNumber([[maybe_unused]] std::string_view groupID, + [[maybe_unused]] std::string_view nodeName, int64_t blockNumber, bool onlyHeader, + bool onlyTxHash, RespFunc respFunc) override + { + LIGHTNODE_LOG(INFO) << "RPC get block by number request: " << blockNumber << " " + << onlyHeader; + + bcos::task::wait([](decltype(this) self, bool onlyHeader, int64_t blockNumber, + RespFunc respFunc) -> task::Task { + bcostars::Block block; + if (onlyHeader) + { + co_await self->localLedger().template getBlock( + blockNumber, block); + } + else + { + co_await self->remoteLedger().template getBlock( + blockNumber, block); + + if (!RANGES::empty(block.transactionsMetaData)) + { + // Check transaction merkle + crypto::merkle::Merkle merkle; + auto hashesRange = block.transactionsMetaData | RANGES::views::transform([ + ](const bcostars::TransactionMetaData& transactionMetaData) -> auto& { + return transactionMetaData.hash; + }); + std::vector> merkles; + merkle.generateMerkle(hashesRange, merkles); + + if (RANGES::empty(merkles)) + { + BOOST_THROW_EXCEPTION( + std::runtime_error{"Unable to generate transaction merkle root!"}); + } + + if (!bcos::concepts::bytebuffer::equalTo( + block.blockHeader.data.txsRoot, *RANGES::rbegin(merkles))) + { + BOOST_THROW_EXCEPTION(std::runtime_error{"No match transaction root!"}); + } + } + + // Check parentBlock + if (blockNumber > 0) + { + decltype(block) parentBlock; + co_await self->localLedger().template getBlock( + blockNumber - 1, parentBlock); + + std::array parentHash; + bcos::concepts::hash::calculate(parentBlock, parentHash); + + if (RANGES::empty(block.blockHeader.data.parentInfo) || + (block.blockHeader.data.parentInfo[0].blockNumber != + parentBlock.blockHeader.data.blockNumber) || + !bcos::concepts::bytebuffer::equalTo( + block.blockHeader.data.parentInfo[0].blockHash, parentHash)) + { + LIGHTNODE_LOG(ERROR) << "No match parentHash!"; + BOOST_THROW_EXCEPTION(std::runtime_error{"No match parentHash!"}); + } + } + } + + using ResultType = std::remove_cvref_t; + if constexpr (std::is_same_v) + { + self->toErrorResp(block, respFunc); + co_return; + } + else + { + Json::Value resp; + toJsonResp(block, resp, onlyHeader); + + respFunc(nullptr, resp); + } + }(this, onlyHeader, blockNumber, std::move(respFunc))); + } + + void getBlockHashByNumber([[maybe_unused]] std::string_view _groupID, + [[maybe_unused]] std::string_view _nodeName, int64_t blockNumber, + RespFunc respFunc) override + { + LIGHTNODE_LOG(INFO) << "RPC get block hash by number request: " << blockNumber; + + auto hash = std::make_unique(); + auto& hashRef = *hash; + bcos::task::wait(localLedger().getBlockHashByNumber(blockNumber, hashRef), + [this, m_hash = std::move(hash), m_respFunc = std::move(respFunc)]( + std::exception_ptr error = {}) mutable { + if (error) + { + this->toErrorResp(error, m_respFunc); + return; + } + Json::Value resp = bcos::toHexStringWithPrefix(*m_hash); + + m_respFunc(nullptr, resp); + }); + } + + void getBlockNumber( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override + { + LIGHTNODE_LOG(INFO) << "RPC get block number request"; + + bcos::task::wait(localLedger().getStatus(), + [this, m_respFunc = std::move(_respFunc)](auto&& result) mutable { + using ResultType = std::remove_cvref_t; + if constexpr (std::is_same_v) + { + this->toErrorResp(result, m_respFunc); + return; + } + else + { + Json::Value resp = result.blockNumber; + + LIGHTNODE_LOG(INFO) << "RPC get block number finished: " << result.blockNumber; + m_respFunc(nullptr, resp); + } + }); + } + + void getCode(std::string_view _groupID, std::string_view _nodeName, + std::string_view _contractAddress, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + + void getABI(std::string_view _groupID, std::string_view _nodeName, + std::string_view _contractAddress, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + + void getSealerList( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + + void getObserverList( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + + void getPbftView( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + + void getPendingTxSize( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + + void getSyncStatus( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + void getConsensusStatus( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + + void getSystemConfigByKey(std::string_view _groupID, std::string_view _nodeName, + std::string_view _keyValue, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + + void getTotalTransactionCount( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + + void getGroupPeers(std::string_view _groupID, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + void getPeers(RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + void getGroupList(RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + void getGroupInfo(std::string_view _groupID, RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + void getGroupInfoList(RespFunc _respFunc) override + { + Json::Value value; + _respFunc(BCOS_ERROR_PTR(-1, "Unspported method!"), value); + } + void getGroupNodeInfo( + std::string_view _groupID, std::string_view _nodeName, RespFunc _respFunc) override + { + LIGHTNODE_LOG(INFO) << "RPC get group node info request"; + + 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"; + + bcos::task::wait(localLedger().getStatus(), + [this, m_respFunc = std::move(_respFunc)](auto&& result) mutable { + using ResultType = std::remove_cvref_t; + if constexpr (std::is_same_v) + { + this->toErrorResp(result, m_respFunc); + return; + } + else + { + Json::Value resp = result.blockNumber; + + LIGHTNODE_LOG(INFO) << "RPC get block number finished: " << result.blockNumber; + m_respFunc(nullptr, resp); + } + }); + } + +private: + auto& localLedger() { return bcos::concepts::getRef(m_localLedger); } + auto& remoteLedger() { return bcos::concepts::getRef(m_remoteLedger); } + auto& remoteTransactionPool() { return bcos::concepts::getRef(m_remoteTransactionPool); } + auto& scheduler() { return bcos::concepts::getRef(m_scheduler); } + + void decodeData(bcos::concepts::bytebuffer::ByteBuffer auto const& input, + bcos::concepts::bytebuffer::ByteBuffer auto& out) + { + auto begin = RANGES::begin(input); + auto end = RANGES::end(input); + auto length = RANGES::size(input); + + if ((length == 0) || (length % 2 != 0)) [[unlikely]] + { + BOOST_THROW_EXCEPTION(std::runtime_error{"Unexpect hex string"}); + } + + if (*begin == '0' && *(begin + 1) == 'x') + { + begin += 2; + length -= 2; + } + + bcos::concepts::resizeTo(out, length / 2); + boost::algorithm::unhex(begin, end, RANGES::begin(out)); + } + + LocalLedgerType m_localLedger; + RemoteLedgerType m_remoteLedger; + TransactionPoolType m_remoteTransactionPool; + SchedulerType m_scheduler; + + std::string m_chainID; + std::string m_groupID; +}; +} // namespace bcos::rpc \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/bcos-lightnode/scheduler/SchedulerWrapperImpl.h" "b/BFPL\345\243\271/lightnode/bcos-lightnode/scheduler/SchedulerWrapperImpl.h" new file mode 100644 index 00000000..319975fe --- /dev/null +++ "b/BFPL\345\243\271/lightnode/bcos-lightnode/scheduler/SchedulerWrapperImpl.h" @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace bcos::scheduler +{ +template +class SchedulerWrapperImpl + : public bcos::concepts::scheduler::SchedulerBase> +{ + friend bcos::concepts::scheduler::SchedulerBase>; + +public: + SchedulerWrapperImpl(SchedulerType scheduler, bcos::crypto::CryptoSuite::Ptr cryptoSuite) + : m_scheduler(std::move(scheduler)), m_cryptoSuite(std::move(cryptoSuite)) + {} + +private: + auto& scheduler() { return bcos::concepts::getRef(m_scheduler); } + + task::Task impl_call(bcos::concepts::transaction::Transaction auto const& transaction, + bcos::concepts::receipt::TransactionReceipt auto& receipt) + { + auto transactionImpl = std::make_shared(m_cryptoSuite, + [&transaction]() { return const_cast(&transaction); }); + + struct Awaitable : public CO_STD::suspend_always + { + Awaitable(decltype(transactionImpl)& transactionImpl, SchedulerType& scheduler, + std::remove_cvref_t& receipt) + : m_transactionImpl(transactionImpl), m_scheduler(scheduler), m_receipt(receipt) + {} + + void await_suspend(CO_STD::coroutine_handle::promise_type> handle) + { + bcos::concepts::getRef(m_scheduler) + .call(std::move(m_transactionImpl), + [this, m_handle = std::move(handle)](Error::Ptr&& error, + protocol::TransactionReceipt::Ptr&& transactionReceipt) mutable { + if (!error) + { + auto tarsImpl = std::dynamic_pointer_cast< + bcostars::protocol::TransactionReceiptImpl>(transactionReceipt); + + m_receipt = std::move( + const_cast(tarsImpl->inner())); + } + else + { + m_error = std::move(error); + } + + m_handle.resume(); + }); + } + + decltype(transactionImpl)& m_transactionImpl; + SchedulerType& m_scheduler; + std::remove_cvref_t& m_receipt; + Error::Ptr m_error; + }; + + Awaitable awaitable(transactionImpl, m_scheduler, receipt); + co_await awaitable; + + auto& error = awaitable.m_error; + if (error) + { + BOOST_THROW_EXCEPTION(*error); + } + + co_return; + } + + + SchedulerType m_scheduler; + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; +}; +} // namespace bcos::scheduler \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/bcos-lightnode/storage/StorageImpl.h" "b/BFPL\345\243\271/lightnode/bcos-lightnode/storage/StorageImpl.h" new file mode 100644 index 00000000..9518a13c --- /dev/null +++ "b/BFPL\345\243\271/lightnode/bcos-lightnode/storage/StorageImpl.h" @@ -0,0 +1,97 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace bcos::storage +{ + +template +class StorageImpl : public bcos::concepts::storage::StorageBase> +{ +public: + StorageImpl(StorageType storage) : m_storage(std::move(storage)) {} + StorageImpl(const StorageImpl&) = default; + StorageImpl(StorageImpl&&) = default; + StorageImpl& operator=(const StorageImpl&) = default; + StorageImpl& operator=(StorageImpl&&) = default; + ~StorageImpl() = default; + + std::optional impl_getRow(std::string_view table, std::string_view key) + { + Error::UniquePtr error; + std::optional entry; + + storage().asyncGetRow( + table, key, [&error, &entry](Error::UniquePtr errorOut, std::optional entryOut) { + error = std::move(errorOut); + entry = std::move(entryOut); + }); + + if (error) + BOOST_THROW_EXCEPTION(*error); + + return entry; + } + + std::vector> impl_getRows( + std::string_view table, RANGES::range auto const& keys) + { + Error::UniquePtr error; + std::vector> entries; + + auto callback = [&error, &entries](Error::UniquePtr errorOut, + std::vector> entriesOut) { + error = std::move(errorOut); + entries = std::move(entriesOut); + }; + + std::vector viewArray; + viewArray.reserve(RANGES::size(keys)); + for (auto&& it : keys) + { + viewArray.emplace_back( + std::string_view((const char*)RANGES::data(it), RANGES::size(it))); + } + storage().asyncGetRows(table, viewArray, std::move(callback)); + + if (error) + BOOST_THROW_EXCEPTION(*error); + + return entries; + } + + void impl_setRow(std::string_view table, std::string_view key, Entry entry) + { + Error::UniquePtr error; + + storage().asyncSetRow(table, key, std::move(entry), + [&error](Error::UniquePtr errorOut) { error = std::move(errorOut); }); + + if (error) + BOOST_THROW_EXCEPTION(*error); + } + + void impl_createTable(std::string tableName) + { + Error::UniquePtr error; + + storage().asyncCreateTable(std::move(tableName), std::string{}, + [&error](Error::UniquePtr errorOut, [[maybe_unused]] auto&& table) { + error = std::move(errorOut); + }); + if (error) + BOOST_THROW_EXCEPTION(*error); + }; + +private: + constexpr auto& storage() { return bcos::concepts::getRef(m_storage); } + + StorageType m_storage; +}; + +static_assert(bcos::concepts::storage::Storage>, "fail!"); +} // namespace bcos::storage \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/bcos-lightnode/transaction_pool/TransactionPoolImpl.h" "b/BFPL\345\243\271/lightnode/bcos-lightnode/transaction_pool/TransactionPoolImpl.h" new file mode 100644 index 00000000..52d61e82 --- /dev/null +++ "b/BFPL\345\243\271/lightnode/bcos-lightnode/transaction_pool/TransactionPoolImpl.h" @@ -0,0 +1,61 @@ +#pragma once + +#include "../Log.h" +#include "bcos-crypto/interfaces/crypto/CryptoSuite.h" +#include "bcos-tars-protocol/protocol/TransactionImpl.h" +#include "bcos-tars-protocol/protocol/TransactionReceiptImpl.h" +#include "bcos-tars-protocol/tars/TransactionReceipt.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::transaction_pool +{ + +template +class TransactionPoolImpl : public bcos::concepts::transacton_pool::TransactionPoolBase< + TransactionPoolImpl> +{ + friend bcos::concepts::transacton_pool::TransactionPoolBase< + TransactionPoolImpl>; + +public: + TransactionPoolImpl( + bcos::crypto::CryptoSuite::Ptr cryptoSuite, TransactionPoolType transactionPool) + : m_cryptoSuite(std::move(cryptoSuite)), m_transactionPool(std::move(transactionPool)) + {} + +private: + task::Task impl_submitTransaction( + bcos::concepts::transaction::Transaction auto transaction, + bcos::concepts::receipt::TransactionReceipt auto& receipt) + { + TRANSACTIONPOOL_LOG(INFO) << "Submit transaction request"; + + auto transactionImpl = std::make_shared(m_cryptoSuite, + [m_transaction = std::move(transaction)]() mutable { return &m_transaction; }); + + auto submitResult = co_await concepts::getRef(m_transactionPool) + .submitTransaction(std::move(transactionImpl)); + + if (submitResult && submitResult->transactionReceipt()) + { + auto receiptImpl = + std::dynamic_pointer_cast( + submitResult->transactionReceipt()); + receipt = std::move(const_cast(receiptImpl->inner())); + } + + TRANSACTIONPOOL_LOG(INFO) << "Submit transaction successed"; + } + + bcos::crypto::CryptoSuite::Ptr m_cryptoSuite; + TransactionPoolType m_transactionPool; +}; +} // namespace bcos::transaction_pool \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/CMakeLists.txt" "b/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/CMakeLists.txt" new file mode 100644 index 00000000..ad8cf589 --- /dev/null +++ "b/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/CMakeLists.txt" @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.17) + +file(GLOB_RECURSE SOURCES "bcos-lightnode/*.h") + +add_executable(fisco-bcos-lightnode ${SOURCES} main.cpp) +target_link_libraries(fisco-bcos-lightnode PUBLIC bcos-cpp-sdk ${PROTOCOL_INIT_LIB} ${GATEWAY_TARGET} ${FRONT_TARGET} ${RPC_TARGET} ${STORAGE_TARGET} ${UTILITIES_TARGET} ${COMMAND_HELPER_LIB} ${TOOL_TARGET} ${TARS_PROTOCOL_TARGET} ${CRYPTO_TARGET} ${LIGHTNODE_TARGET}) \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/RPCInitializer.h" "b/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/RPCInitializer.h" new file mode 100644 index 00000000..34866561 --- /dev/null +++ "b/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/RPCInitializer.h" @@ -0,0 +1,186 @@ +#pragma once + +#include "client/LedgerClientImpl.h" +#include "client/P2PClientImpl.h" +#include "client/SchedulerClientImpl.h" +#include "client/TransactionPoolClientImpl.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::lightnode +{ + +static auto initRPC(bcos::tool::NodeConfig::Ptr nodeConfig, std::string nodeID, + bcos::gateway::Gateway::Ptr gateway, bcos::crypto::KeyFactory::Ptr keyFactory, + bcos::concepts::ledger::Ledger auto localLedger, + bcos::concepts::ledger::Ledger auto remoteLedger, + bcos::concepts::transacton_pool::TransactionPool auto transactionPool, + bcos::concepts::scheduler::Scheduler auto scheduler) +{ + bcos::rpc::RpcFactory rpcFactory(nodeConfig->chainId(), gateway, keyFactory, nullptr); + auto wsConfig = rpcFactory.initConfig(nodeConfig); + auto wsService = rpcFactory.buildWsService(wsConfig); + auto jsonrpc = std::make_shared>(localLedger, remoteLedger, + transactionPool, scheduler, nodeConfig->chainId(), nodeConfig->groupId()); + + auto jsonrpcWeakPtr = std::weak_ptr>(jsonrpc); + + wsService->registerMsgHandler(bcos::protocol::MessageType::HANDESHAKE, + [nodeConfig, nodeID, localLedger](std::shared_ptr msg, + std::shared_ptr session) { + RPC_LOG(INFO) << "LightNode handshake request"; + + auto groupInfoCodec = std::make_shared(); + bcos::cppsdk::service::HandshakeResponse handshakeResponse(std::move(groupInfoCodec)); + + auto status = ~bcos::concepts::getRef(localLedger).getStatus(); + + handshakeResponse.mutableGroupBlockNumber().insert( + std::make_pair(nodeConfig->groupId(), status.blockNumber)); + + // Generate genesis info + Json::Value genesisConfig; + genesisConfig["consensusType"] = nodeConfig->consensusType(); + genesisConfig["blockTxCountLimit"] = nodeConfig->ledgerConfig()->blockTxCountLimit(); + genesisConfig["txGasLimit"] = (int64_t)(nodeConfig->txGasLimit()); + genesisConfig["consensusLeaderPeriod"] = + nodeConfig->ledgerConfig()->leaderSwitchPeriod(); + Json::Value sealerList(Json::arrayValue); + auto consensusNodeList = nodeConfig->ledgerConfig()->consensusNodeList(); + for (auto const& node : consensusNodeList) + { + Json::Value sealer; + sealer["nodeID"] = node->nodeID()->hex(); + sealer["weight"] = node->weight(); + sealerList.append(sealer); + } + genesisConfig["sealerList"] = sealerList; + Json::FastWriter fastWriter; + std::string genesisConfigStr = fastWriter.write(genesisConfig); + // Generate genesis info end + + auto groupInfo = std::make_shared(); + groupInfo->setChainID(nodeConfig->chainId()); + groupInfo->setGenesisConfig(genesisConfigStr); + groupInfo->setGroupID(nodeConfig->groupId()); + groupInfo->setWasm(nodeConfig->isWasm()); + groupInfo->setIniConfig(""); + + auto nodeInfo = std::make_shared(); + + Json::Value iniConfig; + iniConfig["isWasm"] = nodeConfig->isWasm(); + iniConfig["smCryptoType"] = nodeConfig->smCryptoType(); + iniConfig["chainID"] = nodeConfig->chainId(); + std::string iniStr = fastWriter.write(iniConfig); + + nodeInfo->setWasm(nodeConfig->isWasm()); + nodeInfo->setSmCryptoType(nodeConfig->smCryptoType()); + + nodeInfo->setIniConfig(iniStr); + nodeInfo->setMicroService(false); + nodeInfo->setNodeName(nodeConfig->nodeName()); + nodeInfo->setNodeID(nodeID); + nodeInfo->setNodeCryptoType( + (nodeConfig->smCryptoType() ? group::NodeCryptoType::SM_NODE : + group::NodeCryptoType::NON_SM_NODE)); + + + auto protocol = bcos::protocol::ProtocolInfo(); + protocol.setMinVersion(4); + protocol.setMaxVersion(1); + nodeInfo->setNodeProtocol(std::move(protocol)); + nodeInfo->setNodeType(bcos::protocol::NodeType::None); + groupInfo->appendNodeInfo(std::move(nodeInfo)); + + std::vector groupInfoList{std::move(groupInfo)}; + handshakeResponse.setGroupInfoList(groupInfoList); + handshakeResponse.setProtocolVersion(1); + + std::string response; + handshakeResponse.encode(response); + + msg->setPayload(std::make_shared(response.begin(), response.end())); + session->asyncSendMessage(msg); + RPC_LOG(INFO) << LOG_DESC("LightNode handshake success") + << LOG_KV("version", session->version()) + << LOG_KV("endpoint", session ? session->endPoint() : "unknown") + << LOG_KV("handshakeResponse", response); + }); + wsService->registerMsgHandler(bcos::rpc::AMOPClientMessageType::AMOP_SUBTOPIC, + [](std::shared_ptr msg, + std::shared_ptr session) { + RPC_LOG(TRACE) << "LightNode amop topic request"; + }); + wsService->registerMsgHandler(bcos::protocol::MessageType::RPC_REQUEST, + [jsonrpc = std::move(jsonrpc)](std::shared_ptr msg, + std::shared_ptr session) mutable { + auto buffer = msg->payload(); + auto req = std::string_view((const char*)buffer->data(), buffer->size()); + + jsonrpc->onRPCRequest(req, [m_buffer = std::move(buffer), msg = std::move(msg), + session = std::move(session)](bcos::bytes resp) { + if (session && session->isConnected()) + { + auto buffer = std::make_shared(std::move(resp)); + msg->setPayload(buffer); + session->asyncSendMessage(msg); + } + else + { + // remove the callback + RPC_LOG(WARNING) + << LOG_DESC("Unable to send response for session has been inactive") + << LOG_KV("req", + std::string_view((const char*)m_buffer->data(), m_buffer->size())) + << LOG_KV("resp", std::string_view((const char*)resp.data(), resp.size())) + << LOG_KV("seq", msg->seq()) + << LOG_KV("endpoint", session ? session->endPoint() : std::string("")); + } + }); + }); + + auto httpServer = wsService->httpServer(); + if (httpServer) + { + httpServer->setHttpReqHandler( + [jsonrpcWeakPtr](const std::string_view req, std::function sender) { + auto jsonrpc = jsonrpcWeakPtr.lock(); + if (jsonrpc) + { + jsonrpc->onRPCRequest(req, std::move(sender)); + } + }); + } + + return wsService; +} + +} // namespace bcos::lightnode \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/client/LedgerClientImpl.h" "b/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/client/LedgerClientImpl.h" new file mode 100644 index 00000000..42cdec21 --- /dev/null +++ "b/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/client/LedgerClientImpl.h" @@ -0,0 +1,133 @@ +#pragma once + +#include + +#include "P2PClientImpl.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::ledger +{ + +class LedgerClientImpl : public bcos::concepts::ledger::LedgerBase +{ + friend bcos::concepts::ledger::LedgerBase; + +public: + LedgerClientImpl(std::shared_ptr p2p) : m_p2p(std::move(p2p)) {} + +private: + auto& p2p() { return bcos::concepts::getRef(m_p2p); } + + template + void processGetBlockFlags(bool& onlyHeaderFlag) + { + if constexpr (std::is_same_v) + { + onlyHeaderFlag = true; + } + } + + template + task::Task impl_getBlock(bcos::concepts::block::BlockNumber auto blockNumber, + bcos::concepts::block::Block auto& block) + { + bcostars::RequestBlock request; + request.blockNumber = blockNumber; + request.onlyHeader = false; + + (processGetBlockFlags(request.onlyHeader), ...); + + bcostars::ResponseBlock response; + auto nodeID = co_await p2p().randomSelectNode(); + co_await p2p().sendMessageByNodeID( + bcos::protocol::LIGHTNODE_GET_BLOCK, nodeID, request, response); + + if (response.error.errorCode) + BOOST_THROW_EXCEPTION(std::runtime_error(response.error.errorMessage)); + + std::swap(response.block, block); + } + + task::Task impl_getTransactions(RANGES::range auto const& hashes, RANGES::range auto& out) + { + using DataType = RANGES::range_value_t>; + using RequestType = std::conditional_t, + bcostars::RequestTransactions, bcostars::RequestReceipts>; + using ResponseType = std::conditional_t, + bcostars::ResponseTransactions, bcostars::ResponseReceipts>; + auto moduleID = bcos::concepts::transaction::Transaction ? + protocol::LIGHTNODE_GET_TRANSACTIONS : + protocol::LIGHTNODE_GET_RECEIPTS; + + RequestType request; + request.hashes.reserve(RANGES::size(hashes)); + for (auto& hash : hashes) + { + request.hashes.emplace_back(std::vector(hash.begin(), hash.end())); + } + request.withProof = true; + + ResponseType response; + auto nodeID = co_await p2p().randomSelectNode(); + co_await p2p().sendMessageByNodeID(moduleID, std::move(nodeID), request, response); + + if (response.error.errorCode) + BOOST_THROW_EXCEPTION(std::runtime_error(response.error.errorMessage)); + + if constexpr (bcos::concepts::transaction::Transaction) + { + bcos::concepts::resizeTo(out, response.transactions.size()); + std::move(RANGES::begin(response.transactions), RANGES::end(response.transactions), + RANGES::begin(out)); + } + else + { + bcos::concepts::resizeTo(out, response.receipts.size()); + std::move(RANGES::begin(response.receipts), RANGES::end(response.receipts), + RANGES::begin(out)); + } + } + + task::Task impl_getStatus() + { + bcostars::RequestGetStatus request; + bcostars::ResponseGetStatus response; + + auto nodeID = co_await p2p().randomSelectNode(); + + co_await p2p().sendMessageByNodeID( + protocol::LIGHTNODE_GET_STATUS, std::move(nodeID), request, response); + + if (response.error.errorCode) + { + LIGHTNODE_LOG(WARNING) << "Get status failed, errorCode: " << response.error.errorCode + << " " << response.error.errorMessage; + BOOST_THROW_EXCEPTION(std::runtime_error(response.error.errorMessage)); + } + + bcos::concepts::ledger::Status status; + status.total = response.total; + status.failed = response.failed; + status.blockNumber = response.blockNumber; + + LIGHTNODE_LOG(DEBUG) << "Got status from remote: " << status.blockNumber << " " + << response.blockNumber; + + co_return status; + } + + std::shared_ptr m_p2p; +}; +} // namespace bcos::ledger \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/client/P2PClientImpl.h" "b/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/client/P2PClientImpl.h" new file mode 100644 index 00000000..04345551 --- /dev/null +++ "b/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/client/P2PClientImpl.h" @@ -0,0 +1,195 @@ +#pragma once + +#include "bcos-concepts/Exception.h" +#include "bcos-crypto/interfaces/crypto/KeyInterface.h" +#include "bcos-lightnode/Log.h" +#include "bcos-utilities/BoostLog.h" +#include +#include +#include +#include +#include +#include +#include + +namespace bcos::p2p +{ + +// clang-format off +struct NoNodeAvailable: public bcos::error::Exception {}; +// clang-format on + +class P2PClientImpl +{ +public: + P2PClientImpl(bcos::front::FrontServiceInterface::Ptr front, + bcos::gateway::GatewayInterface::Ptr gateway, bcos::crypto::KeyFactoryImpl::Ptr keyFactory, + std::string groupID) + : m_front(std::move(front)), + m_gateway(std::move(gateway)), + m_keyFactory(std::move(keyFactory)), + m_groupID(std::move(groupID)), + m_rng(std::random_device{}()) + {} + + task::Task sendMessageByNodeID(int moduleID, crypto::NodeIDPtr nodeID, + bcos::concepts::serialize::Serializable auto const& request, + bcos::concepts::serialize::Serializable auto& response) + { + bcos::bytes requestBuffer; + bcos::concepts::serialize::encode(request, requestBuffer); + + using ResponseType = std::remove_cvref_t; + struct Awaitable + { + Awaitable(bcos::front::FrontServiceInterface::Ptr& front, int moduleID, + crypto::NodeIDPtr nodeID, bcos::bytes buffer, ResponseType& response) + : m_front(front), + m_moduleID(moduleID), + m_nodeID(std::move(nodeID)), + m_requestBuffer(std::move(buffer)), + m_response(response) + {} + constexpr bool await_ready() const { return false; } + + void await_suspend(CO_STD::coroutine_handle::promise_type> handle) + { + LIGHTNODE_LOG(DEBUG) << "P2P client send message: " << m_moduleID << " | " + << m_nodeID->hex() << " | " << m_requestBuffer.size(); + bcos::concepts::getRef(m_front).asyncSendMessageByNodeID(m_moduleID, m_nodeID, + bcos::ref(m_requestBuffer), 30000, + [m_handle = std::move(handle), this](Error::Ptr error, bcos::crypto::NodeIDPtr, + bytesConstRef data, const std::string&, front::ResponseFunc) mutable { + LIGHTNODE_LOG(DEBUG) << "P2P client receive message: " << m_moduleID + << " | " << m_nodeID->hex() << " | " << data.size() + << " | " << (error ? error->errorCode() : 0) << " | " + << (error ? error->errorMessage() : ""); + if (!error) + { + bcos::concepts::serialize::decode(data, m_response); + } + else + { + m_error = std::move(error); + } + + m_handle.resume(); + }); + } + + constexpr void await_resume() const + { + if (m_error) + { + BOOST_THROW_EXCEPTION(*m_error); + } + } + + // Request params + bcos::front::FrontServiceInterface::Ptr& m_front; + int m_moduleID; + crypto::NodeIDPtr m_nodeID; + bcos::bytes m_requestBuffer; + + // Response params + Error::Ptr m_error; + ResponseType& m_response; + }; + + auto awaitable = Awaitable(m_front, moduleID, nodeID, std::move(requestBuffer), response); + co_await awaitable; + } + + task::Task randomSelectNode() + { + struct Awaitable + { + Awaitable(bcos::gateway::GatewayInterface::Ptr& gateway, std::string& groupID, + std::mt19937& rng) + : m_gateway(gateway), m_groupID(groupID), m_rng(rng) + {} + + constexpr bool await_ready() const noexcept { return false; } + void await_suspend(CO_STD::coroutine_handle<> handle) + { + bcos::concepts::getRef(m_gateway).asyncGetPeers( + [this, m_handle = handle](Error::Ptr error, const gateway::GatewayInfo::Ptr&, + const gateway::GatewayInfosPtr& peerGatewayInfos) mutable { + if (error) + { + m_error = std::move(error); + } + else + { + if (!peerGatewayInfos->empty()) + { + std::set nodeIDs; + for (const auto& peerGatewayInfo : *peerGatewayInfos) + { + auto nodeIDInfo = peerGatewayInfo->nodeIDInfo(); + auto it = nodeIDInfo.find(m_groupID); + + if (it != nodeIDInfo.end() && !it->second.empty()) + { + nodeIDs.insert(it->second.begin(), it->second.end()); + } + } + + if (!nodeIDs.empty()) + { + std::uniform_int_distribution distribution{ + 0U, nodeIDs.size() - 1}; + auto nodeIDIt = nodeIDs.begin(); + auto step = distribution(m_rng); + for (size_t i = 0; i < step; ++i) + { + ++nodeIDIt; + } + + m_nodeID = *nodeIDIt; + } + } + } + + m_handle.resume(); + }); + } + void await_resume() + { + if (m_error) + { + BOOST_THROW_EXCEPTION(*(m_error)); + } + } + + bcos::gateway::GatewayInterface::Ptr& m_gateway; + std::string& m_groupID; + std::mt19937& m_rng; + + Error::Ptr m_error; + std::string m_nodeID; + }; + + auto awaitable = Awaitable(m_gateway, m_groupID, m_rng); + co_await awaitable; + auto& nodeID = awaitable.m_nodeID; + + if (nodeID.empty()) + { + BOOST_THROW_EXCEPTION(NoNodeAvailable{}); + } + + bcos::bytes nodeIDBin; + boost::algorithm::unhex(nodeID.begin(), nodeID.end(), std::back_inserter(nodeIDBin)); + auto nodeIDPtr = m_keyFactory->createKey(nodeIDBin); + co_return nodeIDPtr; + } + +private: + bcos::front::FrontServiceInterface::Ptr m_front; + bcos::gateway::GatewayInterface::Ptr m_gateway; + bcos::crypto::KeyFactoryImpl::Ptr m_keyFactory; + std::string m_groupID; + std::mt19937 m_rng; +}; +} // namespace bcos::p2p \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/client/SchedulerClientImpl.h" "b/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/client/SchedulerClientImpl.h" new file mode 100644 index 00000000..661c9983 --- /dev/null +++ "b/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/client/SchedulerClientImpl.h" @@ -0,0 +1,41 @@ +#pragma once + +#include + +#include "P2PClientImpl.h" +#include +#include +#include + +namespace bcos::scheduler +{ +class SchedulerClientImpl : public bcos::concepts::scheduler::SchedulerBase +{ + friend bcos::concepts::scheduler::SchedulerBase; + +public: + SchedulerClientImpl(std::shared_ptr p2p) : m_p2p(std::move(p2p)) {} + +private: + auto& p2p() { return bcos::concepts::getRef(m_p2p); } + + task::Task impl_call(bcos::concepts::transaction::Transaction auto const& transaction, + bcos::concepts::receipt::TransactionReceipt auto& receipt) + { + bcostars::RequestSendTransaction request; + request.transaction = std::move(transaction); + + bcostars::ResponseSendTransaction response; + auto nodeID = co_await p2p().randomSelectNode(); + co_await p2p().sendMessageByNodeID( + bcos::protocol::LIGHTNODE_CALL, nodeID, request, response); + + if (response.error.errorCode) + BOOST_THROW_EXCEPTION(std::runtime_error(response.error.errorMessage)); + + std::swap(response.receipt, receipt); + } + + std::shared_ptr m_p2p; +}; +} // namespace bcos::scheduler \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/client/TransactionPoolClientImpl.h" "b/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/client/TransactionPoolClientImpl.h" new file mode 100644 index 00000000..7011a081 --- /dev/null +++ "b/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/client/TransactionPoolClientImpl.h" @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include "P2PClientImpl.h" +#include +#include +#include +#include +#include + +namespace bcos::transaction_pool +{ + +class TransactionPoolClientImpl + : public bcos::concepts::transacton_pool::TransactionPoolBase +{ + friend bcos::concepts::transacton_pool::TransactionPoolBase; + +public: + TransactionPoolClientImpl(std::shared_ptr p2p) : m_p2p(std::move(p2p)) {} + +private: + auto& p2p() { return bcos::concepts::getRef(m_p2p); } + + task::Task impl_submitTransaction( + bcos::concepts::transaction::Transaction auto transaction, + bcos::concepts::receipt::TransactionReceipt auto& receipt) + { + bcostars::RequestSendTransaction request; + request.transaction = std::move(transaction); + + bcostars::ResponseSendTransaction response; + auto nodeID = co_await p2p().randomSelectNode(); + co_await p2p().sendMessageByNodeID( + bcos::protocol::LIGHTNODE_SEND_TRANSACTION, nodeID, request, response); + + if (response.error.errorCode) + BOOST_THROW_EXCEPTION(std::runtime_error(response.error.errorMessage)); + + std::swap(response.receipt, receipt); + } + + std::shared_ptr m_p2p; +}; +} // namespace bcos::transaction_pool \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/main.cpp" "b/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/main.cpp" new file mode 100644 index 00000000..08db42fa --- /dev/null +++ "b/BFPL\345\243\271/lightnode/fisco-bcos-lightnode/main.cpp" @@ -0,0 +1,229 @@ +/** + * Copyright (C) 2021 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. + * + + * @brief main for the fisco-bcos + * @file main.cpp + * @author: ancelmo + * @date 2022-07-04 + */ + +#include + +#include "RPCInitializer.h" +#include "bcos-crypto/interfaces/crypto/CryptoSuite.h" +#include "bcos-lightnode/ledger/LedgerImpl.h" +#include "libinitializer/CommandHelper.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static auto newStorage(const std::string& path) +{ + boost::filesystem::create_directories(path); + rocksdb::Options options; + options.create_if_missing = true; + options.compression = rocksdb::kZSTD; + options.max_open_files = 512; + + // open DB + rocksdb::DB* rocksdb = nullptr; + rocksdb::Status status = rocksdb::DB::Open(options, path, &rocksdb); + if (!status.ok()) + { + BCOS_LOG(INFO) << LOG_DESC("open rocksDB failed") << LOG_KV("error", status.ToString()); + BOOST_THROW_EXCEPTION(std::runtime_error("open rocksDB failed, err:" + status.ToString())); + } + return std::make_shared( + std::unique_ptr(rocksdb), nullptr); +} + +static auto startSyncerThread(bcos::concepts::ledger::Ledger auto fromLedger, + bcos::concepts::ledger::Ledger auto toLedger, + std::shared_ptr wsService, std::string groupID, + std::string nodeName, std::shared_ptr stopToken) +{ + std::thread worker([fromLedger = std::move(fromLedger), toLedger = std::move(toLedger), + wsService = std::move(wsService), groupID = std::move(groupID), + nodeName = std::move(nodeName), + stopToken = std::move(stopToken)]() mutable { + bcos::pthread_setThreadName("blkNotify"); + while (!(*stopToken)) + { + try + { + auto ledger = bcos::concepts::getRef(toLedger); + + auto beforeStatus = ~ledger.getStatus(); + ~ledger.template sync, bcostars::Block>( + fromLedger, true); + auto afterStatus = ~ledger.getStatus(); + + // Notify the client if block number changed + if (afterStatus.blockNumber > beforeStatus.blockNumber) + { + auto sessions = wsService->sessions(); + std::string group; + Json::Value response; + response["group"] = groupID; + response["nodeName"] = nodeName; + response["blockNumber"] = afterStatus.blockNumber; + auto resp = response.toStyledString(); + + for (auto& session : sessions) + { + if (session && session->isConnected()) + { + auto message = wsService->messageFactory()->buildMessage(); + message->setPacketType(bcos::protocol::MessageType::BLOCK_NOTIFY); + message->setPayload( + std::make_shared(resp.begin(), resp.end())); + session->asyncSendMessage(message); + } + } + } + } + catch (std::exception& e) + { + LIGHTNODE_LOG(INFO) + << "Sync block fail, may be connecting" << boost::diagnostic_information(e); + } + std::this_thread::sleep_for(std::chrono::seconds(2)); + } + }); + + + return worker; +} + + +void starLightnode(bcos::tool::NodeConfig::Ptr nodeConfig, auto ledger, auto front, auto gateway, + auto keyFactory, auto nodeID) +{ + LIGHTNODE_LOG(INFO) << "Init lightnode p2p client..."; + auto p2pClient = std::make_shared( + front, gateway, keyFactory, nodeConfig->groupId()); + auto remoteLedger = std::make_shared(p2pClient); + auto remoteTransactionPool = + std::make_shared(p2pClient); + auto transactionPool = + std::make_shared(p2pClient); + auto scheduler = std::make_shared(p2pClient); + + LIGHTNODE_LOG(INFO) << "Prepare genesis block..."; + bcostars::Block genesisBlock; + genesisBlock.blockHeader.data.blockNumber = 0; + if (nodeConfig->compatibilityVersion() >= + static_cast(bcos::protocol::BlockVersion::V3_1_VERSION)) + { + genesisBlock.blockHeader.data.version = + static_cast( + nodeConfig->compatibilityVersion()); + } + bcos::concepts::bytebuffer::assignTo( + nodeConfig->genesisData(), genesisBlock.blockHeader.data.extraData); + ~ledger->setupGenesisBlock(std::move(genesisBlock)); + + LIGHTNODE_LOG(INFO) << "Init lightnode rpc..."; + auto wsService = bcos::lightnode::initRPC( + nodeConfig, nodeID, gateway, keyFactory, ledger, remoteLedger, transactionPool, scheduler); + wsService->start(); + + LIGHTNODE_LOG(INFO) << "Init lightnode block syner..."; + auto stopToken = std::make_shared(false); + auto syncer = startSyncerThread( + remoteLedger, ledger, wsService, nodeConfig->groupId(), nodeConfig->nodeName(), stopToken); + syncer.join(); +} + +int main([[maybe_unused]] int argc, [[maybe_unused]] const char* argv[]) +{ + auto param = bcos::initializer::initAirNodeCommandLine(argc, argv, false); + bcos::initializer::showNodeVersionMetric(); + + std::string configFile = param.configFilePath; + std::string genesisFile = param.genesisFilePath; + + boost::property_tree::ptree configProperty; + boost::property_tree::read_ini(configFile, configProperty); + + auto logInitializer = std::make_shared(); + logInitializer->initLog(configProperty); + + g_BCOSConfig.setCodec(std::make_shared()); + + auto keyFactory = std::make_shared(); + auto nodeConfig = std::make_shared(keyFactory); + nodeConfig->loadGenesisConfig(genesisFile); + nodeConfig->loadConfig(configFile); + + auto protocolInitializer = bcos::initializer::ProtocolInitializer(); + protocolInitializer.init(nodeConfig); + protocolInitializer.loadKeyPair(nodeConfig->privateKeyPath()); + auto nodeID = protocolInitializer.keyPair()->publicKey()->hex(); + + auto front = std::make_shared(); + // gateway + bcos::gateway::GatewayFactory gatewayFactory(nodeConfig->chainId(), "local", nullptr); + auto gateway = gatewayFactory.buildGateway(configFile, true, nullptr, "localGateway"); + auto protocolInfo = g_BCOSConfig.protocolInfo(bcos::protocol::ProtocolModuleID::GatewayService); + gateway->gatewayNodeManager()->registerNode(nodeConfig->groupId(), + protocolInitializer.keyPair()->publicKey(), bcos::protocol::OBSERVER_NODE, front, + protocolInfo); + gateway->start(); + + // front + front->setMessageFactory(std::make_shared()); + front->setGroupID(nodeConfig->groupId()); + front->setNodeID(protocolInitializer.keyPair()->publicKey()); + front->setIoService(std::make_shared()); + front->setGatewayInterface(gateway); + front->setThreadPool(std::make_shared("p2p", 1)); + front->registerModuleMessageDispatcher(bcos::protocol::BlockSync, + [](const bcos::crypto::NodeIDPtr&, const std::string&, bcos::bytesConstRef) {}); + front->registerModuleMessageDispatcher(bcos::protocol::AMOP, + [](const bcos::crypto::NodeIDPtr&, const std::string&, bcos::bytesConstRef) {}); + front->start(); + + // local ledger + auto storage = newStorage(nodeConfig->storagePath()); + bcos::storage::StorageImpl storageWrapper(std::move(storage)); + + if (nodeConfig->smCryptoType()) + { + auto localLedger = std::make_shared>( + std::move(storageWrapper)); + + starLightnode(nodeConfig, localLedger, front, gateway, keyFactory, nodeID); + } + else + { + auto localLedger = std::make_shared>( + std::move(storageWrapper)); + + starLightnode(nodeConfig, localLedger, front, gateway, keyFactory, nodeID); + } + + return 0; +} diff --git "a/BFPL\345\243\271/lightnode/tests/CMakeLists.txt" "b/BFPL\345\243\271/lightnode/tests/CMakeLists.txt" new file mode 100644 index 00000000..71ea7b60 --- /dev/null +++ "b/BFPL\345\243\271/lightnode/tests/CMakeLists.txt" @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.17) + +project(test-lightnode) + +find_package(Boost REQUIRED unit_test_framework) + +add_executable(test-lightnode LedgerTest.cpp StorageTest.cpp TransactionPoolTest.cpp main.cpp) +target_link_libraries(test-lightnode PUBLIC bcos-lightnode ${TABLE_TARGET} ${TARS_PROTOCOL_TARGET} Boost::unit_test_framework) + +add_test(NAME test-lightnode COMMAND test-lightnode) \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/tests/LedgerTest.cpp" "b/BFPL\345\243\271/lightnode/tests/LedgerTest.cpp" new file mode 100644 index 00000000..ca97f040 --- /dev/null +++ "b/BFPL\345\243\271/lightnode/tests/LedgerTest.cpp" @@ -0,0 +1,382 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace bcos::ledger; + +namespace std +{ +ostream& operator<<(ostream& os, std::vector const& buffer) +{ + auto hexBuffer = boost::algorithm::hex_lower(buffer); + os << string_view{(const char*)hexBuffer.data(), hexBuffer.size()}; + return os; +} + +ostream& operator<<( + ostream& os, std::pair, bcos::storage::Entry> const& value) +{ + auto hexBuffer = boost::algorithm::hex_lower(std::string(value.second.get())); + os << std::get<0>(value.first) << ":" << std::get<1>(value.first) << " - " << hexBuffer; + return os; +} + +ostream& operator<<(ostream& os, std::array const& buffer) +{ + std::string hex; + boost::algorithm::hex_lower((const char*)buffer.data(), + (const char*)buffer.data() + buffer.size(), std::back_inserter(hex)); + os << hex; + return os; +} + +ostream& operator<<(ostream& os, bcos::storage::Entry const&) +{ + return os; +} +} // namespace std + +struct MockMemoryStorage : bcos::concepts::storage::StorageBase +{ + MockMemoryStorage( + std::map, bcos::storage::Entry, std::less<>>& data1) + : bcos::concepts::storage::StorageBase(), data(data1){}; + + std::optional impl_getRow(std::string_view table, std::string_view key) + { + auto entryIt = data.find(std::tuple{table, key}); + if (entryIt != data.end()) + { + return entryIt->second; + } + return {}; + } + + std::vector> impl_getRows( + std::string_view table, RANGES::range auto const& keys) + { + std::vector> output; + output.reserve(RANGES::size(keys)); + for (auto&& key : keys) + { + output.emplace_back(getRow(table, bcos::concepts::bytebuffer::toView(key))); + } + return output; + } + + void impl_setRow(std::string_view table, std::string_view key, bcos::storage::Entry entry) + { + auto it = data.find(std::tuple{table, key}); + if (it != data.end()) + { + it->second = std::move(entry); + } + else + { + data.emplace(std::tuple{std::string{table}, std::string{key}}, std::move(entry)); + } + } + + void impl_createTable([[maybe_unused]] std::string_view tableName) {} + + std::map, bcos::storage::Entry, std::less<>>& data; +}; + +struct LedgerImplFixture +{ + LedgerImplFixture() : storage{data} + { + // Put some entry into data + bcostars::BlockHeader header; + header.data.blockNumber = 10086; + header.data.gasUsed = "1000"; + header.data.timestamp = 5000; + + bcos::storage::Entry headerEntry; + std::vector headerBuffer; + bcos::concepts::serialize::encode(header, headerBuffer); + headerEntry.setField(0, std::move(headerBuffer)); + data.emplace(std::tuple{SYS_NUMBER_2_BLOCK_HEADER, "10086"}, std::move(headerEntry)); + + bcostars::Block transactionsBlock; + transactionsBlock.transactionsMetaData.resize(count); + for (auto i = 0u; i < count; ++i) + { + std::string hashStr = "hash_" + boost::lexical_cast(i); + transactionsBlock.transactionsMetaData[i].hash.assign(hashStr.begin(), hashStr.end()); + + // transaction + decltype(transactionsBlock.transactions)::value_type transaction; + transaction.data.chainID = "chain"; + transaction.data.groupID = "group"; + transaction.importTime = 1000; + + std::vector txBuffer; + bcos::concepts::serialize::encode(transaction, txBuffer); + bcos::storage::Entry txEntry; + txEntry.setField(0, std::move(txBuffer)); + + data.emplace(std::tuple{SYS_HASH_2_TX, hashStr}, std::move(txEntry)); + + // receipt + decltype(transactionsBlock.receipts)::value_type receipt; + receipt.data.blockNumber = 10086; + receipt.data.contractAddress = "contract"; + + std::vector receiptBuffer; + bcos::concepts::serialize::encode(receipt, receiptBuffer); + bcos::storage::Entry receiptEntry; + receiptEntry.setField(0, std::move(receiptBuffer)); + data.emplace(std::tuple{SYS_HASH_2_RECEIPT, hashStr}, std::move(receiptEntry)); + } + + std::vector txsBuffer; + bcos::concepts::serialize::encode(transactionsBlock, txsBuffer); + bcos::storage::Entry txsEntry; + txsEntry.setField(0, std::move(txsBuffer)); + data.emplace(std::tuple{SYS_NUMBER_2_TXS, "10086"}, std::move(txsEntry)); + + bcostars::Block nonceBlock; + nonceBlock.nonceList.emplace_back(std::string("i am a nonce")); + + std::vector nonceBuffer; + bcos::concepts::serialize::encode(nonceBlock, nonceBuffer); + bcos::storage::Entry nonceEntry; + nonceEntry.setField(0, std::move(nonceBuffer)); + data.emplace(std::tuple{SYS_BLOCK_NUMBER_2_NONCES, "10086"}, std::move(nonceEntry)); + } + + std::map, bcos::storage::Entry, std::less<>> data; + MockMemoryStorage storage; + + constexpr static size_t count = 100; +}; + +BOOST_FIXTURE_TEST_SUITE(LedgerImplTest, LedgerImplFixture) + +BOOST_AUTO_TEST_CASE(getBlock) +{ + bcos::ledger::LedgerImpl + ledger{storage}; + + bcostars::Block block; + bcos::task::syncWait( + ledger + .getBlock( + 10086, block)); + BOOST_CHECK_EQUAL(block.blockHeader.data.blockNumber, 10086); + BOOST_CHECK_EQUAL(block.blockHeader.data.gasUsed, "1000"); + BOOST_CHECK_EQUAL(block.blockHeader.data.timestamp, 5000); + + BOOST_CHECK_EQUAL(block.transactions.size(), count); + BOOST_CHECK_EQUAL(block.receipts.size(), count); + + for (auto i = 0U; i < count; ++i) + { + BOOST_CHECK_EQUAL(block.transactions[i].data.chainID, "chain"); + BOOST_CHECK_EQUAL(block.transactions[i].data.groupID, "group"); + BOOST_CHECK_EQUAL(block.transactions[i].importTime, 1000); + + BOOST_CHECK_EQUAL(block.receipts[i].data.blockNumber, 10086); + BOOST_CHECK_EQUAL(block.receipts[i].data.contractAddress, "contract"); + } + + bcostars::Block block2; + bcos::task::syncWait(ledger.getBlock(10086, block2)); + BOOST_CHECK_EQUAL(block2.blockHeader.data.blockNumber, 10086); + BOOST_CHECK_EQUAL(block2.blockHeader.data.gasUsed, "1000"); + BOOST_CHECK_EQUAL(block2.blockHeader.data.timestamp, 5000); + + BOOST_CHECK_EQUAL(block2.transactions.size(), count); + BOOST_CHECK_EQUAL(block2.receipts.size(), count); + + for (auto i = 0u; i < count; ++i) + { + BOOST_CHECK_EQUAL(block2.transactions[i].data.chainID, "chain"); + BOOST_CHECK_EQUAL(block2.transactions[i].data.groupID, "group"); + BOOST_CHECK_EQUAL(block2.transactions[i].importTime, 1000); + + BOOST_CHECK_EQUAL(block2.receipts[i].data.blockNumber, 10086); + BOOST_CHECK_EQUAL(block2.receipts[i].data.contractAddress, "contract"); + } + + bcostars::Block block3; + BOOST_CHECK_THROW( + bcos::task::syncWait(ledger.getBlock(10087, block3)), + bcos::ledger::NotFoundBlockHeader); + BOOST_CHECK_THROW( + bcos::task::syncWait(ledger.getBlock(10087, block3)), + bcos::ledger::NotFoundBlockHeader); +} + +BOOST_AUTO_TEST_CASE(setBlockAndGetInfo) +{ + LedgerImpl ledger{ + storage}; + + bcostars::Block block; + block.blockHeader.data.blockNumber = 100; + + for (auto i = 0U; i < count; ++i) + { + bcostars::Transaction transaction; + transaction.data.blockLimit = 1000; + transaction.data.to = "i am to"; + transaction.data.version = i; + + bcostars::TransactionReceipt receipt; + receipt.data.contractAddress = "contract to"; + if (i >= 70) + { + // receipt.data.status = -1; // FIXME: This line cause ut failed on aarch64 + } + + block.transactions.emplace_back(std::move(transaction)); + block.receipts.emplace_back(std::move(receipt)); + } + bcos::task::syncWait(ledger.setTransactions( + block.transactions)); + + bcos::concepts::hash::calculate( + block, block.blockHeader.dataHash); + + BOOST_CHECK_NO_THROW(bcos::task::syncWait(ledger.setBlock(block))); + bcostars::Block gotBlock; + ~ledger.getBlock(100, gotBlock); + + BOOST_CHECK_EQUAL(gotBlock.blockHeader.data.blockNumber, block.blockHeader.data.blockNumber); + BOOST_CHECK_EQUAL(gotBlock.transactions.size(), block.transactions.size()); + BOOST_CHECK_EQUAL(gotBlock.receipts.size(), block.receipts.size()); + BOOST_CHECK_EQUAL_COLLECTIONS(gotBlock.transactions.begin(), gotBlock.transactions.end(), + block.transactions.begin(), block.transactions.end()); + BOOST_CHECK_EQUAL_COLLECTIONS(gotBlock.receipts.begin(), gotBlock.receipts.end(), + block.receipts.begin(), block.receipts.end()); + + std::array hash; + bcos::task::syncWait(ledger.getBlockHashByNumber(100, hash)); + + std::array blockHash; + bcos::concepts::hash::calculate( + gotBlock, blockHash); + + int64_t newNumber = 100; + bcos::task::syncWait(ledger.getBlockNumberByHash(hash, newNumber)); + BOOST_CHECK_EQUAL(newNumber, 100); + + BOOST_CHECK_EQUAL(hash, blockHash); +} + +BOOST_AUTO_TEST_CASE(notExistsBlock) +{ + LedgerImpl ledger{ + storage}; + + std::vector hash; + bcos::task::syncWait(ledger.getBlockHashByNumber(50, hash)); + BOOST_CHECK(RANGES::empty(hash)); + + int64_t number = 0; + bcos::task::syncWait(ledger.getBlockNumberByHash(hash, number)); + BOOST_CHECK_EQUAL(number, -1); +} + +BOOST_AUTO_TEST_CASE(ledgerSync) +{ + using Hasher = bcos::crypto::hasher::openssl::OpenSSL_SM3_Hasher; + + std::map, bcos::storage::Entry, std::less<>> fromData; + MockMemoryStorage fromStorage(fromData); + LedgerImpl fromLedger{std::move(fromStorage)}; + + std::map, bcos::storage::Entry, std::less<>> toData; + MockMemoryStorage toStorage(toData); + LedgerImpl toLedger{std::move(toStorage)}; + + bcostars::Block genesisBlock; + genesisBlock.blockHeader.data.blockNumber = 0; + bcos::task::syncWait(fromLedger.setupGenesisBlock(genesisBlock)); + bcos::task::syncWait(toLedger.setupGenesisBlock(genesisBlock)); + + std::array lastBlockHash; + bcos::concepts::hash::calculate(genesisBlock, lastBlockHash); + + constexpr static size_t blockCount = 10; + for (auto number = 1U; number < blockCount; ++number) + { + // write some block + bcostars::Block block; + block.blockHeader.data.blockNumber = number; + + bcostars::ParentInfo parentInfo; + parentInfo.blockNumber = number - 1; + bcos::concepts::bytebuffer::assignTo(lastBlockHash, parentInfo.blockHash); + block.blockHeader.data.parentInfo.push_back(parentInfo); + + for (auto i = 0U; i < count; ++i) + { + bcostars::Transaction transaction; + transaction.data.blockLimit = 1000; + transaction.data.to = "i am to"; + transaction.data.version = i; + + bcostars::TransactionReceipt receipt; + receipt.data.contractAddress = "contract to"; + if (i >= 70) + { + receipt.data.status = -1; + } + + bcostars::TransactionMetaData metaData; + bcos::concepts::hash::calculate(transaction, metaData.hash); + + block.transactionsMetaData.emplace_back(std::move(metaData)); + block.transactions.emplace_back(std::move(transaction)); + block.receipts.emplace_back(std::move(receipt)); + + bcos::crypto::merkle::Merkle merkler; + } + bcos::task::syncWait(fromLedger.setTransactions(block.transactions)); + bcos::task::syncWait(toLedger.setTransactions(block.transactions)); + + BOOST_CHECK_NO_THROW( + bcos::task::syncWait(fromLedger.setBlock(block))); + bcos::concepts::hash::calculate(block, lastBlockHash); + } + + bcos::task::syncWait(toLedger.sync(fromLedger, false)); + + // get all block + std::vector fromBlocks(blockCount); + std::vector toBlocks(blockCount); + for (auto i = 1U; i < blockCount; ++i) + { + bcos::task::syncWait(fromLedger.getBlock(i, fromBlocks[i])); + bcos::task::syncWait(toLedger.getBlock(i, toBlocks[i])); + } + + BOOST_CHECK_EQUAL_COLLECTIONS( + fromBlocks.begin(), fromBlocks.end(), toBlocks.begin(), toBlocks.end()); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/tests/P2PClientTest.cpp" "b/BFPL\345\243\271/lightnode/tests/P2PClientTest.cpp" new file mode 100644 index 00000000..902368c9 --- /dev/null +++ "b/BFPL\345\243\271/lightnode/tests/P2PClientTest.cpp" @@ -0,0 +1,15 @@ +#include +#include + +struct P2PClientFixture +{ +}; + +BOOST_FIXTURE_TEST_SUITE(P2PClientTest, P2PClientFixture) + +BOOST_AUTO_TEST_CASE(randomGetNodeID) { + +} + + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/tests/RPCTest.cpp" "b/BFPL\345\243\271/lightnode/tests/RPCTest.cpp" new file mode 100644 index 00000000..e69de29b diff --git "a/BFPL\345\243\271/lightnode/tests/SchedulerTest.cpp" "b/BFPL\345\243\271/lightnode/tests/SchedulerTest.cpp" new file mode 100644 index 00000000..e69de29b diff --git "a/BFPL\345\243\271/lightnode/tests/StorageTest.cpp" "b/BFPL\345\243\271/lightnode/tests/StorageTest.cpp" new file mode 100644 index 00000000..597691c4 --- /dev/null +++ "b/BFPL\345\243\271/lightnode/tests/StorageTest.cpp" @@ -0,0 +1,28 @@ +#include +#include +#include + +using namespace bcos::storage; + +struct StorageSyncWrapperFixture +{ + StorageSyncWrapperFixture() : storage(std::make_shared(nullptr)) {} + + StorageImpl storage; +}; + +BOOST_FIXTURE_TEST_SUITE(StorageSyncWrapperTest, StorageSyncWrapperFixture) + +BOOST_AUTO_TEST_CASE(getRow) +{ + Entry entry; + entry.importFields({"Hello world!"}); + storage.setRow("table", "key", std::move(entry)); + + auto gotEntry = storage.getRow("table", "key"); + auto field = gotEntry->get(); + + BOOST_CHECK_EQUAL(field, "Hello world!"); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/tests/TransactionPoolTest.cpp" "b/BFPL\345\243\271/lightnode/tests/TransactionPoolTest.cpp" new file mode 100644 index 00000000..b30755e4 --- /dev/null +++ "b/BFPL\345\243\271/lightnode/tests/TransactionPoolTest.cpp" @@ -0,0 +1,118 @@ +#include "bcos-task/Wait.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template +class MockTransactionPoolMT +{ +public: + bcos::task::Task submitTransaction( + bcos::protocol::Transaction::Ptr transaction) + { + if constexpr (withError) + { + auto error = std::make_shared(-1, "mock error!"); + BOOST_THROW_EXCEPTION(*error); + } + else + { + auto result = + std::make_shared(nullptr); + bcostars::TransactionReceipt receipt; + receipt.data.status = 100; + receipt.data.blockNumber = 10086; + auto receiptObj = std::make_shared( + nullptr, [receipt = std::move(receipt)]() mutable { return &receipt; }); + result->setTransactionReceipt(receiptObj); + co_return result; + } + } +}; + +class MockTransactionPoolST +{ +public: + bcos::task::Task submitTransaction( + bcos::protocol::Transaction::Ptr transaction) + { + std::cout << "start resume at " << std::this_thread::get_id() << std::endl; + + auto result = std::make_shared(nullptr); + bcostars::TransactionReceipt receipt; + receipt.data.status = 79; + auto receiptObj = std::make_shared( + nullptr, [receipt = std::move(receipt)]() mutable { return &receipt; }); + result->setTransactionReceipt(receiptObj); + std::cout << "resume ended " << std::this_thread::get_id() << std::endl; + + co_return result; + } +}; + +struct TransactionPoolFixture +{ +}; + +BOOST_FIXTURE_TEST_SUITE(TransactionPoolTest, TransactionPoolFixture) + +BOOST_AUTO_TEST_CASE(mtTxPool) +{ + MockTransactionPoolMT mock1; + bcos::transaction_pool::TransactionPoolImpl transactionPool(nullptr, mock1); + + bcostars::Transaction transaction; + bcostars::TransactionReceipt receipt; + + std::cout << "submitTransaction start at " << std::this_thread::get_id() << std::endl; + bcos::task::syncWait(transactionPool.submitTransaction(transaction, receipt)); + std::cout << "submitTransaction success at " << std::this_thread::get_id() << std::endl; + BOOST_CHECK_EQUAL(receipt.data.blockNumber, 10086); + BOOST_CHECK_EQUAL(receipt.data.status, 100); + + MockTransactionPoolMT mock2; + bcos::transaction_pool::TransactionPoolImpl transactionPool2(nullptr, mock2); + BOOST_CHECK_THROW( + bcos::task::syncWait(transactionPool2.submitTransaction(transaction, receipt)), + bcos::Error); +} + +BOOST_AUTO_TEST_CASE(stTxPool) +{ + bcostars::Transaction transaction; + bcostars::TransactionReceipt receipt; + + MockTransactionPoolST mock3; + bcos::transaction_pool::TransactionPoolImpl transactionPool3(nullptr, mock3); + bcos::task::syncWait(transactionPool3.submitTransaction(transaction, receipt)); + + BOOST_CHECK_EQUAL(receipt.data.status, 79); +} + +bcos::task::Task mainTask() +{ + bcostars::Transaction transaction; + bcostars::TransactionReceipt receipt; + + MockTransactionPoolMT mock2; + bcos::transaction_pool::TransactionPoolImpl transactionPool2(nullptr, mock2); + BOOST_CHECK_THROW(co_await transactionPool2.submitTransaction(transaction, receipt), + boost::wrapexcept); +} + +BOOST_AUTO_TEST_CASE(multiCoro) +{ + bcos::task::syncWait(mainTask()); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git "a/BFPL\345\243\271/lightnode/tests/main.cpp" "b/BFPL\345\243\271/lightnode/tests/main.cpp" new file mode 100644 index 00000000..0cdfc68c --- /dev/null +++ "b/BFPL\345\243\271/lightnode/tests/main.cpp" @@ -0,0 +1,4 @@ +#define BOOST_TEST_MODULE FISCO_BCOS_Tests +#define BOOST_TEST_MAIN + +#include \ No newline at end of file diff --git "a/BFPL\345\243\271/tests/CMakeLists.txt" "b/BFPL\345\243\271/tests/CMakeLists.txt" new file mode 100644 index 00000000..928666b2 --- /dev/null +++ "b/BFPL\345\243\271/tests/CMakeLists.txt" @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.14) + +project(FISCO-BCOS-Test) + +file(GLOB_RECURSE SOURCES "unittest/*.cpp") + +find_package(TBB CONFIG REQUIRED) + +add_executable(fisco-bcos-test ${SOURCES}) +find_package(Boost REQUIRED unit_test_framework program_options) +target_link_libraries(fisco-bcos-test PUBLIC ${TOOL_TARGET} ${CRYPTO_TARGET} ${TARS_PROTOCOL_TARGET} Boost::program_options Boost::unit_test_framework TBB::tbb) + +# add_test(NAME fisco-bcos-test WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND fisco-bcos-test) + +add_subdirectory(perf) \ No newline at end of file diff --git "a/BFPL\345\243\271/tests/perf/CMakeLists.txt" "b/BFPL\345\243\271/tests/perf/CMakeLists.txt" new file mode 100644 index 00000000..fb4ca0ea --- /dev/null +++ "b/BFPL\345\243\271/tests/perf/CMakeLists.txt" @@ -0,0 +1,3 @@ +add_executable(storage-bench benchmark.cpp) +find_package(Boost REQUIRED program_options) +target_link_libraries(storage-bench ${CRYPTO_TARGET} ${TABLE_TARGET} ${STORAGE_TARGET} Boost::program_options) \ No newline at end of file diff --git "a/BFPL\345\243\271/tests/perf/benchmark.cpp" "b/BFPL\345\243\271/tests/perf/benchmark.cpp" new file mode 100644 index 00000000..27e6c38d --- /dev/null +++ "b/BFPL\345\243\271/tests/perf/benchmark.cpp" @@ -0,0 +1,348 @@ +#include "bcos-crypto/hash/Keccak256.h" +#include "bcos-framework/storage/StorageInterface.h" +#include "bcos-storage/RocksDBStorage.h" +#include "bcos-table/src/KeyPageStorage.h" +#include "bcos-table/src/StateStorage.h" +#include "bcos-table/src/StateStorageInterface.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace bcos; +using namespace bcos::crypto; +using namespace bcos::storage; + +int main(int argc, const char* argv[]) +{ + boost::program_options::options_description main_options("Usage of Table benchmark"); + main_options.add_options()("help,h", "print help information")("pageSize,p", + boost::program_options::value()->default_value(0), + "sizeof page(if >0 use KeyPageStorage, else use stateStorage)")("chainLength,c", + boost::program_options::value()->default_value(1), "storage queue length")("total,t", + boost::program_options::value()->default_value(100000), + "data set size")("onlyWrite,o", boost::program_options::value()->default_value(false), + "only test write performance")("sorted,s", + boost::program_options::value()->default_value(false), "use sorted data set")( + "db,d", boost::program_options::value()->default_value(0), "init db keys count"); + boost::program_options::variables_map vm; + try + { + boost::program_options::store( + boost::program_options::parse_command_line(argc, argv, main_options), vm); + } + catch (...) + { + std::cout << "invalid parameters" << std::endl; + std::cout << main_options << std::endl; + exit(0); + } + if (vm.count("help")) + { + std::cout << main_options << std::endl; + exit(0); + } + int keyPageSize = vm["pageSize"].as(); + int total = vm["total"].as(); + int storageChainLength = vm["chainLength"].as(); + bool onlyWrite = vm["onlyWrite"].as(); + bool sorted = vm["sorted"].as(); + int dbKeys = vm["db"].as(); + + storageChainLength = storageChainLength > 0 ? storageChainLength : 1; + // set log level + boost::log::core::get()->set_filter( + boost::log::trivial::severity >= boost::log::trivial::error); + + + // prepare data set + int max = std::max(total, dbKeys); + std::vector keySet(max, ""); + std::vector valueSet(max, ""); +#pragma omp parallel for + for (int i = 0; i < max; ++i) + { + keySet[i] = boost::uuids::to_string(boost::uuids::random_generator()()); + boost::erase_all(keySet[i], "-"); + valueSet[i] = boost::uuids::to_string(boost::uuids::random_generator()()); + boost::erase_all(valueSet[i], "-"); + } + if (sorted) + { + std::sort(keySet.begin(), keySet.end()); + std::sort(valueSet.begin(), valueSet.end()); + } + + std::cout << "pageSize=" << keyPageSize << "|Total=" << total << "|kv size=" << keySet[0].size() + << "|chain storage len=" << storageChainLength << std::endl + << "use keypage=" << (keyPageSize > 0 ? "true" : "false") << std::endl; + // create storage + StateStorageInterface::Ptr storage = nullptr; + auto dbPath = "./testdata/testdb"; + boost::filesystem::remove_all(dbPath); + boost::filesystem::create_directories(dbPath); + rocksdb::DB* db; + rocksdb::Options options; + options.create_if_missing = true; + options.enable_blob_files = keyPageSize > 1 ? true : false; + rocksdb::Status s = rocksdb::DB::Open(options, dbPath, &db); + if (!s.ok()) + { + std::cout << "open db failed, " << (int)s.code() << s.getState() << std::endl; + return -1; + } + // insert init keys to rocksDB + if (dbKeys > 0) + { + rocksdb::WriteBatch b; + +#pragma omp parallel for + for (int i = 0; i < dbKeys; ++i) + { +#pragma omp critical + b.Put(keySet[i], valueSet[i]); + } + db->Write(rocksdb::WriteOptions(), &b); + } + + auto rocksDBStorage = + std::make_shared(std::unique_ptr(db), nullptr); + + if (keyPageSize > 0) + { + storage = std::make_shared(rocksDBStorage, keyPageSize); + } + else + { + storage = std::make_shared(rocksDBStorage); + } + std::vector storages; + // create Table + auto testTableName = "testTable"; + auto table = storage->createTable(testTableName, "value"); + if (!table) + { + std::cout << "create table failed" << std::endl; + return -1; + } + // only write, hash, commit + auto start = std::chrono::system_clock::now(); + auto perStorageKeys = total / storageChainLength; + for (int i = 0; i < storageChainLength; ++i) + { + for (int j = 0; j < perStorageKeys; ++j) + { + auto key = keySet[j + perStorageKeys * i]; + auto entry = table->newEntry(); + entry.set(valueSet[j + perStorageKeys * i]); + table->setRow(key, entry); + } + storage->setReadOnly(true); + storages.push_back(storage); + if (keyPageSize > 0) + { + storage = std::make_shared(storage, keyPageSize); + } + else + { + storage = std::make_shared(storage); + } + table = storage->openTable(testTableName).value(); + } + auto OnlyWriteEnd = std::chrono::system_clock::now(); + std::cout << "sequential write: " + << std::chrono::duration_cast(OnlyWriteEnd - start).count() + << "ms" << std::endl; + // only read after write + for (int i = 0; i < total && !onlyWrite; ++i) + { + auto key = keySet[i]; + auto entry = table->getRow(key); + if (entry->get() != valueSet[i]) + { + std::cout << i << " get row failed at sequential read, value wrong:" << entry->get() + << "!=" << valueSet[i] << std::endl; + return -1; + } + } + auto onlyWriteReadEnd = std::chrono::system_clock::now(); + // commit and read + auto hashImpl = std::make_shared(); + for (int i = 0; i < storageChainLength && !onlyWrite; ++i) + { + auto s = storages[i]; + s->hash(hashImpl); + TraverseStorageInterface::Ptr t = + std::dynamic_pointer_cast(s); + bcos::protocol::TwoPCParams p; + rocksDBStorage->asyncPrepare(p, *t, [](bcos::Error::Ptr, uint64_t, const std::string&) { + // std::cout << "asyncPrepare finished" << std::endl; + }); + rocksDBStorage->asyncCommit(p, [](bcos::Error::Ptr, uint64_t) { + // std::cout << "asyncCommit finished" << std::endl; + }); + s.reset(); + } + auto hashAndCommitEnd = std::chrono::system_clock::now(); + storages.clear(); + storage.reset(); + table = Table(nullptr, nullptr); + rocksDBStorage.reset(); + db = nullptr; + s = rocksdb::DB::Open(options, dbPath, &db); + if (!s.ok()) + { + std::cout << "open db failed, " << (int)s.code() << s.getState() << std::endl; + return -1; + } + rocksDBStorage = + std::make_shared(std::unique_ptr(db), nullptr); + if (keyPageSize > 0) + { + storage = std::make_shared(rocksDBStorage, keyPageSize); + } + else + { + storage = std::make_shared(rocksDBStorage); + } + auto prepareCleanStorageEnd = std::chrono::system_clock::now(); + + std::cout << "sequential read : " + << std::chrono::duration_cast( + onlyWriteReadEnd - OnlyWriteEnd) + .count() + << "ms" << std::endl + << "hash and commit : " + << std::chrono::duration_cast( + hashAndCommitEnd - onlyWriteReadEnd) + .count() + << "ms" << std::endl; + if (!onlyWrite) + { // load table meta data + table = storage->openTable(testTableName).value(); + auto entry = table->getRow(keySet[0]); + } + auto loadTableMetaEnd = std::chrono::system_clock::now(); + for (int i = 0; i < total && !onlyWrite; ++i) + { // read + auto entry = table->getRow(keySet[i]); + if (entry->get() != valueSet[i]) + { + std::cout << i << " get row failed at clean read, value wrong:" << entry->get() + << "!=" << valueSet[i] << std::endl; + return -1; + } + } + auto cleanReadEnd = std::chrono::system_clock::now(); + std::cout << "clean read : " + << std::chrono::duration_cast( + cleanReadEnd - prepareCleanStorageEnd) + .count() + << "ms/" + << std::chrono::duration_cast( + loadTableMetaEnd - prepareCleanStorageEnd) + .count() + << std::endl; + // read and write, hash, commit + storage.reset(); + table = Table(nullptr, nullptr); + rocksDBStorage.reset(); + boost::filesystem::remove_all(dbPath); + boost::filesystem::create_directories(dbPath); + s = rocksdb::DB::Open(options, dbPath, &db); + if (!s.ok()) + { + std::cout << "open db failed, " << (int)s.code() << s.getState() << std::endl; + return -1; + } + // insert init keys to rocksDB + if (dbKeys > 0) + { + rocksdb::WriteBatch b; + +#pragma omp parallel for + for (int i = 0; i < dbKeys; ++i) + { +#pragma omp critical + b.Put(keySet[i], valueSet[i]); + } + db->Write(rocksdb::WriteOptions(), &b); + } + rocksDBStorage = + std::make_shared(std::unique_ptr(db), nullptr); + if (keyPageSize > 0) + { + storage = std::make_shared(rocksDBStorage, keyPageSize); + } + else + { + storage = std::make_shared(rocksDBStorage); + } + table = storage->createTable(testTableName, "value"); + if (!table) + { + std::cout << "create table failed" << std::endl; + return -1; + } + auto prepareCleanDBEnd = std::chrono::system_clock::now(); + std::cout << "prepareCleanDB : " + << std::chrono::duration_cast( + prepareCleanDBEnd - cleanReadEnd) + .count() + << "ms" << std::endl; + for (int i = 0; i < storageChainLength && !onlyWrite; ++i) + { + for (int j = 0; j < perStorageKeys; ++j) + { + auto key = keySet[j + perStorageKeys * i]; + auto entryO = table->getRow(key); + if (entryO) + { + std::cout << "get duplicated key" << std::endl; + return -1; + } + auto entry = table->newEntry(); + entry.set(valueSet[j + perStorageKeys * i]); + table->setRow(key, entry); + } + storage->setReadOnly(true); + storages.push_back(storage); + if (keyPageSize > 0) + { + storage = std::make_shared(storage, keyPageSize); + } + else + { + storage = std::make_shared(storage); + } + table = storage->openTable(testTableName).value(); + } + auto readWriteEnd = std::chrono::system_clock::now(); + for (int i = 0; i < total && !onlyWrite; ++i) + { + auto key = keySet[i]; + auto entry = table->getRow(key); + if (entry->get() != valueSet[i]) + { + std::cout << "get row failed after write" << std::endl; + return -1; + } + } + auto readWriteReadEnd = std::chrono::system_clock::now(); + std::cout << "read write : " + << std::chrono::duration_cast( + readWriteEnd - prepareCleanDBEnd) + .count() + << "ms" << std::endl + << "sequential read : " + << std::chrono::duration_cast( + readWriteReadEnd - readWriteEnd) + .count() + << "ms" << std::endl; +} \ No newline at end of file diff --git "a/BFPL\345\243\271/tests/unittest/main.cpp" "b/BFPL\345\243\271/tests/unittest/main.cpp" new file mode 100644 index 00000000..0cdfc68c --- /dev/null +++ "b/BFPL\345\243\271/tests/unittest/main.cpp" @@ -0,0 +1,4 @@ +#define BOOST_TEST_MODULE FISCO_BCOS_Tests +#define BOOST_TEST_MAIN + +#include \ No newline at end of file diff --git "a/BFPL\345\243\271/tools/.ci/Dockerfile" "b/BFPL\345\243\271/tools/.ci/Dockerfile" new file mode 100644 index 00000000..f4396aaa --- /dev/null +++ "b/BFPL\345\243\271/tools/.ci/Dockerfile" @@ -0,0 +1,45 @@ +FROM ubuntu:22.04 as builder +LABEL maintainer service@fisco.com.cn + +WORKDIR / + +ARG SOURCE_BRANCH +ENV DEBIAN_FRONTEND=noninteractive \ + SOURCE=${SOURCE_BRANCH:-master} +RUN apt-get -q update && apt-get install -qy --no-install-recommends \ + curl git clang make build-essential cmake libssl-dev zlib1g-dev ca-certificates \ + libgmp-dev flex bison patch libzstd-dev unzip ninja-build pkg-config curl zip tar \ + && ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ + && apt-get install -qy --no-install-recommends tzdata \ + && dpkg-reconfigure --frontend noninteractive tzdata \ + && rm -rf /var/lib/apt/lists/* + +RUN curl https://sh.rustup.rs -sSf | bash -s -- -y + +ENV PATH="/root/.cargo/bin:${PATH}" + +# Check cargo is visible +RUN cargo --help + +ENV VCPKG_FORCE_SYSTEM_BINARIES=1 + +RUN git clone https://github.com/FISCO-BCOS/FISCO-BCOS.git --recursive --depth=1 -b ${SOURCE} \ + && mkdir -p FISCO-BCOS/build && cd FISCO-BCOS/build \ + && cmake .. && make -j2 && strip fisco-bcos-air/fisco-bcos || cat /FISCO-BCOS/build/*.log + + +FROM ubuntu:20.04 +LABEL maintainer service@fisco.com.cn + +RUN apt-get -q update && apt-get install -qy --no-install-recommends libssl-dev zlib1g-dev libzstd-dev\ + && ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ + && apt-get install -qy --no-install-recommends tzdata \ + && dpkg-reconfigure --frontend noninteractive tzdata \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=builder /FISCO-BCOS/build/fisco-bcos-air/fisco-bcos /usr/local/bin/ + +EXPOSE 30300 20200 + +ENTRYPOINT ["/usr/local/bin/fisco-bcos"] +CMD ["--version"] \ No newline at end of file diff --git "a/BFPL\345\243\271/tools/.ci/Dockerfile_env" "b/BFPL\345\243\271/tools/.ci/Dockerfile_env" new file mode 100644 index 00000000..350b4de5 --- /dev/null +++ "b/BFPL\345\243\271/tools/.ci/Dockerfile_env" @@ -0,0 +1,13 @@ +FROM ubuntu:20.04 + +LABEL maintainer service@fisco.com.cn + +RUN apt-get -q update && apt-get install -qy --no-install-recommends libzstd-dev\ + git clang build-essential cmake libssl-dev zlib1g-dev ca-certificates \ + && export DEBIAN_FRONTEND=noninteractive \ + && ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ + && apt-get install -qy --no-install-recommends tzdata \ + && apt-get autoremove -y \ + && apt-get clean \ + && rm -rf /tmp/* \ + && rm -rf /var/lib/apt/lists/* diff --git "a/BFPL\345\243\271/tools/.ci/check-commit.sh" "b/BFPL\345\243\271/tools/.ci/check-commit.sh" new file mode 100644 index 00000000..8f6336cb --- /dev/null +++ "b/BFPL\345\243\271/tools/.ci/check-commit.sh" @@ -0,0 +1,123 @@ +#!/bin/bash +# "Copyright [2018] " +# @ function: check code format of {.h, .hpp and .cpp} files +# @ require : Make sure your machine is linux (centos/ubuntu), yum or apt is ready +# @ author : wheatli +# @ file : check-commit.sh +# @ date : 2018 + +SHELL_FOLDER=$( + cd $(dirname $0) + pwd +) + +check_script="clang-format" +commit_limit=10 +file_limit=35 +insert_limit=300 +license_line=20 + +skip_check_words="sync code|release" + +LOG_ERROR() { + content=${1} + echo -e "\033[31m"${content}"\033[0m" +} + +LOG_INFO() { + content=${1} + echo -e "\033[32m"${content}"\033[0m" +} + +execute_cmd() { + command="${1}" + eval ${command} + ret=$? + if [ $ret -ne 0 ]; then + LOG_ERROR "FAILED of command: ${command}" + exit 1 + else + LOG_INFO "SUCCESS of command: ${command}" + fi +} + +function check_codeFormat() { + # Redirect output to stderr. + exec 1>&2 + sum=0 + for file in $(git diff-index --name-status HEAD^ -- | grep -v D | grep -E '\.[ch](pp)?$' | awk '{print $2}'); do + execute_cmd "$check_script -style=file -i $file" + sum=$(expr ${sum} + $?) + done + + if [ ${sum} -ne 0 ]; then + echo "######### ERROR: Format check failed, please adjust them before commit" + exit 1 + fi +} + +function check_PR_limit() { + if [ "${PR_TITLE}" != "" ]; then + local skip=$(echo ${PR_TITLE} | grep -iaE "${skip_check_words}") + if [ ! -z "${skip}" ]; then + LOG_INFO "sync code PR, skip PR limit check!" + exit 0 + else + LOG_INFO "PR: \"${PR_TITLE}\", checking limit..." + fi + else + LOG_INFO "Could not get PR title" + exit 1 + fi + local files=$(git diff --shortstat HEAD^ | awk -F ' ' '{print $1}') + # if [ ${file_limit} -lt ${files} ]; then + # LOG_ERROR "modify ${files} files, limit is ${file_limit}" + # exit 1 + # fi + local need_check_files=$(git diff --numstat HEAD^ |awk '{print $3}'|grep -vE 'test|tools\/|fisco-bcos\/|.github\/') + echo "need check files:" + echo "${need_check_files}" + + if [ ! "${need_check_files}" ]; then + LOG_INFO "No file changed. Ok!" + exit 0 + fi + + local new_files=$(git diff HEAD^ $(echo "${need_check_files}") | grep "new file" | wc -l | xargs ) + local empty_lines=$(git diff HEAD^ $(echo "${need_check_files}") | grep -e '^+\s*$' | wc -l | xargs) + local block_lines=$(git diff HEAD^ $(echo "${need_check_files}") | grep -e '^+\s*[\{\}]\s*$' | wc -l | xargs) + local include_lines=$(git diff HEAD^ $(echo "${need_check_files}") | grep -e '^+\#include' | wc -l | xargs) + local comment_lines=$(git diff HEAD^ $(echo "${need_check_files}") | grep -e "^+\s*\/\/" | wc -l | xargs) + local insertions=$(git diff --shortstat HEAD^ $(echo "${need_check_files}")| awk -F ' ' '{print $4}') + local valid_insertions=$((insertions - new_files * license_line - comment_lines - empty_lines - block_lines - include_lines)) + echo "valid_insertions: ${valid_insertions}, insertions(${insertions}) - new_files(${new_files}) * license_line(${license_line}) - comment_lines(${comment_lines}) - empty_lines(${empty_lines}) - block_lines(${block_lines}) - include_lines(${include_lines})" + if [ ${insert_limit} -lt ${valid_insertions} ]; then + LOG_ERROR "insert ${insertions} lines, valid is ${valid_insertions}, limit is ${insert_limit}" + exit 1 + fi + local deletions=$(git diff --shortstat HEAD^ | awk -F ' ' '{print $6}') + #if [ ${delete_limit} -lt ${deletions} ];then + # LOG_ERROR "delete ${deletions} lines, limit is ${delete_limit}" + # exit 1 + #fi + local commits=$(git rev-list --count HEAD^..HEAD) + if [ ${commit_limit} -lt ${commits} ]; then + LOG_ERROR "${commits} commits, limit is ${commit_limit}" + exit 1 + fi + local unique_commit=$(git log --format="%an %s" HEAD^..HEAD | sort -u | wc -l) + if [ ${unique_commit} -ne ${commits} ]; then + LOG_ERROR "${commits} != ${unique_commit}, please make commit message unique!" + exit 1 + fi + local merges=$(git log --format=%s HEAD^..HEAD | grep -i merge | wc -l) + if [ ${merges} -gt 5 ]; then + LOG_ERROR "PR contain merge : ${merges}, Please rebase!" + exit 1 + fi + LOG_INFO "modify ${files} files, insert ${insertions} lines, valid insertion ${valid_insertions}, delete ${deletions} lines. Total ${commits} commits." + LOG_INFO "Ok!" +} + +check_codeFormat +check_PR_limit diff --git "a/BFPL\345\243\271/tools/.ci/ci_check_air.sh" "b/BFPL\345\243\271/tools/.ci/ci_check_air.sh" new file mode 100644 index 00000000..93bfa64d --- /dev/null +++ "b/BFPL\345\243\271/tools/.ci/ci_check_air.sh" @@ -0,0 +1,173 @@ +#!/bin/bash +console_branch="3.0.0" +fisco_bcos_path="../build/fisco-bcos-air/fisco-bcos" +build_chain_path="BcosAirBuilder/build_chain.sh" +current_path=`pwd` +node_list="node0 node1 node2 node3" +LOG_ERROR() { + local content=${1} + echo -e "\033[31m ${content}\033[0m" +} + +LOG_INFO() { + local content=${1} + echo -e "\033[32m ${content}\033[0m" +} + +stop_node() +{ + cd ${current_path} + LOG_INFO "exit_node >>>>>>> stop all nodes <<<<<<<<<<<" + bash nodes/127.0.0.1/stop_all.sh +} +exit_node() +{ + cd ${current_path} + for node in ${node_list} + do + LOG_ERROR "exit_node ============= print error|warn info for ${node} =============" + cat nodes/127.0.0.1/${node}/log/* |grep -iE 'error|warn|cons|connectedSize|heart' + LOG_ERROR "exit_node ============= print error|warn info for ${node} finish =============" + LOG_ERROR "exit_node ########### print nohup info for ${node} ###########" + cat nodes/127.0.0.1/${node}/nohup.out + LOG_ERROR "exit_node ########### print nohup info for ${node} finish ###########" + done + stop_node + LOG_ERROR "exit_node ######### exit for ${1}" + exit 1 +} + +init() +{ + sm_option="${1}" + cd ${current_path} + echo " ==> fisco-bcos version: " + ${fisco_bcos_path} -v + rm -rf nodes + bash ${build_chain_path} -l "127.0.0.1:4" -e ${fisco_bcos_path} "${sm_option}" + cd nodes/127.0.0.1 && bash start_all.sh +} + +check_consensus() +{ + cd ${current_path}/nodes/127.0.0.1 + LOG_INFO "=== wait for the node to init, waitTime: 20s =====" + sleep 20 + LOG_INFO "=== wait for the node to init finish =====" + for node in ${node_list} + do + LOG_INFO "check_consensus for ${node}" + result=$(cat ${node}/log/* |grep -i reachN) + if [[ -z "${result}" ]]; + then + LOG_ERROR "checkView failed ******* cons info for ${node} *******" + cat ${node}/log/* |grep -i cons + LOG_ERROR "checkView failed ******* print log info for ${node} finish *******" + exit_node "check_consensus for ${node} failed for not reachNewView" + else + LOG_INFO "check_consensus for ${node} success" + fi + done +} + +download_console() +{ + cd ${current_path} + + LOG_INFO "Download console ..." + tar_file=console-${console_branch}.tar.gz + if [ -f "${tar_file}" ]; then + LOG_INFO "Use download cache" + else + curl -#LO https://osp-1257653870.cos.ap-guangzhou.myqcloud.com/FISCO-BCOS/console/releases/v${console_branch}/console.tar.gz + LOG_INFO "Download console success, branch: ${console_branch}" + mv console.tar.gz ${tar_file} + fi + LOG_INFO "Build and Config console ..." + rm -rf console + tar -zxvf ${tar_file} + cd console +} + +config_console() +{ + cd ${current_path}/console/ + use_sm="${1}" + cp -r ${current_path}/nodes/127.0.0.1/sdk/* conf/ + cp conf/config-example.toml conf/config.toml + local sed_cmd="sed -i" + if [ "$(uname)" == "Darwin" ];then + sed_cmd="sed -i .bkp" + fi + use_sm_str="useSMCrypto = \"${use_sm}\"" + ${sed_cmd} "s/useSMCrypto = \"false\"/${use_sm_str}/g" conf/config.toml + LOG_INFO "Build and Config console success ..." +} + +send_transactions() +{ + txs_num="${1}" + cd ${current_path}/console/ + LOG_INFO "Deploy HelloWorld..." + for((i=1;i<=${txs_num};i++)); + do + bash console.sh deploy HelloWorld + sleep 1 + done + blockNumber=`bash console.sh getBlockNumber` + if [ "${blockNumber}" == "${txs_num}" ]; then + LOG_INFO "send transaction success, current blockNumber: ${blockNumber}" + else + exit_node "send transaction failed, current blockNumber: ${blockNumber}" + fi +} + +check_sync() +{ + LOG_INFO "check sync..." + expected_block_number="${1}" + cd ${current_path}/nodes/127.0.0.1 + bash node0/stop.sh && rm -rf node0/log && rm -rf node0/data + bash node0/start.sh + # wait for sync + sleep 10 + block_number=$(cat node0/log/* |grep Report | tail -n 1| awk -F',' '{print $4}' | awk -F'=' '{print $2}') + if [ "${block_number}" == "${expected_block_number}" ]; then + LOG_INFO "check_sync success, current blockNumber: ${block_number}" + else + exit_node "check_sync error, current blockNumber: ${block_number}, expected_block_number: ${expected_block_number}" + fi + LOG_INFO "check sync success..." +} + +clear_node() +{ + cd ${current_path} + bash nodes/127.0.0.1/stop_all.sh + rm -rf nodes +} + +txs_num=10 +# non-sm test +LOG_INFO "======== check non-sm case ========" +init "" +check_consensus +download_console +config_console "false" +send_transactions ${txs_num} +check_sync ${txs_num} +LOG_INFO "======== check non-sm success ========" + +LOG_INFO "======== clear node after non-sm test ========" +clear_node +LOG_INFO "======== clear node after non-sm test success ========" + +# sm test +LOG_INFO "======== check sm case ========" +init "-s" +check_consensus +config_console "true" +send_transactions ${txs_num} +check_sync ${txs_num} +stop_node +LOG_INFO "======== check sm case success ========" \ No newline at end of file diff --git "a/BFPL\345\243\271/tools/.ci/ci_check_pro.sh" "b/BFPL\345\243\271/tools/.ci/ci_check_pro.sh" new file mode 100644 index 00000000..79b7bc71 --- /dev/null +++ "b/BFPL\345\243\271/tools/.ci/ci_check_pro.sh" @@ -0,0 +1,193 @@ +#!/bin/bash +console_branch="3.0.0" +fisco_bcos_service_path="../build/fisco-bcos-tars-service/" +build_chain_path="BcosBuilder/pro/build_chain.py" +current_path=`pwd` # tools +node_list="group0_node_40402 group0_node_40412" +output_dir="pro_nodes" +LOG_ERROR() { + local content=${1} + echo -e "\033[31m ${content}\033[0m" +} + +LOG_INFO() { + local content=${1} + echo -e "\033[32m ${content}\033[0m" +} + +stop_node() +{ + cd ${current_path} + + LOG_INFO "exit_node >>>>>>> stop all pro nodes <<<<<<<<<<<" + bash ${output_dir}/127.0.0.1/stop_all.sh +} + +exit_node() +{ + cd ${current_path} + for node in ${node_list} + do + LOG_ERROR "exit_node ============= print error|warn info for ${node} =============" + cat ${output_dir}/127.0.0.1/${node}/log/* |grep -iE 'error|warn|cons|connectedSize|heart' + LOG_ERROR "exit_node ============= print error|warn info for ${node} finish =============" + LOG_ERROR "exit_node ########### print nohup info for ${node} ###########" + cat ${output_dir}/127.0.0.1/${node}/nohup.out + LOG_ERROR "exit_node ########### print nohup info for ${node} finish ###########" + done + stop_node + LOG_ERROR "exit_node ######### exit for ${1}" + exit 1 +} + +init() +{ + sm_option="${1}" + cd ${current_path} + + echo "===>> ${current_path}" + + rm -rf BcosBuilder/pro/binary/ + + mkdir -p BcosBuilder/pro/binary/ + # copy service binary + mkdir -p BcosBuilder/pro/binary/BcosNodeService + mkdir -p BcosBuilder/pro/binary/BcosGatewayService + mkdir -p BcosBuilder/pro/binary/BcosRpcService + + cp -f ${fisco_bcos_service_path}/NodeService/pro/BcosNodeService BcosBuilder/pro/binary/BcosNodeService + cp -f ${fisco_bcos_service_path}/GatewayService/main/BcosGatewayService BcosBuilder/pro/binary/BcosGatewayService + cp -f ${fisco_bcos_service_path}/RpcService/main/BcosRpcService BcosBuilder/pro/binary/BcosRpcService + rm -rf ${output_dir} + + python3 --version + pip3 --version + pip3 install -r BcosBuilder/requirements.txt + + cd BcosBuilder/pro/ + python3 build_chain.py build -c conf/config-build-example.toml -O ${current_path}/${output_dir} + cd ${current_path} + + cd ${output_dir}/127.0.0.1 && bash start_all.sh +} + +check_consensus() +{ + cd ${current_path}/${output_dir}/127.0.0.1 + LOG_INFO "=== wait for the node to init, waitTime: 20s =====" + sleep 20 + LOG_INFO "=== wait for the node to init finish =====" + for node in ${node_list} + do + LOG_INFO "check_consensus for ${node}" + result=$(cat ${node}/log/*log |grep -i reachN) + if [[ -z "${result}" ]]; + then + LOG_ERROR "checkView failed ******* cons info for ${node} *******" + cat ${node}/log/* |grep -i cons + LOG_ERROR "checkView failed ******* print log info for ${node} finish *******" + exit_node "check_consensus for ${node} failed for not reachNewView" + else + LOG_INFO "check_consensus for ${node} success" + fi + done +} + +download_console() +{ + cd ${current_path} + + LOG_INFO "Download console ..." + tar_file=console-${console_branch}.tar.gz + if [ -f "${tar_file}" ]; then + LOG_INFO "Use download cache" + else + curl -#LO https://osp-1257653870.cos.ap-guangzhou.myqcloud.com/FISCO-BCOS/console/releases/v${console_branch}/console.tar.gz + LOG_INFO "Download console success, branch: ${console_branch}" + mv console.tar.gz ${tar_file} + fi + LOG_INFO "Build and Config console ..." + rm -rf console + tar -zxvf ${tar_file} + cd console +} + +config_console() +{ + cd ${current_path}/console/ + use_sm="${1}" + cp -r ${current_path}/${output_dir}/127.0.0.1/rpc_20200/conf/sdk/* conf/ + cp conf/config-example.toml conf/config.toml + local sed_cmd="sed -i" + if [ "$(uname)" == "Darwin" ];then + sed_cmd="sed -i .bkp" + fi + use_sm_str="useSMCrypto = \"${use_sm}\"" + ${sed_cmd} "s/useSMCrypto = \"false\"/${use_sm_str}/g" conf/config.toml + LOG_INFO "Build and Config console success ..." +} + +send_transactions() +{ + txs_num="${1}" + cd ${current_path}/console/ + LOG_INFO "Deploy HelloWorld..." + for((i=1;i<=${txs_num};i++)); + do + bash console.sh deploy HelloWorld + sleep 1 + done + blockNumber=`bash console.sh getBlockNumber` + if [ "${blockNumber}" == "${txs_num}" ]; then + LOG_INFO "send transaction success, current blockNumber: ${blockNumber}" + else + exit_node "send transaction failed, current blockNumber: ${blockNumber}" + fi +} + +check_sync() +{ + LOG_INFO "check sync..." + expected_block_number="${1}" + cd ${current_path}/${output_dir}/127.0.0.1 + bash group0_node_40402/stop.sh && rm -rf group0_node_40402/log && rm -rf group0_node_40402/group0 + bash group0_node_40402/start.sh + # wait for sync + sleep 10 + block_number=$(cat group0_node_40402/log/*log |grep Report | tail -n 1| awk -F',' '{print $4}' | awk -F'=' '{print $2}') + if [ "${block_number}" == "${expected_block_number}" ]; then + LOG_INFO "check_sync success, current blockNumber: ${block_number}" + else + exit_node "check_sync error, current blockNumber: ${block_number}, expected_block_number: ${expected_block_number}" + fi + LOG_INFO "check sync success..." +} + +clear_node() +{ + cd ${current_path} + bash ${output_dir}/127.0.0.1/stop_all.sh + rm -rf ${output_dir} +} + +txs_num=10 +# non-sm test +LOG_INFO "======== check non-sm case ========" +init "" +check_consensus +download_console +config_console "false" +send_transactions ${txs_num} +check_sync ${txs_num} +stop_node +LOG_INFO "======== check non-sm success ========" + +# TODO: support sm test +# LOG_INFO "======== check sm case ========" +# init "-s" +# check_consensus +# config_console "true" +# send_transactions ${txs_num} +# check_sync ${txs_num} +# stop_node +# LOG_INFO "======== check sm case success ========" \ No newline at end of file diff --git "a/BFPL\345\243\271/tools/.ci/clear_build_cache.sh" "b/BFPL\345\243\271/tools/.ci/clear_build_cache.sh" new file mode 100644 index 00000000..57f9d5e6 --- /dev/null +++ "b/BFPL\345\243\271/tools/.ci/clear_build_cache.sh" @@ -0,0 +1,88 @@ +#!/bin/bash + +BUILD_DIR=./build + +function has_dir_change() { + dir=${1} + checksum=${2} + + checksum_file=${BUILD_DIR}/${dir}.checksum + + echo "Verify ${dir} checksum. current:${checksum}" + + if [ ! -f "${checksum_file}" ]; then + echo "Verify false for checksum file not exists." + return 1 + fi + + origin_checksum=$(cat ${checksum_file}) + + if [ "${origin_checksum}" == "${checksum}" ]; then + echo "Verify ok! No need to clear cache for ${dir}" + return 0 + else + echo "Verify failed for checksum not the same. origin checksum:${origin_checksum}" + return 1 + fi +} + +function write_checksum_file() { + dir=${1} + checksum=${2} + checksum_file=${BUILD_DIR}/${dir}.checksum + checksum_file_dir=${checksum_file%/*} + echo "Generate checksum file dir:" ${checksum_file_dir} + mkdir -p ${checksum_file_dir} + echo -n ${checksum} > ${checksum_file} + echo "Generate checksum file:" ${checksum_file} +} + +function check_and_clear_cache() { + dir=${1} + cache_dir=${2} + checksum=$(git rev-parse HEAD:${dir}) + + has_dir_change ${dir} ${checksum} + + has_change=$? + if [ ${has_change} == 1 ]; then + echo "Dir ${dir} has changed. Clear build cache: \"${cache_dir}\". Current checksum: ${checksum}" + rm -rf ${cache_dir} + + write_checksum_file ${dir} ${checksum} + fi +} + +function check_file_and_clear_cache() { + file=${1} + cache_dir=${2} + checksum_file=${file}_check.md5sum + tmp_checksum_file=${file}_tmp.md5sum + + md5sum $(find . -type f |grep -ia ${file} |grep -vE 'build|vcpkg|deps|md5sum') > ${tmp_checksum_file} + if [ -f "${checksum_file}" ]; then + if diff ${checksum_file} ${tmp_checksum_file}; then + echo "Verify ok! No need to clear cache for ${file}" + return; + fi + fi + + # checksum not ok + + echo "File ${file} has changed. Clear build cache: \"${cache_dir}\" " + rm -rf ${cache_dir} + mv ${tmp_checksum_file} ${checksum_file} +} + +# First: check file change +check_file_and_clear_cache cmake ${BUILD_DIR} + +# Second: check dir change +check_and_clear_cache .github/workflows ${BUILD_DIR} +check_and_clear_cache bcos-tars-protocol/bcos-tars-protocol ${BUILD_DIR}/generated +check_and_clear_cache bcos-sync/bcos-sync/protocol/proto ${BUILD_DIR}/bcos-sync +check_and_clear_cache bcos-protocol/bcos-protocol ${BUILD_DIR}/bcos-protocol +check_and_clear_cache bcos-pbft/bcos-pbft/core/proto ${BUILD_DIR}/bcos-pbft/bcos-pbft/core +check_and_clear_cache bcos-pbft/bcos-pbft/pbft/protocol/proto ${BUILD_DIR}/bcos-pbft/bcos-pbft/pbft/protocol +check_and_clear_cache bcos-txpool/bcos-txpool/sync/protocol/proto ${BUILD_DIR}/bcos-txpool + diff --git "a/BFPL\345\243\271/tools/.ci/requirements.txt" "b/BFPL\345\243\271/tools/.ci/requirements.txt" new file mode 100644 index 00000000..cd6b55db --- /dev/null +++ "b/BFPL\345\243\271/tools/.ci/requirements.txt" @@ -0,0 +1,2 @@ +requests +argparse \ No newline at end of file diff --git "a/BFPL\345\243\271/tools/BcosBuilder/docker/bridge/linux/framework/docker-compose.yml" "b/BFPL\345\243\271/tools/BcosBuilder/docker/bridge/linux/framework/docker-compose.yml" new file mode 100644 index 00000000..b8d5dabf --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/docker/bridge/linux/framework/docker-compose.yml" @@ -0,0 +1,45 @@ +version: "3" +networks: + tars_net: + external: + name: tars-network + +services: + tars-mysql: + image: mysql:5.6 + ports: + - "3310:3306" + networks: + tars_net: + ipv4_address: 172.25.0.2 + environment: + MYSQL_ROOT_PASSWORD: "" + restart: always + volumes: + - ~/app/tars/framework-mysql:/var/lib/mysql + - /etc/localtime:/etc/localtime + + tars-framework: + image: tarscloud/framework:v3.0.1 + networks: + tars_net: + ipv4_address: 172.25.0.3 + # 3000 is the tarsWeb port + ports: + - "3000:3000" + - "3001:3001" + - "20200-20205:20200-20205" + - "30300-30305:30300-30305" + environment: + MYSQL_HOST: "172.25.0.2" + MYSQL_ROOT_PASSWORD: "" + MYSQL_PORT: 3306 + REBUILD: "false" + INET: eth0 + SLAVE: "false" + restart: always + volumes: + - ~/app/tars/framework:/data/tars + - /etc/localtime:/etc/localtime + depends_on: + - tars-mysql diff --git "a/BFPL\345\243\271/tools/BcosBuilder/docker/bridge/linux/node/docker-compose.yml" "b/BFPL\345\243\271/tools/BcosBuilder/docker/bridge/linux/node/docker-compose.yml" new file mode 100644 index 00000000..c4b91c19 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/docker/bridge/linux/node/docker-compose.yml" @@ -0,0 +1,23 @@ +version: "3" +networks: + tars_net: + external: + name: tars-network + +services: + tars-node: + image: tarscloud/tars-node:latest + networks: + tars_net: + ipv4_address: 172.25.0.5 + ports: + - "10200-10205:10200-10205" + - "40300-40305:40300-40305" + - "9000-9010:9000-9010" + environment: + INET: eth0 + WEB_HOST: "http://172.25.0.3:3000" + restart: always + volumes: + - ~/app/tars:/data/tars + - /etc/localtime:/etc/localtime \ No newline at end of file diff --git "a/BFPL\345\243\271/tools/BcosBuilder/docker/bridge/linux/node/gen_compose_files.sh" "b/BFPL\345\243\271/tools/BcosBuilder/docker/bridge/linux/node/gen_compose_files.sh" new file mode 100644 index 00000000..6da30929 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/docker/bridge/linux/node/gen_compose_files.sh" @@ -0,0 +1,38 @@ +#!/bin/bash + +data_dir="~/app/tars/" + + +genOne() { + local id=${1} + cat <"docker-compose${id}.yml" + +version: "3" +networks: + tars_net: + external: + name: tars-network + +services: + tars-node${id}: + image: tarscloud/tars-node:latest + networks: + tars_net: + ipv4_address: 172.25.0.${id} + ports: + - "10200-10205:10200-10205" + - "40300-40305:40300-40305" + - "9000-9010:9000-9010" + environment: + INET: eth0 + WEB_HOST: "http://172.25.0.3:3000" + restart: always + volumes: + - ${data_dir}/node${id}:/data/tars + - /etc/localtime:/etc/localtime +EOF +} + +for id in {5..8} ; do + genOne ${id} +done diff --git "a/BFPL\345\243\271/tools/BcosBuilder/docker/bridge/mac/framework/docker-compose.yml" "b/BFPL\345\243\271/tools/BcosBuilder/docker/bridge/mac/framework/docker-compose.yml" new file mode 100644 index 00000000..9b73c3a1 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/docker/bridge/mac/framework/docker-compose.yml" @@ -0,0 +1,57 @@ +version: "3" +networks: + tars_net: + external: + name: tars-network + +services: + tars-mysql: + image: mysql:5.6 + platform: "linux/x86_64" + deploy: + resources: + limits: + cpus: '1' + memory: 5G + reservations: + cpus: '0.25' + memory: 512M + ports: + - "3310:3306" + networks: + tars_net: + ipv4_address: 172.25.0.2 + environment: + MYSQL_ROOT_PASSWORD: "" + restart: always + + tars-framework: + image: tarscloud/framework:v3.0.1 + platform: "linux/x86_64" + deploy: + resources: + limits: + cpus: '1' + memory: 5G + reservations: + cpus: '0.25' + memory: 512M + networks: + tars_net: + ipv4_address: 172.25.0.3 + # 3000 is the tarsWeb port + ports: + - "3000:3000" + - "3001:3001" + - "20200-20205:20200-20205" + - "30300-30305:30300-30305" + environment: + MYSQL_HOST: "172.25.0.2" + MYSQL_ROOT_PASSWORD: "" + MYSQL_PORT: 3306 + REBUILD: "false" + INET: eth0 + SLAVE: "false" + restart: always + depends_on: + - tars-mysql diff --git "a/BFPL\345\243\271/tools/BcosBuilder/docker/bridge/mac/node/docker-compose.yml" "b/BFPL\345\243\271/tools/BcosBuilder/docker/bridge/mac/node/docker-compose.yml" new file mode 100644 index 00000000..43f00d88 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/docker/bridge/mac/node/docker-compose.yml" @@ -0,0 +1,29 @@ +version: "3" +networks: + tars_net: + external: + name: tars-network + +services: + tars-node: + image: tarscloud/tars-node:latest + platform: "linux/x86_64" + deploy: + resources: + limits: + cpus: '1' + memory: 5G + reservations: + cpus: '0.25' + memory: 512M + networks: + tars_net: + ipv4_address: 172.25.0.5 + ports: + - "10200-10205:10200-10205" + - "40300-40305:40300-40305" + - "9000-9010:9000-9010" + environment: + INET: eth0 + WEB_HOST: "http://172.25.0.3:3000" + restart: always diff --git "a/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/framework/docker-compose.yml" "b/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/framework/docker-compose.yml" new file mode 100644 index 00000000..e7b60c9b --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/framework/docker-compose.yml" @@ -0,0 +1,29 @@ +version: "3" +services: + tars-mysql: + image: mysql:5.6 + network_mode: "host" + environment: + MYSQL_ROOT_PASSWORD: "" + MYSQL_TCP_PORT: 3310 + restart: always + volumes: + - ~/app/tars/framework-mysql:/var/lib/mysql + - /etc/localtime:/etc/localtime + + tars-framework: + image: tarscloud/framework:v3.0.1 + network_mode: "host" + environment: + MYSQL_HOST: "172.17.0.1" + MYSQL_ROOT_PASSWORD: "" + MYSQL_PORT: 3310 + REBUILD: "false" + INET: eth0 + SLAVE: "false" + restart: always + volumes: + - ~/app/tars/framework:/data/tars + - /etc/localtime:/etc/localtime + depends_on: + - tars-mysql diff --git "a/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/compose.yaml" "b/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/compose.yaml" new file mode 100644 index 00000000..91a70398 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/compose.yaml" @@ -0,0 +1,32 @@ +version: '2' + +services: + + prometheus: + container_name: prometheus + image: prom/prometheus:latest + restart: unless-stopped + volumes: + - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml + - /etc/localtime:/etc/localtime + network_mode: host + # ports: + # - ${PROMETHEUS_PORT}:9090 + + grafana: + container_name: grafana + image: grafana/grafana-oss:latest + restart: unless-stopped + user: '0' + network_mode: host + # ports: + # - ${GRAFANA_PORT}:3000 + volumes: + - ./grafana/grafana.ini:/etc/grafana/grafana.ini + - ./grafana/data:/var/lib/grafana + - ./grafana/logs:/var/log/grafana + - /etc/localtime:/etc/localtime + environment: + - GF_USERS_ALLOW_SIGN_UP=false + - GF_AUTH_ANONYMOUS_ENABLED=true + - GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer diff --git "a/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/grafana/grafana.ini" "b/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/grafana/grafana.ini" new file mode 100644 index 00000000..2923b30b --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/grafana/grafana.ini" @@ -0,0 +1,13 @@ +[server] +# The http port to use +http_port = 3001 + +[security] +# disable creation of admin user on first start of grafana +;disable_initial_admin_creation = false +; +;# default admin user, created on startup +admin_user = admin +; +;# default admin password, can be changed before first start of grafana, or in profile settings +admin_password = admin \ No newline at end of file diff --git "a/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/prometheus/prometheus.yml" "b/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/prometheus/prometheus.yml" new file mode 100644 index 00000000..c1de5e42 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/prometheus/prometheus.yml" @@ -0,0 +1,19 @@ +global: + scrape_interval: 15s # By default, scrape targets every 15 seconds. + + # Attach these labels to any time series or alerts when communicating with + # external systems (federation, remote storage, Alertmanager). + external_labels: + monitor: 'bcos' + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: + # The job name is added as a label job= to any timeseries scraped from this config. + - job_name: 'prometheus' + + # Override the global default and scrape targets from this job every 5 seconds. + scrape_interval: 5s + + static_configs: + - targets: [] diff --git "a/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/start_monitor.sh" "b/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/start_monitor.sh" new file mode 100644 index 00000000..e260bd2b --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/start_monitor.sh" @@ -0,0 +1,17 @@ +#!/bin/bash +SHELL_FOLDER=$(cd $(dirname $0);pwd) + +LOG_ERROR() { + content=${1} + echo -e "\033[31m[ERROR] ${content}\033[0m" +} + +LOG_INFO() { + content=${1} + echo -e "\033[32m[INFO] ${content}\033[0m" +} + + +DOCKER_FILE=${SHELL_FOLDER}/compose.yaml +docker-compose -f ${DOCKER_FILE} up -d prometheus grafana 2>&1 + diff --git "a/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/stop_monitor.sh" "b/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/stop_monitor.sh" new file mode 100644 index 00000000..b3a27dc2 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/monitor/stop_monitor.sh" @@ -0,0 +1,17 @@ +#!/bin/bash +SHELL_FOLDER=$(cd $(dirname $0);pwd) + +LOG_ERROR() { + content=${1} + echo -e "\033[31m[ERROR] ${content}\033[0m" +} + +LOG_INFO() { + content=${1} + echo -e "\033[32m[INFO] ${content}\033[0m" +} + + +DOCKER_FILE=${SHELL_FOLDER}/compose.yaml +docker-compose -f ${DOCKER_FILE} stop + diff --git "a/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/node/docker-compose.yml" "b/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/node/docker-compose.yml" new file mode 100644 index 00000000..e93dcdda --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/docker/host/linux/node/docker-compose.yml" @@ -0,0 +1,12 @@ +version: "3" +services: + tars-node: + image: tarscloud/tars-node:latest + network_mode: "host" + environment: + INET: eth0 + WEB_HOST: "http://172.25.0.3:3000" + restart: always + volumes: + - ~/app/tars:/data/tars + - /etc/localtime:/etc/localtime \ No newline at end of file diff --git "a/BFPL\345\243\271/tools/BcosBuilder/max/conf/config-deploy-example.toml" "b/BFPL\345\243\271/tools/BcosBuilder/max/conf/config-deploy-example.toml" new file mode 100644 index 00000000..6cfaa507 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/max/conf/config-deploy-example.toml" @@ -0,0 +1,76 @@ +[tars] +tars_url = "http://127.0.0.1:3000" +tars_token = "" +tars_pkg_dir = "binary/" + +[chain] +chain_id="chain0" +# the rpc-service enable sm-ssl or not, default disable sm-ssl +rpc_sm_ssl=false +# the gateway-service enable sm-ssl or not, default disable sm-ssm +gateway_sm_ssl=false +# the existed rpc service ca path, will generate new ca if not configured +#rpc_ca_cert_path="" +# the existed gateway service ca path, will generate new ca if not configured +#gateway_ca_cert_path="" + +[[group]] +group_id="group0" +# the genesis configuration path of the group, will generate new genesis configuration if not configured +# genesis_config_path = "" +# VM type, now only support evm/wasm +vm_type="evm" +# use sm-crypto or not +sm_crypto=false +# enable auth-check or not +auth_check=false +init_auth_address="" + +# the genesis config +# the number of blocks generated by each leader +leader_period = 1 +# the max number of transactions of a block +block_tx_count_limit = 1000 +# consensus algorithm now support PBFT(consensus_type=pbft) +consensus_type = "pbft" +# transaction gas limit +gas_limit = "3000000000" +# compatible version, can be dynamically upgraded through setSystemConfig +# the default is 3.1.0 +compatibility_version="3.1.0" + +[[agency]] +name = "agencyA" +failover_cluster_url = "172.25.0.3:2379" +# enable data disk encryption for rpc/gateway or not, default is false +enable_storage_security = false +# url of the key center, in format of ip:port, please refer to https://github.com/FISCO-BCOS/key-manager for details +# key_center_url = +# cipher_data_key = + + [agency.rpc] + deploy_ip=["172.25.0.3"] + listen_ip="0.0.0.0" + listen_port=20200 + thread_count=4 + + [agency.gateway] + deploy_ip=["172.25.0.3"] + listen_ip="0.0.0.0" + listen_port=30300 + peers=["172.25.0.3:30300"] + + [[agency.group]] + group_id = "group0" + + [[agency.group.node]] + node_name = "node0" + # the tikv storage pd-addresses + pd_addrs="172.25.0.3:2379" + key_page_size=10240 + deploy_ip = ["172.25.0.3"] + executor_deploy_ip=["172.25.0.3"] + monitor_listen_port = "3901" + # the tikv storage pd-addresses + # monitor log path example:"/home/fisco/tars/framework/app_log/" + monitor_log_path = "" diff --git "a/BFPL\345\243\271/tools/BcosBuilder/max/conf/config-node-expand-example.toml" "b/BFPL\345\243\271/tools/BcosBuilder/max/conf/config-node-expand-example.toml" new file mode 100644 index 00000000..504189f3 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/max/conf/config-node-expand-example.toml" @@ -0,0 +1,41 @@ +[tars] +tars_url = "http://127.0.0.1:3000" +tars_token = "" +tars_pkg_dir = "binary/" + +[chain] +chain_id="chain0" + +[[group]] +group_id="group0" +# the genesis configuration path of the expanded group +genesis_config_path = "generated/chain0/group0/config.genesis" +# use sm-crypto or not +sm_crypto=false + + +[[agency]] +name = "agencyA" +failover_cluster_url = "172.25.0.3:2379" +# enable data disk encryption for rpc/gateway or not, default is false +enable_storage_security = false +# url of the key center, in format of ip:port, please refer to https://github.com/FISCO-BCOS/key-manager for details +# key_center_url = +# cipher_data_key = + + [[agency.group]] + group_id = "group0" + + [[agency.group.node]] + # expand the existed-max-node + node_name = "node1" + # the tikv storage pd-addresses + pd_addrs="172.25.0.5:2379" + key_page_size=10240 + deploy_ip = ["172.25.0.5"] + executor_deploy_ip=["172.25.0.5"] + # enable data disk encryption for bcos node or not, default is false + enable_storage_security = false + # url of the key center, in format of ip:port, please refer to https://github.com/FISCO-BCOS/key-manager for details + # key_center_url = + # cipher_data_key = \ No newline at end of file diff --git "a/BFPL\345\243\271/tools/BcosBuilder/max/conf/config-service-expand-example.toml" "b/BFPL\345\243\271/tools/BcosBuilder/max/conf/config-service-expand-example.toml" new file mode 100644 index 00000000..c022e711 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/max/conf/config-service-expand-example.toml" @@ -0,0 +1,36 @@ +[tars] +tars_url = "http://127.0.0.1:3000" +tars_token = "" +tars_pkg_dir = "binary/" + +[chain] +chain_id="chain0" +rpc_sm_ssl=false +gateway_sm_ssl=false +# the ca path of the expanded rpc service +# must ensure that the path configuration is correct, otherwise the ssl verification will fail +rpc_ca_cert_path="generated/rpc/chain0/ca" +# the ca path of the expanded gateway service +# must ensure that the path configuration is correct, otherwise the ssl verification will fail +gateway_ca_cert_path="generated/gateway/chain0/ca" + +[[agency]] +name = "agencyA" +failover_cluster_url = "172.25.0.3:2379" +# enable data disk encryption for rpc/gateway or not, default is false +enable_storage_security = false +# url of the key center, in format of ip:port, please refer to https://github.com/FISCO-BCOS/key-manager for details +# key_center_url = +# cipher_data_key = + + [agency.rpc] + deploy_ip=["172.25.0.5"] + listen_ip="0.0.0.0" + listen_port=10200 + thread_count=4 + + [agency.gateway] + deploy_ip=["172.25.0.5"] + listen_ip="0.0.0.0" + listen_port=40300 + peers=["172.25.0.3:30300", "172.25.0.3:30301", "172.25.0.5:40300"] \ No newline at end of file diff --git "a/BFPL\345\243\271/tools/BcosBuilder/pro/conf/config-build-example.toml" "b/BFPL\345\243\271/tools/BcosBuilder/pro/conf/config-build-example.toml" new file mode 100644 index 00000000..bf6433b3 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/pro/conf/config-build-example.toml" @@ -0,0 +1,143 @@ +[tars] +tars_pkg_dir = "binary/" + +[chain] +chain_id="chain0" +# the rpc-service enable sm-ssl or not, default disable sm-ssl +rpc_sm_ssl=false +# the gateway-service enable sm-ssl or not, default disable sm-ssm +gateway_sm_ssl=false +# the existed rpc service ca path, will generate new ca if not configured +#rpc_ca_cert_path="" +# the existed gateway service ca path, will generate new ca if not configured +#gateway_ca_cert_path=" + +[[group]] +group_id="group0" +# the genesis configuration path of the group, will generate new genesis configuration if not configured +# genesis_config_path = "" +# VM type, now only support evm/wasm +vm_type="evm" +# use sm-crypto or not +sm_crypto=false +# enable auth-check or not +auth_check=false +init_auth_address="" + +# the genesis config +# the number of blocks generated by each leader +leader_period = 1 +# the max number of transactions of a block +block_tx_count_limit = 1000 +# consensus algorithm now support PBFT(consensus_type=pbft) +consensus_type = "pbft" +# transaction gas limit +gas_limit = "3000000000" +# compatible version, can be dynamically upgraded through setSystemConfig +# the default is 3.1.0 +compatibility_version="3.1.0" + +[[agency]] +name = "agencyA" +# enable data disk encryption for rpc/gateway or not, default is false +enable_storage_security = false +# url of the key center, in format of ip:port, please refer to https://github.com/FISCO-BCOS/key-manager for details +# key_center_url = +# cipher_data_key = + + [agency.rpc] + deploy_ip=["127.0.0.1"] + # rpc listen ip + listen_ip="0.0.0.0" + # rpc listen port + listen_port=20200 + thread_count=4 + # rpc tars server listen ip + tars_listen_ip="0.0.0.0" + # rpc tars server listen port + tars_listen_port=40400 + + [agency.gateway] + deploy_ip=["127.0.0.1"] + # gateway listen ip + listen_ip="0.0.0.0" + # gateway listen port + listen_port=30300 + # gateway connected peers, should be all of the gateway peers info + peers=["127.0.0.1:30300", "127.0.0.1:30301"] + # gateway tars server listen ip + tars_listen_ip="0.0.0.0" + # gateway tars server listen port + tars_listen_port=40401 + + [[agency.group]] + group_id = "group0" + [[agency.group.node]] + # node name, Notice: node_name in the same agency and group must be unique + node_name = "node0" + deploy_ip = "127.0.0.1" + # node tars server listen ip + tars_listen_ip="0.0.0.0" + # node tars server listen port, Notice: the tars server of the node will cost five ports, then the port tars_listen_port ~ tars_listen_port + 4 should be in free + tars_listen_port=40402 + # enable data disk encryption for bcos node or not, default is false + enable_storage_security = false + # url of the key center, in format of ip:port, please refer to https://github.com/FISCO-BCOS/key-manager for details + # key_center_url = + # cipher_data_key = + monitor_listen_port = "3902" + # monitor log path example:"/home/fisco/tars/framework/app_log/" + monitor_log_path = "" + +[[agency]] +name = "agencyB" +# enable data disk encryption for rpc/gateway or not, default is false +enable_storage_security = false +# url of the key center, in format of ip:port, please refer to https://github.com/FISCO-BCOS/key-manager for details +# key_center_url = +# cipher_data_key = + + [agency.rpc] + deploy_ip=["127.0.0.1"] + # rpc listen ip + listen_ip="0.0.0.0" + # rpc listen port + listen_port=20201 + thread_count=4 + # rpc tars server listen ip + tars_listen_ip="0.0.0.0" + # rpc tars server listen port + tars_listen_port=40410 + + [agency.gateway] + deploy_ip=["127.0.0.1"] + # gateway listen ip + listen_ip="0.0.0.0" + # gateway listen port + listen_port=30301 + # gateway connected peers, should be all of the gateway peers info + peers=["127.0.0.1:30300", "127.0.0.1:30301"] + # gateway rpc server listen ip + tars_listen_ip="0.0.0.0" + # gateway rpc server listen port + tars_listen_port=40411 + + [[agency.group]] + group_id = "group0" + + [[agency.group.node]] + # node name, Notice: node_name in the same agency and group must be unique + node_name = "node0" + deploy_ip = "127.0.0.1" + # node tars server listen ip + tars_listen_ip="0.0.0.0" + # node tars server listen port, Notice: the tars server of the node will cost five ports, then the port tars_listen_port ~ tars_listen_port + 4 should be in free + tars_listen_port=40412 + # enable data disk encryption for bcos node or not, default is false + enable_storage_security = false + # url of the key center, in format of ip:port, please refer to https://github.com/FISCO-BCOS/key-manager for details + # key_center_url = + # cipher_data_key = + monitor_listen_port = "3901" + # monitor log path example:"/home/fisco/tars/framework/app_log/" + monitor_log_path = "" diff --git "a/BFPL\345\243\271/tools/BcosBuilder/pro/conf/config-deploy-example.toml" "b/BFPL\345\243\271/tools/BcosBuilder/pro/conf/config-deploy-example.toml" new file mode 100644 index 00000000..79e28361 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/pro/conf/config-deploy-example.toml" @@ -0,0 +1,109 @@ +[tars] +tars_url = "http://127.0.0.1:3000" +tars_token = "" +tars_pkg_dir = "binary/" + +[chain] +chain_id="chain0" +# the rpc-service enable sm-ssl or not, default disable sm-ssl +rpc_sm_ssl=false +# the gateway-service enable sm-ssl or not, default disable sm-ssm +gateway_sm_ssl=false +# the existed rpc service ca path, will generate new ca if not configured +#rpc_ca_cert_path="" +# the existed gateway service ca path, will generate new ca if not configured +#gateway_ca_cert_path=" + +[[group]] +group_id="group0" +# the genesis configuration path of the group, will generate new genesis configuration if not configured +# genesis_config_path = "" +# VM type, now only support evm/wasm +vm_type="evm" +# use sm-crypto or not +sm_crypto=false +# enable auth-check or not +auth_check=false +init_auth_address="" + +# the genesis config +# the number of blocks generated by each leader +leader_period = 1 +# the max number of transactions of a block +block_tx_count_limit = 1000 +# consensus algorithm now support PBFT(consensus_type=pbft) +consensus_type = "pbft" +# transaction gas limit +gas_limit = "3000000000" +# compatible version, can be dynamically upgraded through setSystemConfig +# the default is 3.1.0 +compatibility_version="3.1.0" + +[[agency]] +name = "agencyA" +# enable data disk encryption for rpc/gateway or not, default is false +enable_storage_security = false +# url of the key center, in format of ip:port, please refer to https://github.com/FISCO-BCOS/key-manager for details +# key_center_url = +# cipher_data_key = + + [agency.rpc] + deploy_ip=["172.25.0.3"] + listen_ip="0.0.0.0" + listen_port=20200 + thread_count=4 + + [agency.gateway] + deploy_ip=["172.25.0.3"] + listen_ip="0.0.0.0" + listen_port=30300 + peers=["172.25.0.3:30300", "172.25.0.3:30301"] + + [[agency.group]] + group_id = "group0" + [[agency.group.node]] + node_name = "node0" + deploy_ip = "172.25.0.3" + # enable data disk encryption for bcos node or not, default is false + enable_storage_security = false + # url of the key center, in format of ip:port, please refer to https://github.com/FISCO-BCOS/key-manager for details + # key_center_url = + # cipher_data_key = + monitor_listen_port = "3902" + # monitor log path example:"/home/fisco/tars/framework/app_log/" + monitor_log_path = "" + +[[agency]] +name = "agencyB" +# enable data disk encryption for rpc/gateway or not, default is false +enable_storage_security = false +# url of the key center, in format of ip:port, please refer to https://github.com/FISCO-BCOS/key-manager for details +# key_center_url = +# cipher_data_key = + + [agency.rpc] + deploy_ip=["172.25.0.3"] + listen_ip="0.0.0.0" + listen_port=20201 + thread_count=4 + + [agency.gateway] + deploy_ip=["172.25.0.3"] + listen_ip="0.0.0.0" + listen_port=30301 + peers=["172.25.0.3:30300", "172.25.0.3:30301"] + + [[agency.group]] + group_id = "group0" + + [[agency.group.node]] + node_name = "node0" + deploy_ip = "172.25.0.3" + # enable data disk encryption for bcos node or not, default is false + enable_storage_security = false + # url of the key center, in format of ip:port, please refer to https://github.com/FISCO-BCOS/key-manager for details + # key_center_url = + # cipher_data_key = + monitor_listen_port = "3901" + # monitor log path example:"/home/fisco/tars/framework/app_log/" + monitor_log_path = "" diff --git "a/BFPL\345\243\271/tools/BcosBuilder/pro/conf/config-node-expand-example.toml" "b/BFPL\345\243\271/tools/BcosBuilder/pro/conf/config-node-expand-example.toml" new file mode 100644 index 00000000..862ea319 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/pro/conf/config-node-expand-example.toml" @@ -0,0 +1,43 @@ +[tars] +tars_url = "http://127.0.0.1:3000" +tars_token = "" +tars_pkg_dir = "binary/" + +[chain] +chain_id="chain0" + +[[group]] +group_id="group0" +# the genesis configuration path of the expanded group +genesis_config_path = "generated/chain0/group0/config.genesis" +# use sm-crypto or not +sm_crypto=false + +[[agency]] +name = "agencyA" + [[agency.group]] + group_id = "group0" + + [[agency.group.node]] + node_name = "node1" + deploy_ip = "172.25.0.5" + # enable data disk encryption for bcos node or not, default is false + enable_storage_security = false + # url of the key center, in format of ip:port, please refer to https://github.com/FISCO-BCOS/key-manager for details + # key_center_url = + # cipher_data_key = + monitor_listen_port = "3901" + # monitor log path example:"/home/fisco/tars/framework/app_log/" + monitor_log_path = "" + + [[agency.group.node]] + node_name = "node2" + deploy_ip = "172.25.0.5" + # enable data disk encryption for bcos node or not, default is false + enable_storage_security = false + # url of the key center, in format of ip:port, please refer to https://github.com/FISCO-BCOS/key-manager for details + # key_center_url = + # cipher_data_key = + monitor_listen_port = "3901" + # monitor log path example:"/home/fisco/tars/framework/app_log/" + monitor_log_path = "" diff --git "a/BFPL\345\243\271/tools/BcosBuilder/pro/conf/config-service-expand-example.toml" "b/BFPL\345\243\271/tools/BcosBuilder/pro/conf/config-service-expand-example.toml" new file mode 100644 index 00000000..a1ebda93 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/pro/conf/config-service-expand-example.toml" @@ -0,0 +1,35 @@ +[tars] +tars_url = "http://127.0.0.1:3000" +tars_token = "" +tars_pkg_dir = "binary/" + +[chain] +chain_id="chain0" +rpc_sm_ssl=false +gateway_sm_ssl=false +# the ca path of the expanded rpc service +# must ensure that the path configuration is correct, otherwise the ssl verification will fail +rpc_ca_cert_path="generated/rpc/chain0/ca" +# the ca path of the expanded gateway service +# must ensure that the path configuration is correct, otherwise the ssl verification will fail +gateway_ca_cert_path="generated/gateway/chain0/ca" + +[[agency]] +name = "agencyA" +# enable data disk encryption for rpc/gateway or not, default is false +enable_storage_security = false +# url of the key center, in format of ip:port, please refer to https://github.com/FISCO-BCOS/key-manager for details +# key_center_url = +# cipher_data_key = + + [agency.rpc] + deploy_ip=["172.25.0.5"] + listen_ip="0.0.0.0" + listen_port=10200 + thread_count=4 + + [agency.gateway] + deploy_ip=["172.25.0.5"] + listen_ip="0.0.0.0" + listen_port=40300 + peers=["172.25.0.3:30300", "172.25.0.3:30301", "172.25.0.5:40300"] \ No newline at end of file diff --git "a/BFPL\345\243\271/tools/BcosBuilder/requirements.txt" "b/BFPL\345\243\271/tools/BcosBuilder/requirements.txt" new file mode 100644 index 00000000..d2dda486 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/requirements.txt" @@ -0,0 +1,5 @@ +configparser +requests +toml +uuid +requests-toolbelt diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/command/monitor_command_impl.py" "b/BFPL\345\243\271/tools/BcosBuilder/src/command/monitor_command_impl.py" new file mode 100644 index 00000000..b0fecff7 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/command/monitor_command_impl.py" @@ -0,0 +1,35 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +from controller.monitor_controller import MonitorController +from common import utilities + + +class MonitorCommandImpl: + def __init__(self, config, node_type, output_dir): + self.Monitor_controller = MonitorController(config, node_type, output_dir) + + def deploy_monitor(self): + function = "generate_and_deploy_monitor_services" + notice_info = "deploy all nodes monitor" + return self.execute_command(function, notice_info) + + def start_monitor(self): + function = "start_monitor_services" + notice_info = "start all nodes monitor" + return self.execute_command(function, notice_info) + + def stop_monitor(self): + function = "stop_monitor_services" + notice_info = "stop all nodes monitor" + return self.execute_command(function, notice_info) + + def execute_command(self, function, notice_info): + utilities.print_split_info() + utilities.print_badge(notice_info) + ret = getattr(self.Monitor_controller, function)() + if ret is True: + utilities.print_badge("%s success" % notice_info) + else: + utilities.log_error("%s failed" % notice_info) + utilities.print_split_info() + return ret diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/command/node_command_impl.py" "b/BFPL\345\243\271/tools/BcosBuilder/src/command/node_command_impl.py" new file mode 100644 index 00000000..2b9ec144 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/command/node_command_impl.py" @@ -0,0 +1,75 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +from controller.node_controller import NodeController +from common import utilities + + +class NodeCommandImpl: + def __init__(self, config, node_type, output_dir): + self.node_controller = NodeController(config, node_type, output_dir) + + def gen_node_config(self): + function = "generate_all_config" + notice_info = "generate config for all nodes" + return self.execute_command(function, notice_info) + + def gen_executor_config(self): + function = "generate_all_executor_config" + notice_info = "generate config for all executor" + return self.execute_command(function, notice_info) + + def start_all(self): + function = "start_group" + notice_info = "start all nodes of the given group" + return self.execute_command(function, notice_info) + + def stop_all(self): + function = "stop_group" + notice_info = "stop all nodes of the given group" + return self.execute_command(function, notice_info) + + def upgrade_nodes(self): + function = "upgrade_group" + notice_info = "upgrade all nodes of the given group" + return self.execute_command(function, notice_info) + + def deploy_nodes(self): + function = "generate_and_deploy_group_services" + notice_info = "deploy all nodes of the given group" + return self.execute_command(function, notice_info) + + def upload_nodes(self): + function = "deploy_group_services" + notice_info = "upload all nodes config of the given group" + return self.execute_command(function, notice_info) + + def undeploy_nodes(self): + function = "undeploy_group" + notice_info = "undeploy all nodes of the given group" + return self.execute_command(function, notice_info) + + def generate_expand_config(self): + function = "generate_all_expand_config" + notice_info = "generate expand config for the given group" + return self.execute_command(function, notice_info) + + def expand_nodes(self): + function = "expand_and_deploy_all_nodes" + notice_info = "expand nodes for the given group" + return self.execute_command(function, notice_info) + + def expand_executors(self): + function = "expand_and_deploy_all_executors" + notice_info = "expand executors for the given group" + return self.execute_command(function, notice_info) + + def execute_command(self, function, notice_info): + utilities.print_split_info() + utilities.print_badge(notice_info) + ret = getattr(self.node_controller, function)() + if ret is True: + utilities.print_badge("%s success" % notice_info) + else: + utilities.log_error("%s failed" % notice_info) + utilities.print_split_info() + return ret diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/command/service_command_impl.py" "b/BFPL\345\243\271/tools/BcosBuilder/src/command/service_command_impl.py" new file mode 100644 index 00000000..68276ac9 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/command/service_command_impl.py" @@ -0,0 +1,129 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +from controller.service_controller import ServiceController +from common import utilities + + +class ServiceCommandImpl: + def __init__(self, config, service_type, node_type, output_dir): + self.config = config + self.service_type = service_type + self.service_controller = ServiceController( + config, service_type, node_type, output_dir) + + def gen_service_config(self, is_build_opr = False): + utilities.print_split_info() + utilities.print_badge("generate service config") + ret = self.service_controller.gen_all_service_config(is_build_opr) + if ret is True: + utilities.print_badge("generate service config success") + else: + utilities.log_error("generate service config failed") + utilities.print_split_info() + return ret + + def upload_service(self): + # upload the generated_config + utilities.print_split_info() + utilities.print_badge("upload service, type: %s" % self.service_type) + ret = self.service_controller.deploy_all() + if ret is True: + utilities.print_badge( + "upload service success, type: %s" % self.service_type) + else: + utilities.log_error( + "upload service failed, type: %s" % self.service_type) + utilities.print_split_info() + return ret + + def deploy_service(self): + # generate_config + utilities.print_split_info() + utilities.print_badge("deploy service, type: %s" % self.service_type) + ret = self.gen_service_config() + if ret is False: + utilities.log_error( + "deploy service failed for generate config failed, type: %s" % self.service_type) + return False + ret = self.service_controller.deploy_all() + if ret is True: + utilities.print_badge( + "deploy service success, type: %s" % self.service_type) + else: + utilities.log_error( + "deploy service failed, type: %s" % self.service_type) + utilities.print_split_info() + return ret + + def delete_service(self): + utilities.print_split_info() + utilities.print_badge("delete service, type: %s" % self.service_type) + ret = self.service_controller.undeploy_all() + if ret is True: + utilities.print_badge( + "delete service success, type: %s" % self.service_type) + else: + utilities.log_error( + "delete service failed, type: %s" % self.service_type) + utilities.print_split_info() + return ret + + def upgrade_service(self): + utilities.print_split_info() + utilities.print_badge("upgrade service, type: %s" % self.service_type) + ret = self.service_controller.upgrade_all() + if ret is True: + utilities.print_badge( + "upgrade service success, type: %s" % self.service_type) + else: + utilities.log_error( + "upgrade service failed, type: %s" % self.service_type) + utilities.print_split_info() + return ret + + def start_service(self): + utilities.print_split_info() + utilities.print_badge("start service, type: %s" % self.service_type) + ret = self.service_controller.start_all() + if ret is True: + utilities.print_badge( + "start service success, type: %s" % self.service_type) + else: + utilities.log_error( + "start service failed, type: %s" % self.service_type) + utilities.print_split_info() + return ret + + def stop_service(self): + utilities.print_split_info() + utilities.print_badge("stop service, type: %s" % self.service_type) + ret = self.service_controller.stop_all() + if ret is True: + utilities.print_badge( + "stop service success, type: %s" % self.service_type) + else: + utilities.log_error( + "stop service failed, type: %s" % self.service_type) + utilities.print_split_info() + return ret + + def expand_service(self): + utilities.print_split_info() + utilities.print_badge("expand service, type: %s" % self.service_type) + # generate config + ret = self.gen_service_config() + if ret is False: + utilities.log_error( + "expand service failed for generate config failed, type: %s" % self.service_type) + return False + # expand service + utilities.print_badge("begin expand service") + ret = self.service_controller.expand_all() + if ret is True: + utilities.print_badge( + "expand service success, type: %s" % self.service_type) + else: + utilities.log_error( + "expand service failed, type: %s" % self.service_type) + utilities.print_split_info() + return ret diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/common/parser_handler.py" "b/BFPL\345\243\271/tools/BcosBuilder/src/common/parser_handler.py" new file mode 100644 index 00000000..533b4162 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/common/parser_handler.py" @@ -0,0 +1,454 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +from common import utilities +from common.utilities import CommandInfo +from argparse import RawTextHelpFormatter +import argparse +import sys +from common.utilities import ServiceInfo +import toml +import os +import uuid +from config.chain_config import ChainConfig +from controller.binary_controller import BinaryController +from command.service_command_impl import ServiceCommandImpl +from command.node_command_impl import NodeCommandImpl +from command.monitor_command_impl import MonitorCommandImpl +from networkmgr.network_manager import NetworkManager +from controller.node_controller import NodeController +from config.service_config_generator import ServiceConfigGenerator +from config.node_config_generator import NodeConfigGenerator +from config.tars_config_generator import TarsConfigGenerator + + +class _HelpAction(argparse._HelpAction): + def __call__(self, parser, namespace, values, option_string=None): + # retrieve subparsers from parser + subparsers_actions = [ + action for action in parser._actions + if isinstance(action, argparse._SubParsersAction)] + # there will probably only be one subparser_action, + # but better save than sorry + for subparsers_action in subparsers_actions: + # get all subparsers and print help + for choice, subparser in subparsers_action.choices.items(): + help_info = "help for subcommand '{}'".format(choice) + print("----------- %s -----------" % help_info) + print(subparser.format_help()) + parser.exit() + + +def get_description_prefix(subparser_name, command, service_type): + return "* %s %s: python3 %s %s -o deploy -t %s" % (command, service_type, sys.argv[0], subparser_name, service_type) + + +def parse_command(): + parser = argparse.ArgumentParser( + prog=sys.argv[0], description="", formatter_class=RawTextHelpFormatter, add_help=False) + parser.add_argument("-h", '--help', action=_HelpAction) + + sub_parsers = parser.add_subparsers(dest="command") + subparser_name = CommandInfo.download_binary + help_info = "Download binary, eg: python3 build_chain.py download_binary -t cdn" + binary_parser = sub_parsers.add_parser(description=utilities.format_info(help_info), + name=subparser_name, help="download binary", formatter_class=RawTextHelpFormatter) + help_info = "[Optional] Specify the source of the download, support %s now, default type is cdn" % ( + ','.join(CommandInfo.download_type)) + binary_parser.add_argument( + "-t", '--type', help=help_info, default="cdn", required=False) + help_info = "[Optional] Specify the version of the binary, default is %s" % CommandInfo.default_binary_version + binary_parser.add_argument( + "-v", "--version", default=CommandInfo.default_binary_version, help=help_info, required=False) + help_info = "[Optional] Specify the path of the binary, default is binary" + binary_parser.add_argument( + "-p", "--path", default="binary", help=help_info, required=False) + + subparser_name = CommandInfo.chain_sub_parser_name + deploy_nodes_command = get_description_prefix( + subparser_name, "deploy", ServiceInfo.node_service_type) + deploy_rpc_service_command = get_description_prefix(subparser_name, + "deploy", ServiceInfo.rpc_service_type) + deploy_gateway_service_command = get_description_prefix(subparser_name, + "deploy", ServiceInfo.gateway_service_type) + description = "e.g:\n%s\n%s\n%s" % ( + deploy_nodes_command, deploy_rpc_service_command, deploy_gateway_service_command) + chain_parser = sub_parsers.add_parser(description=utilities.format_info(description), + name=subparser_name, help="chain operation", formatter_class=RawTextHelpFormatter) + # command option + help_info = "[Required] specify the command: \n* command list: %s\n" % ( + CommandInfo.service_command_list_str) + chain_parser.add_argument( + "-o", '--op', help=help_info, required=True) + # config option + help_info = "[Optional] the config file, default is config.toml:\n * config to deploy chain example: conf/config-deploy-example.toml\n * config to expand node example: conf/config-node-expand-example.toml\n * config to expand rpc/gateway example: conf/config-service-expand-example.toml" + chain_parser.add_argument( + "-c", "--config", help=help_info, default="config.toml") + # service type option + supported_service_type_str = ', '.join(ServiceInfo.supported_service_type) + help_info = "[Required] the service type:\n* now support: %s \n" % ( + supported_service_type_str) + chain_parser.add_argument("-t", "--type", help=help_info, default="") + help_info = "[Optional] specify the output dir, default is ./generated" + chain_parser.add_argument( + "-O", "--output", default="./generated", help=help_info, required=False) + + #--------------------------------------------------------------------------------------------------- + subparser_name = CommandInfo.build_package_parser_name + build_nodes_command = "python3 build_chain.py build -c conf/config-build-example.toml -O output_dir" + build_expand_rpc_command = "python3 build_chain.py build -c conf/config-build-example.toml -t rpc -O output_dir" + build_expand_gateway_command = "python3 build_chain.py build -c conf/config-build-example.toml -t gateway -O output_dir" + build_expand_node_command = "python3 build_chain.py build -c conf/config-build-example.toml -t node -O output_dir" + + description = "e.g:\n%s\n%s\n%s\n%s" % ( + build_nodes_command, build_expand_node_command, build_expand_rpc_command, build_expand_gateway_command) + build_parser = sub_parsers.add_parser(description=utilities.format_info(description), + name=subparser_name, help="build operation", formatter_class=RawTextHelpFormatter) + # command option + help_info = "[Optional] specify the type: \n* type list: %s\n" % ( + CommandInfo.build_command_type_list_str) + + build_parser.add_argument( + "-t", '--type', help=help_info, required=False, default = "all") + + # config option + help_info = "[Required] the config file, default is config.toml:\n * config to build chain example: conf/config-build-example.toml" + build_parser.add_argument( + "-c", "--config", help=help_info, required=True) + + help_info = "[Optional] specify the output dir, default is ./generated" + build_parser.add_argument( + "-O", "--output", default="./generated", help=help_info, required=False) + #--------------------------------------------------------------------------------------------------- + + #--------------------------------------------------------------------------------------------------- + subparser_name = CommandInfo.merge_config_parser_name + merge_config_tars_command = "python3 build_chain.py merge-config -t tars -c tars0.conf tars1.conf -O output_dir" + merge_config_p2p_command = "python3 build_chain.py merge-config -t p2p -c nodes0.json nodes1.json -O output_dir" + + description = "e.g:\n%s" % (merge_config_tars_command) + merge_config_parser = sub_parsers.add_parser(description=utilities.format_info(description), + name=subparser_name, help="merge config operation", formatter_class=RawTextHelpFormatter) + # command option + help_info = "[Required] specify the type: \n* type list: %s\n" % ( + CommandInfo.merge_config_type_str) + + merge_config_parser.add_argument( + "-t", '--type', help=help_info, required=True) + + # config option + help_info = "[Required] the config files to be\n" + merge_config_parser.add_argument( + "-c", "--config", nargs='+', help=help_info, required=True) + + help_info = "[Required] specify the output dir" + merge_config_parser.add_argument( + "-O", "--output", help=help_info, required=True) + #--------------------------------------------------------------------------------------------------- + + # create_subnet_parser parser + description = "e.g: python3 %s create-subnet -n tars-network -s 172.25.0.0/16" % ( + sys.argv[0]) + create_subnet_parser = sub_parsers.add_parser(description=utilities.format_info(description), + name=CommandInfo.network_create_subnet, help="create docker subnet", formatter_class=RawTextHelpFormatter) + help_info = "[Optional] specified the network name, default is tars-network\n" + create_subnet_parser.add_argument( + "-n", "--name", help=help_info, default="tars-network") + + help_info = "[Required] specified the subnet, e.g. 172.25.0.0/16\n" + create_subnet_parser.add_argument( + "-s", "--subnet", help=help_info, default=None, required=True) + + # network_add_vxlan + # description = ( + # "Note: only support linux now\ne.g: python3 %s add-vxlan -n tars-network -d ${remote_ip} -v docker_vxlan") % (sys.argv[0]) + # add_vxlan_parser = sub_parsers.add_parser(description=utilities.format_info(description), + # name=CommandInfo.network_add_vxlan, help="add vxlan for docker subnet", formatter_class=RawTextHelpFormatter) + # subnet option + #help_info = "[Optional] specified the subnet, default is tars-network\n" + # add_vxlan_parser.add_argument( + # "-n", "--network", help=help_info, default="tars-network") + # remote ip + #help_info = "[Required] specified the dstip to create vxlan network" + # add_vxlan_parser.add_argument( + # "-d", "--dstip", help=help_info, default=None, required=True) + + # vxlan network name + #help_info = "[Required] specified the vxlan name to create vxlan network, e.g.: vxlan_docker" + # add_vxlan_parser.add_argument( + # "-v", "--vxlan", help=help_info, default=None, required=True) + + args = parser.parse_args() + return args + + +def is_chain_command(args): + return (args.command == CommandInfo.chain_sub_parser_name) + + +def is_build_package_command(args): + return (args.command == CommandInfo.build_package_parser_name) + +def is_create_subnet_command(args): + return (args.command == CommandInfo.network_create_subnet) + + +def is_add_vxlan_command(args): + return (args.command == CommandInfo.network_add_vxlan) + + +def is_download_binary_command(args): + return (args.command == CommandInfo.download_binary) + +def is_merge_config_command(args): + return (args.command == CommandInfo.merge_config_parser_name) + +def chain_operations(args, node_type): + if is_chain_command(args) is False: + return + if os.path.exists(args.config) is False: + utilities.log_error("The config file '%s' not found!" % args.config) + sys.exit(-1) + toml_config = toml.load(args.config) + op_type = args.type + if op_type not in ServiceInfo.supported_service_type: + utilities.log_error("the service type must be " + + ', '.join(ServiceInfo.supported_service_type)) + return + + output_dir = args.output + # if os.path.exists(output_dir): + # utilities.log_info( output_dir + " is already exists, please switch directory or remove it after confirm the directory is no longer in use") + # sys.exit(-1) + + utilities.log_info("generator output dir is %s" % output_dir) + + command = args.op + if op_type == ServiceInfo.rpc_service_type or op_type == ServiceInfo.gateway_service_type: + if command in CommandInfo.service_command_impl.keys(): + chain_config = ChainConfig(toml_config, node_type, output_dir, False, True) + command_impl = ServiceCommandImpl( + chain_config, args.type, node_type, output_dir) + impl_str = CommandInfo.service_command_impl[command] + cmd_func_attr = getattr(command_impl, impl_str) + cmd_func_attr() + return + if op_type == ServiceInfo.monitor_service_type: + if command in CommandInfo.node_command_to_impl.keys(): + chain_config = ChainConfig(toml_config, node_type, output_dir, True, True) + command_impl = MonitorCommandImpl(chain_config, node_type, output_dir) + impl_str = CommandInfo.monitor_command_to_impl[command] + cmd_func_attr = getattr(command_impl, impl_str) + cmd_func_attr() + return + if op_type == ServiceInfo.node_service_type: + if command in CommandInfo.node_command_to_impl.keys(): + chain_config = ChainConfig(toml_config, node_type, output_dir, True, True) + command_impl = NodeCommandImpl(chain_config, node_type, output_dir) + impl_str = CommandInfo.node_command_to_impl[command] + cmd_func_attr = getattr(command_impl, impl_str) + cmd_func_attr() + return + if op_type == ServiceInfo.executor_service_type: + if command in CommandInfo.executor_command_to_impl.keys(): + chain_config = ChainConfig(toml_config, node_type, output_dir, True, True) + command_impl = NodeCommandImpl(chain_config, node_type, output_dir) + impl_str = CommandInfo.executor_command_to_impl[command] + cmd_func_attr = getattr(command_impl, impl_str) + cmd_func_attr() + return + utilities.log_info("unimplemented command, op_type: " + str(op_type)) + + +def create_subnet_operation(args): + if is_create_subnet_command(args) is False: + return + docker_network_name = args.name + if docker_network_name is None or len(docker_network_name) == 0: + utilities.log_error( + "Must set the docker network name! e.g. tars-network") + sys.exit(-1) + subnet_ip_segment = args.subnet + if subnet_ip_segment is None or len(subnet_ip_segment) == 0: + utilities.log_error("Must set the subnet! e.g. 172.25.0.0.1") + sys.exist(-1) + NetworkManager.create_sub_net(subnet_ip_segment, docker_network_name) + utilities.print_split_info() + + +def add_vxlan_operation(args): + if is_add_vxlan_command(args) is False: + return + utilities.print_split_info() + network = args.network + if network is None or len(network) == 0: + utilities.log_error( + "Must set a valid non-empty network name, e.g. tars-network") + return + dstip = args.dstip + if dstip is None or len(dstip) == 0: + utilities.log_error("Must set a valid non-empty dst ip") + return + vxlan_name = args.vxlan + if vxlan_name is None or len(vxlan_name) == 0: + utilities.log_error("Must set a valid non-empty vxlan name") + return + NetworkManager.create_bridge(network, vxlan_name, dstip) + utilities.print_split_info() + + +def download_binary_operation(args, node_type): + if is_download_binary_command(args) is False: + return + utilities.print_split_info() + binary_path = args.path + version = args.version + if version.startswith("v") is False: + version = "v" + version + if args.type not in CommandInfo.download_type: + utilities.log_error("Unsupported download type %s, only support %s now" % ( + args.type, ', '.join(CommandInfo.download_type))) + return + use_cdn = True + if args.type == "git": + use_cdn = False + binary_controller = BinaryController( + version, binary_path, use_cdn, node_type) + binary_controller.download_all_binary() + utilities.print_split_info() + +def merge_config_operation(args): + if is_merge_config_command(args) is False: + return + + utilities.print_split_info() + + utilities.log_info("* merge-config operation ") + + output_dir = args.output + utilities.log_info("* output dir: " + output_dir) + + type = args.type + utilities.log_info("* type: " + type) + + if (type in ["tars"]) is False: + utilities.log_error("Unsupported types, only 'tars' type is supported") + sys.exit(-1) + + if len(args.config) <= 1: + utilities.log_error("Merge operation is supported for more than two config files") + sys.exit(-1) + + tars_proxy_conf_path = os.path.join(output_dir, "tars_proxy.ini") + p2p_conf_path = os.path.join(output_dir, "nodes.json") + if os.path.exists(tars_proxy_conf_path): + utilities.log_error(tars_proxy_conf_path + " is already exist, please change the output dir") + sys.exit(-1) + + config = args.config + for c in config: + if os.path.exists(c) is False: + utilities.log_error("The config file '%s' not found!" % c) + sys.exit(-1) + utilities.mkdir(output_dir) + if type == "tars": + merge_tars_config(config, tars_proxy_conf_path) + else: + merge_p2p_config(config, p2p_conf_path) + + utilities.print_split_info() + utilities.log_info("* merge config output dir is %s" % output_dir) + +def merge_tars_config(config, store_tars_conf_path): + + merged_tars_conf_gen = TarsConfigGenerator(store_tars_conf_path) + for c in config: + utilities.log_info("* tars config: " + str(c)) + tars_conf = TarsConfigGenerator(c) + tars_service_names = ["gateway", "rpc", "txpool", "scheduler", "pbft", "ledger", "front"] + for service_name in tars_service_names: + conf_items = tars_conf.get_service_config_items(service_name) + if conf_items is None: + continue + + for k in conf_items: + utilities.log_info(" service name: %s ,endpoint: %s" % (service_name, conf_items[k])) + merged_tars_conf_gen.append_config_item(service_name, conf_items[k]) + + merged_tars_conf_gen.restore_init_config(os.path.join(store_tars_conf_path)) + +def merge_p2p_config(config, store_tars_conf_path): + # TODO: impl p2p config merge + return + +def build_package_operation(args, node_type): + if is_build_package_command(args) is False: + return + + utilities.print_split_info() + + if node_type == "max": + utilities.log_info("build_chain.py doesn't support building install package on Max version, please use pro version instead") + sys.exit(-1) + + args = parse_command() + if os.path.exists(args.config) is False: + utilities.log_error("the config file '%s' not found!" % args.config) + sys.exit(-1) + + output_dir = args.output + utilities.log_info("* output dir: " + output_dir) + + if os.path.exists(output_dir): + utilities.log_info( output_dir + " already exists, please switch directory or remove it after confirm the directory is no longer in use") + sys.exit(-1) + + toml_config = toml.load(args.config) + chain_config = ChainConfig(toml_config, node_type, output_dir, True, False) + + utilities.file_must_exist(chain_config.tars_config.tars_pkg_dir) + utilities.file_must_exist(os.path.join(chain_config.tars_config.tars_pkg_dir, "BcosNodeService")) + utilities.file_must_exist(os.path.join(chain_config.tars_config.tars_pkg_dir, "BcosRpcService")) + utilities.file_must_exist(os.path.join(chain_config.tars_config.tars_pkg_dir, "BcosGatewayService")) + + if node_type == "max": + utilities.file_must_exist(os.path.join(chain_config.tars_config.tars_pkg_dir, "BcosExecutorService")) + utilities.file_must_exist(os.path.join(chain_config.tars_config.tars_pkg_dir, "BcosMaxNodeService")) + + # TODO: port conflict check + + type = args.type + utilities.log_info("* type: " + type) + + is_rpc_build = False + if type in ["all", "rpc"]: + # gen rpc config + rpc_config_gen = ServiceConfigGenerator(chain_config, "rpc", node_type, output_dir) + rpc_config_gen.generate_all_config(True) + is_rpc_build = True + + is_gateway_build = False + if type in ["all", "gateway"]: + # gen gateway config + gateway_config_gen = ServiceConfigGenerator(chain_config, "gateway", node_type, output_dir) + gateway_config_gen.generate_all_config(True) + is_gateway_build = True + + is_node_build = False + if type in ["all", "node"]: + # gen node config + node_config_gen = NodeConfigGenerator(chain_config, node_type, output_dir) + node_config_gen.generate_all_config(False, True) + is_node_build = True + + # copy tars proxy json file + if is_rpc_build: + rpc_config_gen.copy_tars_proxy_conf() + if is_gateway_build: + gateway_config_gen.copy_tars_proxy_conf() + if is_node_build: + node_config_gen.copy_tars_proxy_conf() + + + utilities.print_split_info() + utilities.log_info("* build package output dir is %s" % output_dir) + diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/common/utilities.py" "b/BFPL\345\243\271/tools/BcosBuilder/src/common/utilities.py" new file mode 100644 index 00000000..f1b4be1d --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/common/utilities.py" @@ -0,0 +1,360 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +import sys +import re +import os +import subprocess +import logging + +logging.basicConfig(format='%(message)s', + level=logging.INFO) + + +class ServiceInfo: + node_service_type = "node" + executor_service_type = "executor" + rpc_service_type = "rpc" + gateway_service_type = "gateway" + monitor_service_type = "monitor" + + ssl_file_list = ["ca.crt", "ssl.key", "ssl.crt"] + sm_ssl_file_list = ["sm_ca.crt", "sm_ssl.key", + "sm_ssl.crt", "sm_enssl.key", "sm_enssl.crt"] + + rpc_service = "BcosRpcService" + rpc_service_obj = ["RpcServiceObj"] + gateway_service = "BcosGatewayService" + gateway_service_obj = ["GatewayServiceObj"] + + single_node_service = "BcosNodeService" + single_node_obj_name_list = [ + "LedgerServiceObj", "SchedulerServiceObj", "TxPoolServiceObj", "PBFTServiceObj", "FrontServiceObj"] + + rpc_name = "rpc" + gateway_name = "gateway" + ledger_name = "ledger" + front_name = "front" + executor_name = "executor" + scheduler_name = "scheduler" + txpool_name = "txpool" + + max_node_service = "BcosMaxNodeService" + max_node_service_obj = single_node_obj_name_list + executor_service = "BcosExecutorService" + executor_service_obj = ["ExecutorServiceObj"] + + supported_vm_types = ["evm", "wasm"] + supported_consensus_list = ["pbft"] + tars_pkg_postfix = ".tgz" + default_listen_ip = "0.0.0.0" + cert_generationscript_path = "../src/scripts/generate_cert.sh" + supported_service_type = [node_service_type, monitor_service_type, executor_service_type, + rpc_service_type, gateway_service_type] + + +class ConfigInfo: + tpl_abs_path = "../src/tpl/" + tpl_src_mtail_path = "../src/scripts/" + tpl_binary_path = "./binary/" + tpl_monitor_path = "../docker/host/linux/monitor/" + pwd_path = os.getcwd() + rpc_config_tpl_path = os.path.join( + pwd_path, tpl_abs_path, "config.ini.rpc") + gateway_config_tpl_path = os.path.join( + pwd_path, tpl_abs_path, "config.ini.gateway") + genesis_config_tpl_path = os.path.join( + pwd_path, tpl_abs_path, "config.genesis") + node_config_tpl_path = os.path.join( + pwd_path, tpl_abs_path, "config.ini.node") + mtail_config_tpl_path = os.path.join( + pwd_path, tpl_abs_path, "node.mtail") + monitor_config_tpl_path = os.path.join( + pwd_path, tpl_monitor_path, "") + prometheus_config_tpl_path = os.path.join( + pwd_path, tpl_monitor_path, "prometheus/prometheus.yml") + executor_config_tpl_path = os.path.join( + pwd_path, tpl_abs_path, "config.ini.executor") + tars_start_tpl_path = os.path.join( + pwd_path, tpl_abs_path, "tars_start.sh") + tars_stop_tpl_path = os.path.join( + pwd_path, tpl_abs_path, "tars_stop.sh") + tars_rpc_conf_tpl_path = os.path.join( + pwd_path, tpl_abs_path, "tars_rpc.conf") + tars_gateway_conf_tpl_path = os.path.join( + pwd_path, tpl_abs_path, "tars_gateway.conf") + tars_node_conf_tpl_path = os.path.join( + pwd_path, tpl_abs_path, "tars_node.conf") + + +class CommandInfo: + gen_config = "gen-config" + upload = "upload" + deploy = "deploy" + upgrade = "upgrade" + undeploy = "undeploy" + start = "start" + stop = "stop" + expand = "expand" + network_create_subnet = "create-subnet" + network_add_vxlan = "add-vxlan" + download_binary = "download_binary" + download_type = ["cdn", "git"] + default_binary_version = "v3.1.0" + command_list = [gen_config, upload, deploy, + upgrade, undeploy, expand, start, stop] + service_command_list_str = ', '.join(command_list) + chain_sub_parser_name = "chain" + node_command_to_impl = {gen_config: "gen_node_config", upload: "upload_nodes", deploy: "deploy_nodes", + upgrade: "upgrade_nodes", undeploy: "undeploy_nodes", start: "start_all", stop: "stop_all", expand: "expand_nodes"} + monitor_command_to_impl = {deploy: "deploy_monitor", + start: "start_monitor", stop: "stop_monitor"} + executor_command_to_impl = { + gen_config: "gen_executor_config", expand: "expand_executors"} + service_command_impl = {gen_config: "gen_service_config", upload: "upload_service", deploy: "deploy_service", + upgrade: "upgrade_service", undeploy: "delete_service", start: "start_service", stop: "stop_service", expand: "expand_service"} + + build_package_parser_name = "build" + build_command_type_list = ["rpc", "gateway", "node", "all"] + build_command_type_list_str = ', '.join(build_command_type_list) + + merge_config_parser_name = "merge-config" + # merge_config_type_list = ["p2p", "tars"] + merge_config_type_list = ["tars"] + merge_config_type_str = ', '.join(merge_config_type_list) + +def log_error(error_msg): + logging.error("\033[31m%s \033[0m" % error_msg) + + +def log_info(error_msg): + logging.info("\033[32m%s \033[0m" % error_msg) + + +def format_info(info): + return ("\033[32m%s \033[0m" % info) + + +def log_debug(error_msg): + logging.debug("%s" % error_msg) + + +def get_item_value(config, key, default_value, must_exist, desc): + if key in config: + return config[key] + if must_exist: + raise Exception("the value for %s.%s must be set" % (desc, key)) + return default_value + + +def get_value(config, section, key, default_value, must_exist): + if section in config and key in config[section]: + return config[section][key] + if must_exist: + raise Exception("the value for %s must be set" % key) + return default_value + + +def execute_command_and_getoutput(command): + status, output = subprocess.getstatusoutput(command) + if status != 0: + log_error( + "execute command %s failed, error message: %s" % (command, output)) + return (False, output) + return (True, output) + + +def execute_command(command): + (ret, result) = execute_command_and_getoutput(command) + return ret + + +def mkdir(path): + if not os.path.exists(path): + os.makedirs(path) + +def removeDir(path): + if os.path.exists(path): + os.removedirs(path) + +def mkfiledir(filepath): + parent_dir = os.path.abspath(os.path.join(filepath, "..")) + mkdir(parent_dir) + + +def generate_service_name(prefix, service_name): + return prefix + service_name + + +def convert_bool_to_str(value): + if value is True: + return "true" + return "false" + + +def generate_cert_with_command(sm_type, command, outputdir, ca_cert_info): + """ + generate cert for the network + """ + sm_mode = "" + if sm_type is True: + sm_mode = " -s" + generate_cert_cmd = "bash %s -o %s -c %s %s %s" % ( + ServiceInfo.cert_generationscript_path, outputdir, command, sm_mode, ca_cert_info) + if execute_command(generate_cert_cmd) is False: + log_error("%s failed" % command) + sys.exit(-1) + return True + + +def execute_monitor_with_command(monitor_execute__scripts_path): + """ + execute common for the monitor + """ + execute_monitor_cmd = "bash %s" % ( + monitor_execute__scripts_path) + if execute_command(execute_monitor_cmd) is False: + log_error("%s failed exec " % monitor_execute__scripts_path) + sys.exit(-1) + return True + + +def execute_mtail_with_command(mtail_execute_scripts_path, mtail_listen_port): + """ + execute mtail for the monitor + """ + execute_mtail_cmd = "bash %s %s " % ( + mtail_execute_scripts_path, mtail_listen_port) + if execute_command(execute_mtail_cmd) is False: + log_error("%s failed start" % mtail_execute_scripts_path) + sys.exit(-1) + return True + + +def execute_ansible_copy_with_command(deploy_ip, srcDir, destDir): + """ + execute ansible for the monitor + + """ + execute_copy_matil_cmd = 'ansible %s -m copy -a "src=%s dest=%s/ mode=0755"' % ( + deploy_ip, srcDir, destDir) + if execute_command(execute_copy_matil_cmd) is False: + log_error("%s failed copy mtail config for ansible" % srcDir) + sys.exit(-1) + return True + + +def execute_ansible_with_command(start_scripts_path, deploy_ip, mtail_listen_port): + """ + execute ansible for the monitor + + """ + execute_matil_cmd = 'ansible %s -m shell -a "bash %s %s "' % ( + deploy_ip, start_scripts_path, mtail_listen_port) + if execute_command(execute_matil_cmd) is False: + log_error("%s failed start mtail config by ansible" % + start_scripts_path) + sys.exit(-1) + return True + + +def execute_ansible_with_monitor_command(start_scripts_path, deploy_ip): + """ + execute ansible for the monitor + + """ + execute_matil_cmd = 'ansible %s -m shell -a "%s"' % ( + deploy_ip, start_scripts_path) + if execute_command(execute_matil_cmd) is False: + log_error("%s failed start mtail config by ansible" % + start_scripts_path) + sys.exit(-1) + return True + + +def generate_private_key(sm_type, outputdir): + return generate_cert_with_command(sm_type, "generate_private_key", outputdir, "") + + +def generate_cert(sm_type, outputdir): + return generate_cert_with_command(sm_type, "generate_all_cert", outputdir, "") + + +def generate_ca_cert(sm_type, cacert_dir): + command = "generate_ca_cert" + ca_cert_info = "-d %s" % cacert_dir + return generate_cert_with_command(sm_type, command, cacert_dir, ca_cert_info) + + +def generate_node_cert(sm_type, ca_cert_path, outputdir): + command = "generate_node_cert" + ca_cert_info = "-d %s" % ca_cert_path + return generate_cert_with_command(sm_type, command, outputdir, ca_cert_info) + + +def generate_sdk_cert(sm_type, ca_cert_path, outputdir): + command = "generate_sdk_cert" + ca_cert_info = "-d %s" % ca_cert_path + return generate_cert_with_command(sm_type, command, outputdir, ca_cert_info) + + +def print_split_info(): + log_info("=========================================================") + + +def print_badge(badge): + log_info("----------- %s -----------" % badge) + +def file_must_exist(file_path): + if not os.path.exists(file_path): + log_error("%s does not exist, please check" % file_path) + sys.exit(-1) + +def try_to_rename_tgz_package(root_path, tars_pkg_path, service_name, org_service_name): + renamed_package_path = "" + if os.path.exists(tars_pkg_path) is False: + log_error("rename pkg: the tars pkg path %s doesn't exist, service: %s" % ( + tars_pkg_path, service_name)) + return (False, renamed_package_path) + + org_package_name = org_service_name + ServiceInfo.tars_pkg_postfix + org_package_path = os.path.join(tars_pkg_path, org_package_name) + if os.path.exists(org_package_path) is False: + log_error("rename pkg: the tars pkg path %s doesn't exist, service: %s" % ( + tars_pkg_path, service_name)) + return (False, renamed_package_path) + + unzip_binary_path = os.path.join("./", org_service_name, org_service_name) + if service_name == org_service_name: + return (True, org_package_path) + renamed_package_name = service_name + ServiceInfo.tars_pkg_postfix + renamed_package_path = os.path.join("./", renamed_package_name) + renamed_binary_path = os.path.join("./", service_name, service_name) + + mkdir_command = "mkdir -p %s" % os.path.join("./", service_name) + unzip_command = "tar -xvf %s" % org_package_path + mv_command = "mv %s %s && rm -rf %s" % ( + unzip_binary_path, renamed_binary_path, os.path.join("./", org_service_name)) + zip_command = "tar -cvzf %s %s" % (renamed_package_path, + renamed_binary_path) + rm_command = "rm -rf %s" % os.path.join("./", service_name) + generated_package_path = os.path.join(root_path, renamed_package_path) + mv_pkg_command = "mkdir -p %s && mv %s %s" % ( + root_path, renamed_package_path, root_path) + command = "%s && %s && %s && %s && %s && %s" % ( + mkdir_command, unzip_command, mv_command, zip_command, rm_command, mv_pkg_command) + ret = execute_command(command) + if ret is False: + log_error("try_to_rename_tgz_package failed, service: %s" % + service_name) + return (ret, generated_package_path) + + +def check_service_name(tag, service_name): + """ + Note: tars service name can only contain letters and numbers + """ + service_name_len = len(service_name) + ret = re.search(r'^[A-Za-z0-9]+', service_name).span() + if ret is None or (ret[0] != 0 or ret[1] != service_name_len): + raise Exception( + "the %s must be letters|numbers, invalid value: %s" % (tag, service_name)) diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/config/chain_config.py" "b/BFPL\345\243\271/tools/BcosBuilder/src/config/chain_config.py" new file mode 100644 index 00000000..98d0b0ed --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/config/chain_config.py" @@ -0,0 +1,398 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +import json +import sys +import os + +from common import utilities +from common.utilities import ServiceInfo + + +class TarsConfig: + def __init__(self, config, requireUrl): + self.config = config + section = "tars" + self.tars_pkg_dir = utilities.get_value( + self.config, section, "tars_pkg_dir", "binary", False) + self.tars_pkg_dir = self.tars_pkg_dir.strip() + + if not requireUrl: + utilities.log_info("* Don't load tars token and url") + return + + self.tars_url = utilities.get_value( + self.config, section, "tars_url", None, True) + self.tars_url = self.tars_url.strip() + self.tars_token = utilities.get_value( + self.config, section, "tars_token", None, True) + self.tars_token = self.tars_token.strip() + + if len(self.tars_token) == 0: + utilities.log_error("Must config 'tars.tars_token'") + sys.exit(-1) + + +class GenesisConfig: + def __init__(self, config, sm_type, chain_id, group_id): + self.config = config + self.desc = "[[group]]" + self.sm_crypto = sm_type + self.group_id = group_id + self.chain_id = chain_id + self.leader_period = utilities.get_item_value( + self.config, "leader_period", 1, False, self.desc) + self.block_tx_count_limit = utilities.get_item_value( + self.config, "block_tx_count_limit", 1000, False, self.desc) + self.consensus_type = utilities.get_item_value( + self.config, "consensus_type", "pbft", False, self.desc) + self.gas_limit = utilities.get_item_value( + self.config, "gas_limit", "3000000000", False, self.desc) + self.compatibility_version = utilities.get_item_value( + self.config, "compatibility_version", "3.1.0", False, self.desc) + self.vm_type = utilities.get_item_value( + self.config, "vm_type", "evm", False, self.desc) + self.auth_check = utilities.get_item_value( + self.config, "auth_check", False, False, self.desc) + self.init_auth_address = utilities.get_item_value( + self.config, "init_auth_address", "", self.auth_check, self.desc) + + +class AgencyConfig: + def __init__(self, config, chain_id, default_url, enforce_failover): + """ + init the agencyConfig + """ + self.config = config + self.chain_id = chain_id + # the agencyName + self.desc = "[[agency]]." + self.name = utilities.get_item_value( + self.config, "name", None, True, self.desc) + utilities.check_service_name("agency.name", self.name) + # the rpc service_name + self.rpc_service_name = self.name + utilities.ServiceInfo.rpc_service + # the gateway service_name + self.gateway_service_name = self.name + utilities.ServiceInfo.gateway_service + # the failover cluster url + self.failover_cluster_url = utilities.get_item_value( + self.config, "failover_cluster_url", default_url, enforce_failover, self.desc) + # load storage_security config + self.enable_storage_security = utilities.get_item_value( + self.config, "enable_storage_security", False, False, self.desc) + self.key_center_url = utilities.get_item_value( + self.config, "key_center_url", "", False, self.desc) + self.cipher_data_key = utilities.get_item_value( + self.config, "cipher_data_key", "", False, self.desc) + + +class ServiceInfoConfig: + def __init__(self, config, agency_config, service_obj_list, name, service_type, tpl_config_file, ca_cert_path, sm_ssl, binary_name): + self.config = config + self.name = name + self.ca_cert_path = ca_cert_path + self.service_type = service_type + self.tpl_config_file = tpl_config_file + self.agency_config = agency_config + self.service_obj_list = service_obj_list + self.sm_ssl = sm_ssl + self.binary_name = binary_name + # the service deploy ip + self.desc = "[agency." + service_type + "]." + self.deploy_ip_list = utilities.get_item_value( + self.config, "deploy_ip", None, True, self.desc) + self.listen_ip = utilities.get_item_value( + self.config, "listen_ip", ServiceInfo.default_listen_ip, False, self.desc) + self.listen_port = utilities.get_item_value( + self.config, "listen_port", 20200, False, self.desc) + self.thread_count = utilities.get_item_value( + self.config, "thread_count", 4, False, self.desc) + # peers info + self.peers = utilities.get_item_value( + self.config, "peers", [], False, self.desc) + # tars listen ip + self.tars_listen_ip = utilities.get_item_value( + self.config, "tars_listen_ip", ServiceInfo.default_listen_ip, False, self.desc) + # tars listen port + self.tars_listen_port = utilities.get_item_value( + self.config, "tars_listen_port", 40400, False, self.desc) + + +class NodeServiceConfig: + def __init__(self, app_name, base_service_name, service_name, service_obj_list, deploy_ip_list, config_file_list, need_add_ini_config): + self.app_name = app_name + self.service_name = service_name + self.service_obj_list = service_obj_list + self.deploy_ip_list = deploy_ip_list + self.base_service_name = base_service_name + # Note: in max-node mode, only contains [config.genesis, node.pem] + # in pro-node mode, contains [config.ini, config.genesis, node.pem] + self.config_file_list = config_file_list + # Note: in max-node mode, the ini config files prefixed with deploy ip + self.ini_config_file = "config.ini" + self.need_add_ini_config = need_add_ini_config + + +class NodeConfig: + def __init__(self, config, chain_id, group_id, agency_config, node_service_base_name, node_service_obj_list, sm_crypto, node_type): + self.chain_id = chain_id + self.group_id = group_id + self.agency_config = agency_config + self.config = config + # the node name + self.desc = "[[agency.group.node]]." + self.node_name = utilities.get_item_value( + self.config, "node_name", None, True, self.desc) + self.node_name = self.node_name.strip() + + # parse key_page_size + self.key_page_size = utilities.get_item_value( + self.config, "key_page_size", 10240, False, self.desc) + # load storage_security + self.enable_storage_security = utilities.get_item_value( + self.config, "enable_storage_security", False, False, self.desc) + self.key_center_url = utilities.get_item_value( + self.config, "key_center_url", "", False, self.desc) + self.cipher_data_key = utilities.get_item_value( + self.config, "cipher_data_key", "", False, self.desc) + self.deploy_ip = utilities.get_item_value( + self.config, "deploy_ip", None, True, self.desc) + self.tars_listen_ip = utilities.get_item_value( + self.config, "tars_listen_ip", "0.0.0.0", False, self.desc) + self.tars_listen_port = utilities.get_item_value( + self.config, "tars_listen_port", 40400, False, self.desc) + self.monitor_listen_port = utilities.get_item_value( + self.config, "monitor_listen_port", None, False, self.desc) + self.monitor_log_path = utilities.get_item_value( + self.config, "monitor_log_path", None, False, self.desc) + # parse node_service_config + self.node_service_base_name = node_service_base_name + self.node_service_obj_list = node_service_obj_list + self.sm_crypto = sm_crypto + self.service_list = [] + self.__parse_node_service_config(node_type) + + def __parse_node_service_config(self, node_type): + """ + parse and load the node_service config + """ + # the max_node service config + self.node_service_name = self.get_service_name( + self.node_service_base_name) + + node_deploy_ip = utilities.get_item_value( + self.config, "deploy_ip", None, True, self.desc) + deploy_ip_list = [] + self.node_config_file_list = None + if node_type != "max": + self.node_config_file_list = [ + "config.ini", "config.genesis", "node.pem"] + deploy_ip_list.append(node_deploy_ip) + else: + self.node_config_file_list = ["config.genesis", "node.pem"] + deploy_ip_list = node_deploy_ip + + self.node_service = NodeServiceConfig(self.agency_config.chain_id, self.node_service_base_name, self.node_service_name, + self.node_service_obj_list, deploy_ip_list, self.node_config_file_list, True) + self.service_list.append(self.node_service) + + def get_service_name(self, service_base_name): + return (self.agency_config.name + self.group_id + self.node_name + service_base_name) + + +class ProNodeConfig(NodeConfig): + """ + the pro-node config + """ + + def __init__(self, config, chain_id, group_id, agency_config, sm_crypto): + NodeConfig.__init__(self, config, chain_id, group_id, agency_config, + utilities.ServiceInfo.single_node_service, utilities.ServiceInfo.single_node_obj_name_list, sm_crypto, "pro") + + +class MaxNodeConfig(NodeConfig): + def __init__(self, config, chain_id, group_id, agency_config, sm_crypto): + """ + the max-node config + """ + NodeConfig.__init__(self, config, chain_id, group_id, agency_config, + utilities.ServiceInfo.max_node_service, utilities.ServiceInfo.single_node_obj_name_list, sm_crypto, "max") + # load the pd_addrs + self.pd_addrs = utilities.get_item_value( + self.config, "pd_addrs", None, True, self.desc) + # the executor service config + self.__parse_executor_service_config() + # load service name(for executor) + self.__load_service_name() + # enforce turnoff the storage_security + self.enable_storage_security = False + + def __parse_executor_service_config(self): + """ + parse and load the executor service_config + """ + executor_service_name = self.get_service_name( + utilities.ServiceInfo.executor_service) + executor_service_deploy_ip = utilities.get_item_value( + self.config, "executor_deploy_ip", None, True, self.desc) + self.executor_config_file_list = ["config.ini", "config.genesis"] + self.executor_service = NodeServiceConfig(self.chain_id, utilities.ServiceInfo.executor_service, executor_service_name, + utilities.ServiceInfo.executor_service_obj, executor_service_deploy_ip, self.executor_config_file_list, False) + self.service_list.append(self.executor_service) + + def __load_service_name(self): + self.scheduler_service_name = self.node_service_name + self.txpool_service_name = self.node_service_name + + +class GroupConfig: + def __init__(self, config, chain_id, output_dir): + self.config = config + self.chain_id = chain_id + self.desc = "[[group]]." + self.group_id = utilities.get_item_value( + self.config, "group_id", "group", False, self.desc) + # check the groupID + utilities.check_service_name("group_id", self.group_id) + default_genesis_config_path = os.path.join( + output_dir + "/", self.chain_id, self.group_id, "config.genesis") + self.genesis_config_path = utilities.get_item_value( + self.config, "genesis_config_path", default_genesis_config_path, False, self.desc) + # self.vm_type = utilities.get_item_value( + # self.config, "vm_type", "evm", False, self.desc) + self.sm_crypto = utilities.get_item_value( + self.config, "sm_crypto", False, False, self.desc) + # self.auth_check = utilities.get_item_value( + # self.config, "auth_check", False, False, self.desc) + # self.init_auth_address = utilities.get_item_value( + # self.config, "init_auth_address", "", self.auth_check, self.desc) + self.genesis_config = GenesisConfig( + self.config, self.sm_crypto, self.chain_id, self.group_id) + self.node_list = [] + + def append_node_list(self, node_list): + self.node_list = self.node_list + node_list + + +class ChainConfig: + def __init__(self, config, node_type, output_dir, should_load_node_config, require_tars_url): + self.config = config + self.output_dir = output_dir + self.node_type = node_type + self.enforce_failover = False + if self.node_type == "max": + self.enforce_failover = True + self.default_failover_url = "127.0.0.1:2379" + self.tars_config = TarsConfig(config, require_tars_url) + self.desc = "[chain]." + self.__load_chain_config() + # load the group list + self.group_list = {} + self.__load_group_list() + # agency_name to agency_config + self.agency_list = {} + # rpc_service_name to rpc_service + self.rpc_service_list = {} + # gateway_service_name to gateway_service + self.gateway_service_list = {} + # the node list + self.node_list = {} + self.__load_agency_config(should_load_node_config) + + def __load_group_list(self): + """ + load the group list + """ + group_list_config = utilities.get_item_value( + self.config, "group", [], False, self.desc) + for group in group_list_config: + group_config = GroupConfig(group, self.chain_id, self.output_dir) + self.group_list[group_config.group_id] = group_config + + def __load_chain_config(self): + """ + load the chain_config + """ + self.chain_id = utilities.get_value( + self.config, "chain", "chain_id", "chain", False) + # check the chain_id + utilities.check_service_name("chain_id", self.chain_id) + default_rpc_ca_cert = os.path.join( + self.output_dir, "rpc", self.chain_id, "ca") + + self.rpc_ca_cert_path = utilities.get_value( + self.config, "chain", "rpc_ca_cert_path", default_rpc_ca_cert, False) + + default_gateway_ca_cert = os.path.join( + self.output_dir, "gateway", self.chain_id, "ca") + + self.gateway_ca_cert_path = utilities.get_value( + self.config, "chain", "gateway_ca_cert_path", default_gateway_ca_cert, False) + self.rpc_sm_ssl = utilities.get_value( + self.config, "chain", "rpc_sm_ssl", False, False) + self.gateway_sm_ssl = utilities.get_value( + self.config, "chain", "gateway_sm_ssl", False, False) + + def __load_agency_config(self, should_load_node_config): + """ + load the agency config + """ + agency_list = utilities.get_item_value( + self.config, "agency", [], False, "") + for agency in agency_list: + # parse the agency config + agency_config = AgencyConfig( + agency, self.chain_id, self.default_failover_url, self.enforce_failover) + self.agency_list[agency_config.name] = agency_config + # parse the rpc service config + rpc_config_section = utilities.get_item_value( + agency, "rpc", None, False, "[agency.rpc]") + if rpc_config_section is not None: + rpc_config = ServiceInfoConfig(rpc_config_section, agency_config, utilities.ServiceInfo.rpc_service_obj, agency_config.rpc_service_name, + utilities.ServiceInfo.rpc_service_type, utilities.ConfigInfo.rpc_config_tpl_path, self.rpc_ca_cert_path, self.rpc_sm_ssl, utilities.ServiceInfo.rpc_service) + self.rpc_service_list[rpc_config.name] = rpc_config + # parse the gateway service config + gateway_config_section = utilities.get_item_value( + agency, "gateway", None, False, "[agency.gateway]") + if gateway_config_section is not None: + gateway_config = ServiceInfoConfig(gateway_config_section, agency_config, utilities.ServiceInfo.gateway_service_obj, agency_config.gateway_service_name, + utilities.ServiceInfo.gateway_service_type, utilities.ConfigInfo.gateway_config_tpl_path, self.gateway_ca_cert_path, self.gateway_sm_ssl, utilities.ServiceInfo.gateway_service) + self.gateway_service_list[gateway_config.name] = gateway_config + # parse the node config + if should_load_node_config is True: + self.__load_node_config(agency, agency_config) + + def __check_duplicate_node_name(self, service_name): + for key, _ in self.node_list.items(): + if service_name == key: + return True + return False + + def __load_node_config(self, agency_config_section, agency_config): + agency_group_list = utilities.get_item_value( + agency_config_section, "group", [], False, "[[agency.group]]") + for group_config in agency_group_list: + group_id = utilities.get_item_value( + group_config, "group_id", None, True, "[[agency.group]]") + if group_id not in self.group_list.keys(): + utilities.log_error( + "Load node config failed for the group %s configuration has not been setted." % group_id) + sys.exit(-1) + group_config_obj = self.group_list.get(group_id) + node_config = utilities.get_item_value( + group_config, "node", None, False, "[[agency.group.node]]") + group_node_list = [] + for node in node_config: + node_service = None + if self.node_type == "max": + node_service = MaxNodeConfig( + node, self.chain_id, group_id, agency_config, group_config_obj.sm_crypto) + else: + node_service = ProNodeConfig( + node, self.chain_id, group_id, agency_config, group_config_obj.sm_crypto) + if self.__check_duplicate_node_name(node_service.node_service_name): + utilities.log_error("The duplicate node name: " + node_service.node_name + + " appears in group: " + group_id + " of the agency: " + agency_config.name) + sys.exit(-1) + self.node_list[node_service.node_service_name] = node_service + group_node_list.append(node_service) + self.group_list[group_id].append_node_list(group_node_list) diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/config/max_node_config_generator.py" "b/BFPL\345\243\271/tools/BcosBuilder/src/config/max_node_config_generator.py" new file mode 100644 index 00000000..3a93a5d3 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/config/max_node_config_generator.py" @@ -0,0 +1,124 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +from common import utilities +from config.node_config_generator import NodeConfigGenerator +import os + + +class MaxNodeConfigGenerator(NodeConfigGenerator): + def __init__(self, chain_config, node_type, output_dir): + NodeConfigGenerator.__init__(self, chain_config, node_type, output_dir) + self.chain_config = chain_config + self.root_dir = output_dir + self.ini_tmp_config_file = "config.ini" + self.genesis_tmp_config_file = 'config.genesis' + + def generate_all_config(self, enforce_genesis_exists, is_build_opr = False): + """ + generate all config for max-node + """ + for group_config in self.chain_config.group_list.values(): + utilities.print_badge( + "generate genesis config for group %s" % group_config.group_id) + if self.generate_all_genesis_config(group_config, enforce_genesis_exists, is_build_opr) is False: + return False + utilities.print_badge( + "generate genesis config for %s success" % group_config.group_id) + utilities.print_badge( + "generate ini config for BcosMaxNodeService of group %s" % group_config.group_id) + if self.generate_max_node_ini_config(group_config) is False: + return False + utilities.print_badge( + "generate ini config for BcosMaxNodeService of group %s success" % group_config.group_id) + utilities.print_badge( + "generate ini config for BcosExecutorService of group %s" % group_config.group_id) + if self.__generate_all_executor_config(group_config) is False: + return False + utilities.print_badge( + "generate ini config for BcosExecutorService of group %s success" % group_config.group_id) + return True + + def generate_max_node_ini_config(self, group_config): + """ + generate all ini config file + """ + for node_config in group_config.node_list: + if self.__generate_and_store_max_node_ini_config(node_config, group_config) is False: + return False + return True + + def __generate_and_store_max_node_ini_config(self, node_config, group_config,): + """ + generate and store ini config for given node + """ + for ip in node_config.node_service.deploy_ip_list: + utilities.print_badge("generate ini config for ip %s" % ip) + ini_config_content = self.generate_node_config( + group_config, node_config, node_config.node_service.service_name, self.node_type, False) + node_path = self.__get_and_generate_ini_config_base_path(node_config, ip) + + if os.path.exists(node_path) is False: + utilities.mkdir(node_path) + + ini_config_path = os.path.join(node_path, self.ini_tmp_config_file) + ret = self.store_config(ini_config_content, "ini", ini_config_path, node_config.node_service.service_name, False) + if ret is False: + utilities.log_error("generate ini config for ip %s failed", ip) + return False + utilities.print_badge("generate ini config for ip %s success" % ip) + return True + + def __get_and_generate_ini_config_base_path(self, node_config, deploy_ip): + path = os.path.join(self.root_dir, node_config.agency_config.chain_id, + node_config.group_id, node_config.node_service.service_name, deploy_ip) + if os.path.exists(path) is False: + utilities.mkdir(path) + return path + + def generate_all_executor_config(self): + """ + generate all config for max-node + """ + for group_config in self.chain_config.group_list.values(): + if self.__generate_all_executor_config(group_config) is False: + return False + utilities.print_badge( + "generate ini config for BcosExecutorService of group %s success" % group_config.group_id) + return True + + def __generate_all_executor_config(self, group_config): + """ + generate the config for all executor service + """ + for max_node_config in group_config.node_list: + if self.__generate_executor_config(max_node_config, group_config) is False: + return False + return True + + def __generate_executor_config(self, max_node_config, group_config): + (ret, executor_genesis_content) = self.generate_genesis_config( + group_config, True) + if ret is False: + return False + executor_config_content = self.generate_executor_config( + group_config, max_node_config, max_node_config.executor_service.service_name) + + executor_dir = self.__get_and_generate_executor_base_path( + max_node_config) + executor_config_path = os.path.join( + executor_dir, self.ini_tmp_config_file) + executor_genesis_path = os.path.join( + executor_dir, self.genesis_tmp_config_file) + + ini_store = self.store_config(executor_config_content, "executor ini", + executor_config_path, max_node_config.executor_service.service_name, True) + genesis_store = self.store_config(executor_genesis_content, "executor genesis", + executor_genesis_path, max_node_config.executor_service.service_name, True) + return ini_store and genesis_store + + def __get_and_generate_executor_base_path(self, node_config): + path = os.path.join(self.root_dir, node_config.agency_config.chain_id, + node_config.group_id, node_config.executor_service.service_name) + if os.path.exists(path) is False: + utilities.mkdir(path) + return path diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/config/monitor_config_generator.py" "b/BFPL\345\243\271/tools/BcosBuilder/src/config/monitor_config_generator.py" new file mode 100644 index 00000000..ca0244a7 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/config/monitor_config_generator.py" @@ -0,0 +1,230 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +from common import utilities +from common.utilities import ConfigInfo +import os +import shutil + + +class MonitorConfigGenerator: + """ + the common Monitor config generator + """ + + def __init__(self, config, node_type, output_dir): + self.config = config + self.node_type = node_type + self.root_dir = output_dir + self.monitor_start_scirpts_file = "start_monitor.sh" + self.monitor_stop_scirpts_file = "stop_monitor.sh" + self.monitor_tpl_config = ConfigInfo.monitor_config_tpl_path + self.prometheus_tpl_config = ConfigInfo.prometheus_config_tpl_path + self.mtail_tmp_config_file = "node.mtail" + self.mtail_tmp_path = "mtail/" + self.mtail_start_scirpts_file = "start_mtail_monitor.sh" + self.mtail_stop_scirpts_file = "stop_mtail_monitor.sh" + self.mtail_binary_file = os.path.join( + ConfigInfo.tpl_binary_path, "mtail") + self.mtail_src_tpl_config = os.path.join( + ConfigInfo.tpl_src_mtail_path, self.mtail_tmp_path) + + def generate_all_mtail_config(self, group_config): + """ + generate all mtail config file + """ + for node_config in group_config.node_list: + if self.__generate_and_store_mtail_config(node_config, group_config) is False: + return False + + return True + + def generate_mtail_config(self): + """ + generate mtail config for all-node + """ + for group_config in self.config.group_list.values(): + utilities.print_badge( + "generate mtail config for group %s" % group_config.group_id) + if self.generate_all_mtail_config(group_config) is False: + return False + utilities.print_badge( + "generate mtail config for group %s success" % group_config.group_id) + return True + + def __check_monitor_config(self, node_config): + if node_config.monitor_listen_port is None: + raise Exception( + "the monitor_listen_port for node %s must be set" % node_config.node_service_name) + if node_config.monitor_log_path is None: + raise Exception( + "the monitor_log_path for node %s must be set" % node_config.node_service_name) + + def start_monitor_config(self): + for group_config in self.config.group_list.values(): + for node_config in group_config.node_list: + self.__check_monitor_config(node_config) + node_path = self.__get_and_generate_node_log_base_path( + node_config) + mtail_start_scripts_path = os.path.join( + node_path, self.mtail_tmp_path, self.mtail_start_scirpts_file) + if self.node_type == "pro": + if utilities.execute_ansible_with_command(mtail_start_scripts_path, node_config.deploy_ip, node_config.monitor_listen_port) is False: + return False + else: + for ip in node_config.deploy_ip: + if utilities.execute_ansible_with_command(mtail_start_scripts_path, ip, node_config.monitor_listen_port) is False: + return False + monitor_start_scripts_path = os.path.join( + self.monitor_tpl_config, self.monitor_start_scirpts_file) + return utilities.execute_monitor_with_command(monitor_start_scripts_path) + + def stop_monitor_config(self): + for group_config in self.config.group_list.values(): + for node_config in group_config.node_list: + node_path = self.__get_and_generate_node_log_base_path( + node_config) + mtail_start_scripts_path = os.path.join( + node_path, self.mtail_tmp_path, self.mtail_stop_scirpts_file) + if self.node_type == "pro": + if utilities.execute_ansible_with_command(mtail_start_scripts_path, node_config.deploy_ip, node_config.monitor_listen_port) is False: + return False + else: + for ip in node_config.deploy_ip: + if utilities.execute_ansible_with_command(mtail_start_scripts_path, ip, node_config.monitor_listen_port) is False: + return False + monitor_stop_scripts_path = os.path.join( + self.monitor_tpl_config, self.monitor_stop_scirpts_file) + return utilities.execute_monitor_with_command(monitor_stop_scripts_path) + + def generate_monitor_config(self): + """ + generate graphna&prometheus config for all-node + """ + utilities.print_badge(" generate graphna&prometheus config ") + targets = "" + for group_config in self.config.group_list.values(): + for node_config in group_config.node_list: + self.__check_monitor_config(node_config) + if self.node_type == "pro": + if targets == "": + targets = '"' + node_config.deploy_ip + ':' + \ + node_config.monitor_listen_port + '"' + else: + targets += ',"' + node_config.deploy_ip + \ + ':' + node_config.monitor_listen_port + '"' + else: + for ip in node_config.deploy_ip: + if targets == "": + targets = '"' + ip + ':' + node_config.monitor_listen_port + '"' + else: + targets += ',"' + ip + ':' + node_config.monitor_listen_port + '"' + if self.__generate_and_store_monitor_config(targets) is False: + return False + utilities.print_badge( + "generate graphna&prometheus config success") + return True + + def __generate_mtail_config(self, mtail_config_path, node_config): + """ + generate node config: config.ini.tmp + """ + mtail_config = "" + with open(mtail_config_path, 'r', encoding='utf-8') as f: + # mtail_config = f.read() + for line in f.readlines(): + if(line.find('group') == 0): + line = 'group="%s' % (node_config.group_id,) + '"\n' + if(line.find('node') == 0): + line = 'node="%s' % (node_config.node_name,) + '"\n' + if(line.find('chain') == 0): + line = 'chain="%s' % (node_config.chain_id,) + '"\n' + if(line.find('host') == 0): + line = 'host="%s' % (node_config.deploy_ip,) + '"\n' + mtail_config += line + return mtail_config + + def store_mtail_config(self, config_content, config_type, config_path, desc): + """ + store the generated genesis config content for given node + """ + utilities.log_info("* store %s config for %s\n\t path: %s" % + (config_type, desc, config_path)) + with open(config_path, 'w') as configFile: + configFile.write(config_content) + utilities.log_info("* store %s config for %s success" % + (config_type, desc)) + return True + + def __get_and_generate_node_log_base_path(self, node_config): + path = os.path.join(node_config.monitor_log_path, node_config.agency_config.chain_id, + node_config.node_service.service_name) + return path + + def __generate_and_store_mtail_config(self, node_config, group_config): + """ + generate and store mtatil config for given node + """ + node_log_path = self.__get_and_generate_node_log_base_path(node_config) + mtail_target_tpl_config = os.path.join( + node_log_path, self.mtail_tmp_path) + shutil.copytree(self.mtail_src_tpl_config, mtail_target_tpl_config) + shutil.copy(self.mtail_binary_file, mtail_target_tpl_config) + mtail_config_path = os.path.join( + node_log_path, self.mtail_tmp_path, self.mtail_tmp_config_file) + config_content = self.__generate_mtail_config( + mtail_config_path, node_config) + if self.store_mtail_config(config_content, "mtail", mtail_config_path, node_config.node_service.service_name) is False: + return False + + mtail_start_scripts_path = os.path.join( + node_log_path, self.mtail_tmp_path, self.mtail_start_scirpts_file) + mtail_ansible_src_tpl_config = os.path.join(node_log_path, "mtail") + mtail_ansible_dest_tpl_config = os.path.join(node_log_path) + if self.node_type == "pro": + if utilities.execute_ansible_copy_with_command(node_config.deploy_ip, mtail_ansible_src_tpl_config, mtail_ansible_dest_tpl_config) is False: + return False + return utilities.execute_ansible_with_command(mtail_start_scripts_path, node_config.deploy_ip, node_config.monitor_listen_port) + else: + for ip in node_config.deploy_ip: + if utilities.execute_ansible_copy_with_command(ip, mtail_ansible_src_tpl_config, mtail_ansible_dest_tpl_config) is False: + return False + if utilities.execute_ansible_with_command(mtail_start_scripts_path, ip, node_config.monitor_listen_port) is False: + return False + return True + + def __generate_monitor_config(self, targets): + """ + generate node config: config.ini.tmp + """ + monitor_config = "" + with open(self.prometheus_tpl_config, 'r', encoding='utf-8') as f: + for line in f.readlines(): + if(line.find(' - targets: [') == 0): + line = line.replace( + ' - targets: [', ' - targets: [' + targets) + monitor_config += line + return monitor_config + + def store_monitor_config(self, config_content, config_type, config_path): + """ + store the generated genesis config content for given node + """ + utilities.log_info("* store %s config \n\t path: %s" % + (config_type, config_path)) + with open(config_path, "w", encoding="utf-8") as f2: + f2.write(config_content) + utilities.log_info("* store %s config success" % + (config_type)) + return True + + def __generate_and_store_monitor_config(self, targets): + """ + generate and store graphna&prometheus config for monitor + """ + monitor_config_content = self.__generate_monitor_config(targets) + if self.store_monitor_config(monitor_config_content, "monitor", self.prometheus_tpl_config) is False: + return False + + monitor_start_scripts_path = os.path.join( + self.monitor_tpl_config, self.monitor_start_scirpts_file) + return utilities.execute_monitor_with_command(monitor_start_scripts_path) diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/config/node_config_generator.py" "b/BFPL\345\243\271/tools/BcosBuilder/src/config/node_config_generator.py" new file mode 100644 index 00000000..3dc3c06f --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/config/node_config_generator.py" @@ -0,0 +1,543 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +import configparser +import platform +import shutil +from common import utilities +from common.utilities import ConfigInfo +from service.key_center_service import KeyCenterService +import uuid +import os +import sys + +from common.utilities import execute_command_and_getoutput +from common.utilities import mkdir + + +class NodeConfigGenerator: + """ + the common node config generator + """ + + def __init__(self, config, node_type, output_dir): + self.config = config + self.genesis_tpl_config = ConfigInfo.genesis_config_tpl_path + self.node_tpl_config = ConfigInfo.node_config_tpl_path + self.executor_tpl_config = ConfigInfo.executor_config_tpl_path + self.node_pem_file = "node.pem" + self.node_id_file = "node.nodeid" + self.output_dir = output_dir + self.root_dir = output_dir + self.genesis_tmp_config_file = 'config.genesis' + self.ini_tmp_config_file = "config.ini" + self.node_type = node_type + + def generate_genesis_config_nodeid(self, nodeid_list, group_config): + """ + generate the genesis config + """ + utilities.log_info("* generate genesis config nodeid") + config_content = configparser.ConfigParser( + comment_prefixes='/', allow_no_value=True) + config_content.read(self.genesis_tpl_config) + chain_section = "chain" + config_content[chain_section]["sm_crypto"] = utilities.convert_bool_to_str( + group_config.genesis_config.sm_crypto) + config_content[chain_section]["group_id"] = str( + group_config.genesis_config.group_id) + config_content[chain_section]["chain_id"] = str( + group_config.genesis_config.chain_id) + consensus_section = "consensus" + config_content[consensus_section]["consensus_type"] = group_config.genesis_config.consensus_type + config_content[consensus_section]["block_tx_count_limit"] = str( + group_config.genesis_config.block_tx_count_limit) + config_content[consensus_section]["leader_period"] = str( + group_config.genesis_config.leader_period) + i = 0 + for nodeid in nodeid_list: + key = "node." + str(i) + value = nodeid.strip() + ":1" + config_content[consensus_section][key] = value + i = i + 1 + tx_section = "tx" + config_content[tx_section]["gas_limit"] = str( + group_config.genesis_config.gas_limit) + version_section = "version" + config_content[version_section]["compatibility_version"] = str( + group_config.genesis_config.compatibility_version) + + executor_section = "executor" + config_content[executor_section]["is_wasm"] = utilities.convert_bool_to_str( + group_config.genesis_config.vm_type != "evm") + config_content[executor_section]["is_auth_check"] = utilities.convert_bool_to_str( + group_config.genesis_config.auth_check) + config_content[executor_section]["auth_admin_account"] = group_config.genesis_config.init_auth_address + + utilities.log_info("* chain_id: %s" % + config_content[chain_section]["group_id"]) + utilities.log_info("* group_id: %s" % + config_content[chain_section]["chain_id"]) + utilities.log_info("* consensus_type: %s" % + config_content[consensus_section]["consensus_type"]) + utilities.log_info("* block_tx_count_limit: %s" % + config_content[consensus_section]["block_tx_count_limit"]) + utilities.log_info("* leader_period: %s" % + config_content[consensus_section]["leader_period"]) + utilities.log_info("* gas_limit: %s" % + config_content[tx_section]["gas_limit"]) + utilities.log_info("* compatibility_version: %s" % + config_content[version_section]["compatibility_version"]) + utilities.log_info("* generate_genesis_config_nodeid success") + return config_content + + def generate_executor_config(self, group_config, node_config, node_name): + """ + generate the config.ini for executorService + """ + executor_ini_config = configparser.ConfigParser( + comment_prefixes='/', allow_no_value=True) + executor_ini_config.read(self.executor_tpl_config) + # chain config + self.__update_chain_info(executor_ini_config, node_config) + # service config + service_section = "service" + executor_ini_config[service_section]["node_name"] = node_name + executor_ini_config[service_section]["scheduler"] = self.config.chain_id + \ + "." + node_config.scheduler_service_name + executor_ini_config[service_section]["txpool"] = self.config.chain_id + \ + "." + node_config.txpool_service_name + # executor config + self.__update_storage_info( + executor_ini_config, node_config, self.node_type) + return executor_ini_config + + def generate_node_config(self, group_config, node_config, node_name, node_type, is_build_opr): + """ + generate node config: config.ini.tmp + """ + ini_config = configparser.ConfigParser( + comment_prefixes='/', allow_no_value=True) + ini_config.read(self.node_tpl_config) + self.__update_service_info( + ini_config, node_config, node_name, is_build_opr) + self.__update_failover_info(ini_config, node_config, node_type) + # set storage config + self.__update_storage_info(ini_config, node_config, node_type) + # set storage_security config + # access key_center to encrypt the certificates and the private keys + self.__update_storage_security_info(ini_config, node_config, node_type) + return ini_config + + def __update_chain_info(self, ini_config, node_config): + """ + update chain info + """ + chain_section = "chain" + ini_config[chain_section]["sm_crypto"] = utilities.convert_bool_to_str( + node_config.sm_crypto) + ini_config[chain_section]["group_id"] = node_config.group_id + ini_config[chain_section]["chain_id"] = node_config.chain_id + + def __update_service_info(self, ini_config, node_config, node_name, is_build_opr): + """ + update service info + """ + service_section = "service" + ini_config[service_section]["node_name"] = node_name + ini_config["service"]['without_tars_framework'] = "true" if is_build_opr else "false" + ini_config["service"]['tars_proxy_conf'] = 'conf/tars_proxy.ini' + ini_config[service_section]["rpc"] = self.config.chain_id + \ + "." + node_config.agency_config.rpc_service_name + ini_config[service_section]["gateway"] = self.config.chain_id + \ + "." + node_config.agency_config.gateway_service_name + if hasattr(node_config, 'executor_service'): + ini_config[service_section]["executor"] = self.config.chain_id + \ + "." + node_config.executor_service.service_name + + def __update_failover_info(self, ini_config, node_config, node_type): + # generate the member_id for failover + failover_section = "failover" + ini_config[failover_section]["member_id"] = str(uuid.uuid1()) + if node_type == "max": + ini_config[failover_section]["enable"] = utilities.convert_bool_to_str( + True) + ini_config[failover_section]["cluster_url"] = node_config.agency_config.failover_cluster_url + else: + ini_config[failover_section]["enable"] = utilities.convert_bool_to_str( + False) + + def __update_storage_info(self, ini_config, node_config, node_type): + if node_type != "max": + return + storage_section = "storage" + if ini_config.has_option(storage_section, "data_path"): + ini_config.remove_option(storage_section, "data_path") + ini_config[storage_section]["type"] = "tikv" + ini_config[storage_section]["pd_addrs"] = node_config.pd_addrs + ini_config[storage_section]["key_page_size"] = str( + node_config.key_page_size) + + def __update_storage_security_info(self, ini_config, node_config, node_type): + """ + update the storage_security for config.ini + """ + section = "storage_security" + # not support storage_security for max-node + if node_type == "max": + if ini_config.has_section(section): + ini_config.remove_section(section) + return + ini_config[section]["enable"] = utilities.convert_bool_to_str( + node_config.enable_storage_security) + ini_config[section]["key_center_url"] = node_config.key_center_url + ini_config[section]["cipher_data_key"] = node_config.cipher_data_key + + def __generate_pem_file(self, outputdir, node_config): + """ + generate private key to the given path + """ + pem_path = os.path.join(outputdir, self.node_pem_file) + node_id_path = os.path.join(outputdir, self.node_id_file) + + # if the file is not exist, generate it + if os.path.exists(pem_path) is False or os.path.exists(node_id_path) is False: + utilities.generate_private_key( + node_config.sm_crypto, outputdir) + # encrypt the node.pem with key_center + if node_config.enable_storage_security is True: + key_center = KeyCenterService( + node_config.key_center_url, node_config.cipher_data_key) + ret = key_center.encrypt_file(pem_path) + if ret is False: + return (False, "", pem_path, node_id_path) + node_id = "" + with open(node_id_path, 'r', encoding='utf-8') as f: + node_id = f.read() + return (True, node_id, pem_path, node_id_path) + + def get_config_file_path_list(self, node_service_config, node_config): + """ + get config file path for given config files + """ + path = os.path.join(self.root_dir, node_config.agency_config.chain_id, + node_config.group_id, node_service_config.service_name) + config_file_path_list = [] + for config in node_service_config.config_file_list: + config_file_path_list.append(os.path.join(path, config)) + return config_file_path_list + + def get_ini_config_file_path(self, node_service_config, node_config, deploy_ip): + """ + get config file path for given config files + """ + return os.path.join(self.root_dir, node_config.agency_config.chain_id, + node_config.group_id, node_service_config.service_name, deploy_ip, node_service_config.ini_config_file) + + def __get_and_generate_node_base_path(self, node_config, is_build_opr): + if not is_build_opr: + path = os.path.join(self.root_dir, node_config.agency_config.chain_id, + node_config.group_id, node_config.node_service.service_name) + else: + path = os.path.join(self.output_dir, node_config.deploy_ip, + node_config.group_id + "_node_" + str(node_config.tars_listen_port), "conf") + if os.path.exists(path) is False: + utilities.mkdir(path) + return path + + def generate_node_pem(self, node_config, is_build_opr): + """ + generate private key for the node + """ + path = self.__get_and_generate_node_base_path( + node_config, is_build_opr) + return self.__generate_pem_file(path, node_config) + + def generate_all_ini_config(self, group_config, is_build_opr): + """ + generate all ini config file + """ + for node_config in group_config.node_list: + if self.__generate_and_store_ini_config(node_config, group_config, is_build_opr) is False: + return False + if is_build_opr: + self.__copy_tars_conf_and_bin_file( + node_config, "BcosNodeService") + self.__generate_and_store_tars_proxy_config(node_config) + return True + + def _generate_all_node_pem(self, group_config, is_build_opr): + """ + generate all node.pem and return the nodeID + """ + nodeid_list = [] + for node_config in group_config.node_list: + (ret, node_id, node_pem_path, node_id_path) = self.generate_node_pem( + node_config, is_build_opr) + if ret is False: + return (False, nodeid_list) + utilities.log_info( + "* generate pem file for %s\n\t- pem_path: %s\n\t- node_id_path: %s\n\t- node_id: %s\n\t- sm_crypto: %d" % (node_config.node_service.service_name, node_pem_path, node_id_path, node_id, group_config.sm_crypto)) + nodeid_list.append(node_id) + return (True, nodeid_list) + + def __genesis_config_generated(self, group_config): + if os.path.exists(group_config.genesis_config_path): + utilities.log_info( + "* the genesis config file has been set, path: %s" % group_config.genesis_config_path) + return True + return False + + def generate_genesis_config(self, group_config, must_genesis_exists, is_build_opr=False): + if self.__genesis_config_generated(group_config): + config_content = configparser.ConfigParser( + comment_prefixes='/', allow_no_value=True) + config_content.read(group_config.genesis_config_path) + (ret, nodeid_list) = self._generate_all_node_pem( + group_config, is_build_opr) + return (ret, config_content) + if must_genesis_exists is True: + utilities.log_error("Please set the genesis config path firstly!") + sys.exit(-1) + (ret, nodeid_list) = self._generate_all_node_pem( + group_config, is_build_opr) + if ret is False: + return (False, None) + config_content = self.generate_genesis_config_nodeid( + nodeid_list, group_config) + return (True, config_content) + + def generate_all_config(self, enforce_genesis_exists, is_build_opr): + """ + generate all config for max-node + """ + for group_config in self.config.group_list.values(): + utilities.print_badge( + "generate genesis config for group %s" % group_config.group_id) + if self.generate_all_genesis_config(group_config, enforce_genesis_exists, is_build_opr) is False: + return False + utilities.print_badge( + "generate genesis config for %s success" % group_config.group_id) + utilities.print_badge( + "generate ini config for group %s" % group_config.group_id) + if self.generate_all_ini_config(group_config, is_build_opr) is False: + return False + utilities.print_badge( + "generate ini config for group %s success" % group_config.group_id) + return True + + def generate_all_genesis_config(self, group_config, enforce_genesis_exists, is_build_opr): + """ + generate the genesis config for all max_nodes + """ + (ret, genesis_config_content) = self.generate_genesis_config( + group_config, enforce_genesis_exists, is_build_opr) + if ret is False: + return False + if os.path.exists(group_config.genesis_config_path) is False: + desc = group_config.chain_id + "." + group_config.group_id + self.store_config(genesis_config_content, "genesis", + group_config.genesis_config_path, desc, False) + for node_config in group_config.node_list: + node_path = self.__get_and_generate_node_base_path( + node_config, is_build_opr) + genesis_config_path = os.path.join( + node_path, self.genesis_tmp_config_file) + if self.store_config(genesis_config_content, "genesis", genesis_config_path, node_config.node_service.service_name, False) is False: + return False + return True + + def store_config(self, config_content, config_type, config_path, desc, ignore_if_exists): + """ + store the generated genesis config content for given node + """ + if os.path.exists(config_path) and ignore_if_exists is False: + utilities.log_error("* store %s config for %s failed for the config %s already exists." % + (config_type, desc, config_path)) + return False + utilities.log_info("* store %s config for %s\n\t path: %s" % + (config_type, desc, config_path)) + + if os.path.exists(os.path.dirname(config_path)) is False: + utilities.mkdir(os.path.dirname(config_path)) + + with open(config_path, 'w') as configFile: + config_content.write(configFile) + utilities.log_info("* store %s config for %s success" % + (config_type, desc)) + return True + + def copy_tars_proxy_conf(self): + self.__copy_service_tars_proxy_conf() + + def __copy_service_tars_proxy_conf(self): + for group_config in self.config.group_list.values(): + for node_config in group_config.node_list: + conf_dir = self.__get_and_generate_node_base_path( + node_config, True) + agency_name = node_config.agency_config.name + tars_proxy_conf = os.path.join( + self.output_dir, node_config.agency_config.chain_id, agency_name + "_tars_proxy.ini") + copy_cmd = "cp " + tars_proxy_conf + " " + conf_dir + "/tars_proxy.ini" + utilities.execute_command(copy_cmd) + utilities.log_info("* copy tars_proxy.ini: " + + tars_proxy_conf + " ,dir: " + conf_dir) + + def __generate_and_store_ini_config(self, node_config, group_config, is_build_opr): + """ + generate and store ini config for given node + """ + ini_config_content = self.generate_node_config( + group_config, node_config, node_config.node_service.service_name, self.node_type, is_build_opr) + node_path = self.__get_and_generate_node_base_path( + node_config, is_build_opr) + + if os.path.exists(node_path) is False: + utilities.mkdir(node_path) + + ini_config_path = os.path.join(node_path, self.ini_tmp_config_file) + return self.store_config(ini_config_content, "ini", ini_config_path, node_config.node_service.service_name, False) + + def __get_tars_proxy_conf_section_index(self, section, config): + if not config.has_section(section): + config.add_section(section) + return 0 + + index = 0 + while True: + proxy_index_str = "proxy." + str(index) + if proxy_index_str in config[section]: + index += 1 + continue + return index + + def __generate_and_store_tars_proxy_config(self, service_config): + agency_name = service_config.agency_config.name + chain_id = service_config.agency_config.chain_id + tars_conf_dir = os.path.join(self.output_dir, chain_id) + + if os.path.exists(tars_conf_dir) is False: + utilities.mkdir(tars_conf_dir) + + agency_tars_conf_path = os.path.join( + tars_conf_dir, agency_name + "_tars_proxy.ini") + + tars_proxy_config = configparser.ConfigParser( + comment_prefixes='/', allow_no_value=True) + + if os.path.exists(agency_tars_conf_path): + tars_proxy_config.read(agency_tars_conf_path) + + index = self.__get_tars_proxy_conf_section_index( + "txpool", tars_proxy_config) + tars_proxy_config["txpool"]["proxy." + str( + index)] = service_config.deploy_ip + ":" + str(service_config.tars_listen_port) + + index = self.__get_tars_proxy_conf_section_index( + "scheduler", tars_proxy_config) + tars_proxy_config["scheduler"]["proxy." + str( + index)] = service_config.deploy_ip + ":" + str(service_config.tars_listen_port + 1) + + index = self.__get_tars_proxy_conf_section_index( + "pbft", tars_proxy_config) + tars_proxy_config["pbft"]["proxy." + str( + index)] = service_config.deploy_ip + ":" + str(service_config.tars_listen_port + 2) + + index = self.__get_tars_proxy_conf_section_index( + "ledger", tars_proxy_config) + tars_proxy_config["ledger"]["proxy." + str( + index)] = service_config.deploy_ip + ":" + str(service_config.tars_listen_port + 3) + + index = self.__get_tars_proxy_conf_section_index( + "front", tars_proxy_config) + tars_proxy_config["front"]["proxy." + str( + index)] = service_config.deploy_ip + ":" + str(service_config.tars_listen_port + 4) + + with open(agency_tars_conf_path, 'w') as f: + tars_proxy_config.write(f) + + return + + def __copy_tars_conf_and_bin_file(self, node_config, service_name): + + conf_dir = self.__get_and_generate_node_base_path(node_config, True) + base_dir = os.path.dirname(conf_dir) + + # copy start.sh stop.sh tars.conf + tars_start_file = os.path.join( + ConfigInfo.tpl_abs_path, "tars_start.sh") + tars_stop_file = os.path.join(ConfigInfo.tpl_abs_path, "tars_stop.sh") + tars_conf_file = os.path.join( + ConfigInfo.tpl_abs_path, "tars_node.conf") + + tars_start_all_file = os.path.join( + ConfigInfo.tpl_abs_path, "tars_start_all.sh") + tars_stop__all_file = os.path.join( + ConfigInfo.tpl_abs_path, "tars_stop_all.sh") + + start_all_file = os.path.join(base_dir, "../", "start_all.sh") + stop_all_file = os.path.join(base_dir, "../", "stop_all.sh") + + start_file = os.path.join(base_dir, "start.sh") + stop_file = os.path.join(base_dir, "stop.sh") + conf_file = os.path.join(conf_dir, "tars.conf") + + shutil.copy(tars_start_file, start_file) + shutil.copy(tars_stop_file, stop_file) + shutil.copy(tars_conf_file, conf_file) + + if not os.path.exists(start_all_file): + shutil.copy(tars_start_all_file, start_all_file) + + if not os.path.exists(stop_all_file): + shutil.copy(tars_stop__all_file, stop_all_file) + + # copy service binary exec + shutil.copy(os.path.join(self.config.tars_config.tars_pkg_dir, + service_name, service_name), base_dir) + + sys_name = platform.system() + if sys_name.lower() == "darwin": + sed = "sed -i .bak " + else: + sed = "sed -i " + + sed_cmd = sed + "s/@SERVICE_NAME@/" + service_name + "/g " + start_file + execute_command_and_getoutput(sed_cmd) + + sed_cmd = sed + "s/@SERVICE_NAME@/" + service_name + "/g " + stop_file + execute_command_and_getoutput(sed_cmd) + + sed_cmd = sed + "s/@TARS_APP@/" + self.config.chain_id + "/g " + conf_file + execute_command_and_getoutput(sed_cmd) + sed_cmd = sed + "s/@TARS_SERVER@/" + \ + node_config.node_service.service_name + "/g " + conf_file + execute_command_and_getoutput(sed_cmd) + + # tars config + sed_cmd = sed + "s/@TARS_LISTEN_IP@/" + \ + node_config.deploy_ip + "/g " + conf_file + execute_command_and_getoutput(sed_cmd) + sed_cmd = sed + "s/@TXPOOL_LISTEN_PORT@/" + \ + str(node_config.tars_listen_port + 0) + "/g " + conf_file + execute_command_and_getoutput(sed_cmd) + sed_cmd = sed + "s/@SCHEDULER_LISTEN_PORT@/" + \ + str(node_config.tars_listen_port + 1) + "/g " + conf_file + execute_command_and_getoutput(sed_cmd) + sed_cmd = sed + "s/@PBFT_LISTEN_PORT@/" + \ + str(node_config.tars_listen_port + 2) + "/g " + conf_file + execute_command_and_getoutput(sed_cmd) + sed_cmd = sed + "s/@LEDGER_LISTEN_PORT@/" + \ + str(node_config.tars_listen_port + 3) + "/g " + conf_file + execute_command_and_getoutput(sed_cmd) + sed_cmd = sed + "s/@FRONT_LISTEN_PORT@/" + \ + str(node_config.tars_listen_port + 4) + "/g " + conf_file + execute_command_and_getoutput(sed_cmd) + + if os.path.exists(start_file + ".bak"): + os.remove(start_file + ".bak") + + if os.path.exists(stop_file + ".bak"): + os.remove(stop_file + ".bak") + + if os.path.exists(conf_file + ".bak"): + os.remove(conf_file + ".bak") diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/config/service_config_generator.py" "b/BFPL\345\243\271/tools/BcosBuilder/src/config/service_config_generator.py" new file mode 100644 index 00000000..8a9422d3 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/config/service_config_generator.py" @@ -0,0 +1,397 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +import configparser +import platform +import shutil + +from common import utilities +from common.utilities import ServiceInfo +from common.utilities import ConfigInfo +from service.key_center_service import KeyCenterService +import json +import os +import uuid + +from common.utilities import execute_command_and_getoutput +class ServiceConfigGenerator: + def __init__(self, config, service_type, node_type, output_dir): + self.config = config + self.ini_file = "config.ini" + self.network_file = "nodes.json" + self.service_type = service_type + self.output_dir = output_dir + self.root_dir = output_dir + "/" + self.service_type + self.node_type = node_type + + def generate_all_config(self, is_build_opr = False): + if self.service_type == ServiceInfo.gateway_service_type: + return self.generate_gateway_config_files(is_build_opr) + else: + return self.generate_rpc_config_files(is_build_opr) + + def generate_rpc_config_files(self, is_build_opr): + utilities.log_info("* generate config for the rpc service, build opr: %s" % str(is_build_opr)) + section = "rpc" + for rpc_service in self.config.rpc_service_list.keys(): + rpc_service_config = self.config.rpc_service_list[rpc_service] + if self.__generate_config_files(section, rpc_service_config, is_build_opr) is False: + return False + if is_build_opr: + self.__copy_tars_conf_file(rpc_service_config, "rpc", "BcosRpcService") + self.__generate_and_store_tars_proxy_config(rpc_service_config) + utilities.log_info("* generate config for the rpc service success") + return True + + def generate_gateway_config_files(self, is_build_opr): + utilities.log_info("* generate config for the gateway service, build opr: %s" % str(is_build_opr)) + section = "p2p" + for gateway_service in self.config.gateway_service_list.keys(): + gateway_service_config = self.config.gateway_service_list[gateway_service] + if self.__generate_config_files(section, gateway_service_config, is_build_opr) is False: + return False + if self.__generate_gateway_connection_info_for_all_deploy_ip(gateway_service_config, is_build_opr) is False: + return False + if is_build_opr: + self.__copy_tars_conf_file(gateway_service_config, "gateway", "BcosGatewayService") + self.__generate_and_store_tars_proxy_config(gateway_service_config) + + utilities.log_info("* generate config for the gateway service success") + return True + + def copy_tars_proxy_conf(self): + if self.service_type == ServiceInfo.gateway_service_type: + return self.__copy_service_tars_proxy_conf(self.config.gateway_service_list) + else: + return self.__copy_service_tars_proxy_conf(self.config.rpc_service_list) + + def __copy_service_tars_proxy_conf(self, service_list): + for service_name in service_list.keys(): + service_config = service_list[service_name] + for deploy_ip in service_config.deploy_ip_list: + conf_dir = self.__get_service_config_base_path(service_config, deploy_ip, True) + agency_name = service_config.agency_config.name + tars_proxy_conf = os.path.join(self.output_dir, service_config.agency_config.chain_id, agency_name + "_tars_proxy.ini") + copy_cmd = "cp " + tars_proxy_conf + " " + conf_dir + "/tars_proxy.ini" + utilities.execute_command(copy_cmd) + utilities.log_info("* copy tars_proxy.ini: " + tars_proxy_conf + " ,dir: " + conf_dir) + + + def __get_tars_proxy_conf_section_index(self, section, config): + if not config.has_section(section): + config.add_section(section) + return 0 + + index = 0 + while True: + proxy_index_str = "proxy." + str(index) + if proxy_index_str in config[section]: + index += 1 + continue + return index + + def __generate_and_store_tars_proxy_config(self, service_config): + agency_name = service_config.agency_config.name + chain_id = service_config.agency_config.chain_id + tars_conf_dir = os.path.join(self.output_dir, chain_id) + agency_tars_proxy_conf = os.path.join(tars_conf_dir, agency_name + "_tars_proxy.ini") + + tars_proxy_config = configparser.ConfigParser( + comment_prefixes='/', allow_no_value=True) + + if os.path.exists(agency_tars_proxy_conf): + tars_proxy_config.read(agency_tars_proxy_conf) + + index = self.__get_tars_proxy_conf_section_index(self.service_type, tars_proxy_config) + + section = self.service_type + for deploy_ip in service_config.deploy_ip_list: + tars_proxy_config[section]['proxy.' + str(index)] = deploy_ip + ":" + str(service_config.tars_listen_port) + index += 1 + + if os.path.exists(os.path.dirname(agency_tars_proxy_conf)) is False: + utilities.mkdir(os.path.dirname(agency_tars_proxy_conf)) + + with open(agency_tars_proxy_conf, 'w') as f: + tars_proxy_config.write(f) + + return + + def __copy_tars_conf_file(self, service_config, service_type, service_name): + for ip in service_config.deploy_ip_list: + conf_dir = self.__get_service_config_base_path(service_config, ip, True) + base_dir = os.path.dirname(conf_dir) + + # copy service binary exec + shutil.copy(os.path.join(self.config.tars_config.tars_pkg_dir, service_name, service_name), base_dir) + + # copy ssl/ca.crt ssl/ssl.crt ssl/ssl.key + cert_dir = os.path.join(conf_dir, "ssl", "*") + cp_cmd = "cp " + cert_dir + " " + conf_dir + os.system(cp_cmd) + + # copy start.sh stop.sh tars.conf + tars_start_all_file = os.path.join(ConfigInfo.tpl_abs_path, "tars_start_all.sh") + tars_stop__all_file = os.path.join(ConfigInfo.tpl_abs_path, "tars_stop_all.sh") + + start_all_file = os.path.join(base_dir, "../", "start_all.sh") + stop_all_file = os.path.join(base_dir, "../", "stop_all.sh") + + tars_start_file = os.path.join(ConfigInfo.tpl_abs_path, "tars_start.sh") + tars_stop_file = os.path.join(ConfigInfo.tpl_abs_path, "tars_stop.sh") + tars_conf_file = os.path.join(ConfigInfo.tpl_abs_path, "tars_" + service_type + ".conf") + + start_file = os.path.join(base_dir, "start.sh") + stop_file = os.path.join(base_dir, "stop.sh") + conf_file = os.path.join(conf_dir, "tars.conf") + + shutil.copy(tars_start_file, start_file) + shutil.copy(tars_stop_file, stop_file) + shutil.copy(tars_conf_file, conf_file) + + if not os.path.exists(start_all_file): + shutil.copy(tars_start_all_file, start_all_file) + + if not os.path.exists(stop_all_file): + shutil.copy(tars_stop__all_file, stop_all_file) + + sys_name = platform.system() + if sys_name.lower() == "darwin": + sed = "sed -i .bak " + else: + sed = "sed -i " + + sed_cmd = sed + "s/@SERVICE_NAME@/" + service_name + "/g " + start_file + execute_command_and_getoutput(sed_cmd) + sed_cmd = sed + "s/@SERVICE_NAME@/" + service_name + "/g " + stop_file + execute_command_and_getoutput(sed_cmd) + + sed_cmd = sed + "s/@TARS_APP@/" + self.config.chain_id + "/g " + conf_file + + execute_command_and_getoutput(sed_cmd) + sed_cmd = sed + "s/@TARS_SERVER@/" + service_config.agency_config.name + service_name + "/g " + conf_file + execute_command_and_getoutput(sed_cmd) + + sed_cmd = sed + "s/@TARS_LISTEN_IP@/" + ip + "/g " + conf_file + execute_command_and_getoutput(sed_cmd) + sed_cmd = sed + "s/@TARS_LISTEN_PORT@/" + str(service_config.tars_listen_port) + "/g " + conf_file + execute_command_and_getoutput(sed_cmd) + + if os.path.exists(start_file + ".bak"): + os.remove(start_file + ".bak") + + if os.path.exists(stop_file + ".bak"): + os.remove(stop_file + ".bak") + + if os.path.exists(conf_file + ".bak"): + os.remove(conf_file + ".bak") + + shutil.rmtree(os.path.join(conf_dir, "ssl")) + + def __get_cert_config_file_list(self, service_config, ip): + cert_config_file_list = [] + if service_config.sm_ssl is False: + cert_config_file_list = ["ca.crt", "ssl.crt", "ssl.key"] + else: + cert_config_file_list = [ + "sm_ca.crt", "sm_ssl.crt", "sm_ssl.key", "sm_enssl.crt", "sm_enssl.key"] + cert_config_path_list = [] + path = self.__get_service_config_base_path(service_config, ip) + for cert in cert_config_file_list: + cert_config_path_list.append(os.path.join(path, "ssl", cert)) + return (cert_config_file_list, cert_config_path_list) + + def __get_config_file_info(self, config_file_list, service_config, ip): + path = self.__get_service_config_base_path(service_config, ip) + (cert_file_list, cert_file_path_list) = self.__get_cert_config_file_list( + service_config, ip) + config_path_list = [] + for config_file in config_file_list: + config_path_list.append(os.path.join(path, config_file)) + return (config_file_list + cert_file_list, config_path_list + cert_file_path_list) + + def get_config_file_list(self, service_config, ip): + if service_config.service_type == utilities.ServiceInfo.rpc_service_type: + return self.__get_config_file_info(["config.ini"], service_config, ip) + else: + return self.__get_config_file_info(["config.ini", "nodes.json"], service_config, ip) + + def __generate_config_files(self, section, service_config, is_build_opr): + utilities.print_badge( + "* generate config for the %s service %s" % (section, service_config.name)) + utilities.log_info("* generate %s for the %s service %s" % + (self.ini_file, section, service_config.name)) + if self.__generate_and_store_ini_config(service_config, section, is_build_opr) is False: + return False + + utilities.log_info("* generate %s for the %s service %s success" % + (self.ini_file, section, service_config.name)) + utilities.log_info("* generate cert for the %s service %s" % + (section, service_config.name)) + if self.__generate_cert_for_all_deploy_ip(service_config, is_build_opr) is False: + return False + utilities.log_info( + "* generate cert for the %s service %s success" % (section, service_config.name)) + utilities.print_badge( + "* generate config for the %s service success%s" % (section, service_config.name)) + return True + + def __generate_and_store_ini_config(self, service_config, section, is_build_opr): + """ + generate and store ini config + """ + ini_config_content = self.__generate_ini_config( + service_config, section, is_build_opr) + if self.__store_all_config_file(service_config, ini_config_content, is_build_opr) is False: + return False + return True + + def __generate_ini_config(self, service_config, section, is_build_opr): + """ + generate config.ini.tmp + """ + ini_config = configparser.ConfigParser( + comment_prefixes='/', allow_no_value=True) + ini_config.read(service_config.tpl_config_file) + + ini_config[section]['listen_ip'] = service_config.listen_ip + ini_config[section]['listen_port'] = str(service_config.listen_port) + ini_config[section]['sm_ssl'] = utilities.convert_bool_to_str( + service_config.sm_ssl) + ini_config[section]['thread_count'] = str( + service_config.thread_count) + ini_config["service"]['gateway'] = service_config.agency_config.chain_id + \ + "." + service_config.agency_config.gateway_service_name + ini_config["service"]['rpc'] = service_config.agency_config.chain_id + \ + "." + service_config.agency_config.rpc_service_name + + ini_config["service"]['without_tars_framework'] = "true" if is_build_opr else "false" + ini_config["service"]['tars_proxy_conf'] = 'conf/tars_proxy.ini' + + ini_config["chain"]['chain_id'] = service_config.agency_config.chain_id + # generate failover config + failover_section = "failover" + if self.node_type == "max": + ini_config[failover_section]["enable"] = utilities.convert_bool_to_str( + True) + else: + ini_config[failover_section]["enable"] = utilities.convert_bool_to_str( + False) + ini_config[failover_section]["cluster_url"] = service_config.agency_config.failover_cluster_url + + # generate uuid according to chain_id and gateway_service_name + uuid_name = ini_config["service"]['gateway'] + ini_config[section]['uuid'] = str( + uuid.uuid3(uuid.NAMESPACE_URL, uuid_name)) + + self.__update_storage_security_info(ini_config, service_config) + + return ini_config + + def __update_storage_security_info(self, ini_config, service_config): + """ + update the storage_security for config.ini + """ + # TODO: access key_center to encrypt the certificates and the private keys + section = "storage_security" + ini_config[section]["enable"] = utilities.convert_bool_to_str( + service_config.agency_config.enable_storage_security) + ini_config["chain"]["sm_crypto"] = utilities.convert_bool_to_str( + service_config.sm_ssl) + ini_config[section]["key_center_url"] = service_config.agency_config.key_center_url + ini_config[section]["cipher_data_key"] = service_config.agency_config.cipher_data_key + + def __store_config_file(self, path, ini_config_content): + ini_path = os.path.join(path, self.ini_file) + if os.path.exists(ini_path) is True: + utilities.log_error( + "config file %s already exists, please delete after confirming carefully" % ini_path) + return False + utilities.mkfiledir(ini_path) + with open(ini_path, 'w') as configfile: + ini_config_content.write(configfile) + utilities.log_info("* store %s" % ini_path) + return True + + def __store_all_config_file(self, service_config, ini_config_content, is_build_opr): + for ip in service_config.deploy_ip_list: + path = self.__get_service_config_base_path(service_config, ip, is_build_opr) + if self.__store_config_file(path, ini_config_content) is False: + return False + return True + + def __get_service_config_base_path(self, service_config, deploy_ip, is_build_opr = False): + if not is_build_opr: + config_path = os.path.join(self.root_dir, service_config.agency_config.chain_id, service_config.name, deploy_ip) + else: + config_path = os.path.join(self.output_dir, deploy_ip, service_config.service_type + '_' + str(service_config.listen_port), "conf") + return config_path + + def __generate_cert_for_all_deploy_ip(self, service_config, is_build_opr): + for ip in service_config.deploy_ip_list: + self.__generate_cert(service_config, ip, is_build_opr) + + def __generate_cert(self, service_config, deploy_ip, is_build_opr): + output_dir = self.__get_service_config_base_path( + service_config, deploy_ip, is_build_opr) + if self.__ca_generated(service_config) is False: + # generate the ca cert + utilities.generate_ca_cert( + service_config.sm_ssl, service_config.ca_cert_path) + utilities.log_info( + "* generate cert, ip: %s, output path: %s" % (deploy_ip, output_dir)) + utilities.generate_node_cert( + service_config.sm_ssl, service_config.ca_cert_path, output_dir) + if service_config.agency_config.enable_storage_security is True: + key_center = KeyCenterService( + service_config.agency_config.key_center_url, service_config.agency_config.cipher_data_key) + if service_config.sm_ssl is True: + ret = key_center.encrypt_file( + os.path.join(output_dir, "ssl", "sm_ssl.key")) + if ret is False: + return False + ret = key_center.encrypt_file( + os.path.join(output_dir, "ssl", "sm_enssl.key")) + if ret is False: + return False + else: + ret = key_center.encrypt_file( + os.path.join(output_dir, "ssl", "ssl.key")) + if ret is False: + return False + if service_config.service_type == ServiceInfo.rpc_service_type: + utilities.log_info( + "* generate sdk cert, output path: %s" % (output_dir)) + utilities.generate_sdk_cert( + service_config.sm_ssl, service_config.ca_cert_path, output_dir) + return True + + def __generate_gateway_connection_info_for_all_deploy_ip(self, service_config, is_build_opr): + for ip in service_config.deploy_ip_list: + if self.__generate_gateway_connection_info(service_config, ip, is_build_opr) is False: + return False + return True + + def __generate_gateway_connection_info(self, service_config, ip, is_build_opr): + path = self.__get_service_config_base_path(service_config, ip, is_build_opr) + network_file_path = os.path.join(path, self.network_file) + if os.path.exists(network_file_path): + utilities.log_error( + "config file %s already exists, please delete after confirming carefully" % network_file_path) + return False + peers = {} + peers["nodes"] = service_config.peers + utilities.mkfiledir(network_file_path) + with open(network_file_path, 'w') as configfile: + json.dump(peers, configfile) + utilities.log_info( + "* generate gateway connection file: %s" % network_file_path) + return True + + def __ca_generated(self, service_config): + if service_config.sm_ssl is False: + if os.path.exists(os.path.join(service_config.ca_cert_path, "ca.crt")) and os.path.exists(os.path.join(service_config.ca_cert_path, "ca.key")): + return True + else: + if os.path.exists(os.path.join(service_config.ca_cert_path, "sm_ca.crt")) and os.path.exists(os.path.join(service_config.ca_cert_path, "sm_ca.key")): + return True + return False diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/config/tars_config_generator.py" "b/BFPL\345\243\271/tools/BcosBuilder/src/config/tars_config_generator.py" new file mode 100644 index 00000000..e9f969a4 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/config/tars_config_generator.py" @@ -0,0 +1,40 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +import configparser +import os +import shutil + +class TarsConfigGenerator: + + def __init__(self, tars_conf): + self.tars_conf = tars_conf + self.tars_ini = configparser.ConfigParser( + comment_prefixes='/', allow_no_value=True) + if os.path.exists(tars_conf): + self.tars_ini.read(tars_conf) + + def __get_tars_proxy_conf_section_index(self, section): + if not self.tars_ini.has_section(section): + self.tars_ini.add_section(section) + return 0 + + index = 0 + while True: + proxy_index_str = "proxy." + str(index) + if proxy_index_str in self.tars_ini[section]: + index += 1 + continue + return index + + def append_config_item(self, service_name, endpoint): + index = self.__get_tars_proxy_conf_section_index(service_name) + self.tars_ini[service_name]["proxy." + str(index)] = endpoint + + def get_service_config_items(self, service_name): + if service_name in self.tars_ini: + return self.tars_ini[service_name] + return None + + def restore_init_config(self, tars_conf_path): + with open(tars_conf_path, 'w') as f: + self.tars_ini.write(f) \ No newline at end of file diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/controller/binary_controller.py" "b/BFPL\345\243\271/tools/BcosBuilder/src/controller/binary_controller.py" new file mode 100644 index 00000000..bf19a379 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/controller/binary_controller.py" @@ -0,0 +1,95 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from common import utilities +import requests +import sys +import os +import tarfile + + +class BinaryController: + def __init__(self, version, binary_path, use_cdn, node_type): + self.version = version + self.mtail_version = "3.0.0-rc49" + self.binary_path = binary_path + self.binary_postfix = "-linux-x86_64.tgz" + self.mtail_binary_name = "mtail_3.0.0-rc49_Linux_x86_64.tar.gz" + self.cdn_link_header = "https://osp-1257653870.cos.ap-guangzhou.myqcloud.com/FISCO-BCOS" + if node_type == "pro": + self.binary_list = ["BcosRpcService", + "BcosGatewayService", "BcosNodeService"] + elif node_type == "max": + self.binary_list = ["BcosRpcService", "BcosGatewayService", + "BcosMaxNodeService", "BcosExecutorService"] + else: + utilities.log_error("Unsupported node_type %s" % node_type) + sys.exit(-1) + self.use_cdn = use_cdn + self.last_percent = 0 + self.download_prefix = "https://github.com/FISCO-BCOS/FISCO-BCOS/releases/download/" + self.mtail_download_url = "https://github.com/google/mtail/releases/download/v3.0.0-rc49/%s" % self.mtail_binary_name + if self.use_cdn is True: + self.download_prefix = "%s/FISCO-BCOS/releases/" % ( + self.cdn_link_header) + self.mtail_download_url = "%s/FISCO-BCOS/tools/mtail/%s" % ( + self.cdn_link_header, self.mtail_binary_name) + + def download_all_binary(self): + utilities.print_badge( + "Download binary, use_cdn: %s, version: %s" % (self.use_cdn, self.version)) + for binary in self.binary_list: + download_url = self.get_binary_download_url(binary) + if self.download_binary(binary + ".tgz", download_url) is False: + return False + self.un_tar_gz(self.get_required_binary_path(binary + ".tgz")) + if self.download_binary(self.mtail_binary_name, self.mtail_download_url) is False: + return False + binary_file_path = os.path.join( + self.binary_path, self.mtail_binary_name) + self.un_tar_gz(binary_file_path) + return True + + def get_binary_download_url(self, binary_name): + return ("%s%s/%s%s") % (self.download_prefix, self.version, binary_name, self.binary_postfix) + + def get_required_binary_path(self, binary_name): + return os.path.join(self.binary_path, binary_name) + + def get_downloaded_binary_path(self, binary_name): + return binary_name + self.binary_postfix + + def download_binary(self, binary_name, download_url): + if os.path.exists(self.binary_path) is False: + utilities.mkdir(self.binary_path) + binary_file_path = self.get_required_binary_path(binary_name) + utilities.log_info("Download url: %s" % download_url) + with open(binary_file_path, 'wb') as file: + response = requests.get(download_url, stream=True) + total = response.headers.get('content-length') + if total is None or int(total) < 100000: + utilities.log_error("Download binary %s failed, Please check the existence of the binary version %s" % ( + binary_name, self.version)) + return False + utilities.log_info("* Download %s from %s\n* size: %fMB, dst_path: %s" % ( + binary_name, download_url, float(total)/float(1000000), binary_file_path)) + downloaded = 0 + total = int(total) + for data in response.iter_content(chunk_size=max(int(total/1000), 1024*1024)): + downloaded += len(data) + file.write(data) + done = int(50*downloaded/total) + utilities.log_info("Download percent: %d%%" % + (downloaded/total * 100)) + sys.stdout.write('\r[{}{}]'.format( + '█' * done, '.' * (50-done))) + sys.stdout.flush() + sys.stdout.write('\n') + utilities.log_info("* Download %s from %s success" % + (binary_name, download_url)) + return True + + def un_tar_gz(self, file_name): + tar = tarfile.open(file_name) + tar.extractall(self.binary_path) + tar.close() diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/controller/monitor_controller.py" "b/BFPL\345\243\271/tools/BcosBuilder/src/controller/monitor_controller.py" new file mode 100644 index 00000000..2af20344 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/controller/monitor_controller.py" @@ -0,0 +1,37 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +from config.monitor_config_generator import MonitorConfigGenerator + + +class MonitorController: + """ + the monitor controller + """ + + def __init__(self, config, node_type, output_dir): + self.config = config + self.binary_name = "" + self.download_url = "" + self.node_type = node_type + self.monitor_generator = MonitorConfigGenerator( + self.config, node_type, output_dir) + + def generate_monitor_config(self): + if self.monitor_generator.generate_monitor_config() is False: + return False + return self.monitor_generator.generate_mtail_config() + + def generate_and_deploy_monitor_services(self): + if self.generate_monitor_config() is False: + return False + return True + + def start_monitor_services(self): + if self.monitor_generator.start_monitor_config() is False: + return False + return True + + def stop_monitor_services(self): + if self.monitor_generator.stop_monitor_config() is False: + return False + return True diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/controller/node_controller.py" "b/BFPL\345\243\271/tools/BcosBuilder/src/controller/node_controller.py" new file mode 100644 index 00000000..80062502 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/controller/node_controller.py" @@ -0,0 +1,222 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +from config.max_node_config_generator import MaxNodeConfigGenerator +from config.node_config_generator import NodeConfigGenerator +from common import utilities +from service.tars_service import TarsService + + +class NodeController: + """ + the max node controller + """ + + def __init__(self, config, node_type, output_dir): + self.config = config + if node_type == "max": + self.node_generator = MaxNodeConfigGenerator( + self.config, node_type, output_dir) + else: + self.node_generator = NodeConfigGenerator( + self.config, node_type, output_dir) + + def generate_all_config(self, is_build_opr=False): + return self.node_generator.generate_all_config(False, is_build_opr) + + def generate_all_executor_config(self): + return self.node_generator.generate_all_executor_config() + + def deploy_group_services(self): + """ + deploy max node for all group + """ + for node in self.config.node_list.values(): + if self.__deploy_all_service(node) is False: + return False + return True + + def upgrade_group(self): + utilities.log_info("upgrade services for all the group nodes") + for node in self.config.node_list.values(): + utilities.log_info("upgrade service for node %s" % + node.node_name) + if self.__upgrade_all_service(node) is False: + return False + return True + + def stop_group(self): + for node in self.config.node_list.values(): + if self.__stop_all(node) is False: + return False + return True + + def start_group(self): + for node in self.config.node_list.values(): + if self.__start_all(node) is False: + return False + return True + + def generate_and_deploy_group_services(self): + if self.generate_all_config(False) is False: + return False + if self.deploy_group_services() is False: + return False + return True + + def undeploy_group(self): + utilities.log_info("undeploy services for all the group nodes") + for node_config in self.config.node_list.values(): + for service in node_config.service_list: + if self.__undeploy_service(service) is False: + return False + return True + + def generate_all_expand_config(self): + """ + generate expand config + """ + if self.node_generator.generate_all_config(True, False) is False: + return False + return True + + def generate_all_executor_expand_config(self): + """ + generate expand config + """ + if self.node_generator.generate_all_executor_config() is False: + return False + return True + + def expand_and_deploy_all_nodes(self): + """ + expand and deploy all nodes + """ + if self.generate_all_expand_config() is False: + return False + if self.deploy_group_services() is False: + return False + return True + + def expand_and_deploy_all_executors(self): + """ + expand and deploy all executor + """ + if self.generate_all_executor_expand_config() is False: + return False + if self.deploy_group_services() is False: + return False + return True + + def __start_all(self, node): + tars_service_obj = TarsService(self.config.tars_config.tars_url, + self.config.tars_config.tars_token, self.config.chain_id, "") + for service in node.service_list: + if tars_service_obj.restart_server(service.service_name) is False: + utilities.log_error("start node %s failed" % + service.service_name) + return False + else: + utilities.log_info("start node %s success" % + service.service_name) + return True + + def __stop_all(self, node): + ret = True + tars_service_obj = TarsService(self.config.tars_config.tars_url, + self.config.tars_config.tars_token, self.config.chain_id, "") + for service in node.service_list: + if tars_service_obj.stop_server(service.service_name) is False: + utilities.log_error("stop node %s failed" % + service.service_name) + ret = False + else: + utilities.log_info("stop node %s success" % + service.service_name) + return ret + + def __undeploy_service(self, node_service_config): + for ip in node_service_config.deploy_ip_list: + utilities.log_info("undeploy service %s from %s" % + (node_service_config.service_name, ip)) + tars_service_obj = TarsService( + self.config.tars_config.tars_url, self.config.tars_config.tars_token, self.config.chain_id, ip) + ret = tars_service_obj.undeploy_tars( + node_service_config.service_name) + if ret is False: + utilities.log_error("undeploy service %s from %s failed" % ( + node_service_config.service_name, ip)) + else: + utilities.log_info("undeploy service %s from %s success" % ( + node_service_config.service_name, ip)) + return True + + def __upgrade_all_service(self, node_config): + for service in node_config.service_list: + for ip in service.deploy_ip_list: + tars_service_obj = TarsService( + self.config.tars_config.tars_url, self.config.tars_config.tars_token, self.config.chain_id, ip) + if self.__upgrade_service(ip, tars_service_obj, node_config, service) is False: + return False + return True + + def __deploy_all_service(self, node_config): + for service in node_config.service_list: + for ip in service.deploy_ip_list: + if self.__deploy_service(ip, node_config, service) is False: + return False + return True + + def __deploy_service(self, deploy_ip, node_config, node_service_config): + tars_service_obj = TarsService(self.config.tars_config.tars_url, + self.config.tars_config.tars_token, self.config.chain_id, deploy_ip) + # create application + tars_service_obj.create_application() + # create service + if tars_service_obj.deploy_single_service(node_service_config.service_name, node_service_config.service_obj_list, True) is False: + return False + return self.__upgrade_service(deploy_ip, tars_service_obj, node_config, node_service_config) + + def __upgrade_service(self, deploy_ip, tars_service_obj, node_config, node_service_config): + # upload package + (ret, patch_id) = self.__upload_package( + tars_service_obj, node_service_config) + if ret is False: + return False + # add configuration + config_path_list = self.node_generator.get_config_file_path_list( + node_service_config, node_config) + # Note: config.genesis, node.pem is the service-dimension configuration + ret = tars_service_obj.add_node_config_list( + deploy_ip, node_service_config.config_file_list, node_service_config.service_name, config_path_list) + if ret is False: + return False + # add ini configuration + if self.node_generator.node_type == "max" and node_service_config.need_add_ini_config is True: + ini_config_path = self.node_generator.get_ini_config_file_path( + node_service_config, node_config, deploy_ip) + ini_config_file_list = [] + ini_config_file_list.append(node_service_config.ini_config_file) + ini_config_path_list = [] + ini_config_path_list.append(ini_config_path) + ret = tars_service_obj.add_config_list( + ini_config_file_list, node_service_config.service_name, deploy_ip, ini_config_path_list, True) + if ret is False: + return False + # patch tars + (ret, server_id) = tars_service_obj.get_server_id( + node_service_config.service_name, deploy_ip) + if ret is False: + return False + return tars_service_obj.patch_tars(server_id, patch_id) + + def __upload_package(self, tars_service, node_service_config): + """ + upload package + """ + (ret, package_path) = utilities.try_to_rename_tgz_package("generated", + self.config.tars_config.tars_pkg_dir, node_service_config.service_name, node_service_config.base_service_name) + if ret is False: + utilities.log_error( + "upload package for service %s failed" % node_service_config.service_name) + return (False, -1) + return tars_service.upload_tars_package(node_service_config.service_name, package_path) diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/controller/service_controller.py" "b/BFPL\345\243\271/tools/BcosBuilder/src/controller/service_controller.py" new file mode 100644 index 00000000..1a1ed745 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/controller/service_controller.py" @@ -0,0 +1,215 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from config.service_config_generator import ServiceConfigGenerator +from service.tars_service import TarsService +from common.utilities import ServiceInfo +from common import utilities + + +class ServiceController: + """ + common controller for rpc/gateway + """ + + def __init__(self, config, service_type, node_type, output_dir): + self.config = config + self.service_type = service_type + self.service_list = self.config.rpc_service_list + self.node_type = node_type + self.output_dir = output_dir + self.config_generator = ServiceConfigGenerator( + self.config, self.service_type, self.node_type, self.output_dir) + if self.service_type == ServiceInfo.gateway_service_type: + self.service_list = self.config.gateway_service_list + + def deploy_all(self): + for service in self.service_list.values(): + if self.__deploy_service(service) is False: + utilities.log_error("deploy service %s failed" % service.name) + return False + return True + + def stop_all(self): + ret = True + for service in self.service_list.values(): + if self.__stop_service(service) is False: + ret = False + utilities.log_error("stop service %s failed" % service.name) + else: + utilities.log_info("stop service %s success" % service.name) + return ret + + def start_all(self): + for service in self.service_list.values(): + if self.__start_service(service) is False: + utilities.log_error("start service %s failed" % service.name) + return False + else: + utilities.log_info("start service %s success" % service.name) + return True + + def undeploy_all(self): + ret = True + for service in self.service_list.values(): + if self.__undeploy_service(service) is False: + ret = False + utilities.log_error( + "undeploy service %s failed" % service.name) + else: + utilities.log_info( + "undeploy service %s success" % service.name) + return ret + + def upgrade_all(self): + for service in self.service_list.values(): + if self.__upgrade_service(service) is False: + utilities.log_error("upgrade service %s failed" % service.name) + return False + else: + utilities.log_info("upgrade service %s success" % service.name) + return True + + def gen_all_service_config(self, is_build_opr): + if self.config_generator.generate_all_config(is_build_opr) is False: + utilities.log_error( + "gen configuration for %s service failed" % self.service_type) + return False + return True + + def expand_all(self): + for service in self.service_list.values(): + if self.__expand_service_list(service) is False: + utilities.log_error("expand service %s to %s failed, type: %s!" % ( + service.name, service.deploy_ip_list, self.service_type)) + return False + return True + + def __deploy_service(self, service_config): + if len(service_config.deploy_ip_list) == 0: + utilities.log_info("No service to deploy") + for deploy_ip in service_config.deploy_ip_list: + utilities.log_info("deploy_service to %s, app: %s, name: %s" % ( + deploy_ip, self.config.chain_id, service_config.name)) + if self.__deploy_service_to_given_ip(service_config, deploy_ip) is False: + return False + return True + + def __expand_service_list(self, service_config): + for ip in service_config.deploy_ip_list: + utilities.log_info("expand to %s, app: %s, name: %s" % ( + ip, self.config.chain_id, service_config.name)) + if self.__deploy_service_to_given_ip(service_config, ip) is False: + return False + return True + + def __upgrade_service(self, service_config): + for ip in service_config.deploy_ip_list: + utilities.log_info("upgrade_service %s to %s" % + (service_config.name, ip)) + ret = self.__upgrade_service_to_given_ip(service_config, ip) + if ret is False: + return False + return True + + def __deploy_service_to_given_ip(self, service_config, deploy_ip): + tars_service = TarsService(self.config.tars_config.tars_url, + self.config.tars_config.tars_token, self.config.chain_id, deploy_ip) + # create application + tars_service.create_application() + # create the service + obj_list = service_config.service_obj_list + # deploy service + ret = tars_service.deploy_single_service( + service_config.name, obj_list, True) + if ret is False: + return False + # add configuration files + (config_file_list, config_path_list) = self.config_generator.get_config_file_list( + service_config, deploy_ip) + ret = tars_service.add_config_list( + config_file_list, service_config.name, deploy_ip, config_path_list, True) + if ret is False: + return False + return self.__upgrade_service_by_config_info(tars_service, service_config) + + def __expand_service_to_given_ip(self, service_config, node_name, expand_node_ip): + tars_service = TarsService(self.config.tars_config.tars_url, + self.config.tars_config.tars_token, self.config.chain_id, expand_node_ip) + # expand the service + obj_list = service_config.service_obj_list + expand_node_list = [expand_node_ip] + ret = tars_service.expand_server_with_preview( + service_config.name, node_name, expand_node_list, obj_list) + if ret is False: + utilities.log_error("expand service failed, app: %s, service: %s, node: %s" % ( + self.config.chain_id, service_config.name, expand_node_ip)) + return False + # add configuration files + (config_file_list, config_path_list) = self.config_generator.get_config_file_list( + service_config, expand_node_ip) + ret = tars_service.add_config_list( + config_file_list, service_config.name, expand_node_ip, config_path_list, True) + if ret is False: + return False + # patch the service + return self.__upgrade_service_by_config_info(tars_service, service_config) + + def __upgrade_service_to_given_ip(self, service_config, deploy_ip): + tars_service = TarsService(self.config.tars_config.tars_url, + self.config.tars_config.tars_token, self.config.chain_id, deploy_ip) + return self.__upgrade_service_by_config_info(tars_service, service_config) + + def __upgrade_service_by_config_info(self, tars_service, service_config): + # upload package + (ret, patch_id) = self.__upload_package( + tars_service, service_config.name, service_config.binary_name) + if ret is False: + return False + # patch tars + # get the service info + (ret, server_id) = tars_service.get_server_id( + service_config.name, tars_service.deploy_ip) + if ret is False: + return False + return tars_service.patch_tars(server_id, patch_id) + + def __undeploy_service(self, service_config): + for ip in service_config.deploy_ip_list: + tars_service = TarsService(self.config.tars_config.tars_url, + self.config.tars_config.tars_token, self.config.chain_id, ip) + utilities.log_info( + "undeploy service for node %s, service: %s" % (ip, service_config.name)) + if tars_service.undeploy_tars(service_config.name) is False: + utilities.log_error( + "undeploy service %s for node %s failed" % (ip, service_config.name)) + return True + + def __start_service(self, service_config): + for ip in service_config.deploy_ip_list: + tars_service = TarsService(self.config.tars_config.tars_url, + self.config.tars_config.tars_token, self.config.chain_id, ip) + if tars_service.restart_server(service_config.name) is False: + utilities.log_error("start service for node %s failed" % ip) + return False + return True + + def __stop_service(self, service_config): + for ip in service_config.deploy_ip_list: + tars_service = TarsService(self.config.tars_config.tars_url, + self.config.tars_config.tars_token, self.config.chain_id, ip) + utilities.log_info("stop service %s, node: %s" % + (service_config.name, ip)) + if tars_service.stop_server(service_config.name) is False: + utilities.log_error("stop service for node %s failed" % ip) + return False + return True + + def __upload_package(self, tars_service, service_name, org_service_name): + (ret, package_path) = utilities.try_to_rename_tgz_package("generated", + self.config.tars_config.tars_pkg_dir, service_name, org_service_name) + if ret is False: + utilities.log_error( + "upload package for service %s failed for rename package name failed" % service_name) + return (False, -1) + return tars_service.upload_tars_package(service_name, package_path) diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/networkmgr/network_manager.py" "b/BFPL\345\243\271/tools/BcosBuilder/src/networkmgr/network_manager.py" new file mode 100644 index 00000000..e3c9879a --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/networkmgr/network_manager.py" @@ -0,0 +1,64 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +from common import utilities + + +class NetworkManager: + def create_sub_net(subnet, docker_network_name): + """ + create the subnet + """ + utilities.log_info("* create docker subnet %s, name: %s" % + (subnet, docker_network_name)) + command = "docker network create -d bridge --subnet=%s %s --opt com.docker.network.driver.mtu=1400" % ( + subnet, docker_network_name) + if utilities.execute_command(command) is False: + utilities.log_error("create the docker subnet failed") + return False + return True + + def get_docker_network_id(docker_network_name): + """ + get the docker network id + """ + command = "docker network ls | grep -i \"%s\" | awk -F\' \' \'{print $1}\'" % docker_network_name + (ret, result) = utilities.execute_command_and_getoutput(command) + if ret is False: + utilities.log_error( + "* get docker network id for %s failed" % docker_network_name) + utilities.log_info( + "* get docker network id for %s success, id: %s" % (docker_network_name, result)) + return (ret, result) + + def create_bridge(docker_network_name, docker_vxlan_name, remote_ip): + """ + add the bridge + """ + dstport = 4789 + dev_name = "eth0" + utilities.log_info("* set the bridge interconnection network, docker_network: %s, docker_vxlan_name: %s, remote_ip: %s, dstport: %s" % + (docker_network_name, docker_vxlan_name, remote_ip, dstport)) + (ret, network_id) = NetworkManager.get_docker_network_id(docker_network_name) + basic_error_info = "Failed to set the bridge interconnection network" + if ret is False: + utilities.log_error("%s, please check the network name! remote ip: %s, network name: %s" % ( + basic_error_info, remote_ip, docker_network_name)) + return False + # add ip link + ip_link_command = "ip link add %s type vxlan id 200 remote %s dstport %d dev %s" % ( + docker_vxlan_name, remote_ip, dstport, dev_name) + if utilities.execute_command(ip_link_command) is False: + utilities.log_error("%s" % basic_error_info) + return False + # setup the network + ip_set_up_command = "ip link set %s up" % docker_vxlan_name + if utilities.execute_command(ip_set_up_command) is False: + utilities.log_error("%s" % basic_error_info) + return False + # add bridge + bridge_add_command = "brctl addif br-%s %s" % ( + network_id, docker_vxlan_name) + if utilities.execute_command(bridge_add_command) is False: + utilities.log_error("%s" % basic_error_info) + return False + return True diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/scripts/generate_cert.sh" "b/BFPL\345\243\271/tools/BcosBuilder/src/scripts/generate_cert.sh" new file mode 100644 index 00000000..26af3890 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/scripts/generate_cert.sh" @@ -0,0 +1,688 @@ +#!/bin/bash +set -e + +dirpath="$(cd "$(dirname "$0")" && pwd)" +# cd "${dirpath}" + +cdn_link_header="https://osp-1257653870.cos.ap-guangzhou.myqcloud.com/FISCO-BCOS" + +command="" +output_dir="cert" + +# for cert generation +sm_cert_conf='sm_cert.cnf' +days=36500 +rsa_key_length=2048 + +macOS="" +x86_64_arch="true" + +sm_mode='false' +sm2_params="sm_sm2.param" +OPENSSL_CMD="${HOME}/.fisco/tassl-1.1.1b" + +ca_cert_path="" +ip_param="" + +LOG_WARN() { + local content=${1} + echo -e "\033[31m[ERROR] ${content}\033[0m" +} + +LOG_INFO() { + local content=${1} + echo -e "\033[32m[INFO] ${content}\033[0m" +} + +LOG_FALT() { + local content=${1} + echo -e "\033[31m[FALT] ${content}\033[0m" + exit 1 +} + +dir_must_exists() { + if [ ! -d "$1" ]; then + LOG_FALT "$1 DIR does not exist, please check!" + fi +} + +file_must_not_exists() { + if [ -f "$1" ]; then + LOG_FALT "$1 file already exist, please check!" + fi +} + +file_must_exists() { + if [ ! -f "$1" ]; then + LOG_FALT "$1 file does not exist, please check!" + fi +} + +check_env() { + if [ "$(uname)" == "Darwin" ];then + macOS="macOS" + fi + if [ "$(uname -m)" != "x86_64" ];then + x86_64_arch="false" + fi +} + +check_name() { + local name="$1" + local value="$2" + [[ "$value" =~ ^[a-zA-Z0-9._-]+$ ]] || { + LOG_FALT "$name name [$value] invalid, it should match regex: ^[a-zA-Z0-9._-]+\$" + } +} + +generate_sm_sm2_param() { + local output=$1 + cat <"${output}" +-----BEGIN EC PARAMETERS----- +BggqgRzPVQGCLQ== +-----END EC PARAMETERS----- + +EOF +} + +generate_sm_cert_conf() { + local output=$1 + cat <"${output}" +oid_section = new_oids + +[ new_oids ] +tsa_policy1 = 1.2.3.4.1 +tsa_policy2 = 1.2.3.4.5.6 +tsa_policy3 = 1.2.3.4.5.7 + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +#################################################################### +[ CA_default ] + +dir = ./demoCA # Where everything is kept +certs = $dir/certs # Where the issued certs are kept +crl_dir = $dir/crl # Where the issued crl are kept +database = $dir/index.txt # database index file. +#unique_subject = no # Set to 'no' to allow creation of + # several ctificates with same subject. +new_certs_dir = $dir/newcerts # default place for new certs. + +certificate = $dir/cacert.pem # The CA certificate +serial = $dir/serial # The current serial number +crlnumber = $dir/crlnumber # the current crl number + # must be commented out to leave a V1 CRL +crl = $dir/crl.pem # The current CRL +private_key = $dir/private/cakey.pem # The private key +RANDFILE = $dir/private/.rand # private random number file + +x509_extensions = usr_cert # The extensions to add to the cert + +name_opt = ca_default # Subject Name options +cert_opt = ca_default # Certificate field options + +default_days = 36500 # how long to certify for +default_crl_days= 30 # how long before next CRL +default_md = default # use public key default MD +preserve = no # keep passed DN ordering + +policy = policy_match + +[ policy_match ] +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +#################################################################### +[ req ] +default_bits = 2048 +default_md = sm3 +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +x509_extensions = v3_ca # The extensions to add to the self signed cert + +string_mask = utf8only + +# req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +countryName = CN +countryName_default = CN +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default =GuangDong +localityName = Locality Name (eg, city) +localityName_default = ShenZhen +organizationalUnitName = Organizational Unit Name (eg, section) +organizationalUnitName_default = fisco +commonName = Organizational commonName (eg, fisco) +commonName_default = fisco +commonName_max = 64 + +[ usr_cert ] +basicConstraints=CA:FALSE +nsComment = "OpenSSL Generated Certificate" + +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature + +[ v3enc_req ] + +# Extensions to add to a certificate request +basicConstraints = CA:FALSE +keyUsage = keyAgreement, keyEncipherment, dataEncipherment + +[ v3_agency_root ] +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer +basicConstraints = CA:true +keyUsage = cRLSign, keyCertSign + +[ v3_ca ] +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer +basicConstraints = CA:true +keyUsage = cRLSign, keyCertSign + +EOF +} + +generate_cert_conf() { + local output=$1 + cat <"${output}" +[ca] +default_ca=default_ca +[default_ca] +default_days = 36500 +default_md = sha256 + +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req +[req_distinguished_name] +countryName = CN +countryName_default = CN +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default =GuangDong +localityName = Locality Name (eg, city) +localityName_default = ShenZhen +organizationalUnitName = Organizational Unit Name (eg, section) +organizationalUnitName_default = FISCO-BCOS +commonName = Organizational commonName (eg, FISCO-BCOS) +commonName_default = FISCO-BCOS +commonName_max = 64 + +[ v3_req ] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v4_req ] +basicConstraints = CA:TRUE + +EOF +} + +gen_chain_cert() { + + if [ ! -f "${cert_conf}" ]; then + generate_cert_conf "${cert_conf}" + fi + + local chaindir="${1}" + + file_must_not_exists "${chaindir}"/ca.key + file_must_not_exists "${chaindir}"/ca.crt + file_must_exists "${cert_conf}" + + mkdir -p "$chaindir" + dir_must_exists "$chaindir" + + ${OPENSSL_CMD} genrsa -out "${chaindir}"/ca.key "${rsa_key_length}" + ${OPENSSL_CMD} req -new -x509 -days "${days}" -subj "/CN=FISCO-BCOS/O=FISCO-BCOS/OU=chain" -key "${chaindir}"/ca.key -config "${cert_conf}" -out "${chaindir}"/ca.crt + if [ ! -f "${chaindir}/cert.cnf" ];then + mv "${cert_conf}" "${chaindir}" + fi + LOG_INFO "Build ca cert successfully!" +} + +gen_rsa_node_cert() { + local capath="${1}" + local ndpath="${2}" + local type="${3}" + + file_must_exists "$capath/ca.key" + file_must_exists "$capath/ca.crt" + # check_name node "$node" + + file_must_not_exists "$ndpath"/"${type}".key + file_must_not_exists "$ndpath"/"${type}".crt + + mkdir -p "${ndpath}" + dir_must_exists "${ndpath}" + + ${OPENSSL_CMD} genrsa -out "${ndpath}"/"${type}".key "${rsa_key_length}" 2> /dev/null + ${OPENSSL_CMD} req -new -sha256 -subj "/CN=FISCO-BCOS/O=fisco-bcos/OU=agency" -key "$ndpath"/"${type}".key -config "$capath"/cert.cnf -out "$ndpath"/"${type}".csr + ${OPENSSL_CMD} x509 -req -days "${days}" -sha256 -CA "${capath}"/ca.crt -CAkey "$capath"/ca.key -CAcreateserial \ + -in "$ndpath"/"${type}".csr -out "$ndpath"/"${type}".crt -extensions v4_req -extfile "$capath"/cert.cnf 2>/dev/null + + ${OPENSSL_CMD} pkcs8 -topk8 -in "$ndpath"/"$type".key -out "$ndpath"/pkcs8_node.key -nocrypt + cp "$capath"/ca.crt "$capath"/cert.cnf "$ndpath"/ + + rm -f "$ndpath"/"${type}".csr + rm -f "$ndpath"/"${type}".key + + mv "$ndpath"/pkcs8_node.key "$ndpath"/"${type}".key + + LOG_INFO "Build node cert successful!" +} + +gen_sm_chain_cert() { + local chaindir="${1}" + name=$(basename "$chaindir") + check_name chain "$name" + + if [ ! -f "${sm_cert_conf}" ];then + generate_sm_cert_conf 'sm_cert.cnf' + elif [ ! -f "sm_cert.cnf" ];then + cp -f "${sm_cert_conf}" . + fi + + generate_sm_sm2_param "${sm2_params}" + + mkdir -p "$chaindir" + dir_must_exists "$chaindir" + + "$OPENSSL_CMD" genpkey -paramfile "${sm2_params}" -out "$chaindir/sm_ca.key" + "$OPENSSL_CMD" req -config sm_cert.cnf -x509 -days "${days}" -subj "/CN=FISCO-BCOS/O=FISCO-BCOS/OU=chain" -key "$chaindir/sm_ca.key" -extensions v3_ca -out "$chaindir/sm_ca.crt" + if [ ! -f "${chaindir}/${sm_cert_conf}" ];then + cp "${sm_cert_conf}" "${chaindir}" + fi + if [ ! -f "${chaindir}/${sm2_params}" ];then + cp "${sm2_params}" "${chaindir}" + fi +} + +gen_sm_node_cert_with_ext() { + local capath="$1" + local certpath="$2" + local type="$3" + local extensions="$4" + + file_must_exists "$capath/sm_ca.key" + file_must_exists "$capath/sm_ca.crt" + + file_must_not_exists "$ndpath/sm_${type}.crt" + file_must_not_exists "$ndpath/sm_${type}.key" + + "$OPENSSL_CMD" genpkey -paramfile "$capath/${sm2_params}" -out "$certpath/sm_${type}.key" + "$OPENSSL_CMD" req -new -subj "/CN=FISCO-BCOS/O=fisco-bcos/OU=${type}" -key "$certpath/sm_${type}.key" -config "$capath/sm_cert.cnf" -out "$certpath/sm_${type}.csr" + + echo "use $(basename "$capath") to sign $(basename $certpath) ${type}" + "$OPENSSL_CMD" x509 -sm3 -req -CA "$capath/sm_ca.crt" -CAkey "$capath/sm_ca.key" -days "${days}" -CAcreateserial -in "$certpath/sm_${type}.csr" -out "$certpath/sm_${type}.crt" -extfile "$capath/sm_cert.cnf" -extensions "$extensions" + + rm -f "$certpath/sm_${type}.csr" +} + +generate_multi_nodes_cert_impl() +{ + local capath="${1}" + local type="${2}" + local output="${3}" + + if [[ -z ${ip_param} ]];then + LOG_FALT "ip param is empty, please use \'-l\' command to set ip param." + fi + + local ip_array=(${ip_param//,/ }) + # check params + for line in ${ip_array[*]}; do + ip=${line%:*} + num=${line#*:} + + # check ip format + if [ -z $(echo ${ip} | grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$") ]; then + LOG_FALT "Invalid ip address, please check the IP address: ${ip}" + fi + done + + # generate rsa node cert for every server + for line in ${ip_array[*]}; do + ip=${line%:*} + num=${line#*:} + + for ((i = 0; i < num; ++i)); do + if [[ "${sm_mode}" == "false" ]]; then + gen_rsa_node_cert "${capath}" "${output}/${ip}_${i}/" "${type}" + else + gen_sm_node_cert "${capath}" "${output}/${ip}_${i}/" "${type}" + fi + done + done + +} + +generate_multi_nodes_private_key_impl() +{ + local output="${1}" + local nodeids_dir="${output}/nodeids" + + if [[ -z ${ip_param} ]];then + LOG_FALT "ip param is empty, please use \'-l\' command to set ip param." + fi + + local ip_array=(${ip_param//,/ }) + # check params + for line in ${ip_array[*]}; do + ip=${line%:*} + num=${line#*:} + + # check ip format + if [ -z $(echo ${ip} | grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$") ]; then + LOG_FALT "Invalid ip address, please check the IP address: ${ip}" + fi + done + + mkdir -p "${nodeids_dir}" + + # generate rsa node cert for every server + for line in ${ip_array[*]}; do + ip=${line%:*} + num=${line#*:} + + for ((i = 0; i < num; ++i)); do + if [[ "${sm_mode}" == "false" ]]; then + generate_node_account "${output}/${ip}_${i}/" + else + generate_sm_node_account "${output}/${ip}_${i}/" + fi + + file_must_exists "${output}/${ip}_${i}/node.nodeid" + cp "${output}/${ip}_${i}/node.nodeid" "${nodeids_dir}/${ip}_${i}.nodeid" + done + done + +} + +gen_sm_node_cert() { + local capath="${1}" + local ndpath="${2}" + local type="${3}" + + file_must_exists "$capath/sm_ca.key" + file_must_exists "$capath/sm_ca.crt" + + mkdir -p "$ndpath" + dir_must_exists "$ndpath" + local node=$(basename "$ndpath") + check_name node "$node" + + gen_sm_node_cert_with_ext "$capath" "$ndpath" ${type} v3_req + gen_sm_node_cert_with_ext "$capath" "$ndpath" "en${type}" v3enc_req + #nodeid is pubkey + $OPENSSL_CMD ec -in "$ndpath/sm_${type}.key" -text -noout 2> /dev/null | sed -n '7,11p' | sed 's/://g' | tr "\n" " " | sed 's/ //g' | awk '{print substr($0,3);}' | cat > "$ndpath/sm_${type}.nodeid" + + cp "$capath/sm_ca.crt" "$ndpath" +} + +generate_single_node_cert() { + local sm_mode="$1" + local ca_cert_path="${2}" + local node_cert_path="${3}" + local type="${4}" + + mkdir -p ${node_cert_path} + if [[ "${sm_mode}" == "false" ]]; then + gen_rsa_node_cert "${ca_cert_path}" "${node_cert_path}" "${type}" 2>&1 + else + gen_sm_node_cert "${ca_cert_path}" "${node_cert_path}" "${type}" 2>&1 + fi +} + +generate_chain_cert(){ + local sm_mode="$1" + local chain_cert_path="$2" + mkdir -p "${chain_cert_path}" + if [[ "${sm_mode}" == "false" ]]; then + gen_chain_cert "${chain_cert_path}" 2>&1 + else + gen_sm_chain_cert "${chain_cert_path}" 2>&1 + fi +} + +generate_node_account() +{ + local output_path="${1}" + if [ ! -d "${output_path}" ];then + mkdir -p ${output_path} + fi + if [ ! -f /tmp/secp256k1.param ];then + ${OPENSSL_CMD} ecparam -out /tmp/secp256k1.param -name secp256k1 + fi + ${OPENSSL_CMD} genpkey -paramfile /tmp/secp256k1.param -out ${output_path}/node.pem + # generate nodeid + ${OPENSSL_CMD} ec -text -noout -in "${output_path}/node.pem" 2> /dev/null | sed -n '7,11p' | tr -d ": \n" | awk '{print substr($0,3);}' | cat >"$output_path"/node.nodeid +} + +generate_sm_node_account() +{ + local output_path="${1}" + if [ ! -d "${output_path}" ];then + mkdir -p ${output_path} + fi + if [ ! -f ${sm2_params} ];then + generate_sm_sm2_param ${sm2_params} + fi + ${OPENSSL_CMD} genpkey -paramfile ${sm2_params} -out ${output_path}/node.pem 2>/dev/null + $OPENSSL_CMD ec -in "$output_path/node.pem" -text -noout 2> /dev/null | sed -n '7,11p' | sed 's/://g' | tr "\n" " " | sed 's/ //g' | awk '{print substr($0,3);}' | cat > "$output_path/node.nodeid" +} + +exit_with_clean() +{ + local content=${1} + echo -e "\033[31m[ERROR] ${content}\033[0m" + if [ -d "${output_dir}" ];then + rm -rf ${output_dir} + fi + exit 1 +} + +check_openssl() +{ + # Notice: The OpenSSL path is specified, OpenSSL 1.1.x is required + local openssl_cmd="${1}" + [ ! -z "$(${openssl_cmd} version | grep 1.1)" ] || { + LOG_FALT "Openssl 1.1.x is required, you should install openssl first Or use \"openssl version\" command to check whether the openssl version is suitable." + #echo "download openssl from https://www.openssl.org." + exit 1 + } +} + +check_and_install_tassl(){ + if [ -f "${OPENSSL_CMD}" ];then + return + fi + # https://en.wikipedia.org/wiki/Uname#Examples + local x86_64_name="x86_64" + local arm_name="aarch64" + local tassl_mid_name="linux" + if [[ -n "${macOS}" ]];then + x86_64_name="x86_64" + arm_name="arm64" + tassl_mid_name="macOS" + fi + + local tassl_post_fix="x86_64" + local platform="$(uname -m)" + if [[ "${platform}" == "${arm_name}" ]];then + tassl_post_fix="aarch64" + elif [[ "${platform}" == "${x86_64_name}" ]];then + tassl_post_fix="x86_64" + else + LOG_FATAL "Unsupported platform ${platform} for ${tassl_mid_name}" + exit 1 + fi + local tassl_package_name="tassl-1.1.1b-${tassl_mid_name}-${tassl_post_fix}" + local tassl_tgz_name="${tassl_package_name}.tar.gz" + local tassl_link_prefix="${cdn_link_header}/FISCO-BCOS/tools/tassl-1.1.1b/${tassl_tgz_name}" + LOG_INFO "Downloading tassl binary from ${tassl_link_prefix}..." + curl -#LO --insecure "${tassl_link_prefix}" + tar zxvf ${tassl_tgz_name} && rm ${tassl_tgz_name} + chmod u+x ${tassl_package_name} + mkdir -p "${HOME}"/.fisco + mv ${tassl_package_name} "${HOME}"/.fisco/tassl-1.1.1b +} + +help() { + echo $1 + cat < [Required] the operation command, support generate_all_cert/generate_ca_cert/generate_node_cert/generate_multi_nodes_cert/generate_sdk_cert and generate_private_key/generate_multi_private_key now + -o [Optional] output directory default ./cert + -s [Optional] SM SSL connection ,or not, default no + -d [Optional] ca certificate path, specify ca certificate path to generate node/sdk certificate + -l [Optional] list of ip for generate certificate or private key, working with generate_multi_nodes_cert/generate_multi_private_key + -O [Optional] specify the OpenSSL path(Notice: OpenSSL 1.1.x is required), default download TASSL 1.1.1b to local dir ~/.fisco/tassl-1.1.1b + -h Help +EOF + exit 0 +} + +parse_params() { + while getopts "o:O:d:c:sl:h" option; do + case $option in + c) command="$OPTARG";; + d) + ca_cert_path="$OPTARG" + dir_must_exists "${ca_cert_path}" + ;; + o) + output_dir="$OPTARG" + mkdir -p "$output_dir" + dir_must_exists "${output_dir}" + ;; + s) sm_mode="true" ;; + l) ip_param="$OPTARG" ;; + O) + OPENSSL_CMD="$OPTARG" + check_openssl "${OPENSSL_CMD}" + LOG_INFO "The OpenSSL path is specified: ${OPENSSL_CMD}" + ;; + h) help ;; + *) help ;; + esac + done +} + +generate_all_cert(){ + LOG_INFO "generate all cert" + cert_dir="${output_dir}/ssl" + sdk_dir="${output_dir}/sdk" + mkdir -p "$ca_cert_path" + mkdir -p "$cert_dir" + mkdir -p "$sdk_dir" + generate_chain_cert "${sm_mode}" "${ca_cert_path}" + generate_single_node_cert "${sm_mode}" "${ca_cert_path}" "${cert_dir}" "ssl" + generate_single_node_cert "${sm_mode}" "${ca_cert_path}" "${sdk_dir}" "sdk" + LOG_INFO "generate all cert success" +} + +generate_ca_cert() +{ + LOG_INFO "generate ca cert" + mkdir -p "$ca_cert_path" + generate_chain_cert "${sm_mode}" "${ca_cert_path}" + LOG_INFO "generate ca cert success" +} + +generate_node_cert() +{ + LOG_INFO "generate node cert" + cert_dir="${output_dir}/ssl" + mkdir -p "$cert_dir" + generate_single_node_cert "${sm_mode}" "${ca_cert_path}" "${cert_dir}" "ssl" + LOG_INFO "generate node cert success" +} + +generate_multi_nodes_cert() +{ + LOG_INFO "generate multi nodes cert, ip param: ${ip_param}" + generate_multi_nodes_cert_impl "${ca_cert_path}" "ssl" "${output_dir}" + LOG_INFO "generate multi nodes cert success" +} + +generate_sdk_cert() +{ + LOG_INFO "generate sdk cert" + sdk_dir="${output_dir}/sdk" + mkdir -p "$sdk_dir" + generate_single_node_cert "${sm_mode}" "${ca_cert_path}" "${sdk_dir}" "sdk" + LOG_INFO "generate node cert success" +} + +generate_cert_node_private_key() +{ + LOG_INFO "Generate node private key" + if [[ "${sm_mode}" == "false" ]]; then + generate_node_account "${output_dir}" + else + generate_sm_node_account "${output_dir}" + fi + LOG_INFO "Generate node private key success" +} + +generate_multi_nodes_private_key() +{ + LOG_INFO "generate multi nodes private key, sm_mode: ${sm_mode}, ip param: ${ip_param}" + generate_multi_nodes_private_key_impl "${output_dir}" + LOG_INFO "generate multi nodes private key success" +} + +main() { + parse_params "$@" + # FIXME: use openssl 1.1 to generate gm certificates + check_env + check_and_install_tassl + cert_conf="${output_dir}/cert.cnf" + if [ -z "${ca_cert_path}" ];then + ca_cert_path="${output_dir}/ca" + fi + if [[ "${command}" == "generate_all_cert" ]]; then + generate_all_cert + elif [[ "${command}" == "generate_ca_cert" ]]; then + generate_ca_cert + elif [[ "${command}" == "generate_node_cert" ]]; then + generate_node_cert + elif [[ "${command}" == "generate_multi_nodes_cert" ]]; then + generate_multi_nodes_cert + elif [[ "${command}" == "generate_sdk_cert" ]]; then + generate_sdk_cert + elif [[ "${command}" == "generate_private_key" ]]; then + generate_cert_node_private_key + elif [[ "${command}" == "generate_multi_private_key" ]]; then + generate_multi_nodes_private_key + else + LOG_FALT "Unsupported command" + fi +} + +main "$@" diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/scripts/mtail/node.mtail" "b/BFPL\345\243\271/tools/BcosBuilder/src/scripts/mtail/node.mtail" new file mode 100644 index 00000000..1f222994 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/scripts/mtail/node.mtail" @@ -0,0 +1,51 @@ +hidden text host +host = "${ip}" + +#node +hidden text node +node = "${node}" + +#chain id +hidden text chain +chain = "chain0" + +#group id +hidden text group +group = "group0" + + +gauge p2p_session_actived by host , node +/\[P2PService\]\[Service\]\[METRIC\]heartBeat,connected count=(?P\d+)/ { + p2p_session_actived[host][node] = \$count +} + +gauge block_exec_duration_milliseconds_gauge by chain , group , host , node +/\[CONSENSUS\]\[Core\]\[METRIC\]asyncExecuteBlock success.*?timeCost=(?P\d+)/ { + block_exec_duration_milliseconds_gauge[chain][group][host][node] = \$timeCost +} + +histogram block_exec_duration_milliseconds buckets 0, 50, 100, 150 by chain , group , host , node +/\[CONSENSUS\]\[Core\]\[METRIC\]asyncExecuteBlock success.*?timeCost=(?P\d+)/ { + block_exec_duration_milliseconds[chain][group][host][node] = \$timeCost +} + +gauge block_commit_duration_milliseconds_gauge by chain , group , host , node +/\[CONSENSUS\]\[PBFT\]\[STORAGE\]\[METRIC\]commitStableCheckPoint success.*?timeCost=(?P\d+)/ { + block_commit_duration_milliseconds_gauge[chain][group][host][node] = \$timeCost +} + + +histogram block_commit_duration_milliseconds buckets 0, 50, 100, 150 by chain , group , host , node +/\[CONSENSUS\]\[PBFT\]\[STORAGE\]\[METRIC\]commitStableCheckPoint success.*?timeCost=(?P\d+)/ { + block_commit_duration_milliseconds[chain][group][host][node] = \$timeCost +} + +gauge ledger_block_height by chain , group , host , node +/\[LEDGER\]\[METRIC\]asyncPrewriteBlock,number=(?P\d+)/ { + ledger_block_height[chain][group][host][node] = \$number +} + +gauge txpool_pending_tx_size by chain , group , host , node +/\[TXPOOL\]\[METRIC\]batchFetchTxs success,.*?pendingTxs=(?P\d+)/ { + txpool_pending_tx_size[chain][group][host][node] = \$pendingTxs +} diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/scripts/mtail/start_mtail_monitor.sh" "b/BFPL\345\243\271/tools/BcosBuilder/src/scripts/mtail/start_mtail_monitor.sh" new file mode 100644 index 00000000..c6fb0a94 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/scripts/mtail/start_mtail_monitor.sh" @@ -0,0 +1,44 @@ +#!/bin/bash +SHELL_FOLDER=$(cd $(dirname $0);pwd) + +LOG_ERROR() { + content=${1} + echo -e "\033[31m[ERROR] ${content}\033[0m" +} + +LOG_INFO() { + content=${1} + echo -e "\033[32m[INFO] ${content}\033[0m" +} + +mtail=${SHELL_FOLDER}/mtail +mtailScript=${SHELL_FOLDER}/node.mtail +port=$1 +#logDir=$2 +export RUST_LOG=bcos_wasm=error +cd ${SHELL_FOLDER} +node=$(basename ${SHELL_FOLDER}) +node_pid=$(ps aux|grep ${mtail}|grep -v grep|awk '{print $2}') +if [ ! -z ${node_pid} ];then + echo " ${node} is Listening, pid is $node_pid." + exit 0 +else + nohup ${mtail} -logtostderr -progs ${mtailScript} -logs '../*.log' -port ${port} >>nohup.out 2>&1 & + sleep 1.5 +fi + +try_times=4 +i=0 +while [ $i -lt ${try_times} ] +do + node_pid=$(ps aux|grep ${mtail}|grep -v grep|awk '{print $2}') + success_flag=$(tail -n20 nohup.out | grep Listening) + if [[ ! -z ${node_pid} && ! -z "${success_flag}" ]];then + echo -e "\033[32m ${node} start successfully\033[0m" + exit 0 + fi + sleep 0.5 + ((i=i+1)) +done +echo -e "\033[31m Exceed waiting time. Please try again to start ${node} \033[0m" +tail -n20 nohup.out diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/scripts/mtail/stop_mtail_monitor.sh" "b/BFPL\345\243\271/tools/BcosBuilder/src/scripts/mtail/stop_mtail_monitor.sh" new file mode 100644 index 00000000..6661a109 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/scripts/mtail/stop_mtail_monitor.sh" @@ -0,0 +1,36 @@ +#!/bin/bash +SHELL_FOLDER=$(cd $(dirname $0);pwd) + +LOG_ERROR() { + content=${1} + echo -e "\033[31m[ERROR] ${content}\033[0m" +} + +LOG_INFO() { + content=${1} + echo -e "\033[32m[INFO] ${content}\033[0m" +} + +mtail=${SHELL_FOLDER}/mtail +node=$(basename ${SHELL_FOLDER}) +node_pid=$(ps aux|grep ${mtail}|grep -v grep|awk '{print $2}') +try_times=10 +i=0 +if [ -z ${node_pid} ];then + echo " ${node} isn't running." + exit 0 +fi +[ ! -z ${node_pid} ] && kill ${node_pid} > /dev/null + +while [ $i -lt ${try_times} ] +do + sleep 1 + node_pid=$(ps aux|grep ${mtail}|grep -v grep|awk '{print $2}') + if [ -z ${node_pid} ];then + echo -e "\033[32m stop ${node} success.\033[0m" + exit 0 + fi + ((i=i+1)) +done +echo " Exceed maximum number of retries. Please try again to stop ${node}" +exit 1 diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/service/key_center_service.py" "b/BFPL\345\243\271/tools/BcosBuilder/src/service/key_center_service.py" new file mode 100644 index 00000000..032c62a4 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/service/key_center_service.py" @@ -0,0 +1,69 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +from common import utilities +import requests +import base64 +import json +import time +import os + + +class KeyCenterService: + """ + the key center service + """ + + def __init__(self, url, cipher_data_key): + if url.startswith("http://"): + self.url = url + else: + self.url = "http://%s" % url + self.cipher_data_key = cipher_data_key + + def encrypt_file(self, file_path): + utilities.log_info("encrypt %s with key center %s" % + (file_path, self.url)) + if os.path.exists(file_path) is False: + utilities.log_error("The file %s not exists!" % (file_path)) + return False + method = "encWithCipherKey" + params = [] + # read the file + with open(file_path) as cert_obj: + cert_data = str(base64.b64encode( + cert_obj.read().encode("utf-8")), "utf-8") + params.append(cert_data) + params.append(self.cipher_data_key) + payload = json.dumps({"jsonrpc": "2.0", "method": method, + "params": params, "id": 83}) + headers = {'content-type': "application/json"} + try: + response = requests.request( + "POST", self.url, data=payload, headers=headers) + response_data = response.json() + if "result" not in response_data or "dataKey" not in response_data["result"]: + utilities.log_error("encrypt %s with key center %s failed for error response: %s" % ( + file_path, self.url, response_data)) + return False + # backup the file_path + backup_file_path = "%s_%d.backup" % (file_path, time.time()) + utilities.log_info("backup the original file %s to %s" % + (file_path, backup_file_path)) + (ret, message) = utilities.execute_command_and_getoutput( + "cp %s %s" % (file_path, backup_file_path)) + if ret is False: + utilities.log_error("encrypt %s with key center %s failed for backup file failed, error: %s" % ( + file_path, self.url, message)) + return False + # write the encrypted content into file_path + cipher_file_data = response_data["result"]["dataKey"] + with open(file_path, "w") as f: + f.write(cipher_file_data) + utilities.log_info( + "encrypt %s with key center %s success" % (file_path, self.url)) + return True + + except Exception as e: + utilities.log_error("encrypt %s with key center %s failed, error info %s" % ( + file_path, self.url, e)) + return False diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/service/tars_service.py" "b/BFPL\345\243\271/tools/BcosBuilder/src/service/tars_service.py" new file mode 100644 index 00000000..a4728d57 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/service/tars_service.py" @@ -0,0 +1,551 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +from common import utilities +from common.utilities import ServiceInfo +from requests_toolbelt import MultipartEncoder +import requests +import uuid +import time +import os + + +class TarsService: + 'basic class to access the tars' + + def __init__(self, tars_url, tars_token, app_name, deploy_ip): + self.tars_url = tars_url + self.tars_token = tars_token + self.deploy_ip = deploy_ip + if self.tars_url.endswith('/') is False: + self.tars_url = self.tars_url + '/' + self.add_application_url = self.tars_url + 'api/add_application' + self.deploy_service_url = self.tars_url + 'api/deploy_server' + self.get_port_url = self.tars_url + 'api/auto_port' + self.add_config_url = self.tars_url + 'api/add_config_file' + self.update_config_url = self.tars_url + 'api/update_config_file' + # upload and publish the package + self.upload_package_url = self.tars_url + 'api/upload_patch_package' + self.add_task_url = self.tars_url + 'api/add_task' + self.get_server_list_url = self.tars_url + 'api/server_list' + self.config_file_list_url = self.tars_url + 'api/config_file_list' + self.node_config_file_list_url = self.tars_url + 'api/node_config_file_list' + self.get_server_patch_url = self.tars_url + "api/get_server_patch" + self.expand_server_preview_url = self.tars_url + "api/expand_server_preview" + self.expand_server_url = self.tars_url + "api/expand_server" + self.fetch_config_url = self.tars_url + "api/config_file" + self.app_name = app_name + self.token_param = {'ticket': self.tars_token} + + def create_application(self): + "create application" + if self.app_exists() is True: + # utilities.log_error( + # "application %s already exists" % self.app_name) + return False + utilities.log_debug("create application: %s" % self.app_name) + request_data = {'f_name': self.app_name} + response = requests.post( + self.add_application_url, params=self.token_param, data=request_data) + return TarsService.parse_response("create application " + self.app_name, response) + + def parse_response(operation, response): + if response.status_code != 200: + utilities.log_error("%s failed, error message: %s, error code: %d" % + (operation, response.content, response.status_code)) + return False + result = response.json() + error_msg = result['err_msg'] + if len(error_msg) > 0: + utilities.log_error("%s failed, error message: %s" % + (operation, error_msg)) + return False + return True + + def get_auto_port(self): + return self.get_node_auto_port(self.deploy_ip) + + def get_node_auto_port(self, node_name): + # get the auto_port + utilities.log_debug("get the un-occupied port") + params = {"node_name": node_name, "ticket": self.tars_token} + response = requests.get(self.get_port_url, params=params) + if TarsService.parse_response("get the un-occupied port", response) is False: + return (False, 0) + result = response.json() + if 'data' not in result: + utilities.log_error("get empty un-occupied port") + return (False, 0) + node_info = result['data'] + if len(node_info) <= 0: + utilities.log_error("get empty un-occupied port") + return (False, 0) + if 'port' not in node_info[0]: + utilities.log_error("get empty un-occupied port") + return (False, 0) + port = node_info[0]['port'] + utilities.log_debug( + "get the un-occupied port success, port: %s" % (port)) + return (True, int(port)) + + def deploy_single_service(self, service_name, obj_name_list, allow_duplicated): + "deploy single service" + if self.server_exists(service_name) is True and allow_duplicated is False: + utilities.log_error("service %s already exists." % service_name) + return False + utilities.log_info("deploy service %s" % service_name) + adapters = [] + for obj_name in obj_name_list: + # get the un-occupied port + (ret, port) = self.get_auto_port() + if ret is False: + utilities.log_error( + "deploy service %s failed for get un-occupied port failed" % service_name) + return False + adapters.append({"obj_name": obj_name, "port": port, "bind_ip": self.deploy_ip, "port_type": "tcp", + "thread_num": 8, "max_connections": 100000, "queuecap": 50000, "queuetimeout": 20000}) + request_data = {"application": self.app_name, "server_name": service_name, "node_name": self.deploy_ip, + "server_type": "tars_cpp", "template_name": "tars.cpp.default", 'adapters': adapters} + response = requests.post( + self.deploy_service_url, params=self.token_param, json=request_data) + if TarsService.parse_response("deploy service " + service_name, response) is False: + return False + return True + + def fetch_server_config_file(self, config_file_name, server_name): + (ret, config_id) = self.get_server_config_file_id( + config_file_name, server_name) + if ret is False: + utilities.log_error("fetch server config file failed, please check the existence of specified service, service: %s, config: %s" % + (server_name, config_file_name)) + return (False, "") + param = {"ticket": self.tars_token, "id": config_id} + response = requests.get(self.fetch_config_url, params=param) + if TarsService.parse_response("fetch service config " + server_name, response) is False: + return (False, "") + utilities.log_debug( + "fetch service config file success, response: %s" % response.content) + result = response.json() + if "data" not in result or "config" not in result["data"]: + utilities.log_error( + "fetch service config file failed, response %s" % response.content) + return (False, "") + return (True, result["data"]["config"]) + + def deploy_service_list(self, service_list, obj_list, allow_duplicated): + "deploy service list" + i = 0 + for service in service_list: + if self.deploy_single_service(service, obj_list[i], allow_duplicated) is False: + utilities.log_error("deploy service list failed, service list: %s" % + service_list) + return False + i = i + 1 + return True + + def get_level(server_name): + # service level + level = 5 + # app level + if len(server_name) == 0: + level = 1 + return level + + def add_server_config_file(self, deploy_ip, config_file_name, server_name, config_file_path, empty_server_config): + content = "\n" + if os.path.exists(config_file_path) is False: + utilities.log_error("add service config error:\n the config file %s doesn't exist, service: %s" % ( + config_file_path, server_name)) + return False + if empty_server_config is False: + try: + fp = open(config_file_path) + content = fp.read() + except OSError as reason: + utilities.log_error( + "load the configuration failed, error: %s" % str(reason)) + return False + request_data = {"level": TarsService.get_level(server_name), "application": self.app_name, "node_name": deploy_ip, + "server_name": server_name, "filename": config_file_name, "config": content} + response = requests.post( + self.add_config_url, params=self.token_param, json=request_data) + if TarsService.parse_response("add application config file", response) is False: + return False + if response.status_code != 200: + return False + return True + + def add_non_empty_server_config_file(self, deploy_ip, config_file_name, server_name, config_file_path): + "add server the config file" + utilities.log_debug("add config file for application %s, config file path: %s, service_name: %s" % ( + self.app_name, config_file_path, server_name)) + ret = self.add_server_config_file( + deploy_ip, config_file_name, server_name, config_file_path, False) + if ret is False: + ret = self.update_service_config( + config_file_name, server_name, "", config_file_path) + return ret + + def add_node_config_list(self, deploy_ip, config_list, service_name, config_file_list): + i = 0 + for config_file_path in config_file_list: + config = config_list[i] + if self.add_non_empty_server_config_file(deploy_ip, config, service_name, config_file_path) is False: + utilities.log_error("add_node_config_list failed, config files info: %s" % + config_list) + return False + i = i+1 + return True + + def add_config_file(self, config_file_name, server_name, node_name, config_file_path, empty_server_config): + "add the config file" + (ret, id) = self.get_server_config_file_id( + config_file_name, server_name) + if ret is False: + utilities.log_debug("add config file for application %s, config file path: %s, service_name: %s" % + (self.app_name, config_file_path, server_name)) + self.add_server_config_file( + "", config_file_name, server_name, config_file_path, empty_server_config) + (ret, id) = self.get_config_file_id( + config_file_name, server_name, node_name) + if ret is False: + utilities.log_debug("add config file for node: %s, app: %s, config: %s, service: %s" % + (node_name, self.app_name, config_file_path, server_name)) + self.add_server_config_file( + node_name, config_file_name, server_name, config_file_path, empty_server_config) + return self.update_service_config(config_file_name, server_name, node_name, config_file_path) + + def update_service_config(self, config_file_name, server_name, node_name, config_file_path): + utilities.log_debug("update config file for application %s, config file path: %s, node: %s" % + (self.app_name, config_file_path, node_name)) + if os.path.exists(config_file_path) is False: + utilities.log_error("update service config error:\n the config file %s doesn't exist, service: %s" % ( + config_file_path, server_name)) + return False + ret = True + config_id = 0 + if len(node_name) == 0: + (ret, config_id) = self.get_server_config_file_id( + config_file_name, server_name) + else: + ret, config_id = self.get_config_file_id( + config_file_name, server_name, node_name) + if ret is False: + return False + try: + fp = open(config_file_path) + content = fp.read() + except OSError as reason: + utilities.log_error( + "load the configuration failed, error: %s" % str(reason)) + request_data = {"id": config_id, "config": content, + "reason": "update config file"} + response = requests.post( + self.update_config_url, params=self.token_param, json=request_data) + if TarsService.parse_response("update config file for application " + self.app_name + ", config file:" + config_file_name, response) is False: + return False + return True + + def get_config_file_id(self, config_file_name, server_name, node_name): + (ret, server_config_id) = self.get_server_config_file_id( + config_file_name, server_name) + if ret is False: + return (False, 0) + params = {"ticket": self.tars_token, "config_id": server_config_id, "level": TarsService.get_level(server_name), "application": self.app_name, + "server_name": server_name, "set_name": "", "set_area": "", "set_group": ""} + response = requests.get(self.node_config_file_list_url, params=params) + if TarsService.parse_response("query the node config file id for " + config_file_name, response) is False: + return (False, 0) + result = response.json() + if "data" not in result or len(result["data"]) == 0: + utilities.log_debug("the config %s doesn't exist" % + (config_file_name)) + return (False, 0) + # try to find the config file info + for item in result["data"]: + if "filename" in item and item["filename"] == config_file_name and item["node_name"] == node_name: + return (True, item["id"]) + utilities.log_info("the node config file %s not found, node: %s, :%s:" % ( + config_file_name, node_name, str(result["data"]))) + return (False, 0) + + def get_server_config_file_id(self, config_file_name, server_name): + utilities.log_debug("query the config file id for %s" % + config_file_name) + params = {"ticket": self.tars_token, "level": TarsService.get_level(server_name), "application": self.app_name, + "server_name": server_name, "set_name": "", "set_area": "", "set_group": ""} + response = requests.get( + self.config_file_list_url, params=params) + if TarsService.parse_response("query the config file id for " + config_file_name, response) is False: + return (False, 0) + result = response.json() + if "data" not in result or len(result["data"]) == 0: + utilities.log_debug( + "the config file id not found for %s because of empty return data, response: %s" % (config_file_name, response.content)) + return (False, 0) + # try to find the config file info + for item in result["data"]: + if "filename" in item and item["filename"] == config_file_name: + utilities.log_debug("get_server_config_file_id, server: %s, config_id: %s" % ( + server_name, item["id"])) + return (True, item["id"]) + utilities.log_debug("the config file %s not found" % config_file_name) + return (False, 0) + + def add_config_list(self, config_list, service_name, node_name, config_file_list, empty_server_config): + i = 0 + for config_file_path in config_file_list: + config = config_list[i] + utilities.log_info( + "* add config for service %s, node: %s, config: %s" % (service_name, node_name, config)) + utilities.log_debug("add config for service %s, node: %s, config: %s, path: %s" % ( + service_name, node_name, config, config_file_path)) + if self.add_config_file(config, service_name, node_name, config_file_path, empty_server_config) is False: + utilities.log_error("add_config_list failed, config files info: %s" % + config_list) + return False + i = i+1 + return True + + def get_server_patch(self, task_id): + utilities.log_debug("get server patch, task_id: %s" % task_id) + params = {"task_id": task_id, "ticket": self.tars_token} + response = requests.get(self.get_server_patch_url, params=params) + if TarsService.parse_response("get server patch", response) is False: + return (False, "") + if response.status_code != 200: + return (False, "") + # get the id + result = response.json() + result_data = result['data'] + if 'id' not in result_data: + utilities.log_error( + "get_server_patch failed for empty return message") + return (False, "") + id = result_data['id'] + return (True, id) + + def upload_tars_package(self, service_name, package_path): + """ + upload the tars package + """ + package_name = service_name + ServiceInfo.tars_pkg_postfix + utilities.log_debug("upload tars package for service %s, package_path: %s, package_name: %s" % + (service_name, package_path, package_name)) + if os.path.exists(package_path) is False: + utilities.log_error("upload tars package for service %s failed for the path %s not exists" % ( + service_name, package_path)) + return (False, 0) + task_id = str(uuid.uuid4()) + form_data = MultipartEncoder(fields={"application": self.app_name, "task_id": task_id, "module_name": service_name, "comment": "upload package", "suse": ( + package_name, open(package_path, 'rb'), 'text/plain/binary')}) + + response = requests.post(self.upload_package_url, data=form_data, params=self.token_param, headers={ + 'Content-Type': form_data.content_type}) + if TarsService.parse_response("upload tars package " + package_path, response) is False: + return (False, 0) + # get the id + (ret, id) = self.get_server_patch(task_id) + if ret is True: + utilities.log_info( + "upload tar package %s success, config id: %s" % (package_path, id)) + return (ret, id) + + def get_server_info(self, tree_node_id): + params = {'tree_node_id': tree_node_id, "ticket": self.tars_token} + response = requests.get(self.get_server_list_url, params=params) + if TarsService.parse_response("get server info by tree node id: " + tree_node_id, response) is False: + utilities.log_error("get server info by tree node id for error response, tree_node_id: %s, msg: %s" % ( + tree_node_id, response.content)) + return (False, response) + return (True, response) + + def app_exists(self): + (ret, response) = self.get_server_info("1" + self.app_name) + if ret is False: + return False + result = response.json() + if 'data' in result and len(result["data"]) > 0: + return True + return False + + def server_exists(self, service_name): + (ret, server_id) = self.get_server_id(service_name, self.deploy_ip) + return ret + + def get_server_id(self, service_name, node_name): + # tree_node_id + tree_node_id = "1" + self.app_name + ".5" + service_name + (ret, response) = self.get_server_info(tree_node_id) + if ret is False: + return (False, 0) + if TarsService.parse_response("get server list ", response) is False: + utilities.log_error("get server info failed for error response, server name: %s, msg: %s" % ( + service_name, response.content)) + return (False, 0) + result = response.json() + if 'data' not in result: + return (False, 0) + server_infos = result['data'] + for item in server_infos: + if "node_name" in item and len(node_name) > 0 and item["node_name"] == node_name: + return (True, item["id"]) + if "id" in item and len(node_name) == 0: + return (True, item["id"]) + return (False, 0) + + def upload_and_publish_package(self, service_name, package_path): + """ + upload and publish the tars package + """ + # get the service info + (ret, server_id) = self.get_server_id(service_name, self.deploy_ip) + if ret is False: + utilities.log_error( + "upload and publish package failed for get the server info failed, server: %s" % service_name) + return False + # upload the tars package + (ret, patch_id) = self.upload_tars_package(service_name, package_path) + if ret is False: + return False + # patch tars + self.patch_tars(server_id, patch_id) + return True + + def expand_server_preview(self, server_name, node_name, expanded_node_list): + """ + expand the server preview + """ + utilities.log_info("expand_server_preview, app: %s, server_name: %s, expanded_node_list: %s" % ( + self.app_name, server_name, '.'.join(expanded_node_list))) + request_data = {"application": self.app_name, "server_name": server_name, "node_name": node_name, "set": "", "expand_nodes": expanded_node_list, + "enable_set": "false", "set_name": "", "set_area": "", "set_group": "", "copy_node_config": "false", "nodeName": []} + response = requests.post( + self.expand_server_preview_url, params=self.token_param, json=request_data) + if TarsService.parse_response("expand server preview", response) is False: + utilities.log_error("expand server preview for error response, server name: %s, msg: %s" % ( + server_name, response.content)) + return False + utilities.log_info("expand server preview response %s" % + response.content) + return True + + def expand_server(self, server_name, node_name, expanded_node_list, obj_list): + """ + expand the server + """ + utilities.log_info("expand_server, app: %s, server_name: %s" % ( + self.app_name, server_name)) + expand_servers_info = [] + for node in expanded_node_list: + for obj in obj_list: + (ret, port) = self.get_node_auto_port(node) + if ret is False: + utilities.log_error( + "expand server failed for get node auto port failed, server: %s, node: %s" % (server_name, node)) + return False + node_info = {"bind_ip": node, "node_name": node, + "obj_name": obj, "port": port, "set": ""} + expand_servers_info.append(node_info) + request_data = {"application": self.app_name, "server_name": server_name, "set": "", "node_name": node_name, + "copy_node_config": "false", "expand_preview_servers": expand_servers_info} + response = requests.post( + self.expand_server_url, params=self.token_param, json=request_data) + if TarsService.parse_response("expand server", response) is False: + utilities.log_error("expand server for error response, server name: %s, msg: %s" % ( + server_name, response.content)) + return False + utilities.log_info("expand server response %s" % response.content) + return True + + def expand_server_with_preview(self, server_name, node_name, expanded_node_list, obj_list): + ret = self.expand_server_preview( + server_name, node_name, expanded_node_list) + if ret is False: + utilities.log_error("expand server failed for expand preview failed, app: %s, server: %s, expanded_node_list: %s" % ( + self.app_name, server_name, '.'.join(expanded_node_list))) + return False + return self.expand_server(server_name, node_name, expanded_node_list, obj_list) + + def patch_tars(self, server_id, patch_id): + utilities.log_debug("patch tars for application %s, server_id: %s, patch_id: %s" % ( + self.app_name, server_id, patch_id)) + items = [{"server_id": server_id, "command": "patch_tars", "parameters": { + "patch_id": patch_id, "bak_flag": 'false', "update_text": "", "group_name": ""}}] + request_data = {"serial": 'true', "items": items} + response = requests.post( + self.add_task_url, params=self.token_param, json=request_data) + if TarsService.parse_response("patch tars ", response) is False: + utilities.log_error("patch tars failed for error response, server id: %s, msg: %s" % ( + server_id, response.content)) + return False + utilities.log_debug("patch tars response %s" % response.content) + return True + + def add_task(self, service_name, command): + """ + current supported commands are: stop, restart, undeploy_tars, patch_tars + """ + utilities.log_debug("add_task for service %s, command is %s" % + (service_name, command)) + (ret, server_id) = self.get_server_id(service_name, self.deploy_ip) + if ret is False: + utilities.log_error("%s failed for get server id failed, please check the existence of %s" % ( + command, service_name)) + return False + items = [{"server_id": server_id, "command": command, "parameters": {}}] + request_data = {"serial": 'true', "items": items} + response = requests.post( + self.add_task_url, params=self.token_param, json=request_data) + if TarsService.parse_response("execute command " + command, response) is False: + utilities.log_error("add_task failed for error response, server name: %s, msg: %s" % ( + service_name, response.content)) + return False + return True + + def stop_server(self, service_name): + """ + stop the givn service + """ + return self.add_task(service_name, "stop") + + def stop_server_list(self, server_list): + for server in server_list: + if self.stop_server(server) is False: + return False + return True + + def restart_server(self, service_name): + """ + restart the given service + """ + return self.add_task(service_name, "restart") + + def undeploy_tars(self, service_name): + """ + undeploy the tars service + """ + return self.add_task(service_name, "undeploy_tars") + + def undeploy_server_list(self, server_list): + for server in server_list: + if self.undeploy_tars(server) is False: + return False + return True + + def restart_server_list(self, server_list): + for server in server_list: + if self.restart_server(server) is False: + return False + time.sleep(5) + return True + + def get_service_list(self): + return self.get_server_info("1" + self.app_name) + + def upload_and_publish_package_list(self, service_list, service_path_list): + i = 0 + for service in service_list: + service_path = service_path_list[i] + self.upload_and_publish_package(service, service_path) + i = i+1 + time.sleep(10) diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.genesis" "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.genesis" new file mode 100644 index 00000000..f67e6bca --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.genesis" @@ -0,0 +1,34 @@ +[chain] + ; use SM crypto or not, should never be changed + sm_crypto=false + ; the group id, should never be changed + group_id=group0 + ; the chain id, should never be changed + chain_id=chain0 +[consensus] + ; consensus algorithm now support PBFT(consensus_type=pbft) + consensus_type=pbft + ; the max number of transactions of a block + block_tx_count_limit=1000 + ; the number of blocks generated by each leader + leader_period=100 + ; the node id of consensusers + node.0= + +[version] + ; compatible version, can be dynamically upgraded through setSystemConfig + ; the default is 3.1.0 + compatibility_version=3.1.0 + +[tx] + ; transaction gas limit + gas_limit=3000000000 + +[executor] + ; use the wasm virtual machine or not, default use evm + is_wasm = false + ; enable auth_check or not, default disable + is_auth_check=false + auth_admin_account= + ; enable serial execute or not, default use parallel + is_serial_execute=false \ No newline at end of file diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.ini.executor" "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.ini.executor" new file mode 100644 index 00000000..48330adb --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.ini.executor" @@ -0,0 +1,27 @@ +[chain] + ; use SM crypto or not, should never be changed + sm_crypto=false + ; the group id, should never be changed + group_id=group0 + ; the chain id, should never be changed + chain_id=chain0 +[service] + scheduler=chain0 + + ; run without tars framework + ; without_tars_framework = true + ; tars_proxy_conf = conf/tars_proxy.ini + +[storage] + enable_cache=true + pd_addrs= + key_page_size=10240 + +[log] + enable=true + log_path=./log + ; info debug trace + level=info + ; MB + max_log_file_size=200 + diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.ini.gateway" "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.ini.gateway" new file mode 100644 index 00000000..25cd152d --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.ini.gateway" @@ -0,0 +1,77 @@ +[p2p] + listen_ip=0.0.0.0 + listen_port=30300 + ; ssl or sm ssl + sm_ssl=false + nodes_path=./ + nodes_file=nodes.json + +[service] + ;rpc=chain0 + + ; run without tars framework + ; without_tars_framework = true + ; tars_proxy_conf = conf/tars_proxy.ini + +[cert] + ; directory the certificates located in + ca_path=./conf + ; the ca certificate file + ca_cert=ca.crt + ; the node private key file + node_key=ssl.key + ; the node certificate file + node_cert=ssl.crt + +[chain] + ; use SM crypto or not, should never be changed + sm_crypto=false + chain_id=chain0 + +[storage_security] + ; enable data disk encryption or not, default is false + enable=false + ; url of the key center, in format of ip:port + ;key_center_url= + ;cipher_data_key= + +[failover] + ; enable failover or not, default disable + enable = false + ; the address of etcd, can configure multiple comma-separated + cluster_url= "127.0.0.1:2379" + +[flow_control] + ; the module that does not limit bandwidth + ; list of all modules: raft,pbft,amop,block_sync,txs_sync,light_node,cons_txs_sync + + ; modules_without_bw_limit=raft,pbft,cons_txs_sync + + ; restrict the outgoing bandwidth of the node + ; both integer and decimal is supported for the node, unit: Mb + ; total_outgoing_bw_limit=10 + + ; restrict the outgoing bandwidth of the the connection + ; both integer and decimal is supported for the connection, unit: Mb + ; conn_outgoing_bw_limit=2 + + ; specify IP to limit bandwidth, format: conn_outgoing_bw_limit_x.x.x.x=n + ; conn_outgoing_bw_limit_192.108.0.1=3 + ; conn_outgoing_bw_limit_192.108.0.2=3 + ; conn_outgoing_bw_limit_192.108.0.3=3 + + ; default bandwidth limit for the group + ; group_outgoing_bw_limit=2 + + ; specify group to limit bandwidth, group_outgoing_bw_limit_groupName=n + ; group_outgoing_bw_limit_group0=2 + ; group_outgoing_bw_limit_group1=2 + ; group_outgoing_bw_limit_group2=2 + +[log] + enable=true + log_path=./log + ; info debug trace + level=info + ; MB + max_log_file_size=200 diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.ini.node" "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.ini.node" new file mode 100644 index 00000000..e89bd029 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.ini.node" @@ -0,0 +1,57 @@ +[service] + rpc=chain0 + gateway=chain0 + + ; run without tars framework + ; without_tars_framework = true + ; tars_proxy_conf = conf/tars_proxy.ini + +[security] + private_key_path=conf/node.pem + +[storage_security] + ; enable data disk encryption or not, default is false + enable=false + ; url of the key center, in format of ip:port + ;key_center_url= + ;cipher_data_key= + +[consensus] + ; min block generation time(ms) + min_seal_time=500 + +[storage] + data_path=data + enable_cache=true + type=RocksDB + pd_addrs= + key_page_size=10240 + +[txpool] + ; size of the txpool, default is 15000 + limit=15000 + ; txs notification threads num, default is 2 + notify_worker_num=2 + ; txs verification threads num, default is the number of CPU cores + ;verify_worker_num=2 + ; txs expiration time, in seconds, default is 10 minutes + txs_expiration_time = 600 + +[failover] + ; enable failover or not, default disable + enable = false + ; the uuid that uniquely identifies the node + member_id= + ; failover time, in seconds, default is 3s + lease_ttl=3 + ; the address of etcd, can configure multiple comma-separated + cluster_url=127.0.0.1:2379 + +[log] + enable=true + log_path=./log + ; info debug trace + level=info + ; MB + max_log_file_size=200 + diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.ini.rpc" "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.ini.rpc" new file mode 100644 index 00000000..2af78bfc --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/config.ini.rpc" @@ -0,0 +1,46 @@ +[rpc] + listen_ip=0.0.0.0 + listen_port=20200 + thread_count=4 + sm_ssl=false + ; ssl connection switch, if disable the ssl connection, default: false + ;disable_ssl=true + +[service] + ;gateway=chain0 + +[chain] + ; use SM crypto or not, should never be changed + sm_crypto=false + chain_id=chain0 + +[cert] + ; directory the certificates located in + ca_path=./conf + ; the ca certificate file + ca_cert=ca.crt + ; the node private key file + node_key=ssl.key + ; the node certificate file + node_cert=ssl.crt + +[storage_security] + ; enable data disk encryption or not, default is false + enable=false + ; url of the key center, in format of ip:port + ;key_center_url= + ;cipher_data_key= + +[failover] + ; enable failover or not, default disable + enable = false + ; the address of etcd, can configure multiple comma-separated + cluster_url= "127.0.0.1:2379" + +[log] + enable=true + log_path=./log + ; info debug trace + level=info + ; MB + max_log_file_size=200 \ No newline at end of file diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_gateway.conf" "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_gateway.conf" new file mode 100644 index 00000000..78b90333 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_gateway.conf" @@ -0,0 +1,40 @@ + + + enableset=n + setdivision=NULL + + app=@TARS_APP@ + server=@TARS_SERVER@ + localip=127.0.0.1 + basepath=./conf/ + datapath=./.data/ + logpath=./log/ + logsize=100M + lognum=10 + logLevel=INFO + deactivating-timeout=3000 + activating-timeout=10000 + opencoroutine=0 + coroutinememsize=1G + coroutinestack=128K + closecout=0 + netthread=4 + <@TARS_APP@.@TARS_SERVER@.GatewayServiceObjAdapter> + allow + endpoint=tcp -h @TARS_LISTEN_IP@ -p @TARS_LISTEN_PORT@ -t 60000 + maxconns=100000 + protocol=tars + queuecap=50000 + queuetimeout=20000 + servant=@TARS_APP@.@TARS_SERVER@.GatewayServiceObj + threads=8 + + + + sync-invoke-timeout=3000 + async-invoke-timeout=5000 + asyncthread=8 + modulename=@TARS_APP@.@TARS_SERVER@ + + + diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_node.conf" "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_node.conf" new file mode 100644 index 00000000..477ab1d8 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_node.conf" @@ -0,0 +1,80 @@ + + + enableset=n + setdivision=NULL + + app=@TARS_APP@ + server=@TARS_SERVER@ + localip=127.0.0.1 + basepath=./conf/ + datapath=./.data/ + logpath=./log + logsize=100M + lognum=10 + logLevel=INFO + deactivating-timeout=3000 + activating-timeout=10000 + opencoroutine=0 + coroutinememsize=1G + coroutinestack=128K + closecout=0 + netthread=4 + <@TARS_APP@.@TARS_SERVER@.TxPoolServiceObjAdapter> + allow + endpoint=tcp -h @TARS_LISTEN_IP@ -p @TXPOOL_LISTEN_PORT@ -t 60000 + maxconns=100000 + protocol=tars + queuecap=50000 + queuetimeout=20000 + servant=@TARS_APP@.@TARS_SERVER@.TxPoolServiceObj + threads=8 + + <@TARS_APP@.@TARS_SERVER@.SchedulerServiceObjAdapter> + allow + endpoint=tcp -h @TARS_LISTEN_IP@ -p @SCHEDULER_LISTEN_PORT@ -t 60000 + maxconns=100000 + protocol=tars + queuecap=50000 + queuetimeout=20000 + servant=@TARS_APP@.@TARS_SERVER@.SchedulerServiceObj + threads=8 + + <@TARS_APP@.@TARS_SERVER@.PBFTServiceObjAdapter> + allow + endpoint=tcp -h @TARS_LISTEN_IP@ -p @PBFT_LISTEN_PORT@ -t 60000 + maxconns=100000 + protocol=tars + queuecap=50000 + queuetimeout=20000 + servant=@TARS_APP@.@TARS_SERVER@.PBFTServiceObj + threads=8 + + <@TARS_APP@.@TARS_SERVER@.LedgerServiceObjAdapter> + allow + endpoint=tcp -h @TARS_LISTEN_IP@ -p @LEDGER_LISTEN_PORT@ -t 60000 + maxconns=100000 + protocol=tars + queuecap=50000 + queuetimeout=20000 + servant=@TARS_APP@.@TARS_SERVER@.LedgerServiceObj + threads=8 + + <@TARS_APP@.@TARS_SERVER@.FrontServiceObjAdapter> + allow + endpoint=tcp -h @TARS_LISTEN_IP@ -p @FRONT_LISTEN_PORT@ -t 60000 + maxconns=100000 + protocol=tars + queuecap=50000 + queuetimeout=20000 + servant=@TARS_APP@.@TARS_SERVER@.FrontServiceObj + threads=8 + + + + sync-invoke-timeout=3000 + async-invoke-timeout=5000 + asyncthread=8 + modulename=@TARS_APP@.@TARS_SERVER@ + + + diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_rpc.conf" "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_rpc.conf" new file mode 100644 index 00000000..264b42ad --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_rpc.conf" @@ -0,0 +1,40 @@ + + + enableset=n + setdivision=NULL + + app=@TARS_APP@ + server=@TARS_SERVER@ + localip=127.0.0.1 + basepath=./conf/ + datapath=./.data/ + logpath=./log/ + logsize=100M + lognum=5 + logLevel=INFO + deactivating-timeout=3000 + activating-timeout=10000 + opencoroutine=0 + coroutinememsize=1G + coroutinestack=128K + closecout=0 + netthread=4 + <@TARS_APP@.@TARS_SERVER@.RpcServiceObjAdapter> + allow + endpoint=tcp -h @TARS_LISTEN_IP@ -p @TARS_LISTEN_PORT@ -t 60000 + maxconns=100000 + protocol=tars + queuecap=50000 + queuetimeout=20000 + servant=@TARS_APP@.@TARS_SERVER@.RpcServiceObj + threads=8 + + + + sync-invoke-timeout=3000 + async-invoke-timeout=5000 + asyncthread=8 + modulename=@TARS_APP@.@TARS_SERVER@ + + + diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_start.sh" "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_start.sh" new file mode 100644 index 00000000..b46f96f3 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_start.sh" @@ -0,0 +1,33 @@ +#!/bin/bash +dirpath="$(cd "$(dirname "$0")" && pwd)" +cd ${dirpath} + +service_name='@SERVICE_NAME@' +service=${dirpath}/${service_name} + +pid=$(ps aux|grep ${service}|grep -v grep|awk '{print $2}') +name=$(basename ${dirpath}) + +if [ ! -z ${pid} ];then + echo " ${name} is running, pid is ${pid}." + exit 0 +else + nohup ${service} --config=conf/tars.conf >>nohup.out 2>&1 & + sleep 1.5 +fi + +try_times=4 +i=0 +while [ $i -lt ${try_times} ] +do + pid=$(ps aux|grep ${service}|grep -v grep|awk '{print $2}') + if [[ ! -z ${pid} ]];then + echo -e "\033[32m ${name} start successfully pid=${pid}\033[0m" + exit 0 + fi + sleep 0.5 + ((i=i+1)) +done +echo -e "\033[31m Exceed waiting time. Please try again to start ${name} \033[0m" +tail -n20 nohup.out + diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_start_all.sh" "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_start_all.sh" new file mode 100644 index 00000000..530631fc --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_start_all.sh" @@ -0,0 +1,14 @@ +#!/bin/bash +dirpath="$(cd "$(dirname "$0")" && pwd)" +cd "${dirpath}" + +dirs=($(ls -l ${dirpath} | awk '/^d/ {print $NF}')) +for dir in ${dirs[*]} +do + if [[ -f "${dirpath}/${dir}/conf/config.ini" && -f "${dirpath}/${dir}/start.sh" ]];then + echo "try to start ${dir}" + bash ${dirpath}/${dir}/start.sh & + fi +done +wait + diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_stop.sh" "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_stop.sh" new file mode 100644 index 00000000..ab667e00 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_stop.sh" @@ -0,0 +1,30 @@ +#!/bin/bash +dirpath="$(cd "$(dirname "$0")" && pwd)" +cd ${dirpath} + +service_name='@SERVICE_NAME@' +service=${dirpath}/${service_name} + +pid=$(ps aux|grep ${service}|grep -v grep|awk '{print $2}') + +if [ -z ${pid} ];then + echo " ${service_name} isn't running." + exit 0 +fi + +kill ${pid} > /dev/null + +i=0 +try_times=10 +while [ $i -lt ${try_times} ] +do + sleep 1 + pid=$(ps aux|grep ${service}|grep -v grep|awk '{print $2}') + if [ -z ${pid} ];then + echo -e "\033[32m stop ${service_name} success.\033[0m" + exit 0 + fi + ((i=i+1)) +done +echo " Exceed maximum number of retries. Please try again to stop ${service_name}" +exit 1 diff --git "a/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_stop_all.sh" "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_stop_all.sh" new file mode 100644 index 00000000..10f29789 --- /dev/null +++ "b/BFPL\345\243\271/tools/BcosBuilder/src/tpl/tars_stop_all.sh" @@ -0,0 +1,14 @@ +#!/bin/bash +dirpath="$(cd "$(dirname "$0")" && pwd)" +cd "${dirpath}" + +dirs=($(ls -l ${dirpath} | awk '/^d/ {print $NF}')) +for dir in ${dirs[*]} +do + if [[ -f "${dirpath}/${dir}/conf/config.ini" && -f "${dirpath}/${dir}/stop.sh" ]];then + echo "try to stop ${dir}" + bash ${dirpath}/${dir}/stop.sh + fi +done +wait + diff --git "a/BFPL\345\243\271/tools/template/Dashboard.json" "b/BFPL\345\243\271/tools/template/Dashboard.json" new file mode 100644 index 00000000..644a7346 --- /dev/null +++ "b/BFPL\345\243\271/tools/template/Dashboard.json" @@ -0,0 +1,1084 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "A beta version of the HLF 1.4 Monitoring Dashboard.", + "editable": true, + "gnetId": 10716, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 18, + "panels": [], + "title": "Fisco Bcos Metrics", + "type": "row" + }, + { + "datasource": null, + "description": "Height of the chain in blocks. ", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": {}, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "index": 0, + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#73BF69", + "value": null + }, + { + "color": "#FADE2A", + "value": 500 + }, + { + "color": "#d44a3a", + "value": 800 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 0, + "y": 1 + }, + "id": 36, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "/^Value$/", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.3.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "IeXF6xX7z" + }, + "exemplar": false, + "expr": "ledger_block_height{node=\"node0\"}", + "format": "table", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "title": "区块高度", + "type": "stat" + }, + { + "datasource": null, + "description": "p2p当前活跃会话数", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": {}, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#73BF69", + "value": null + }, + { + "color": "#FADE2A", + "value": 1000 + }, + { + "color": "#EAB839", + "value": 1010 + } + ] + }, + "unit": "string" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 5, + "y": 1 + }, + "id": 39, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "/^Value$/", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.3.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "IeXF6xX7z" + }, + "exemplar": false, + "expr": "p2p_session_actived{node=\"node0\"}", + "format": "table", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "title": "p2p当前活跃会话数", + "type": "stat" + }, + { + "datasource": null, + "description": "Number of transactions processed.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": {}, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#73BF69", + "value": null + }, + { + "color": "#FADE2A", + "value": 500 + }, + { + "color": "#d44a3a", + "value": 800 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 11, + "y": 1 + }, + "id": 40, + "links": [], + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "allValues" + ], + "fields": "/^Value$/", + "values": true + }, + "textMode": "auto" + }, + "pluginVersion": "7.3.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "IeXF6xX7z" + }, + "exemplar": false, + "expr": "txpool_pending_tx_size{node=\"node0\"}", + "format": "table", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "title": "待打包的交易数量", + "type": "stat" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "Height of the chain in blocks. ", + "fieldConfig": { + "defaults": { + "custom": {}, + "unit": "short" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 1, + "gridPos": { + "h": 6, + "w": 5, + "x": 0, + "y": 5 + }, + "hiddenSeries": false, + "id": 38, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "hideEmpty": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "maxDataPoints": 100, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "IeXF6xX7z" + }, + "exemplar": true, + "expr": "ledger_block_height{}", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "区块高度", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:268", + "format": "short", + "logBase": 1, + "show": true + }, + { + "$$hashKey": "object:269", + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "The number of proposals received.", + "fieldConfig": { + "defaults": { + "custom": {}, + "unit": "none" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 6, + "x": 5, + "y": 5 + }, + "hiddenSeries": false, + "id": 19, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "maxDataPoints": 100, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "IeXF6xX7z" + }, + "exemplar": true, + "expr": "p2p_session_actived{}", + "format": "time_series", + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "p2p当前活跃会话数", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:833", + "format": "none", + "label": "", + "logBase": 1, + "show": true + }, + { + "$$hashKey": "object:834", + "format": "short", + "logBase": 1, + "show": false + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "Number of transactions processed.", + "fieldConfig": { + "defaults": { + "custom": {}, + "unit": "none" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 6, + "x": 11, + "y": 5 + }, + "hiddenSeries": false, + "id": 37, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "maxDataPoints": 100, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "IeXF6xX7z" + }, + "exemplar": true, + "expr": "txpool_pending_tx_size{}", + "format": "time_series", + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "待打包的交易数量", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1024", + "format": "none", + "logBase": 1, + "show": true + }, + { + "$$hashKey": "object:1025", + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "Number of transactions processed.", + "fieldConfig": { + "defaults": { + "custom": {}, + "unit": "none" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 11 + }, + "hiddenSeries": false, + "id": 42, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "maxDataPoints": 100, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "IeXF6xX7z" + }, + "exemplar": true, + "expr": "block_exec_duration_milliseconds_gauge{}", + "format": "time_series", + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "节点区块执行时间", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1024", + "format": "none", + "logBase": 1, + "show": true + }, + { + "$$hashKey": "object:1025", + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "Height of the chain in blocks. ", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 9, + "x": 8, + "y": 11 + }, + "hiddenSeries": false, + "id": 41, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "maxDataPoints": 100, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "IeXF6xX7z" + }, + "exemplar": true, + "expr": "block_commit_duration_milliseconds_gauge", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "节点区块提交时间", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "ledger_block_exec_duration_bucket", + "fieldConfig": { + "defaults": { + "custom": {}, + "unit": "none" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 19 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": false, + "linewidth": 1, + "links": [], + "maxDataPoints": 100, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "IeXF6xX7z" + }, + "exemplar": false, + "expr": "block_exec_duration_milliseconds_bucket", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{le}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "节点区块执行时间", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": 5, + "max": 10, + "min": 0, + "mode": "series", + "name": null, + "show": false, + "values": [ + "total" + ] + }, + "yaxes": [ + { + "$$hashKey": "object:980", + "format": "none", + "label": "个数", + "logBase": 1, + "show": true + }, + { + "$$hashKey": "object:981", + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "ledger_block_commit_duration_bucket", + "fieldConfig": { + "defaults": { + "custom": {}, + "unit": "none" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 9, + "x": 8, + "y": 19 + }, + "hiddenSeries": false, + "id": 11, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": false, + "linewidth": 1, + "links": [], + "maxDataPoints": 100, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "IeXF6xX7z" + }, + "exemplar": false, + "expr": "block_commit_duration_milliseconds_bucket", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{le}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "节点区块提交时间", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "series", + "name": null, + "show": false, + "values": [ + "current" + ] + }, + "yaxes": [ + { + "$$hashKey": "object:1046", + "format": "none", + "label": "个数", + "logBase": 1, + "show": true + }, + { + "$$hashKey": "object:1047", + "format": "short", + "label": "", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + } + ], + "refresh": "", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "", + "title": "Fisco Bcos Monitoring for 3.0.0", + "uid": "pUnN6JgWz", + "version": 1 +} \ No newline at end of file diff --git "a/BFPL\345\243\271/vcpkg-configuration.json" "b/BFPL\345\243\271/vcpkg-configuration.json" new file mode 100644 index 00000000..eebd2ce0 --- /dev/null +++ "b/BFPL\345\243\271/vcpkg-configuration.json" @@ -0,0 +1,21 @@ +{ + "registries": [ + { + "kind": "git", + "repository": "https://github.com/FISCO-BCOS/registry", + "baseline": "d05858c7a8159c18c37ce96aca65cef207aceb6a", + "packages": [ + "openssl", + "evmone", + "evmc", + "tarscpp", + "ethash", + "intx", + "fiscobcos", + "etcd-cpp-apiv3", + "boost-context", + "wedprcrypto" + ] + } + ] +} diff --git "a/BFPL\345\243\271/vcpkg.json" "b/BFPL\345\243\271/vcpkg.json" new file mode 100644 index 00000000..60ddc86b --- /dev/null +++ "b/BFPL\345\243\271/vcpkg.json" @@ -0,0 +1,112 @@ +{ + "name": "fiscobcos", + "version-string": "3.1.0", + "homepage": "https://github.com/FISCO-BCOS/FISCO-BCOS", + "description": "FISCO BCOS", + "dependencies": [ + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + }, + "boost-log", + "boost-beast", + "boost-uuid", + "boost-heap", + "boost-graph", + "boost-property-map", + "boost-chrono", + "boost-iostreams", + "boost-thread", + "boost-test", + "boost-multiprecision", + "boost-program-options", + "ms-gsl", + "zstd", + "tbb", + "zlib", + "redis-plus-plus", + "jsoncpp", + "protobuf", + "cryptopp", + { + "name": "tarscpp", + "version>=": "3.0.3-m" + }, + { + "name": "openssl", + "version>=": "1.1.1-tassl" + }, + { + "name": "boost-context", + "version>=": "1.79.0-m1" + }, + "wedprcrypto", + "range-v3" + ], + "features": { + "fullnode": { + "description": "Full node dependencies", + "dependencies": [ + "evmone", + "boost-coroutine2", + { + "name": "rocksdb", + "features": [ + "zstd" + ] + }, + "zstd" + ] + }, + "lightnode": { + "description": "Light node dependencies", + "dependencies": [ + "evmone", + { + "name": "rocksdb", + "features": [ + "zstd" + ] + } + ] + }, + "etcd": { + "description": "ETCD dependencies", + "dependencies": [ + "etcd-cpp-apiv3", + { + "name": "grpc", + "version>=": "1.44.0" + } + ] + }, + "tcmalloc": { + "description": "tcmalloc dependencies", + "dependencies": [ + { + "name": "gperftools", + "features": [ + "override" + ] + } + ] + }, + "jemalloc": { + "description": "jemalloc dependencies", + "dependencies": [ + "jemalloc" + ] + }, + "mimalloc": { + "description": "mimalloc dependencies", + "dependencies": [ + "mimalloc" + ] + } + }, + "builtin-baseline": "7e3dcf74e37034eea358934a90a11d618520e139" +} \ No newline at end of file -- Gitee